summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changes/VERSION_COMPAT1
-rw-r--r--changes/bug-3914_unhandled-error-on-version-flag1
-rw-r--r--changes/bug_3926_connection-aborted2
-rw-r--r--changes/feature-2858_refactor-vpnlaunchers2
-rw-r--r--changes/feature-3981_cleanlooks-for-bundle-only2
-rw-r--r--changes/feature_3965_soledad-bootstrap-error-handling2
-rw-r--r--changes/feature_provider-check-against-ca-bundle2
-rw-r--r--data/ts/en_US.ts668
-rw-r--r--docs/conf.py28
-rw-r--r--docs/dev/workflow.rst32
-rw-r--r--docs/release_checklist.wiki24
-rw-r--r--docs/testers/howto.rst132
-rw-r--r--docs/user/install.rst13
-rwxr-xr-xpkg/linux/build_bundle.sh12
-rw-r--r--pkg/requirements-docs.pip1
-rw-r--r--setup.cfg8
-rw-r--r--src/leap/bitmask/app.py20
-rw-r--r--src/leap/bitmask/gui/mainwindow.py37
-rw-r--r--src/leap/bitmask/gui/statemachines.py16
-rw-r--r--src/leap/bitmask/gui/wizard.py7
-rw-r--r--src/leap/bitmask/platform_init/initializers.py10
-rw-r--r--src/leap/bitmask/provider/providerbootstrapper.py (renamed from src/leap/bitmask/services/eip/providerbootstrapper.py)68
-rw-r--r--src/leap/bitmask/provider/tests/__init__.py0
-rw-r--r--src/leap/bitmask/provider/tests/test_providerbootstrapper.py (renamed from src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py)12
-rw-r--r--src/leap/bitmask/services/connections.py1
-rw-r--r--src/leap/bitmask/services/eip/__init__.py27
-rw-r--r--src/leap/bitmask/services/eip/connection.py1
-rw-r--r--src/leap/bitmask/services/eip/darwinvpnlauncher.py190
-rw-r--r--src/leap/bitmask/services/eip/eipbootstrapper.py1
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py232
-rw-r--r--src/leap/bitmask/services/eip/vpnlauncher.py290
-rw-r--r--src/leap/bitmask/services/eip/vpnlaunchers.py963
-rw-r--r--src/leap/bitmask/services/eip/vpnprocess.py4
-rw-r--r--src/leap/bitmask/services/eip/windowsvpnlauncher.py69
-rw-r--r--src/leap/bitmask/services/soledad/soledadbootstrapper.py335
-rw-r--r--src/leap/bitmask/util/__init__.py14
-rw-r--r--src/leap/bitmask/util/constants.py2
37 files changed, 1752 insertions, 1477 deletions
diff --git a/changes/VERSION_COMPAT b/changes/VERSION_COMPAT
index cc00ecf7..425478c8 100644
--- a/changes/VERSION_COMPAT
+++ b/changes/VERSION_COMPAT
@@ -8,3 +8,4 @@
#
# BEGIN DEPENDENCY LIST -------------------------
# leap.foo.bar>=x.y.z
+leap.common >= 0.3.4 # because the ca_bundle
diff --git a/changes/bug-3914_unhandled-error-on-version-flag b/changes/bug-3914_unhandled-error-on-version-flag
new file mode 100644
index 00000000..41e023a0
--- /dev/null
+++ b/changes/bug-3914_unhandled-error-on-version-flag
@@ -0,0 +1 @@
+ o Avoid error message if --version flag is used. Closes #3914.
diff --git a/changes/bug_3926_connection-aborted b/changes/bug_3926_connection-aborted
new file mode 100644
index 00000000..58e6fe11
--- /dev/null
+++ b/changes/bug_3926_connection-aborted
@@ -0,0 +1,2 @@
+ o Fix a bug in which failing to authenticate properly left connection
+ in an unconsistent state. Closes: #3926
diff --git a/changes/feature-2858_refactor-vpnlaunchers b/changes/feature-2858_refactor-vpnlaunchers
new file mode 100644
index 00000000..27106f7a
--- /dev/null
+++ b/changes/feature-2858_refactor-vpnlaunchers
@@ -0,0 +1,2 @@
+ o Refactor vpn launchers, reuse code, improve implementations, update
+ documentation. Closes #2858.
diff --git a/changes/feature-3981_cleanlooks-for-bundle-only b/changes/feature-3981_cleanlooks-for-bundle-only
new file mode 100644
index 00000000..c762b8f3
--- /dev/null
+++ b/changes/feature-3981_cleanlooks-for-bundle-only
@@ -0,0 +1,2 @@
+ o Force cleanlooks style for kde only if the app is running from bundle.
+ Closes #3981.
diff --git a/changes/feature_3965_soledad-bootstrap-error-handling b/changes/feature_3965_soledad-bootstrap-error-handling
new file mode 100644
index 00000000..d9f16378
--- /dev/null
+++ b/changes/feature_3965_soledad-bootstrap-error-handling
@@ -0,0 +1,2 @@
+ o Improve error handling during soledad bootstrap. Closes: #3965.
+ Affects: #3619, #3867, #3966
diff --git a/changes/feature_provider-check-against-ca-bundle b/changes/feature_provider-check-against-ca-bundle
new file mode 100644
index 00000000..b3f9042f
--- /dev/null
+++ b/changes/feature_provider-check-against-ca-bundle
@@ -0,0 +1,2 @@
+ o Make the initial provider cert verifications against our modified
+ CA-bundle (includes ca-cert certificates, for now). Closes: #3850
diff --git a/data/ts/en_US.ts b/data/ts/en_US.ts
index 250b58ce..224060f4 100644
--- a/data/ts/en_US.ts
+++ b/data/ts/en_US.ts
@@ -1,17 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1">
<context>
- <name>EIPBootstrapper</name>
+ <name>DarwinVPNLauncher</name>
<message>
- <location filename="../src/leap/services/eip/eipbootstrapper.py" line="151"/>
- <source>The downloaded certificate is not a valid PEM file</source>
+ <location filename="../src/leap/bitmask/services/eip/vpnlaunchers.py" line="691"/>
+ <source>No gateway was found!</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
+ <name>EIPBootstrapper</name>
+</context>
+<context>
<name>LinuxVPNLauncher</name>
<message>
- <location filename="../src/leap/services/eip/vpnlaunchers.py" line="380"/>
+ <location filename="../src/leap/bitmask/services/eip/vpnlaunchers.py" line="435"/>
<source>No gateway was found!</source>
<translation type="unfinished"></translation>
</message>
@@ -19,292 +22,337 @@
<context>
<name>LoggerWindow</name>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="14"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="14"/>
<source>Logs</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="29"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="49"/>
<source>Debug</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="49"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="69"/>
<source>Info</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="69"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="89"/>
<source>Warning</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="89"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="109"/>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="109"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="129"/>
<source>Critical</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/loggerwindow.ui" line="129"/>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="149"/>
<source>Save to file</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/loggerwindow.py" line="113"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="148"/>
<source>Save As</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="24"/>
+ <source>Filter by:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/loggerwindow.ui" line="34"/>
+ <source>Case Insensitive</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>LoginWidget</name>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="14"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="49"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="49"/>
<source>Create a new account</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="56"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="56"/>
<source>&lt;b&gt;Provider:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="76"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="76"/>
<source>Remember username and password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="83"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="83"/>
<source>&lt;b&gt;Username:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="93"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="93"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/login.ui" line="103"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="217"/>
<source>Log In</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>MainWindow</name>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="14"/>
- <source>LEAP</source>
+ <location filename="../src/leap/bitmask/gui/login.py" line="107"/>
+ <source>Other...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/login.py" line="213"/>
+ <source>Cancel</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>MainWindow</name>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="62"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="62"/>
<source>There are new updates available, please restart.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="78"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="78"/>
<source>More...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="233"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="243"/>
<source>Show Log</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="261"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="271"/>
<source>&amp;Session</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="521"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="617"/>
<source>Help</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="281"/>
- <source>&amp;Sign out</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="296"/>
+ <source>&amp;Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="286"/>
- <source>&amp;Quit</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="306"/>
+ <source>&amp;Help</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="291"/>
- <source>About &amp;LEAP</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="311"/>
+ <source>&amp;Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="296"/>
- <source>&amp;Help</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="316"/>
+ <source>Show &amp;logs</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="301"/>
- <source>&amp;Wizard</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="667"/>
+ <source>Hide Main Window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/mainwindow.ui" line="306"/>
- <source>Show &amp;logs</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="493"/>
+ <source> The following components will be updated:
+%s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="230"/>
- <source>No default provider</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="496"/>
+ <source>Updates available</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="233"/>
- <source>Encrypted internet is OFF</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="262"/>
+ <source>Preferences</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1051"/>
- <source>Turn ON</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="666"/>
+ <source>Show Main Window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="553"/>
- <source>Hide Main Window</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="856"/>
+ <source>Please select a valid provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="391"/>
- <source>The LEAPClient app is ready to update, please restart the application.</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="861"/>
+ <source>Please provide a valid username</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="400"/>
- <source> The following components will be updated:
-%s</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="866"/>
+ <source>Please provide a valid Password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="403"/>
- <source>Updates available</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="869"/>
+ <source>Logging in...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="519"/>
- <source>Preferences</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1265"/>
+ <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/gui/mainwindow.py" line="552"/>
- <source>Show Main Window</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1277"/>
+ <source>We could not find &lt;b&gt;pkexec&lt;/b&gt; in your system.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="606"/>
- <source>About LEAP - %s</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1282"/>
+ <source>We could not find openvpn binary.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="614"/>
- <source>version: &lt;b&gt;%s&lt;/b&gt;&lt;br&gt;LEAP is a non-profit dedicated to giving all internet users access to secure communication. Our focus is on adapting encryption technology to make it easy to use and widely available. &lt;a href=&quot;https://leap.se&quot;&gt;More about LEAP&lt;/a&gt;</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1342"/>
+ <source>OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="716"/>
- <source>Could not load provider configuration.</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1359"/>
+ <source>Starting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="746"/>
- <source>Please select a valid provider</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1367"/>
+ <source>Not supported</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="751"/>
- <source>Please provide a valid username</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1370"/>
+ <source>Disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="756"/>
- <source>Please provide a valid Password</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1407"/>
+ <source>Could not load Encrypted Internet Configuration.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="759"/>
- <source>Logging in...</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1540"/>
+ <source>Encrypted Internet could not be launched because you did not authenticate properly.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="995"/>
- <source>Turn OFF</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1546"/>
+ <source>Encrypted Internet finished in an unexpected manner!</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1009"/>
- <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>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="14"/>
+ <source>Bitmask</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1015"/>
- <source>We could not find &lt;b&gt;pkexec&lt;/b&gt; in your system.</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="291"/>
+ <source>Log &amp;out</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1020"/>
- <source>We could not find openvpn binary.</source>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="301"/>
+ <source>About &amp;Bitmask</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1048"/>
- <source>OFF</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="255"/>
+ <source>Mail is OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1100"/>
- <source>Starting...</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="484"/>
+ <source>The Bitmask app is ready to update, please restart the application.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1108"/>
- <source>Not supported</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="624"/>
+ <source>Encrypted Internet is OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1111"/>
- <source>Disabled</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="729"/>
+ <source>About Bitmask - %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1139"/>
- <source>Could not load Encrypted Internet Configuration.</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="743"/>
+ <source>Version: &lt;b&gt;%s&lt;/b&gt;&lt;br&gt;&lt;br&gt;Bitmask is the Desktop client application for the LEAP platform, supporting encrypted internet proxy, secure email, and secure chat (coming soon).&lt;br&gt;&lt;br&gt;LEAP is a non-profit dedicated to giving all internet users access to secure communication. Our focus is on adapting encryption technology to make it easy to use and widely available. &lt;br&gt;&lt;br&gt;&lt;a href=&apos;https://leap.se&apos;&gt;More about LEAP&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1239"/>
- <source>Encrypted Internet could not be launched because you did not authenticate properly.</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="830"/>
+ <source>Unable to login: Problem with provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/mainwindow.py" line="1245"/>
- <source>Encrypted Internet finished in an unexpected manner!</source>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="907"/>
+ <source>Log in cancelled by the user.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1272"/>
+ <source>Encrypted Internet cannot be started because the tuntap extension is not installed properly in your system.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1288"/>
+ <source>Another openvpn instance is already running, and could not be stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1295"/>
+ <source>Another openvpn instance is already running, and could not be stopped because it was not launched by Bitmask. Please stop it and try again.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1385"/>
+ <source>There was a problem with the provider</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1469"/>
+ <source>Something went wrong with the logout.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1504"/>
+ <source>Unable to connect: Problem with provider</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ProviderBootstrapper</name>
<message>
- <location filename="../src/leap/services/eip/providerbootstrapper.py" line="113"/>
+ <location filename="../src/leap/bitmask/services/eip/providerbootstrapper.py" line="121"/>
<source>Provider certificate could not be verified</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/services/eip/providerbootstrapper.py" line="116"/>
+ <location filename="../src/leap/bitmask/services/eip/providerbootstrapper.py" line="124"/>
<source>Provider does not support HTTPS</source>
<translation type="unfinished"></translation>
</message>
@@ -312,7 +360,7 @@
<context>
<name>SRPAuth</name>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="598"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="690"/>
<source>Succeeded</source>
<translation type="unfinished"></translation>
</message>
@@ -320,516 +368,604 @@
<context>
<name>StatusPanel</name>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="14"/>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="14"/>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="23"/>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="23"/>
<source>user@domain.org</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="44"/>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="326"/>
<source>Encrypted Internet: </source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="54"/>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="336"/>
<source>Off</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="83"/>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="365"/>
<source>Turn On</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="178"/>
- <source>0.0 Kb</source>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="311"/>
+ <source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/statuspanel.ui" line="209"/>
- <source>...</source>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="44"/>
+ <source>0 Unread Emails</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="54"/>
+ <source>Disabled</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="67"/>
+ <source>Encrypted Mail:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/statuspanel.ui" line="198"/>
+ <source>0.0 KB/s</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>StatusPanelWidget</name>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="180"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="389"/>
<source>Turn OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="190"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="403"/>
<source>Turn ON</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="232"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="699"/>
<source>ON</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="236"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="461"/>
<source>Authenticating...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="238"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="463"/>
<source>Retrieving configuration...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="240"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="465"/>
<source>Waiting to start...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="242"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="467"/>
<source>Assigning IP</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="250"/>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="478"/>
<source>Unable to start VPN, it&apos;s already running.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="264"/>
- <source>Encryption is OFF</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="270"/>
+ <source>All services are OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="269"/>
- <source>Turning ON</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="276"/>
+ <source>Encrypted Internet is {0}</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/statuspanel.py" line="271"/>
- <source>Encryption is ON</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="278"/>
+ <source>Mail is {0}</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>Wizard</name>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="14"/>
- <source>LEAP First run</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="469"/>
+ <source>Reconnecting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="31"/>
- <source>Welcome</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="501"/>
+ <source>Encrypted Internet is OFF</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="506"/>
+ <source>Encrypted Internet is STARTING</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="508"/>
+ <source>Encrypted Internet is ON</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="534"/>
+ <source>OFF</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="536"/>
+ <source>Mail is OFF</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="542"/>
+ <source>Mail is ON</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="34"/>
- <source>This is the LEAP Client first run wizard</source>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="604"/>
+ <source>Starting...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="571"/>
+ <source>Soledad has started...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="573"/>
+ <source>Soledad is starting, please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="610"/>
+ <source>Looking for key for this user</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="614"/>
+ <source>Found key! Starting mail...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="616"/>
+ <source>Generating new key, please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="618"/>
+ <source>Finished generating key!</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="620"/>
+ <source>Starting mail...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="650"/>
+ <source>SMTP has started...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="656"/>
+ <source>SMTP failed to start, check the logs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="693"/>
+ <source>Failed</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="687"/>
+ <source>IMAP has started...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="693"/>
+ <source>IMAP failed to start, check the logs.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/statuspanel.py" line="697"/>
+ <source>%s Unread Emails</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>WindowsVPNLauncher</name>
+ <message>
+ <location filename="../src/leap/bitmask/services/eip/vpnlaunchers.py" line="871"/>
+ <source>No gateway was found!</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Wizard</name>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="43"/>
+ <source>Welcome</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="43"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="55"/>
<source>Log In with my credentials</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="50"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="62"/>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;/p&gt;&lt;p&gt;If you ever need to modify these options again, you can find the wizard in the &lt;span style=&quot; font-style:italic;&quot;&gt;&apos;Settings&apos;&lt;/span&gt; menu from the main window.&lt;/p&gt;&lt;p&gt;Do you want to &lt;span style=&quot; font-weight:600;&quot;&gt;sign up&lt;/span&gt; for a new account, or &lt;span style=&quot; font-weight:600;&quot;&gt;log in&lt;/span&gt; with an already existing username?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="63"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="75"/>
<source>Sign up for a new account</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="100"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="112"/>
<source>Provider selection</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="103"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="115"/>
<source>Please enter the domain of the provider you want to use for your connection</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="131"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="143"/>
<source>Check</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="151"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="163"/>
<source>https://</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="161"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="173"/>
<source>Checking for a valid provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="167"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="179"/>
<source>Getting provider information</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="174"/>
- <source>Can we stablish a secure connection?</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="247"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="259"/>
<source>Can we reach this provider?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="278"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="290"/>
<source>Provider Information</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="281"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="293"/>
<source>Description of services offered by this provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="290"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="302"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="322"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="334"/>
<source>Desc</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="332"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="344"/>
<source>&lt;b&gt;Services offered:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="342"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="354"/>
<source>services</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="362"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="374"/>
<source>&lt;b&gt;Enrollment policy:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="372"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="384"/>
<source>policy</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="392"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="404"/>
<source>&lt;b&gt;URL:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="402"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="414"/>
<source>URL</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="409"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="421"/>
<source>&lt;b&gt;Description:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="420"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="432"/>
<source>Provider setup</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="423"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="435"/>
<source>Gathering configuration options for this provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="445"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="457"/>
<source>We are downloading some bits that we need to establish a secure connection with the provider for the first time.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="468"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="480"/>
<source>Setting up provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="518"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="530"/>
<source>Getting info from the Certificate Authority</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="525"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="537"/>
<source>Do we trust this Certificate Authority?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="532"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="544"/>
<source>Establishing a trust relationship with this provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="591"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="603"/>
<source>Register new user</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="594"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="606"/>
<source>Register a new user with provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="609"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="621"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="628"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="640"/>
<source>&lt;b&gt;Re-enter password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="638"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="650"/>
<source>Register</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="671"/>
- <source>&lt;b&gt;User:&lt;/b&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="684"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="696"/>
<source>Remember my username and password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="708"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="720"/>
<source>Service selection</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="711"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="723"/>
<source>Please select the services you would like to have</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="733"/>
- <source>Congratulations!</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/ui/wizard.ui" line="736"/>
- <source>You have successfully configured the LEAP Client.</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="86"/>
- <source>Encrypted Internet</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="88"/>
- <source>Encrypted Mail</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="92"/>
- <source>(will need admin password to start)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="155"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="141"/>
<source>&amp;Next &gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="157"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="143"/>
<source>Connect</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="214"/>
- <source>Passwords don&apos;t match</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="217"/>
- <source>Password too short</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="220"/>
- <source>Password too easy</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="223"/>
- <source>Password equal to username</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/gui/wizard.py" line="252"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="204"/>
<source>Starting registration...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="281"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="235"/>
<source>User %s successfully registered.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="301"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="255"/>
<source>Unknown error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="407"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="361"/>
<source>&lt;font color=&apos;red&apos;&gt;&lt;b&gt;Non-existent provider&lt;/b&gt;&lt;/font&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="425"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="379"/>
<source>&lt;font color=&apos;red&apos;&gt;&lt;b&gt;%s&lt;/b&gt;&lt;/font&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="453"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="407"/>
<source>Unable to load provider configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="459"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="413"/>
<source>&lt;font color=&apos;red&apos;&gt;&lt;b&gt;Not a valid provider&lt;/b&gt;&lt;/font&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="525"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="479"/>
<source>Services by %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="544"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="499"/>
<source>Something went wrong while trying to load service %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="561"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="516"/>
<source>Gathering configuration options for %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="570"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="525"/>
<source>Description of services offered by %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/gui/wizard.py" line="591"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="546"/>
<source>Register a new user with %s</source>
<translation type="unfinished"></translation>
</message>
-</context>
-<context>
- <name>__impl</name>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="259"/>
- <source>Unknown user</source>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="26"/>
+ <source>Bitmask first run</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="46"/>
+ <source>This is the Bitmask first run wizard</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="186"/>
+ <source>Can we establish a secure connection?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="271"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="683"/>
+ <source>&lt;b&gt;Username:&lt;/b&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>__impl</name>
+ <message>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="281"/>
<source>The server did not send the salt parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="275"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="285"/>
<source>The server did not send the B parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="307"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="317"/>
<source>The data sent from the server had errors</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="329"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="339"/>
<source>Could not connect to the server</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="348"/>
- <source>Wrong password</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/crypto/srpauth.py" line="355"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="365"/>
<source>Unknown error (%s)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="386"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="396"/>
<source>Problem getting data from server</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="410"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="422"/>
<source>Bad data from server</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="417"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="429"/>
<source>Auth verification failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="425"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="437"/>
<source>Session cookie verification failed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/crypto/srpauth.py" line="262"/>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="272"/>
<source>There was a problem with authentication</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/crypto/srpauth.py" line="178"/>
+ <source>Invalid username or password.</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>msg</name>
<message>
- <location filename="../src/leap/platform_init/initializers.py" line="89"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="91"/>
<source>Missing up/down scripts</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/platform_init/initializers.py" line="175"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="182"/>
<source>TAP Driver</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/platform_init/initializers.py" line="299"/>
- <source>LEAPClient needs to install the necessary drivers for Encrypted Internet to work. Would you like to proceed?</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/platform_init/initializers.py" line="182"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="189"/>
<source>Encrypted Internet uses VPN, which needs a TAP device installed and none has been found. This will ask for administrative privileges.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/platform_init/initializers.py" line="296"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="308"/>
<source>TUN Driver</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/platform_init/initializers.py" line="304"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="316"/>
<source>Encrypted Internet uses VPN, which needs a kernel extension for a TUN device installed, and none has been found. This will ask for administrative privileges.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="130"/>
+ <source>Problem installing files</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="131"/>
+ <source>Some of the files could not be copied.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="311"/>
+ <source>Bitmask needs to install the necessary drivers for Encrypted Internet to work. Would you like to proceed?</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
</TS>
diff --git a/docs/conf.py b/docs/conf.py
index 3c908b2c..3cfd0b5d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -11,7 +11,33 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
-import sys, os
+import sys
+import os
+
+
+class Mock(object):
+ def __init__(self, *args, **kwargs):
+ pass
+
+ def __call__(self, *args, **kwargs):
+ return Mock()
+
+ @classmethod
+ def __getattr__(cls, name):
+ if name in ('__file__', '__path__'):
+ return '/dev/null'
+ elif name[0] == name[0].upper():
+ mockType = type(name, (), {})
+ mockType.__module__ = __name__
+ return mockType
+ else:
+ return Mock()
+
+MOCK_MODULES = ['pysqlite', 'pyopenssl', 'pycryptopp', 'pysqlcipher']
+ #'google.protobuf'
+
+for mod_name in MOCK_MODULES:
+ sys.modules[mod_name] = Mock()
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
diff --git a/docs/dev/workflow.rst b/docs/dev/workflow.rst
index 1e3bd4af..abd228c1 100644
--- a/docs/dev/workflow.rst
+++ b/docs/dev/workflow.rst
@@ -27,11 +27,14 @@ Git flow
--------
We are basing our workflow on what is described in `A successful git branching model <http://nvie.com/posts/a-successful-git-branching-model/>`_.
-.. image:: https://leap.se/code/attachments/13/git-branching-model.png
+.. image:: https://downloads.leap.se/pics/git-branching-model.png
-The author of the aforementioned post has also a handy pdf version of it: `branching_model.pdf`_
+Vincent Driessen, the author of the aforementioned post has also a handy pdf version of it: `branching_model.pdf`_
-However, we use a setup in which each developer maintains her own feature branch in her private repo. After a code review, this feature branch is rebased onto the authoritative integration branch. Thus, the leapcode repo in leap.se (mirrored in github) only maintains the master and develop branches.
+However, we use a slightly modified setup in which each developer maintains her
+own feature branch in her private repo. After a code review, this feature branch
+is rebased onto the authoritative integration branch. Thus, the leapcode repo in
+leap.se (mirrored in github) only maintains the master and develop branches.
A couple of tools that help to follow this process are `git-flow`_ and `git-sweep`_.
@@ -50,30 +53,33 @@ All code ready to be merged into the integration branch is expected to:
Using Github
------------
-Particularly for the Bitmask client, we are using Github. So you should fork the repo from `github`_ . Depending on what kind of work you are going to do (bug or feature) you should create a branch with the following name:
+Particularly for the Bitmask client, we are using Github. So you should fork the repo from `github`_ . Depending on what kind of work you are going to do (bug or feature) you should **create a branch** with the following name:
-`bug/some_descriptive_text`
+``bug/some_descriptive_text``
or
-`feature/some_descriptive_text`
+``feature/some_descriptive_text``
-Do your work there, push it, and create a pull request against the develop branch in the leapcode owned repo. Either you should post the pull request in `#leap-dev` at `Freenode` or we will just notice it when it's created.
+Do your work there, push it, and create a pull request against the develop branch in the main repo (the one owned by leapcode). Now you should wait until we see it, or you can try also posting your pull request in ``#leap-dev`` at `freenode <https://freenode.net>`_.
-Your code will get reviewed/discussed by someone else on the team, and say that you need to make some changes. What you would do is the following:
+Your code will get reviewed/discussed by someone else on the team. In case that you need to make some changes, you would do the following::
git checkout <your branch>
- # edit what you need here ...
+*Edit what you need here ...*
+
+Simple commit, this doesn't need a good commit message::
- # Simple commit, this doesn't need a good commit message
git commit -avm "Fix"
- # This will help you reorder your commits and squash them (so that the
- # final commit list has good representative messages)
+This will help you reorder your commits and squash them (so that the
+final commit list has good representative messages)::
+
git rebase -i develop
- # Since you've rewritten your history, you'll need a force push
+Since you've rewritten your history, you'll need a force push::
+
git push <your remote> +<your branch>
This will update your pull request automatically, but it won't notify us about the update, so you should add a comment saying so, or re-pingthe reviewer.
diff --git a/docs/release_checklist.wiki b/docs/release_checklist.wiki
index b46eb1f6..fc99fdf0 100644
--- a/docs/release_checklist.wiki
+++ b/docs/release_checklist.wiki
@@ -12,18 +12,22 @@
* [ ] git fetch origin
* [ ] git tag -l, and see the latest tagged version (unless it's not a minor version bump, in which case, just bump to it)
* [ ] Checkout release-X.Y.Z (locally, never pushed)
- * [ ] Fold in changes files into the CHANGELOG
- - NOTE: For leap.soledad, the CHANGELOG entries should be divided per package (common, client, server). See older releases for reference.
- - Helper bash line: for i in $(ls changes); do cat changes/$i; echo; done
- * [ ] Update relnotes.txt if needed.
- * [ ] git rm changes/*
+ * [ ] Update relnotes.txt in leap.bitmask if needed.
+
* [ ] Review pkg/requirements.pip for everything and update if needed (that's why the order).
- - See whatever has been introduced in changes/VERSION_COMPAT
- - Reset changes/VERSION_COMPAT
+ - See whatever has been introduced in changes/VERSION_COMPAT
+ - Reset changes/VERSION_COMPAT
* [ ] git commit -av # we should add a commit message here...
- * [ ] git checkout master && git pull origin master && git merge --no-ff release-X.Y.Z && git push origin master
- * [ ] git tag -s X.Y.Z -m "Tag <package> version X.Y.Z" # (note the -s so that it's a signed tag and -m to specify the message for the tag)
- * [ ] git push origin X.Y.Z
+
+ * [ ] Fold in changes files into the CHANGELOG
+ - NOTE: For leap.soledad, the CHANGELOG entries should be divided per package (common, client, server). See older releases for reference.
+ - Helper bash line: for i in $(ls changes); do cat changes/$i; echo; done
+ * [ ] git rm changes/feature*; git rm changes/bug*
+ * [ ] git commit -m "Fold in changes."
+
+ * [ ] git checkout master && git pull origin master && git merge --no-ff release-X.Y.Z --no-edit
+ * [ ] git tag -s X.Y.Z -m "Tag <package> version X.Y.Z" # (note the -s so that it's a signed tag and -m to specify the message for the tag)
+ * [ ] git push origin master; git push origin X.Y.Z
* [ ] git checkout develop && git merge master && git push origin develop
* [ ] Build and upload bundles
* [ ] Use the scripts under pkg/<os>/ to build the the bundles.
diff --git a/docs/testers/howto.rst b/docs/testers/howto.rst
index 61d38787..1e276f7d 100644
--- a/docs/testers/howto.rst
+++ b/docs/testers/howto.rst
@@ -5,34 +5,125 @@ Howto for Testers
This document covers a how-to guide to:
-#. Quickly fetching latest development code, and
-#. Reporting bugs.
+#. :ref:`Where and how report bugs for Bitmask <reporting_bugs>`, and
+#. :ref:`Quickly fetching latest development code <fetchinglatest>`.
Let's go!
+.. _reporting_bugs:
+
+Reporting bugs
+--------------
+
+Report all the bugs you can find to us! If something is not quite working yet,
+we really want to know. Reporting a bug to us is the best way to get it fixed
+quickly, and get our unconditional gratitude.
+
+It is quick, easy, and probably the best way to contribute to Bitmask
+development, other than submitting patches.
+
+.. admonition:: Reporting better bugs
+
+ New to bug reporting? Here you have a `great document about this noble art
+ <http://www.chiark.greenend.org.uk/~sgtatham/bugs.html>`_.
+
+Where to report bugs
+^^^^^^^^^^^^^^^^^^^^
+
+We use the `Bitmask Bug Tracker <https://leap.se/code/projects/eip-client>`_,
+although you can also use `Github issues
+<https://github.com/leapcode/bitmask_client/issues>`_. But we reaaaally prefer if you
+sign up in the former to send your bugs our way.
+
+What to include in your bug report
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+* The symptoms of the bug itself: what went wrong? What items appear broken, or
+ do not work as expected? Maybe an UI element that appears to freeze?
+* The Bitmask version you are running. You can get it by doing `bitmask
+ --version`, or you can go to `Help -> About Bitmask` menu.
+* The installation method you used: bundle? from source code? debian package?
+* Your platform version and other details: Ubuntu 12.04? Debian unstable?
+ Windows 8? OSX 10.8.4? If relevant, your desktop system also (gnome, kde...)
+* When does the bug appear? What actions trigger it? Does it always
+ happen, or is it sporadic?
+* The exact error message, if any.
+* Attachments of the log files, if possible (see section below).
+
+Also, try not to mix several issues in your bug report. If you are finding
+several problems, it's better to issue a separate bug report for each one of
+them.
+
+Attaching log files
+^^^^^^^^^^^^^^^^^^^
+
+If you can spend a little time getting them, please add some logs to the bug
+report. They are **really** useful when it comes to debug a problem. To do it:
+
+Launch Bitmask in debug mode. Logs are way more verbose that way::
+
+ bitmask --debug
+
+Get your hand on the logs. You can achieve that either by clicking on the "Show
+log" button, and saving to file, or directly by specifying the path to the
+logfile in the command line invocation::
+
+ bitmask --debug --logfile /tmp/bitmask.log
+
+Attach the logfile to your bug report.
+
+Need human interaction?
+^^^^^^^^^^^^^^^^^^^^^^^
+
+You can also find us in the ``#leap-dev`` channel on the `freenode network
+<https://freenode.net>`_. If you do not have a IRC client at hand, you can
+`enter the channel via web
+<http://webchat.freenode.net/?nick=leaper....&channels=%23leap-dev&uio=d4>`_.
+
+
.. _fetchinglatest:
Fetching latest development code
---------------------------------
-To allow rapid testing in different platforms, we have put together a quick script that is able to fetch latest development code. It more or less does all the steps covered in the :ref:`Setting up a Work Enviroment <environment>` section, only that in a more compact way suitable (ahem) also for non developers.
+Normally, testing the latest :ref:`client bundles <standalone-bundle>` should be
+enough. We are engaged in a two-week release cycle with minor releases that are
+as stable as possible.
+
+However, if you want to test that some issue has *really* been fixed before the
+next release is out (if you are testing a new provider, for instance), you are
+encouraged to try out the latest in the development branch. If you do not know
+how to do that, or you prefer an automated script, keep reading for a way to
+painlessly fetch the latest development code.
+
+We have put together a script to allow rapid testing in different platforms for
+the brave souls like you. It more or less does all the steps covered in the
+:ref:`Setting up a Work Enviroment <environment>` section, only that in a more
+compact way suitable (ahem) also for non developers.
.. note::
- In the near future, we will be using :ref:`standalone bundles <standalone-bundle>` with the ability to self-update.
+ At some point in the near future, we will be using :ref:`standalone bundles
+ <standalone-bundle>` with the ability to self-update.
Install dependencies
^^^^^^^^^^^^^^^^^^^^
-First, install all the base dependencies plus git, virtualenv and development files needed to compile several extensions::
+First, install all the base dependencies plus git, virtualenv and development
+files needed to compile several extensions::
apt-get install openvpn git-core python-dev python-pyside python-setuptools python-virtualenv
-.. TODO Should review these dependencies. ^^
+.. TODO Should review these dependencies. I think python-sqlite is missing, we
+ have an issue for that^^
+
+.. TODO we really should keep the dependencies in a single file that we are able to
+ include, to avoid phasing out.
+
Bootstrap script
^^^^^^^^^^^^^^^^
.. 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.
+ 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 an improved script.
.. note::
This script could make use of the after_install hook. Read http://pypi.python.org/pypi/virtualenv/
@@ -47,15 +138,22 @@ Download and source the following script in the parent folder where you want you
Tada! If everything went well, you should be able to run bitmask by typing::
- bitmask
+ bitmask --debug
Noticed that your prompt changed? That was *virtualenv*. Keep reading...
Activating the virtualenv
^^^^^^^^^^^^^^^^^^^^^^^^^
-The above bootstrap script has fetched latest code inside a virtualenv, which is an isolated, *virtual* python local environment that avoids messing with your global paths. You will notice you are *inside* a virtualenv because you will see a modified prompt reminding it to you (*bitmask-testbuild* in this case).
+The above bootstrap script has fetched latest code inside a virtualenv, which is
+an isolated, *virtual* python local environment that avoids messing with your
+global paths. You will notice you are *inside* a virtualenv because you will see
+a modified prompt reminding it to you (*bitmask-testbuild* in this case).
-Thus, if you forget to *activate your virtualenv*, bitmask will not run from the local path, and it will be looking for something else in your global path. So, **you have to remember to activate your virtualenv** each time that you open a new shell and want to execute the code you are testing. You can do this by typing::
+Thus, if you forget to *activate your virtualenv*, bitmask will not run from the
+local path, and it will be looking for something else in your global path. So,
+**you have to remember to activate your virtualenv** each time that you open a
+new shell and want to execute the code you are testing. You can do this by
+typing::
$ source bin/activate
@@ -87,11 +185,11 @@ You should be able to cd into the downloaded repo and pull latest changes::
(bitmask-testbuild)$ cd src/bitmask
(bitmask-testbuild)$ git pull origin develop
-However, as a tester you are encouraged to run the whole bootstrap process from time to time to help us catching install and versioniing bugs too.
+However, you are encouraged to run the whole bootstrapping process from time to time to help us catching install and versioning bugs too.
Testing the packages
^^^^^^^^^^^^^^^^^^^^
-When we have a release candidate for the supported platforms (Debian stable, Ubuntu 12.04 by now), we will announce also the URI where you can download the rc for testing in your system. Stay tuned!
+When we have a release candidate for the supported platforms, we will announce also the URI where you can download the rc for testing in your system. Stay tuned!
Testing the status of translations
----------------------------------
@@ -104,13 +202,3 @@ If you want to check the current status of bitmask localization in a language ot
for running Bitmask with the spanish locales.
-Reporting bugs
---------------
-
-.. admonition:: Reporting better bugs
-
- There is a great text on the art of bug reporting, that can be found `online <http://www.chiark.greenend.org.uk/~sgtatham/bugs.html>`_.
-
-.. TODO add a line with ref. to running Bitmask in debug mode...
-
-We use the `Bitmask Bug Tracker <https://leap.se/code/projects/eip-client>`_, although you can also use `Github issues <https://github.com/leapcode/bitmask/issues>`_.
diff --git a/docs/user/install.rst b/docs/user/install.rst
index e5765302..bcac4883 100644
--- a/docs/user/install.rst
+++ b/docs/user/install.rst
@@ -88,14 +88,19 @@ Installing Bitmask is as simple as using `pip <http://www.pip-installer.org/>`_
Show me the code!
-----------------
-You can get the code from LEAP public git repository ::
+For the users that can find their way through python packages,
+you can get the code from LEAP public git repository ::
- $ git clone git://leap.se/bitmask_client
+ $ git clone https://leap.se/git/bitmask_client
Or from the github mirror ::
$ git clone git://github.com/leapcode/bitmask_client.git
-Once you have grabbed a copy of the sources, you can install it into your site-packages easily ::
+Once you have grabbed a copy of the sources, and installed all the base
+dependencies, you can install it into your site-packages easily ::
- $ pyton setup.py install
+ $ make # compile the resources
+ $ sudo python2 setup.py install
+
+Although, like always, it is a better idea to install things in a virtualenv.
diff --git a/pkg/linux/build_bundle.sh b/pkg/linux/build_bundle.sh
index 520ff256..60151a80 100755
--- a/pkg/linux/build_bundle.sh
+++ b/pkg/linux/build_bundle.sh
@@ -34,16 +34,18 @@ BITMASK_BIN=$TEMPLATE_BUNDLE/bitmask
BUNDLE_NAME=Bitmask-linux$ARCH-$VERSION
# clean template
-rm $TEMPLATE_BUNDLE/CHANGELOG
-rm $TEMPLATE_BUNDLE/relnotes.txt
+rm -f $TEMPLATE_BUNDLE/CHANGELOG
+rm -f $TEMPLATE_BUNDLE/relnotes.txt
rm -rf $TEMPLATE_BUNDLE/apps/leap
rm -rf $TEMPLATE_BUNDLE/lib/leap/{common,keymanager,soledad,mail}
# checkout the latest tag in all repos
for repo in $REPOSITORIES; do
cd $REPOS_ROOT/$repo
- git fetch
- # checkout to the latest annotated tag, supress 'detached head' warning
+ git checkout master
+ git pull --ff-only origin master && git fetch
+ git reset --hard origin/master # this avoids problems if you are in a commit far in the past
+ # checkout to the closest annotated tag, supress 'detached head' warning
git checkout --quiet `git describe --abbrev=0`
done
@@ -82,7 +84,7 @@ cp src/launcher $BITMASK_BIN
# copy launcher.py to template bundle
# e.g. TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/
-cd $REPOS_ROOT/bitmask_client_launcher/src/
+cd $REPOS_ROOT/bitmask_launcher/src/
cp launcher.py $TEMPLATE_BUNDLE/apps/
# copy relnotes, joint changelog and LICENSE to TEMPLATE_BUNDLE
diff --git a/pkg/requirements-docs.pip b/pkg/requirements-docs.pip
index 6966869c..ad14d003 100644
--- a/pkg/requirements-docs.pip
+++ b/pkg/requirements-docs.pip
@@ -1 +1,2 @@
sphinx
+Sphinx-PyPI-upload
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 00000000..6c1d4f05
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,8 @@
+[build_sphinx]
+source-dir = docs/
+build-dir = docs/_build
+all_files = 1
+
+[upload_sphinx]
+upload-dir = docs/_build/html
+repository = https://pypi.python.org/pypi
diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py
index 02b1693d..c1859478 100644
--- a/src/leap/bitmask/app.py
+++ b/src/leap/bitmask/app.py
@@ -152,12 +152,6 @@ def main():
"""
Starts the main event loop and launches the main window.
"""
- try:
- event_server.ensure_server(event_server.SERVER_PORT)
- except Exception as e:
- # We don't even have logger configured in here
- print "Could not ensure server: %r" % (e,)
-
_, opts = leap_argparse.init_leapc_args()
if opts.version:
@@ -170,6 +164,12 @@ def main():
logfile = opts.log_file
openvpn_verb = opts.openvpn_verb
+ try:
+ event_server.ensure_server(event_server.SERVER_PORT)
+ except Exception as e:
+ # We don't even have logger configured in here
+ print "Could not ensure server: %r" % (e,)
+
#############################################################
# Given how paths and bundling works, we need to delay the imports
# of certain parts that depend on this path settings.
@@ -179,6 +179,9 @@ def main():
flags.STANDALONE = standalone
BaseConfig.standalone = standalone
+ logger = add_logger_handlers(debug, logfile)
+ replace_stdout_stderr_with_logging(logger)
+
# And then we import all the other stuff
from leap.bitmask.gui import locale_rc
from leap.bitmask.gui import twisted_main
@@ -190,9 +193,6 @@ def main():
# pylint: avoid unused import
assert(locale_rc)
- logger = add_logger_handlers(debug, logfile)
- replace_stdout_stderr_with_logging(logger)
-
if not we_are_the_one_and_only():
# Bitmask is already running
logger.warning("Tried to launch more than one instance "
@@ -211,7 +211,7 @@ def main():
# We force the style if on KDE so that it doesn't load all the kde
# libs, which causes a compatibility issue in some systems.
# For more info, see issue #3194
- if os.environ.get("KDE_SESSION_UID") is not None:
+ if flags.STANDALONE and os.environ.get("KDE_SESSION_UID") is not None:
sys.argv.append("-style")
sys.argv.append("Cleanlooks")
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 200d68aa..92d6906e 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -36,9 +36,9 @@ from leap.bitmask.gui import statemachines
from leap.bitmask.gui.statuspanel import StatusPanelWidget
from leap.bitmask.gui.wizard import Wizard
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper
from leap.bitmask.services.eip.eipconfig import EIPConfig
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
# XXX: Soledad might not work out of the box in Windows, issue #2932
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
@@ -53,12 +53,12 @@ from leap.bitmask.services.eip.vpnprocess import VPN
from leap.bitmask.services.eip.vpnprocess import OpenVPNAlreadyRunning
from leap.bitmask.services.eip.vpnprocess import AlienOpenVPNAlreadyRunning
-from leap.bitmask.services.eip.vpnlaunchers import VPNLauncherException
-from leap.bitmask.services.eip.vpnlaunchers import OpenVPNNotFoundException
-from leap.bitmask.services.eip.vpnlaunchers import EIPNoPkexecAvailable
-from leap.bitmask.services.eip.vpnlaunchers import \
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.services.eip.vpnlauncher import OpenVPNNotFoundException
+from leap.bitmask.services.eip.linuxvpnlauncher import EIPNoPkexecAvailable
+from leap.bitmask.services.eip.linuxvpnlauncher import \
EIPNoPolkitAuthAgentAvailable
-from leap.bitmask.services.eip.vpnlaunchers import EIPNoTunKextLoaded
+from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -234,6 +234,8 @@ class MainWindow(QtGui.QMainWindow):
self._soledad_bootstrapped_stage)
self._soledad_bootstrapper.soledad_timeout.connect(
self._retry_soledad_connection)
+ # XXX missing connect to soledad_failed (signal unrecoverable to user)
+ # TODO wait until chiiph ui refactor.
self._smtp_bootstrapper = SMTPBootstrapper()
self._smtp_bootstrapper.download_config.connect(
@@ -1007,6 +1009,7 @@ class MainWindow(QtGui.QMainWindow):
"""
Retries soledad connection.
"""
+ # XXX should move logic to soledad boostrapper itself
logger.debug("Retrying soledad connection.")
if self._soledad_bootstrapper.should_retry_initialization():
self._soledad_bootstrapper.increment_retries_count()
@@ -1031,8 +1034,9 @@ class MainWindow(QtGui.QMainWindow):
"""
passed = data[self._soledad_bootstrapper.PASSED_KEY]
if not passed:
+ # TODO should actually *display* on the panel.
logger.debug("ERROR on soledad bootstrapping:")
- logger.error(data[self._soledad_bootstrapper.ERROR_KEY])
+ logger.error("%r" % data[self._soledad_bootstrapper.ERROR_KEY])
return
else:
logger.debug("Done bootstrapping Soledad")
@@ -1531,7 +1535,9 @@ class MainWindow(QtGui.QMainWindow):
# TODO we should have a way of parsing the latest lines in the vpn
# log buffer so we can have a more precise idea of which type
# of error did we have (server side, local problem, etc)
- abnormal = True
+
+ qtsigs = self._eip_connection.qtsigs
+ signal = qtsigs.disconnected_signal
# XXX check if these exitCodes are pkexec/cocoasudo specific
if exitCode in (126, 127):
@@ -1540,28 +1546,25 @@ class MainWindow(QtGui.QMainWindow):
"because you did not authenticate properly."),
error=True)
self._vpn.killit()
+ signal = qtsigs.connection_aborted_signal
+
elif exitCode != 0 or not self.user_stopped_eip:
self._status_panel.set_global_status(
self.tr("Encrypted Internet finished in an "
"unexpected manner!"), error=True)
- else:
- abnormal = False
+ signal = qtsigs.connection_died_signal
+
if exitCode == 0 and IS_MAC:
# XXX remove this warning after I fix cocoasudo.
logger.warning("The above exit code MIGHT BE WRONG.")
- # We emit signals to trigger transitions in the state machine:
- qtsigs = self._eip_connection.qtsigs
- if abnormal:
- signal = qtsigs.connection_died_signal
- else:
- signal = qtsigs.disconnected_signal
-
# XXX verify that the logic kees the same w/o the abnormal flag
# after the refactor to EIPConnection has been completed
# (eipconductor taking the most of the logic under transitions
# that right now are handled under status_panel)
#self._stop_eip(abnormal)
+
+ # We emit signals to trigger transitions in the state machine:
signal.emit()
def _on_raise_window_event(self, req):
diff --git a/src/leap/bitmask/gui/statemachines.py b/src/leap/bitmask/gui/statemachines.py
index c3dd5ed3..94726720 100644
--- a/src/leap/bitmask/gui/statemachines.py
+++ b/src/leap/bitmask/gui/statemachines.py
@@ -128,11 +128,25 @@ class ConnectionMachineBuilder(object):
states[_OFF])
# * If we receive the connection_died, we transition
- # to the off state
+ # from on directly to the off state
states[_ON].addTransition(
conn.qtsigs.connection_died_signal,
states[_OFF])
+ # * If we receive the connection_aborted, we transition
+ # from connecting to the off state
+ states[_CON].addTransition(
+ conn.qtsigs.connection_aborted_signal,
+ states[_OFF])
+ # * Connection died can in some cases also be
+ # triggered while we are in CONNECTING
+ # state. I should be avoided, since connection_aborted
+ # is clearer (and reserve connection_died
+ # for transitions from on->off
+ states[_CON].addTransition(
+ conn.qtsigs.connection_died_signal,
+ states[_OFF])
+
# adding states to the machine
for state in states.itervalues():
machine.addState(state)
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index 45734b81..bb38b136 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
First run wizard
"""
@@ -27,15 +26,13 @@ from functools import partial
from PySide import QtCore, QtGui
from twisted.internet import threads
-from leap.bitmask.config import flags
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.srpregister import SRPRegister
-from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.services import get_service_display_name, get_supported
from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_service_display_name, get_supported
from ui_wizard import Ui_Wizard
diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py
index 831c6a1c..d93efbc6 100644
--- a/src/leap/bitmask/platform_init/initializers.py
+++ b/src/leap/bitmask/platform_init/initializers.py
@@ -29,7 +29,9 @@ import tempfile
from PySide import QtGui
from leap.bitmask.config.leapsettings import LeapSettings
-from leap.bitmask.services.eip import vpnlaunchers
+from leap.bitmask.services.eip import get_vpn_launcher
+from leap.bitmask.services.eip.linuxvpnlauncher import LinuxVPNLauncher
+from leap.bitmask.services.eip.darwinvpnlauncher import DarwinVPNLauncher
from leap.bitmask.util import first
from leap.bitmask.util import privilege_policies
@@ -106,7 +108,7 @@ def check_missing():
config = LeapSettings()
alert_missing = config.get_alert_missing_scripts()
- launcher = vpnlaunchers.get_platform_launcher()
+ launcher = get_vpn_launcher()
missing_scripts = launcher.missing_updown_scripts
missing_other = launcher.missing_other_files
@@ -251,7 +253,7 @@ def _darwin_install_missing_scripts(badexec, notfound):
"..",
"Resources",
"openvpn")
- launcher = vpnlaunchers.DarwinVPNLauncher
+ launcher = DarwinVPNLauncher
if os.path.isdir(installer_path):
fd, tempscript = tempfile.mkstemp(prefix="leap_installer-")
@@ -356,7 +358,7 @@ def _linux_install_missing_scripts(badexec, notfound):
"""
success = False
installer_path = os.path.join(os.getcwd(), "apps", "eip", "files")
- launcher = vpnlaunchers.LinuxVPNLauncher
+ launcher = LinuxVPNLauncher
# XXX refactor with darwin, same block.
diff --git a/src/leap/bitmask/services/eip/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py
index 3b7c9899..751da828 100644
--- a/src/leap/bitmask/services/eip/providerbootstrapper.py
+++ b/src/leap/bitmask/provider/providerbootstrapper.py
@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
Provider bootstrapping
"""
@@ -32,11 +31,11 @@ from leap.bitmask.util import get_path_prefix
from leap.bitmask.util.constants import REQUEST_TIMEOUT
from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper
from leap.bitmask.provider.supportedapis import SupportedAPIs
+from leap.common import ca_bundle
from leap.common.certs import get_digest
from leap.common.files import check_and_fix_urw_only, get_mtime, mkdir_p
from leap.common.check import leap_assert, leap_assert_type, leap_check
-
logger = logging.getLogger(__name__)
@@ -85,16 +84,32 @@ class ProviderBootstrapper(AbstractBootstrapper):
self._provider_config = None
self._download_if_needed = False
+ @property
+ def verify(self):
+ """
+ Verify parameter for requests.
+
+ :returns: either False, if checks are skipped, or the
+ path to the ca bundle.
+ :rtype: bool or str
+ """
+ if self._bypass_checks:
+ verify = False
+ else:
+ verify = ca_bundle.where()
+ return verify
+
def _check_name_resolution(self):
"""
Checks that the name resolution for the provider name works
"""
leap_assert(self._domain, "Cannot check DNS without a domain")
-
logger.debug("Checking name resolution for %s" % (self._domain))
# We don't skip this check, since it's basic for the whole
# system to work
+ # err --- but we can do it after a failure, to diagnose what went
+ # wrong. Right now we're just adding connection overhead. -- kali
socket.gethostbyname(self._domain)
def _check_https(self, *args):
@@ -102,24 +117,29 @@ class ProviderBootstrapper(AbstractBootstrapper):
Checks that https is working and that the provided certificate
checks out
"""
-
leap_assert(self._domain, "Cannot check HTTPS without a domain")
-
logger.debug("Checking https for %s" % (self._domain))
# We don't skip this check, since it's basic for the whole
- # system to work
+ # system to work.
+ # err --- but we can do it after a failure, to diagnose what went
+ # wrong. Right now we're just adding connection overhead. -- kali
try:
res = self._session.get("https://%s" % (self._domain,),
- verify=not self._bypass_checks,
+ verify=self.verify,
timeout=REQUEST_TIMEOUT)
res.raise_for_status()
- except requests.exceptions.SSLError:
+ except requests.exceptions.SSLError as exc:
+ logger.exception(exc)
self._err_msg = self.tr("Provider certificate could "
"not be verified")
raise
- except Exception:
+ except Exception as exc:
+ # XXX careful!. The error might be also a SSL handshake
+ # timeout error, in which case we should retry a couple of times
+ # more, for cases where the ssl server gives high latencies.
+ logger.exception(exc)
self._err_msg = self.tr("Provider does not support HTTPS")
raise
@@ -129,11 +149,13 @@ class ProviderBootstrapper(AbstractBootstrapper):
"""
leap_assert(self._domain,
"Cannot download provider info without a domain")
-
logger.debug("Downloading provider info for %s" % (self._domain))
- headers = {}
+ # --------------------------------------------------------------
+ # TODO factor out with the download routines in services.
+ # Watch out! We're handling the verify paramenter differently here.
+ headers = {}
provider_json = os.path.join(get_path_prefix(), "leap", "providers",
self._domain, "provider.json")
mtime = get_mtime(provider_json)
@@ -142,16 +164,18 @@ class ProviderBootstrapper(AbstractBootstrapper):
headers['if-modified-since'] = mtime
uri = "https://%s/%s" % (self._domain, "provider.json")
- verify = not self._bypass_checks
+ verify = self.verify
if mtime: # the provider.json exists
- provider_config = ProviderConfig()
- provider_config.load(provider_json)
+ # So, we're getting it from the api.* and checking against
+ # the provider ca.
try:
- verify = provider_config.get_ca_cert_path()
+ provider_config = ProviderConfig()
+ provider_config.load(provider_json)
uri = provider_config.get_api_uri() + '/provider.json'
+ verify = provider_config.get_ca_cert_path()
except MissingCACert:
- # get_ca_cert_path fails if the certificate does not exists.
+ # no ca? then download from main domain again.
pass
logger.debug("Requesting for provider.json... "
@@ -165,6 +189,9 @@ class ProviderBootstrapper(AbstractBootstrapper):
# Not modified
if res.status_code == 304:
logger.debug("Provider definition has not been modified")
+ # --------------------------------------------------------------
+ # end refactor, more or less...
+ # XXX Watch out, have to check the supported api yet.
else:
provider_definition, mtime = get_content(res)
@@ -181,8 +208,8 @@ class ProviderBootstrapper(AbstractBootstrapper):
else:
api_supported = ', '.join(SupportedAPIs.SUPPORTED_APIS)
error = ('Unsupported provider API version. '
- 'Supported versions are: {}. '
- 'Found: {}.').format(api_supported, api_version)
+ 'Supported versions are: {0}. '
+ 'Found: {1}.').format(api_supported, api_version)
logger.error(error)
raise UnsupportedProviderAPI(error)
@@ -230,7 +257,8 @@ class ProviderBootstrapper(AbstractBootstrapper):
"""
Downloads the CA cert that is going to be used for the api URL
"""
-
+ # XXX maybe we can skip this step if
+ # we have a fresh one.
leap_assert(self._provider_config, "Cannot download the ca cert "
"without a provider config!")
@@ -244,7 +272,7 @@ class ProviderBootstrapper(AbstractBootstrapper):
return
res = self._session.get(self._provider_config.get_ca_cert_uri(),
- verify=not self._bypass_checks,
+ verify=self.verify,
timeout=REQUEST_TIMEOUT)
res.raise_for_status()
diff --git a/src/leap/bitmask/provider/tests/__init__.py b/src/leap/bitmask/provider/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/leap/bitmask/provider/tests/__init__.py
diff --git a/src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py
index b0685676..9b47d60e 100644
--- a/src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py
+++ b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py
@@ -14,15 +14,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
"""
Tests for the Provider Boostrapper checks
These will be whitebox tests since we want to make sure the private
implementation is checking what we expect.
"""
-
import os
import mock
import socket
@@ -39,13 +36,12 @@ from nose.twistedtools import deferred, reactor
from twisted.internet import threads
from requests.models import Response
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services.eip.providerbootstrapper import \
- UnsupportedProviderAPI
-from leap.bitmask.services.eip.providerbootstrapper import WrongFingerprint
-from leap.bitmask.provider.supportedapis import SupportedAPIs
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.tests import fake_provider
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.provider.providerbootstrapper import UnsupportedProviderAPI
+from leap.bitmask.provider.providerbootstrapper import WrongFingerprint
+from leap.bitmask.provider.supportedapis import SupportedAPIs
from leap.common.files import mkdir_p
from leap.common.testing.https_server import where
from leap.common.testing.basetest import BaseLeapTest
diff --git a/src/leap/bitmask/services/connections.py b/src/leap/bitmask/services/connections.py
index f3ab9e8e..8aeb4e0c 100644
--- a/src/leap/bitmask/services/connections.py
+++ b/src/leap/bitmask/services/connections.py
@@ -103,6 +103,7 @@ class AbstractLEAPConnection(object):
# Bypass stages
connection_died_signal = None
+ connection_aborted_signal = None
class Disconnected(State):
"""Disconnected state"""
diff --git a/src/leap/bitmask/services/eip/__init__.py b/src/leap/bitmask/services/eip/__init__.py
index dd010027..6030cac3 100644
--- a/src/leap/bitmask/services/eip/__init__.py
+++ b/src/leap/bitmask/services/eip/__init__.py
@@ -20,7 +20,11 @@ leap.bitmask.services.eip module initialization
import os
import tempfile
-from leap.bitmask.platform_init import IS_WIN
+from leap.bitmask.services.eip.darwinvpnlauncher import DarwinVPNLauncher
+from leap.bitmask.services.eip.linuxvpnlauncher import LinuxVPNLauncher
+from leap.bitmask.services.eip.windowsvpnlauncher import WindowsVPNLauncher
+from leap.bitmask.platform_init import IS_LINUX, IS_MAC, IS_WIN
+from leap.common.check import leap_assert
def get_openvpn_management():
@@ -40,3 +44,24 @@ def get_openvpn_management():
port = "unix"
return host, port
+
+
+def get_vpn_launcher():
+ """
+ Return the VPN launcher for the current platform.
+ """
+ if not (IS_LINUX or IS_MAC or IS_WIN):
+ error_msg = "VPN Launcher not implemented for this platform."
+ raise NotImplementedError(error_msg)
+
+ launcher = None
+ if IS_LINUX:
+ launcher = LinuxVPNLauncher
+ elif IS_MAC:
+ launcher = DarwinVPNLauncher
+ elif IS_WIN:
+ launcher = WindowsVPNLauncher
+
+ leap_assert(launcher is not None)
+
+ return launcher()
diff --git a/src/leap/bitmask/services/eip/connection.py b/src/leap/bitmask/services/eip/connection.py
index 5f05ba07..08b29070 100644
--- a/src/leap/bitmask/services/eip/connection.py
+++ b/src/leap/bitmask/services/eip/connection.py
@@ -40,6 +40,7 @@ class EIPConnectionSignals(QtCore.QObject):
disconnected_signal = QtCore.Signal()
connection_died_signal = QtCore.Signal()
+ connection_aborted_signal = QtCore.Signal()
class EIPConnection(AbstractLEAPConnection):
diff --git a/src/leap/bitmask/services/eip/darwinvpnlauncher.py b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
new file mode 100644
index 00000000..f3b6bfc8
--- /dev/null
+++ b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# darwinvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Darwin VPN launcher implementation.
+"""
+import commands
+import getpass
+import logging
+import os
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.util import get_path_prefix
+
+logger = logging.getLogger(__name__)
+
+
+class EIPNoTunKextLoaded(VPNLauncherException):
+ pass
+
+
+class DarwinVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Darwin Platform
+ """
+ COCOASUDO = "cocoasudo"
+ # XXX need the good old magic translate for these strings
+ # (look for magic in 0.2.0 release)
+ SUDO_MSG = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
+ "missing scripts and fix permissions.\"")
+
+ INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../")
+ INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
+ OPENVPN_BIN = 'openvpn.leap'
+ OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
+ OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
+ INSTALL_PATH_ESCAPED,)
+
+ UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
+ DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
+ OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
+
+ UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
+ OTHER_FILES = []
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath):
+ """
+ Returns a command that can copy the missing scripts.
+ :rtype: str
+ """
+ to = kls.OPENVPN_PATH_ESCAPED
+
+ cmd = "#!/bin/sh\n"
+ cmd += "mkdir -p {0}\n".format(to)
+ cmd += "cp '{0}'/* {1}\n".format(frompath, to)
+ cmd += "chmod 744 {0}/*".format(to)
+
+ return cmd
+
+ @classmethod
+ def is_kext_loaded(kls):
+ """
+ Checks if the needed kext is loaded before launching openvpn.
+
+ :returns: True if kext is loaded, False otherwise.
+ :rtype: bool
+ """
+ return bool(commands.getoutput('kextstat | grep "leap.tun"'))
+
+ @classmethod
+ def _get_icon_path(kls):
+ """
+ Returns the absolute path to the app icon.
+
+ :rtype: str
+ """
+ resources_path = os.path.abspath(
+ os.path.join(os.getcwd(), "../../Contents/Resources"))
+
+ return os.path.join(resources_path, "leap-client.tiff")
+
+ @classmethod
+ def get_cocoasudo_ovpn_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to run openvpn
+ as admin with a nice password prompt. The actual command needs to be
+ appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ sudo_msg = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (sudo_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_cocoasudo_installmissing_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to install missing
+ files as admin with a nice password prompt. The actual command needs to
+ be appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ install_msg = ('"Bitmask needs administrative privileges to install '
+ 'missing scripts and fix permissions."')
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (install_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the OSX implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoTunKextLoaded,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ if not kls.is_kext_loaded():
+ raise EIPNoTunKextLoaded
+
+ # we use `super` in order to send the class to use
+ command = super(DarwinVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ cocoa, cargs = kls.get_cocoasudo_ovpn_cmd()
+ cargs.extend(command)
+ command = cargs
+ command.insert(0, cocoa)
+
+ command.extend(['--setenv', "LEAPUSER", getpass.getuser()])
+
+ return command
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {
+ "DYLD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/eipbootstrapper.py b/src/leap/bitmask/services/eip/eipbootstrapper.py
index 885c4420..5a238a1c 100644
--- a/src/leap/bitmask/services/eip/eipbootstrapper.py
+++ b/src/leap/bitmask/services/eip/eipbootstrapper.py
@@ -28,7 +28,6 @@ from leap.bitmask.services import download_service_config
from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.common import certs as leap_certs
-from leap.bitmask.util import get_path_prefix
from leap.common.check import leap_assert, leap_assert_type
from leap.common.files import check_and_fix_urw_only
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
new file mode 100644
index 00000000..c2c28627
--- /dev/null
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -0,0 +1,232 @@
+# -*- coding: utf-8 -*-
+# linuxvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Linux VPN launcher implementation.
+"""
+import commands
+import logging
+import os
+import subprocess
+import time
+
+from leap.bitmask.config import flags
+from leap.bitmask.util import privilege_policies
+from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
+from leap.common.files import which
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.util import get_path_prefix
+from leap.common.check import leap_assert
+from leap.bitmask.util import first
+
+logger = logging.getLogger(__name__)
+
+
+class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
+ pass
+
+
+class EIPNoPkexecAvailable(VPNLauncherException):
+ pass
+
+
+def _is_pkexec_in_system():
+ """
+ Checks the existence of the pkexec binary in system.
+ """
+ pkexec_path = which('pkexec')
+ if len(pkexec_path) == 0:
+ return False
+ return True
+
+
+def _is_auth_agent_running():
+ """
+ Checks if a polkit daemon is running.
+
+ :return: True if it's running, False if it's not.
+ :rtype: boolean
+ """
+ ps = 'ps aux | grep polkit-%s-authentication-agent-1'
+ opts = (ps % case for case in ['[g]nome', '[k]de'])
+ is_running = map(lambda l: commands.getoutput(l), opts)
+ return any(is_running)
+
+
+def _try_to_launch_agent():
+ """
+ Tries to launch a polkit daemon.
+ """
+ env = None
+ if flags.STANDALONE is True:
+ env = {"PYTHONPATH": os.path.abspath('../../../../lib/')}
+ try:
+ # We need to quote the command because subprocess call
+ # will do "sh -c 'foo'", so if we do not quoute it we'll end
+ # up with a invocation to the python interpreter. And that
+ # is bad.
+ subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
+ shell=True, env=env)
+ except Exception as exc:
+ logger.exception(exc)
+
+
+class LinuxVPNLauncher(VPNLauncher):
+ PKEXEC_BIN = 'pkexec'
+ OPENVPN_BIN = 'openvpn'
+ OPENVPN_BIN_PATH = os.path.join(
+ get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
+
+ SYSTEM_CONFIG = "/etc/leap"
+ UP_DOWN_FILE = "resolv-update"
+ UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
+
+ # We assume this is there by our openvpn dependency, and
+ # we will put it there on the bundle too.
+ # TODO adapt to the bundle path.
+ OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
+ OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
+ OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
+ OPENVPN_DOWN_ROOT_BASE,
+ OPENVPN_DOWN_ROOT_FILE)
+
+ UP_SCRIPT = DOWN_SCRIPT = UP_DOWN_PATH
+ UPDOWN_FILES = (UP_DOWN_PATH,)
+ POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
+ OTHER_FILES = (POLKIT_PATH, )
+
+ @classmethod
+ def maybe_pkexec(kls):
+ """
+ Checks whether pkexec is available in the system, and
+ returns the path if found.
+
+ Might raise:
+ EIPNoPkexecAvailable,
+ EIPNoPolkitAuthAgentAvailable.
+
+ :returns: a list of the paths where pkexec is to be found
+ :rtype: list
+ """
+ if _is_pkexec_in_system():
+ if not _is_auth_agent_running():
+ _try_to_launch_agent()
+ time.sleep(0.5)
+ if _is_auth_agent_running():
+ pkexec_possibilities = which(kls.PKEXEC_BIN)
+ leap_assert(len(pkexec_possibilities) > 0,
+ "We couldn't find pkexec")
+ return pkexec_possibilities
+ else:
+ logger.warning("No polkit auth agent found. pkexec " +
+ "will use its own auth agent.")
+ raise EIPNoPolkitAuthAgentAvailable()
+ else:
+ logger.warning("System has no pkexec")
+ raise EIPNoPkexecAvailable()
+
+ @classmethod
+ def missing_other_files(kls):
+ """
+ 'Extend' the VPNLauncher's missing_other_files to check if the polkit
+ files is outdated. If the polkit file that is in OTHER_FILES exists but
+ is not up to date, it is added to the missing list.
+
+ :returns: a list of missing files
+ :rtype: list of str
+ """
+ # we use `super` in order to send the class to use
+ missing = super(LinuxVPNLauncher, kls).missing_other_files()
+ polkit_file = LinuxPolicyChecker.get_polkit_path()
+ if polkit_file not in missing:
+ if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH):
+ missing.append(polkit_file)
+
+ return missing
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the Linux implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoPkexecAvailable,
+ EIPNoPolkitAuthAgentAvailable,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ # we use `super` in order to send the class to use
+ command = super(LinuxVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ pkexec = kls.maybe_pkexec()
+ if pkexec:
+ command.insert(0, first(pkexec))
+
+ return command
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath, pol_file):
+ """
+ Returns a sh script that can copy the missing files.
+
+ :param frompath: The path where the up/down scripts live
+ :type frompath: str
+ :param pol_file: The path where the dynamically generated
+ policy file lives
+ :type pol_file: str
+
+ :rtype: str
+ """
+ to = kls.SYSTEM_CONFIG
+
+ cmd = '#!/bin/sh\n'
+ cmd += 'mkdir -p "%s"\n' % (to, )
+ cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
+ cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
+ cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
+
+ return cmd
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {
+ "LD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py
new file mode 100644
index 00000000..935d75f1
--- /dev/null
+++ b/src/leap/bitmask/services/eip/vpnlauncher.py
@@ -0,0 +1,290 @@
+# -*- coding: utf-8 -*-
+# vpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Platform independant VPN launcher interface.
+"""
+import getpass
+import logging
+import os
+import stat
+
+from abc import ABCMeta, abstractmethod
+from functools import partial
+
+from leap.bitmask.config import flags
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.util import first
+from leap.bitmask.util import get_path_prefix
+from leap.common.check import leap_assert, leap_assert_type
+from leap.common.files import which
+
+logger = logging.getLogger(__name__)
+
+
+class VPNLauncherException(Exception):
+ pass
+
+
+class OpenVPNNotFoundException(VPNLauncherException):
+ pass
+
+
+def _has_updown_scripts(path, warn=True):
+ """
+ Checks the existence of the up/down scripts and its
+ exec bit if applicable.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.error("Could not find up/down script %s. "
+ "Might produce DNS leaks." % (path,))
+
+ # XXX check if applies in win
+ is_exe = False
+ try:
+ is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0)
+ except OSError as e:
+ logger.warn("%s" % (e,))
+ if warn and not is_exe:
+ logger.error("Up/down script %s is not executable. "
+ "Might produce DNS leaks." % (path,))
+ return is_file and is_exe
+
+
+def _has_other_files(path, warn=True):
+ """
+ Checks the existence of other important files.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.warning("Could not find file during checks: %s. " % (
+ path,))
+ return is_file
+
+
+class VPNLauncher(object):
+ """
+ Abstract launcher class
+ """
+ __metaclass__ = ABCMeta
+
+ UPDOWN_FILES = None
+ OTHER_FILES = None
+
+ @classmethod
+ @abstractmethod
+ def get_vpn_command(kls, eipconfig, providerconfig,
+ socket_host, socket_port, openvpn_verb=1):
+ """
+ Returns the platform dependant vpn launching command
+
+ Might raise:
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ leap_assert_type(eipconfig, EIPConfig)
+ leap_assert_type(providerconfig, ProviderConfig)
+
+ kwargs = {}
+ if flags.STANDALONE:
+ kwargs['path_extension'] = os.path.join(
+ get_path_prefix(), "..", "apps", "eip")
+
+ openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs)
+ if len(openvpn_possibilities) == 0:
+ raise OpenVPNNotFoundException()
+
+ openvpn = first(openvpn_possibilities)
+ args = []
+
+ args += [
+ '--setenv', "LEAPOPENVPN", "1"
+ ]
+
+ if openvpn_verb is not None:
+ args += ['--verb', '%d' % (openvpn_verb,)]
+
+ gateways = []
+ leap_settings = LeapSettings()
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException(kls.tr('No gateway was found!'))
+
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
+
+ for gw in gateways:
+ args += ['--remote', gw, '1194', 'udp']
+
+ args += [
+ '--client',
+ '--dev', 'tun',
+ ##############################################################
+ # persist-tun makes ping-restart fail because it leaves a
+ # broken routing table
+ ##############################################################
+ # '--persist-tun',
+ '--persist-key',
+ '--tls-client',
+ '--remote-cert-tls',
+ 'server'
+ ]
+
+ openvpn_configuration = eipconfig.get_openvpn_configuration()
+ for key, value in openvpn_configuration.items():
+ args += ['--%s' % (key,), value]
+
+ user = getpass.getuser()
+
+ ##############################################################
+ # The down-root plugin fails in some situations, so we don't
+ # drop privs for the time being
+ ##############################################################
+ # args += [
+ # '--user', user,
+ # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
+ # ]
+
+ if socket_port == "unix": # that's always the case for linux
+ args += [
+ '--management-client-user', user
+ ]
+
+ args += [
+ '--management-signal',
+ '--management', socket_host, socket_port,
+ '--script-security', '2'
+ ]
+
+ if _has_updown_scripts(kls.UP_SCRIPT):
+ args += [
+ '--up', '\"%s\"' % (kls.UP_SCRIPT,),
+ ]
+
+ if _has_updown_scripts(kls.DOWN_SCRIPT):
+ args += [
+ '--down', '\"%s\"' % (kls.DOWN_SCRIPT,)
+ ]
+
+ ###########################################################
+ # For the time being we are disabling the usage of the
+ # down-root plugin, because it doesn't quite work as
+ # expected (i.e. it doesn't run route -del as root
+ # when finishing, so it fails to properly
+ # restart/quit)
+ ###########################################################
+ # if _has_updown_scripts(kls.OPENVPN_DOWN_PLUGIN):
+ # args += [
+ # '--plugin', kls.OPENVPN_DOWN_ROOT,
+ # '\'%s\'' % kls.DOWN_SCRIPT # for OSX
+ # '\'script_type=down %s\'' % kls.DOWN_SCRIPT # for Linux
+ # ]
+
+ args += [
+ '--cert', eipconfig.get_client_cert_path(providerconfig),
+ '--key', eipconfig.get_client_cert_path(providerconfig),
+ '--ca', providerconfig.get_ca_cert_path()
+ ]
+
+ command_and_args = [openvpn] + args
+ logger.debug("Running VPN with command:")
+ logger.debug(" ".join(command_and_args))
+
+ return command_and_args
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {}
+
+ @classmethod
+ def missing_updown_scripts(kls):
+ """
+ Returns what updown scripts are missing.
+
+ :rtype: list
+ """
+ leap_assert(kls.UPDOWN_FILES is not None,
+ "Need to define UPDOWN_FILES for this particular "
+ "launcher before calling this method")
+ file_exist = partial(_has_updown_scripts, warn=False)
+ zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
+
+ @classmethod
+ def missing_other_files(kls):
+ """
+ Returns what other important files are missing during startup.
+ Same as missing_updown_scripts but does not check for exec bit.
+
+ :rtype: list
+ """
+ leap_assert(kls.OTHER_FILES is not None,
+ "Need to define OTHER_FILES for this particular "
+ "auncher before calling this method")
+ file_exist = partial(_has_other_files, warn=False)
+ zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
diff --git a/src/leap/bitmask/services/eip/vpnlaunchers.py b/src/leap/bitmask/services/eip/vpnlaunchers.py
deleted file mode 100644
index daa0d81f..00000000
--- a/src/leap/bitmask/services/eip/vpnlaunchers.py
+++ /dev/null
@@ -1,963 +0,0 @@
-# -*- coding: utf-8 -*-
-# vpnlaunchers.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Platform dependant VPN launchers
-"""
-import commands
-import logging
-import getpass
-import os
-import platform
-import stat
-import subprocess
-try:
- import grp
-except ImportError:
- pass # ignore, probably windows
-
-from abc import ABCMeta, abstractmethod
-from functools import partial
-from time import sleep
-
-from leap.bitmask.config import flags
-from leap.bitmask.config.leapsettings import LeapSettings
-
-from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
-from leap.bitmask.util import first
-from leap.bitmask.util import get_path_prefix
-from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
-from leap.bitmask.util import privilege_policies
-from leap.common.check import leap_assert, leap_assert_type
-from leap.common.files import which
-
-
-logger = logging.getLogger(__name__)
-
-
-class VPNLauncherException(Exception):
- pass
-
-
-class OpenVPNNotFoundException(VPNLauncherException):
- pass
-
-
-class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
- pass
-
-
-class EIPNoPkexecAvailable(VPNLauncherException):
- pass
-
-
-class EIPNoTunKextLoaded(VPNLauncherException):
- pass
-
-
-class VPNLauncher(object):
- """
- Abstract launcher class
- """
- __metaclass__ = ABCMeta
-
- UPDOWN_FILES = None
- OTHER_FILES = None
-
- @abstractmethod
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port=None):
- """
- Returns the platform dependant vpn launching command
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- return []
-
- @abstractmethod
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {}
-
- @classmethod
- def missing_updown_scripts(kls):
- """
- Returns what updown scripts are missing.
- :rtype: list
- """
- leap_assert(kls.UPDOWN_FILES is not None,
- "Need to define UPDOWN_FILES for this particular "
- "auncher before calling this method")
- file_exist = partial(_has_updown_scripts, warn=False)
- zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
- missing = filter(lambda (path, exists): exists is False, zipped)
- return [path for path, exists in missing]
-
- @classmethod
- def missing_other_files(kls):
- """
- Returns what other important files are missing during startup.
- Same as missing_updown_scripts but does not check for exec bit.
- :rtype: list
- """
- leap_assert(kls.UPDOWN_FILES is not None,
- "Need to define OTHER_FILES for this particular "
- "auncher before calling this method")
- file_exist = partial(_has_other_files, warn=False)
- zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
- missing = filter(lambda (path, exists): exists is False, zipped)
- return [path for path, exists in missing]
-
-
-def get_platform_launcher():
- launcher = globals()[platform.system() + "VPNLauncher"]
- leap_assert(launcher, "Unimplemented platform launcher: %s" %
- (platform.system(),))
- return launcher()
-
-
-def _is_pkexec_in_system():
- """
- Checks the existence of the pkexec binary in system.
- """
- pkexec_path = which('pkexec')
- if len(pkexec_path) == 0:
- return False
- return True
-
-
-def _has_updown_scripts(path, warn=True):
- """
- Checks the existence of the up/down scripts and its
- exec bit if applicable.
-
- :param path: the path to be checked
- :type path: str
-
- :param warn: whether we should log the absence
- :type warn: bool
-
- :rtype: bool
- """
- is_file = os.path.isfile(path)
- if warn and not is_file:
- logger.error("Could not find up/down script %s. "
- "Might produce DNS leaks." % (path,))
-
- # XXX check if applies in win
- is_exe = False
- try:
- is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0)
- except OSError as e:
- logger.warn("%s" % (e,))
- if warn and not is_exe:
- logger.error("Up/down script %s is not executable. "
- "Might produce DNS leaks." % (path,))
- return is_file and is_exe
-
-
-def _has_other_files(path, warn=True):
- """
- Checks the existence of other important files.
-
- :param path: the path to be checked
- :type path: str
-
- :param warn: whether we should log the absence
- :type warn: bool
-
- :rtype: bool
- """
- is_file = os.path.isfile(path)
- if warn and not is_file:
- logger.warning("Could not find file during checks: %s. " % (
- path,))
- return is_file
-
-
-def _is_auth_agent_running():
- """
- Checks if a polkit daemon is running.
-
- :return: True if it's running, False if it's not.
- :rtype: boolean
- """
- ps = 'ps aux | grep polkit-%s-authentication-agent-1'
- opts = (ps % case for case in ['[g]nome', '[k]de'])
- is_running = map(lambda l: commands.getoutput(l), opts)
- return any(is_running)
-
-
-def _try_to_launch_agent():
- """
- Tries to launch a polkit daemon.
- """
- env = None
- if flags.STANDALONE is True:
- env = {"PYTHONPATH": os.path.abspath('../../../../lib/')}
- try:
- # We need to quote the command because subprocess call
- # will do "sh -c 'foo'", so if we do not quoute it we'll end
- # up with a invocation to the python interpreter. And that
- # is bad.
- subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
- shell=True, env=env)
- except Exception as exc:
- logger.exception(exc)
-
-
-class LinuxVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Linux platform
- """
-
- PKEXEC_BIN = 'pkexec'
- OPENVPN_BIN = 'openvpn'
- OPENVPN_BIN_PATH = os.path.join(
- get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
-
- SYSTEM_CONFIG = "/etc/leap"
- UP_DOWN_FILE = "resolv-update"
- UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
-
- # We assume this is there by our openvpn dependency, and
- # we will put it there on the bundle too.
- # TODO adapt to the bundle path.
- OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
- OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
- OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
- OPENVPN_DOWN_ROOT_BASE,
- OPENVPN_DOWN_ROOT_FILE)
-
- UPDOWN_FILES = (UP_DOWN_PATH,)
- POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
- OTHER_FILES = (POLKIT_PATH, )
-
- def missing_other_files(self):
- """
- 'Extend' the VPNLauncher's missing_other_files to check if the polkit
- files is outdated. If the polkit file that is in OTHER_FILES exists but
- is not up to date, it is added to the missing list.
-
- :returns: a list of missing files
- :rtype: list of str
- """
- missing = VPNLauncher.missing_other_files.im_func(self)
- polkit_file = LinuxPolicyChecker.get_polkit_path()
- if polkit_file not in missing:
- if privilege_policies.is_policy_outdated(self.OPENVPN_BIN_PATH):
- missing.append(polkit_file)
-
- return missing
-
- @classmethod
- def cmd_for_missing_scripts(kls, frompath, pol_file):
- """
- Returns a sh script that can copy the missing files.
-
- :param frompath: The path where the up/down scripts live
- :type frompath: str
- :param pol_file: The path where the dynamically generated
- policy file lives
- :type pol_file: str
-
- :rtype: str
- """
- to = kls.SYSTEM_CONFIG
-
- cmd = '#!/bin/sh\n'
- cmd += 'mkdir -p "%s"\n' % (to, )
- cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
- cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
- cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
-
- return cmd
-
- @classmethod
- def maybe_pkexec(kls):
- """
- Checks whether pkexec is available in the system, and
- returns the path if found.
-
- Might raise EIPNoPkexecAvailable or EIPNoPolkitAuthAgentAvailable
-
- :returns: a list of the paths where pkexec is to be found
- :rtype: list
- """
- if _is_pkexec_in_system():
- if not _is_auth_agent_running():
- _try_to_launch_agent()
- sleep(0.5)
- if _is_auth_agent_running():
- pkexec_possibilities = which(kls.PKEXEC_BIN)
- leap_assert(len(pkexec_possibilities) > 0,
- "We couldn't find pkexec")
- return pkexec_possibilities
- else:
- logger.warning("No polkit auth agent found. pkexec " +
- "will use its own auth agent.")
- raise EIPNoPolkitAuthAgentAvailable()
- else:
- logger.warning("System has no pkexec")
- raise EIPNoPkexecAvailable()
-
- @classmethod
- def maybe_down_plugin(kls):
- """
- Returns the path of the openvpn down-root-plugin, searching first
- in the relative path for the standalone bundle, and then in the system
- path where the debian package puts it.
-
- :returns: the path where the plugin was found, or None
- :rtype: str or None
- """
- cwd = os.getcwd()
- rel_path_in_bundle = os.path.join(
- 'apps', 'eip', 'files', kls.OPENVPN_DOWN_ROOT_FILE)
- abs_path_in_bundle = os.path.join(cwd, rel_path_in_bundle)
- if os.path.isfile(abs_path_in_bundle):
- return abs_path_in_bundle
- abs_path_in_system = kls.OPENVPN_DOWN_ROOT_PATH
- if os.path.isfile(abs_path_in_system):
- return abs_path_in_system
-
- logger.warning("We could not find the down-root-plugin, so no updown "
- "scripts will be run. DNS leaks are likely!")
- return None
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="unix", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command. It will
- look for openvpn in the regular paths and algo in
- path_prefix/apps/eip/ (in case standalone is set)
-
- Might raise:
- VPNLauncherException,
- OpenVPNNotFoundException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
-
- kwargs = {}
- if flags.STANDALONE:
- kwargs['path_extension'] = os.path.join(
- get_path_prefix(), "..", "apps", "eip")
-
- openvpn_possibilities = which(self.OPENVPN_BIN, **kwargs)
-
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = []
-
- pkexec = self.maybe_pkexec()
- if pkexec:
- args.append(openvpn)
- openvpn = first(pkexec)
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
-
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', getpass.getuser(),
- # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- if socket_port == "unix": # that's always the case for linux
- args += [
- '--management-client-user', getpass.getuser()
- ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- plugin_path = self.maybe_down_plugin()
- # If we do not have the down plugin neither in the bundle
- # nor in the system, we do not do updown scripts. The alternative
- # is leaving the user without the ability to restore dns and routes
- # to its original state.
-
- if plugin_path and _has_updown_scripts(self.UP_DOWN_PATH):
- args += [
- '--up', self.UP_DOWN_PATH,
- '--down', self.UP_DOWN_PATH,
- ##############################################################
- # For the time being we are disabling the usage of the
- # down-root plugin, because it doesn't quite work as
- # expected (i.e. it doesn't run route -del as root
- # when finishing, so it fails to properly
- # restart/quit)
- ##############################################################
- # '--plugin', plugin_path,
- # '\'script_type=down %s\'' % self.UP_DOWN_PATH
- ]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (openvpn, " ".join(args)))
-
- return [openvpn] + args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {
- "LD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
- }
-
-
-class DarwinVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Darwin Platform
- """
-
- COCOASUDO = "cocoasudo"
- # XXX need the good old magic translate for these strings
- # (look for magic in 0.2.0 release)
- SUDO_MSG = ("Bitmask needs administrative privileges to run "
- "Encrypted Internet.")
- INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
- "missing scripts and fix permissions.\"")
-
- INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../")
- INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
- OPENVPN_BIN = 'openvpn.leap'
- OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
- OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
- INSTALL_PATH_ESCAPED,)
-
- UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
- DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
- OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
-
- UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
- OTHER_FILES = []
-
- @classmethod
- def cmd_for_missing_scripts(kls, frompath):
- """
- Returns a command that can copy the missing scripts.
- :rtype: str
- """
- to = kls.OPENVPN_PATH_ESCAPED
- cmd = "#!/bin/sh\nmkdir -p %s\ncp \"%s/\"* %s\nchmod 744 %s/*" % (
- to, frompath, to, to)
- return cmd
-
- @classmethod
- def maybe_kextloaded(kls):
- """
- Checks if the needed kext is loaded before launching openvpn.
- """
- return bool(commands.getoutput('kextstat | grep "leap.tun"'))
-
- def _get_resource_path(self):
- """
- Returns the absolute path to the app resources directory
-
- :rtype: str
- """
- return os.path.abspath(
- os.path.join(
- os.getcwd(),
- "../../Contents/Resources"))
-
- def _get_icon_path(self):
- """
- Returns the absolute path to the app icon
-
- :rtype: str
- """
- return os.path.join(self._get_resource_path(),
- "leap-client.tiff")
-
- def get_cocoasudo_ovpn_cmd(self):
- """
- Returns a string with the cocoasudo command needed to run openvpn
- as admin with a nice password prompt. The actual command needs to be
- appended.
-
- :rtype: (str, list)
- """
- iconpath = self._get_icon_path()
- has_icon = os.path.isfile(iconpath)
- args = ["--icon=%s" % iconpath] if has_icon else []
- args.append("--prompt=%s" % (self.SUDO_MSG,))
-
- return self.COCOASUDO, args
-
- def get_cocoasudo_installmissing_cmd(self):
- """
- Returns a string with the cocoasudo command needed to install missing
- files as admin with a nice password prompt. The actual command needs to
- be appended.
-
- :rtype: (str, list)
- """
- iconpath = self._get_icon_path()
- has_icon = os.path.isfile(iconpath)
- args = ["--icon=%s" % iconpath] if has_icon else []
- args.append("--prompt=%s" % (self.INSTALL_MSG,))
-
- return self.COCOASUDO, args
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="unix", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command
-
- Might raise VPNException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
-
- if not self.maybe_kextloaded():
- raise EIPNoTunKextLoaded
-
- kwargs = {}
- if flags.STANDALONE:
- kwargs['path_extension'] = os.path.join(
- get_path_prefix(), "..", "apps", "eip")
-
- openvpn_possibilities = which(
- self.OPENVPN_BIN,
- **kwargs)
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = [openvpn]
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- user = getpass.getuser()
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', user,
- # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- if socket_port == "unix":
- args += [
- '--management-client-user', user
- ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- if _has_updown_scripts(self.UP_SCRIPT):
- args += [
- '--up', '\"%s\"' % (self.UP_SCRIPT,),
- ]
-
- if _has_updown_scripts(self.DOWN_SCRIPT):
- args += [
- '--down', '\"%s\"' % (self.DOWN_SCRIPT,)
- ]
-
- # should have the down script too
- if _has_updown_scripts(self.OPENVPN_DOWN_PLUGIN):
- args += [
- ###########################################################
- # For the time being we are disabling the usage of the
- # down-root plugin, because it doesn't quite work as
- # expected (i.e. it doesn't run route -del as root
- # when finishing, so it fails to properly
- # restart/quit)
- ###########################################################
- # '--plugin', self.OPENVPN_DOWN_PLUGIN,
- # '\'%s\'' % self.DOWN_SCRIPT
- ]
-
- # we set user to be passed to the up/down scripts
- args += [
- '--setenv', "LEAPUSER", "%s" % (user,)]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- command, cargs = self.get_cocoasudo_ovpn_cmd()
- cmd_args = cargs + args
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (command, " ".join(cmd_args)))
-
- return [command] + cmd_args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {
- "DYLD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
- }
-
-
-class WindowsVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Windows platform
- """
-
- OPENVPN_BIN = 'openvpn_leap.exe'
-
- # XXX UPDOWN_FILES ... we do not have updown files defined yet!
- # (and maybe we won't)
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="9876", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command. It will
- look for openvpn in the regular paths and algo in
- path_prefix/apps/eip/ (in case standalone is set)
-
- Might raise VPNException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: the openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
- leap_assert(socket_port != "unix",
- "We cannot use unix sockets in windows!")
-
- openvpn_possibilities = which(
- self.OPENVPN_BIN,
- path_extension=os.path.join(get_path_prefix(),
- "..", "apps", "eip"))
-
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = []
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- # We make it log to a file because we cannot attach to the
- # openvpn process' stdout since it's a process with more
- # privileges than we are
- '--log-append', 'eip.log',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', getpass.getuser(),
- # #'--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (openvpn, " ".join(args)))
-
- return [openvpn] + args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {}
-
-
-if __name__ == "__main__":
- logger = logging.getLogger(name='leap')
- logger.setLevel(logging.DEBUG)
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
- formatter = logging.Formatter(
- '%(asctime)s '
- '- %(name)s - %(levelname)s - %(message)s')
- console.setFormatter(formatter)
- logger.addHandler(console)
-
- try:
- abs_launcher = VPNLauncher()
- except Exception as e:
- assert isinstance(e, TypeError), "Something went wrong"
- print "Abstract Prefixer class is working as expected"
-
- vpnlauncher = get_platform_launcher()
-
- eipconfig = EIPConfig()
- eipconfig.set_api_version('1')
- if eipconfig.load("leap/providers/bitmask.net/eip-service.json"):
- provider = ProviderConfig()
- if provider.load("leap/providers/bitmask.net/provider.json"):
- vpnlauncher.get_vpn_command(eipconfig=eipconfig,
- providerconfig=provider,
- socket_host="/blah")
diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py
index 15ac812b..707967e0 100644
--- a/src/leap/bitmask/services/eip/vpnprocess.py
+++ b/src/leap/bitmask/services/eip/vpnprocess.py
@@ -27,7 +27,7 @@ import socket
from PySide import QtCore
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.services.eip.vpnlaunchers import get_platform_launcher
+from leap.bitmask.services.eip import get_vpn_launcher
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.bitmask.services.eip.udstelnet import UDSTelnet
from leap.bitmask.util import first
@@ -697,7 +697,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
self._socket_host = socket_host
self._socket_port = socket_port
- self._launcher = get_platform_launcher()
+ self._launcher = get_vpn_launcher()
self._last_state = None
self._last_status = None
diff --git a/src/leap/bitmask/services/eip/windowsvpnlauncher.py b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
new file mode 100644
index 00000000..3f1ed43b
--- /dev/null
+++ b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# windowsvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Windows VPN launcher implementation.
+"""
+import logging
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.common.check import leap_assert
+
+logger = logging.getLogger(__name__)
+
+
+class WindowsVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Windows platform
+ """
+
+ OPENVPN_BIN = 'openvpn_leap.exe'
+
+ # XXX UPDOWN_FILES ... we do not have updown files defined yet!
+ # (and maybe we won't)
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="9876", openvpn_verb=1):
+ """
+ Returns the Windows implementation for the vpn launching command.
+
+ Might raise:
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ leap_assert(socket_port != "unix",
+ "We cannot use unix sockets in windows!")
+
+ # we use `super` in order to send the class to use
+ command = super(WindowsVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ return command
diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
index cac91440..d348661d 100644
--- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py
+++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
@@ -14,27 +14,28 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
Soledad bootstrapping
"""
-
import logging
import os
import socket
+from ssl import SSLError
+
from PySide import QtCore
from u1db import errors as u1db_errors
from leap.bitmask.config import flags
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.srpauth import SRPAuth
+from leap.bitmask.services import download_service_config
from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper
from leap.bitmask.services.soledad.soledadconfig import SoledadConfig
-from leap.bitmask.util.request_helpers import get_content
+from leap.bitmask.util import is_file, is_empty_file
from leap.bitmask.util import get_path_prefix
-from leap.common.check import leap_assert, leap_assert_type
-from leap.common.files import get_mtime
+from leap.common.check import leap_assert, leap_assert_type, leap_check
+from leap.common.files import which
from leap.keymanager import KeyManager, openpgp
from leap.keymanager.errors import KeyNotFound
from leap.soledad.client import Soledad
@@ -42,17 +43,28 @@ from leap.soledad.client import Soledad
logger = logging.getLogger(__name__)
+# TODO these exceptions could be moved to soledad itself
+# after settling this down.
+
+class SoledadSyncError(Exception):
+ message = "Error while syncing Soledad"
+
+
+class SoledadInitError(Exception):
+ message = "Error while initializing Soledad"
+
+
class SoledadBootstrapper(AbstractBootstrapper):
"""
Soledad init procedure
"""
-
SOLEDAD_KEY = "soledad"
KEYMANAGER_KEY = "keymanager"
PUBKEY_KEY = "user[public_key]"
MAX_INIT_RETRIES = 10
+ MAX_SYNC_RETRIES = 10
# All dicts returned are of the form
# {"passed": bool, "error": str}
@@ -68,6 +80,7 @@ class SoledadBootstrapper(AbstractBootstrapper):
self._soledad_config = None
self._keymanager = None
self._download_if_needed = False
+
self._user = ""
self._password = ""
self._srpauth = None
@@ -109,68 +122,169 @@ class SoledadBootstrapper(AbstractBootstrapper):
"""
self._soledad_retries += 1
+ def _get_db_paths(self, uuid):
+ """
+ Returns the secrets and local db paths needed for soledad
+ initialization
+
+ :param uuid: uuid for user
+ :type uuid: str
+
+ :return: a tuple with secrets, local_db paths
+ :rtype: tuple
+ """
+ prefix = os.path.join(get_path_prefix(), "leap", "soledad")
+ secrets = "%s/%s.secret" % (prefix, uuid)
+ local_db = "%s/%s.db" % (prefix, uuid)
+
+ # We remove an empty file if found to avoid complains
+ # about the db not being properly initialized
+ if is_file(local_db) and is_empty_file(local_db):
+ try:
+ os.remove(local_db)
+ except OSError:
+ logger.warning("Could not remove empty file %s"
+ % local_db)
+ return secrets, local_db
+
# initialization
def load_and_sync_soledad(self):
"""
Once everthing is in the right place, we instantiate and sync
Soledad
-
- :param srp_auth: SRPAuth object used
- :type srp_auth: SRPAuth
"""
- srp_auth = self.srpauth
- uuid = srp_auth.get_uid()
+ # TODO this method is still too large
+ uuid = self.srpauth.get_uid()
+ token = self.srpauth.get_token()
- prefix = os.path.join(get_path_prefix(), "leap", "soledad")
- secrets_path = "%s/%s.secret" % (prefix, uuid)
- local_db_path = "%s/%s.db" % (prefix, uuid)
+ secrets_path, local_db_path = self._get_db_paths(uuid)
# TODO: Select server based on timezone (issue #3308)
server_dict = self._soledad_config.get_hosts()
- if server_dict.keys():
- selected_server = server_dict[server_dict.keys()[0]]
- server_url = "https://%s:%s/user-%s" % (
- selected_server["hostname"],
- selected_server["port"],
- uuid)
+ if not server_dict.keys():
+ # XXX raise more specific exception, and catch it properly!
+ raise Exception("No soledad server found")
- logger.debug("Using soledad server url: %s" % (server_url,))
+ selected_server = server_dict[server_dict.keys()[0]]
+ server_url = "https://%s:%s/user-%s" % (
+ selected_server["hostname"],
+ selected_server["port"],
+ uuid)
+ logger.debug("Using soledad server url: %s" % (server_url,))
- cert_file = self._provider_config.get_ca_cert_path()
+ cert_file = self._provider_config.get_ca_cert_path()
- # TODO: If selected server fails, retry with another host
- # (issue #3309)
+ logger.debug('local_db:%s' % (local_db_path,))
+ logger.debug('secrets_path:%s' % (secrets_path,))
+
+ try:
+ self._try_soledad_init(
+ uuid, secrets_path, local_db_path,
+ server_url, cert_file, token)
+ except:
+ # re-raise the exceptions from try_init,
+ # we're currently handling the retries from the
+ # soledad-launcher in the gui.
+ raise
+
+ leap_check(self._soledad is not None,
+ "Null soledad, error while initializing")
+
+ # and now, let's sync
+ sync_tries = self.MAX_SYNC_RETRIES
+ while sync_tries > 0:
try:
- self._soledad = Soledad(
- uuid,
- self._password.encode("utf-8"),
- secrets_path=secrets_path,
- local_db_path=local_db_path,
- server_url=server_url,
- cert_file=cert_file,
- auth_token=srp_auth.get_token())
- self._soledad.sync()
-
- # XXX All these errors should be handled by soledad itself,
- # and return a subclass of SoledadInitializationFailed
- except socket.timeout:
- logger.debug("SOLEDAD TIMED OUT...")
- self.soledad_timeout.emit()
- except socket.error as exc:
- logger.error("Socket error while initializing soledad")
- self.soledad_failed.emit()
- except u1db_errors.Unauthorized:
- logger.error("Error while initializing soledad "
- "(unauthorized).")
- self.soledad_failed.emit()
- except Exception as exc:
- logger.error("Unhandled error while initializating "
+ self._try_soledad_sync()
+
+ # at this point, sometimes the client
+ # gets stuck and does not progress to
+ # the _gen_key step. XXX investigate.
+ logger.debug("Soledad has been synced.")
+ # so long, and thanks for all the fish
+ return
+ except SoledadSyncError:
+ # maybe it's my connection, but I'm getting
+ # ssl handshake timeouts and read errors quite often.
+ # A particularly big sync is a disaster.
+ # This deserves further investigation, maybe the
+ # retry strategy can be pushed to u1db, or at least
+ # it's something worthy to talk about with the
+ # ubuntu folks.
+ sync_tries -= 1
+ continue
+
+ # reached bottom, failed to sync
+ # and there's nothing we can do...
+ self.soledad_failed.emit()
+ raise SoledadSyncError()
+
+ def _try_soledad_init(self, uuid, secrets_path, local_db_path,
+ server_url, cert_file, auth_token):
+ """
+ Tries to initialize soledad.
+
+ :param uuid: user identifier
+ :param secrets_path: path to secrets file
+ :param local_db_path: path to local db file
+ :param server_url: soledad server uri
+ :param cert_file: path to the certificate of the ca used
+ to validate the SSL certificate used by the remote
+ soledad server.
+ :type cert_file: str
+ :param auth token: auth token
+ :type auth_token: str
+ """
+ # TODO: If selected server fails, retry with another host
+ # (issue #3309)
+ try:
+ self._soledad = Soledad(
+ uuid,
+ self._password.encode("utf-8"),
+ secrets_path=secrets_path,
+ local_db_path=local_db_path,
+ server_url=server_url,
+ cert_file=cert_file,
+ auth_token=auth_token)
+
+ # XXX All these errors should be handled by soledad itself,
+ # and return a subclass of SoledadInitializationFailed
+
+ # recoverable, will guarantee retries
+ except socket.timeout:
+ logger.debug("SOLEDAD initialization TIMED OUT...")
+ self.soledad_timeout.emit()
+ except socket.error as exc:
+ logger.error("Socket error while initializing soledad")
+ self.soledad_timeout.emit()
+
+ # unrecoverable
+ except u1db_errors.Unauthorized:
+ logger.error("Error while initializing soledad "
+ "(unauthorized).")
+ self.soledad_failed.emit()
+ except Exception as exc:
+ logger.exception("Unhandled error while initializating "
"soledad: %r" % (exc,))
- raise
- else:
- raise Exception("No soledad server found")
+ self.soledad_failed.emit()
+
+ def _try_soledad_sync(self):
+ """
+ Tries to sync soledad.
+ Raises SoledadSyncError if not successful.
+ """
+ try:
+ logger.error("trying to sync soledad....")
+ self._soledad.sync()
+ except SSLError as exc:
+ logger.error("%r" % (exc,))
+ raise SoledadSyncError("Failed to sync soledad")
+ except Exception as exc:
+ logger.exception("Unhandled error while syncing"
+ "soledad: %r" % (exc,))
+ self.soledad_failed.emit()
+ raise SoledadSyncError("Failed to sync soledad")
def _download_config(self):
"""
@@ -179,86 +293,46 @@ class SoledadBootstrapper(AbstractBootstrapper):
leap_assert(self._provider_config,
"We need a provider configuration!")
-
logger.debug("Downloading Soledad config for %s" %
(self._provider_config.get_domain(),))
self._soledad_config = SoledadConfig()
-
- headers = {}
- mtime = get_mtime(
- os.path.join(get_path_prefix(), "leap", "providers",
- self._provider_config.get_domain(),
- "soledad-service.json"))
-
- if self._download_if_needed and mtime:
- headers['if-modified-since'] = mtime
-
- api_version = self._provider_config.get_api_version()
-
- # there is some confusion with this uri,
- config_uri = "%s/%s/config/soledad-service.json" % (
- self._provider_config.get_api_uri(),
- api_version)
- logger.debug('Downloading soledad config from: %s' % config_uri)
-
- # TODO factor out this srpauth protected get (make decorator)
- srp_auth = self.srpauth
- session_id = srp_auth.get_session_id()
- cookies = None
- if session_id:
- cookies = {"_session_id": session_id}
-
- res = self._session.get(config_uri,
- verify=self._provider_config
- .get_ca_cert_path(),
- headers=headers,
- cookies=cookies)
- res.raise_for_status()
-
- self._soledad_config.set_api_version(api_version)
-
- # Not modified
- if res.status_code == 304:
- logger.debug("Soledad definition has not been modified")
- self._soledad_config.load(
- os.path.join(
- "leap", "providers",
- self._provider_config.get_domain(),
- "soledad-service.json"))
- else:
- soledad_definition, mtime = get_content(res)
-
- self._soledad_config.load(data=soledad_definition, mtime=mtime)
- self._soledad_config.save(["leap",
- "providers",
- self._provider_config.get_domain(),
- "soledad-service.json"])
-
+ download_service_config(
+ self._provider_config,
+ self._soledad_config,
+ self._session,
+ self._download_if_needed)
+
+ # soledad config is ok, let's proceed to load and sync soledad
+ # XXX but honestly, this is a pretty strange entry point for that.
+ # it feels like it should be the other way around:
+ # load_and_sync, and from there, if needed, call download_config
self.load_and_sync_soledad()
- def _gen_key(self, _):
+ def _get_gpg_bin_path(self):
"""
- Generates the key pair if needed, uploads it to the webapp and
- nickserver
+ Returns the path to gpg binary.
+ :returns: the gpg binary path
+ :rtype: str
"""
- leap_assert(self._provider_config,
- "We need a provider configuration!")
-
- address = "%s@%s" % (self._user, self._provider_config.get_domain())
-
- logger.debug("Retrieving key for %s" % (address,))
-
- srp_auth = self.srpauth
-
- # TODO: use which implementation with known paths
# TODO: Fix for Windows
- gpgbin = "/usr/bin/gpg"
-
+ gpgbin = None
if flags.STANDALONE:
- gpgbin = os.path.join(get_path_prefix(),
- "..", "apps", "mail", "gpg")
+ gpgbin = os.path.join(
+ get_path_prefix(), "..", "apps", "mail", "gpg")
+ else:
+ gpgbin = which("gpg")
+ leap_check(gpgbin is not None, "Could not find gpg binary")
+ return gpgbin
+ def _init_keymanager(self, address):
+ """
+ Initializes the keymanager.
+ :param address: the address to initialize the keymanager with.
+ :type address: str
+ """
+ srp_auth = self.srpauth
+ logger.debug('initializing keymanager...')
self._keymanager = KeyManager(
address,
"https://nicknym.%s:6425" % (self._provider_config.get_domain(),),
@@ -269,10 +343,25 @@ class SoledadBootstrapper(AbstractBootstrapper):
api_uri=self._provider_config.get_api_uri(),
api_version=self._provider_config.get_api_version(),
uid=srp_auth.get_uid(),
- gpgbinary=gpgbin)
+ gpgbinary=self._get_gpg_bin_path())
+
+ def _gen_key(self, _):
+ """
+ Generates the key pair if needed, uploads it to the webapp and
+ nickserver
+ """
+ leap_assert(self._provider_config is not None,
+ "We need a provider configuration!")
+ leap_assert(self._soledad is not None,
+ "We need a non-null soledad to generate keys")
+
+ address = "%s@%s" % (self._user, self._provider_config.get_domain())
+ self._init_keymanager(address)
+ logger.debug("Retrieving key for %s" % (address,))
+
try:
- self._keymanager.get_key(address, openpgp.OpenPGPKey,
- private=True, fetch_remote=False)
+ self._keymanager.get_key(
+ address, openpgp.OpenPGPKey, private=True, fetch_remote=False)
except KeyNotFound:
logger.debug("Key not found. Generating key for %s" % (address,))
self._keymanager.gen_key(openpgp.OpenPGPKey)
diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py
index f762a350..b58e6e3b 100644
--- a/src/leap/bitmask/util/__init__.py
+++ b/src/leap/bitmask/util/__init__.py
@@ -62,3 +62,17 @@ def update_modification_ts(path):
"""
os.utime(path, None)
return get_modification_ts(path)
+
+
+def is_file(path):
+ """
+ Returns True if the path exists and is a file.
+ """
+ return os.path.isfile(path)
+
+
+def is_empty_file(path):
+ """
+ Returns True if the file at path is empty.
+ """
+ return os.stat(path).st_size is 0
diff --git a/src/leap/bitmask/util/constants.py b/src/leap/bitmask/util/constants.py
index 63f6b1f7..e6a6bdce 100644
--- a/src/leap/bitmask/util/constants.py
+++ b/src/leap/bitmask/util/constants.py
@@ -16,4 +16,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SIGNUP_TIMEOUT = 5
-REQUEST_TIMEOUT = 10
+REQUEST_TIMEOUT = 15