summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2014-09-26 10:15:06 -0300
committerTomás Touceda <chiiph@leap.se>2014-09-26 10:15:06 -0300
commit6f7177f3ca359f8c3e74d094b0dcd0f9239fb069 (patch)
treef04b0eb5c4665a129c5c49719b2da65d325b111f
parente8c028f3eb6cd0681f039c456dc1ac45c98d598f (diff)
parent0db3d5a4aecc0a3c271b92c6187922c50d46df0a (diff)
Merge branch 'release-0.7.0'0.7.0rc20.7.0rc1
-rw-r--r--.tx/config2
-rw-r--r--CHANGELOG.rst28
-rw-r--r--Makefile30
-rw-r--r--data/images/countries/xx.pngbin0 -> 254 bytes
-rw-r--r--data/resources/flags.qrc1
-rw-r--r--data/resources/locale.qrc1
-rw-r--r--data/ts/en_US.ts626
-rw-r--r--docs/man/bitmask-root.1.rst13
-rwxr-xr-xpkg/linux/bitmask-root147
-rwxr-xr-xpkg/postmkvenv.sh8
-rw-r--r--pkg/requirements-dev.pip10
-rw-r--r--pkg/requirements.pip7
-rwxr-xr-xpkg/scripts/bootstrap_develop.sh48
-rw-r--r--relnotes.txt47
-rw-r--r--src/leap/bitmask/backend/components.py12
-rw-r--r--src/leap/bitmask/crypto/certs.py12
-rw-r--r--src/leap/bitmask/frontend_app.py27
-rw-r--r--src/leap/bitmask/gui/account.py49
-rw-r--r--src/leap/bitmask/gui/app.py66
-rw-r--r--src/leap/bitmask/gui/eip_preferenceswindow.py3
-rw-r--r--src/leap/bitmask/gui/eip_status.py5
-rw-r--r--src/leap/bitmask/gui/flashable.py76
-rw-r--r--src/leap/bitmask/gui/mainwindow.py231
-rw-r--r--src/leap/bitmask/gui/passwordwindow.py273
-rw-r--r--src/leap/bitmask/gui/preferences_account_page.py139
-rw-r--r--src/leap/bitmask/gui/preferences_email_page.py35
-rw-r--r--src/leap/bitmask/gui/preferences_vpn_page.py143
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py505
-rw-r--r--src/leap/bitmask/gui/providers.py2
-rw-r--r--src/leap/bitmask/gui/ui/eippreferences.ui102
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui17
-rw-r--r--src/leap/bitmask/gui/ui/password_change.ui182
-rw-r--r--src/leap/bitmask/gui/ui/preferences.ui234
-rw-r--r--src/leap/bitmask/gui/ui/preferences_account_page.ui104
-rw-r--r--src/leap/bitmask/gui/ui/preferences_email_page.ui32
-rw-r--r--src/leap/bitmask/gui/ui/preferences_vpn_page.ui69
-rw-r--r--src/leap/bitmask/gui/wizard.py20
-rw-r--r--src/leap/bitmask/platform_init/initializers.py26
-rw-r--r--src/leap/bitmask/platform_init/locks.py59
-rw-r--r--src/leap/bitmask/services/eip/conductor.py6
-rw-r--r--src/leap/bitmask/services/eip/eipconfig.py43
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py99
-rw-r--r--src/leap/bitmask/services/eip/vpnprocess.py20
-rw-r--r--src/leap/bitmask/services/mail/conductor.py19
-rw-r--r--src/leap/bitmask/services/mail/emailfirewall.py115
-rw-r--r--src/leap/bitmask/services/mail/smtpbootstrapper.py2
-rw-r--r--src/leap/bitmask/util/__init__.py2
-rw-r--r--src/leap/bitmask/util/credentials.py24
-rw-r--r--src/leap/bitmask/util/privilege_policies.py98
49 files changed, 2492 insertions, 1327 deletions
diff --git a/.tx/config b/.tx/config
index 31ea5b1c..c52bc2d7 100644
--- a/.tx/config
+++ b/.tx/config
@@ -1,7 +1,7 @@
[main]
host = https://www.transifex.com
-[bitmask.bitmask]
+[bitmask.bitmask_client]
file_filter = data/translations/<lang>.ts
source_file = data/ts/en_US.ts
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index c47e531a..3c863657 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,34 @@ History
2014
====
+0.7.0 September 26 -- the "one time download, all time updates" release:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+- Select current provider on EIP preferences. Closes #5815.
+- Handle logout correctly when we stop_services to launch the
+ wizard. Related to #5815.
+- Properly remove /tmp/bitmask.lock. Closes #5866.
+- Hide EIP Start button and display correct warning on missing helpers
+ files. Closes #5945.
+- Save default provider if changed on the combo box. Closes #5995.
+- Update the EIP status on provider change. Closes #5996.
+- Update and get ready to start a provider on change. Closes #5997.
+- Use python2 to run bitmask-root to work fine on systems with python3
+ as default. Closes #6048.
+- Use python2.7 in bitmask-root shebang since is the common name for
+ python 2 in Ubuntu, Debian, Arch. Related to #6048.
+- Remove dict comprenension in util, for 2.6 compat.
+- Login shall not wait for eip to finish if eip is not able to
+ start. Closes #5994
+- Properly send the token for querying the EIP certificate. Fixes
+ #6060.
+- Code cleanup and logging improvements.
+- Add email firewall blocking other users to access bitmask imap &
+ smtp. Closes #6040
+- Remove the Advanced Key Management since we don't support stable
+ mail yet. Closes #6087.
+- Single combined preferences window. Closes #4704, #4119, #5885.
+
0.6.1 August 15 -- the "knock knock knocking on beta's door" release:
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/Makefile b/Makefile
index 0008cb39..e7173c80 100644
--- a/Makefile
+++ b/Makefile
@@ -19,9 +19,19 @@ TRANSLAT_DIR = data/translations
#Project file, used for translations
PROJFILE = data/bitmask.pro
-#UI files to compile
-UI_FILES = loggerwindow.ui mainwindow.ui wizard.ui login.ui preferences.ui eip_status.ui mail_status.ui eippreferences.ui advanced_key_management.ui
-#Qt resource files to compile
+# UI files to compile
+UI_FILES = \
+ loggerwindow.ui \
+ wizard.ui \
+ mainwindow.ui login.ui eip_status.ui mail_status.ui \
+ preferences.ui \
+ preferences_account_page.ui \
+ preferences_vpn_page.ui \
+ preferences_email_page.ui \
+ password_change.ui \
+ advanced_key_management.ui
+
+# Qt resource files to compile
RESOURCES = icons.qrc flags.qrc locale.qrc loggerwindow.qrc
#pyuic4 and pyrcc4 binaries
@@ -95,5 +105,19 @@ resource_graph:
./pkg/scripts/monitor_resource.zsh `pgrep bitmask` $(RESOURCE_TIME)
display bitmask-resources.png
+get_wheels:
+ pip install --upgrade setuptools
+ pip install --upgrade pip
+ pip install wheel
+
+gather_wheels:
+ pip wheel --wheel-dir=../wheelhouse pyzmq --build-option "--zmq=bundled"
+ # because fuck u1db externals, that's why...
+ pip wheel --wheel-dir=../wheelhouse --allow-external dirspec --allow-unverified dirspec --allow-external u1db --allow-unverified u1db -r pkg/requirements.pip
+
+install_wheel:
+ # if it's the first time, you'll need to get_wheels first
+ pip install --pre --use-wheel --no-index --find-links=../wheelhouse -r pkg/requirements.pip
+
clean :
$(RM) $(COMPILED_UI) $(COMPILED_RESOURCES) $(COMPILED_UI:.py=.pyc) $(COMPILED_RESOURCES:.py=.pyc)
diff --git a/data/images/countries/xx.png b/data/images/countries/xx.png
new file mode 100644
index 00000000..abd36d39
--- /dev/null
+++ b/data/images/countries/xx.png
Binary files differ
diff --git a/data/resources/flags.qrc b/data/resources/flags.qrc
index 8bdc9c4c..aeecc54f 100644
--- a/data/resources/flags.qrc
+++ b/data/resources/flags.qrc
@@ -58,6 +58,7 @@
<file>../images/countries/us.png</file>
<file>../images/countries/ve.png</file>
<file>../images/countries/vn.png</file>
+ <file>../images/countries/xx.png</file>
<file>../images/countries/za.png</file>
</qresource>
</RCC> \ No newline at end of file
diff --git a/data/resources/locale.qrc b/data/resources/locale.qrc
index ba466c36..787b0025 100644
--- a/data/resources/locale.qrc
+++ b/data/resources/locale.qrc
@@ -2,5 +2,6 @@
<qresource>
<file>../translations/vi.qm</file>
<file>../translations/en_GB.qm</file>
+<file>../translations/es.qm</file>
</qresource>
</RCC>
diff --git a/data/ts/en_US.ts b/data/ts/en_US.ts
index cf74d7b6..041cdc44 100644
--- a/data/ts/en_US.ts
+++ b/data/ts/en_US.ts
@@ -172,7 +172,7 @@ Export canceled.</source>
<context>
<name>ComplainDialog</name>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="419"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="414"/>
<source>Ok, thanks</source>
<translation type="unfinished"></translation>
</message>
@@ -228,22 +228,22 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="153"/>
+ <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="159"/>
<source>Gateway settings for provider &apos;{0}&apos; saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="230"/>
+ <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="236"/>
<source>There was a problem with configuration files.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="122"/>
+ <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="125"/>
<source> (uninitialized)</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="245"/>
+ <location filename="../src/leap/bitmask/gui/eip_preferenceswindow.py" line="251"/>
<source>This is an uninitialized provider, please log in first.</source>
<translation type="unfinished"></translation>
</message>
@@ -261,12 +261,12 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/eip_status.ui" line="66"/>
+ <location filename="../src/leap/bitmask/gui/ui/eip_status.ui" line="79"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/eip_status.ui" line="82"/>
+ <location filename="../src/leap/bitmask/gui/ui/eip_status.ui" line="95"/>
<source>Traffic is being routed in the clear</source>
<translation type="unfinished"></translation>
</message>
@@ -280,149 +280,187 @@ Export canceled.</source>
<source>Turn Off</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/eip_status.ui" line="259"/>
+ <source>Cancel</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>EIPStatusWidget</name>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="411"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="429"/>
<source>Turn ON</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="521"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="569"/>
<source>Authenticating...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="529"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="578"/>
<source>Retrieving configuration...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="531"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="580"/>
<source>Waiting to start...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="533"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="582"/>
<source>Assigning IP</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="535"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="584"/>
<source>Reconnecting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="543"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="592"/>
<source>Unable to start VPN, it&apos;s already running.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="304"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="320"/>
<source>disabled</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="565"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="614"/>
<source>{0}: OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="301"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="317"/>
<source>You must login to use {0}</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="570"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="619"/>
<source>{0}: Starting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="573"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="622"/>
<source>{0}: ON</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="525"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="576"/>
<source>Encrypted Internet is starting</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="397"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="415"/>
<source>Retry</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="433"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="478"/>
<source>Traffic is being routed in the clear.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="434"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="479"/>
<source>Network is unreachable.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="436"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="481"/>
<source>Error connecting</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="451"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="497"/>
<source>Error connecting.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="454"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="500"/>
<source>Bitmask is blocking unencrypted traffic.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="587"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="644"/>
<source>Routing traffic through: &lt;b&gt;{0}&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="634"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="690"/>
<source>Could not load {0} configuration.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="643"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="699"/>
<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/eip_status.py" line="653"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="709"/>
<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/eip_status.py" line="661"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="717"/>
<source>We could not find openvpn binary.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="682"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="746"/>
<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 andtry again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/eip_status.py" line="690"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="754"/>
<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/bitmask/gui/eip_status.py" line="700"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="764"/>
<source>{0} 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/eip_status.py" line="720"/>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="784"/>
<source>Network is unreachable</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="315"/>
+ <source>&lt;font color=red&gt;Disabled: missing helper files&lt;/font&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="731"/>
+ <source>VPN Launcher error. See the logs for more info.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/eip_status.py" line="734"/>
+ <source>Encrypted Internet failed to start</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>Form</name>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/logout.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/logout.ui" line="27"/>
+ <source>...</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/logout.ui" line="50"/>
+ <source>Logout</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>LoggerWindow</name>
@@ -462,7 +500,7 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="162"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="164"/>
<source>Save As</source>
<translation type="unfinished"></translation>
</message>
@@ -477,37 +515,37 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="193"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="195"/>
<source>Send to Pastebin.com</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="190"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="192"/>
<source>Sending to pastebin...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="225"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="207"/>
<source>Your pastebin link &lt;a href=&apos;{0}&apos;&gt;{0}&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="230"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="212"/>
<source>Pastebin OK</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="245"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="227"/>
<source>Sending logs to Pastebin failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="251"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="233"/>
<source>Pastebin Error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="250"/>
+ <location filename="../src/leap/bitmask/gui/loggerwindow.py" line="232"/>
<source>Maximum posts per day reached</source>
<translation type="unfinished"></translation>
</message>
@@ -520,72 +558,62 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/login.ui" line="75"/>
- <source>&lt;b&gt;Provider:&lt;/b&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/login.ui" line="94"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="90"/>
<source>Remember username and password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/login.ui" line="119"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="112"/>
<source>&lt;b&gt;Username:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/login.ui" line="132"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="125"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="246"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="208"/>
<source>Log In</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="124"/>
- <source>Other...</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/login.py" line="241"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="203"/>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/login.ui" line="230"/>
+ <location filename="../src/leap/bitmask/gui/ui/login.ui" line="214"/>
<source>...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="371"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="314"/>
<source>Logout</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="299"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="240"/>
<source>Please select a valid provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="304"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="245"/>
<source>Please provide a valid username</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="309"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="250"/>
<source>Please provide a valid password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="312"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="253"/>
<source>Logging in...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/login.py" line="364"/>
+ <location filename="../src/leap/bitmask/gui/login.py" line="307"/>
<source>Logging out...</source>
<translation type="unfinished"></translation>
</message>
@@ -658,11 +686,6 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mail_status.py" line="308"/>
- <source>Looking for key for this user</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
<location filename="../src/leap/bitmask/gui/mail_status.py" line="312"/>
<source>Found key! Starting mail...</source>
<translation type="unfinished"></translation>
@@ -732,72 +755,72 @@ Export canceled.</source>
<source>Starting&#xe2;&#x80;&#xa6;</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mail_status.py" line="308"/>
+ <source>Initial sync in progress, please wait...</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>MainWindow</name>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="228"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="237"/>
<source>There are new updates available, please restart.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="270"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="279"/>
<source>More...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="870"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="932"/>
<source>Help</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="342"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="351"/>
<source>&amp;Quit</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="352"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="361"/>
<source>&amp;Help</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="357"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="366"/>
<source>&amp;Wizard</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="930"/>
- <source>Hide Main Window</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="728"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="787"/>
<source> The following components will be updated:
%s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="731"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="790"/>
<source>Updates available</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="929"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="244"/>
<source>Show Main Window</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1548"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1718"/>
<source>Starting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1563"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1742"/>
<source>Not supported</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1567"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1746"/>
<source>Disabled</source>
<translation type="unfinished"></translation>
</message>
@@ -807,205 +830,255 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="347"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="356"/>
<source>About &amp;Bitmask</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="211"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="238"/>
<source>Mail is OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="719"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="778"/>
<source>The Bitmask app is ready to update, please restart the application.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="996"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1050"/>
<source>About Bitmask - %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1138"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1206"/>
<source>Unable to login: Problem with provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1219"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1385"/>
<source>Log in cancelled by the user.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1584"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1764"/>
<source>There was a problem with the provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1639"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1819"/>
<source>Something went wrong with the logout.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1606"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1786"/>
<source>Unable to connect: Problem with provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1651"/>
- <source>Login</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="305"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="314"/>
<source>&amp;Bitmask</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="362"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="371"/>
<source>Show &amp;Log</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="367"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="376"/>
<source>Create a new account...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="201"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="228"/>
<source>File</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="121"/>
- <source>Please Log In</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="332"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="341"/>
<source>Account Preferences...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="337"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="346"/>
<source>Internet Preferences...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="375"/>
+ <location filename="../src/leap/bitmask/gui/ui/mainwindow.ui" line="384"/>
<source>Advanced Key Management</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="854"/>
- <source> (offline mode)</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="878"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="940"/>
<source>OFF</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1010"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1064"/>
<source>Version: &lt;b&gt;%s&lt;/b&gt; (%s)&lt;br&gt;&lt;br&gt;%sBitmask 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/bitmask/gui/mainwindow.py" line="1038"/>
- <source>&lt;strong&gt;Instructions to use mail:&lt;/strong&gt;&lt;br&gt;If you use Thunderbird you can use the Bitmask extension helper. Search for &apos;Bitmask&apos; in the add-on manager or download it from: {0}.&lt;br&gt;&lt;br&gt;You can configure Bitmask manually with these options:&lt;br&gt;&lt;em&gt; Incoming -&gt; IMAP, port: {1}&lt;br&gt; Outgoing -&gt; SMTP, port: {2}&lt;br&gt; Username -&gt; your bitmask username.&lt;br&gt; Password -&gt; does not matter, use any text. Just don&apos;t leave it empty and don&apos;t use your account&apos;s password.&lt;/em&gt;</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1039"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1111"/>
<source>Bitmask Help</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1051"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1123"/>
<source>The current client version is not supported by this provider.&lt;br&gt;Please update to latest version.&lt;br&gt;&lt;br&gt;You can get the latest version from &lt;a href=&apos;{0}&apos;&gt;{1}&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1052"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1124"/>
<source>Update Needed</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1062"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1134"/>
<source>This provider is not compatible with the client.&lt;br&gt;&lt;br&gt;Error: API version incompatible.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1062"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1134"/>
<source>Incompatible Provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="302"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="359"/>
<source>Application error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="304"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="323"/>
<source>You are trying to do an operation that requires logging in first.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="362"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="398"/>
<source>Unknown error.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="366"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="402"/>
<source>There was a server problem with authentication.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="370"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="406"/>
<source>Could not establish a connection.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="374"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="410"/>
<source>Invalid username or password.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="897"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="959"/>
<source>Hello!</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="898"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="960"/>
<source>Bitmask has started in the tray.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1252"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1419"/>
<source>Succeeded</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1501"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1672"/>
<source>The server at {0} can&apos;t be found, because the DNS lookup failed. DNS is the network service that translates a website&apos;s name to its Internet address. Either your computer is having trouble connecting to the network, or you are missing some helper files that are needed to securely use DNS while {1} is active. To install these helper files, quit this application and start it again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1504"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1674"/>
<source>Connection Error</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1748"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1932"/>
<source>Quitting...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1749"/>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1933"/>
<source>Bitmask is quitting, please wait.</source>
<translation type="unfinished"></translation>
</message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="361"/>
+ <source>There is a problem contacting the backend, please restart Bitmask.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1078"/>
+ <source>bitmask.net/help</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1086"/>
+ <source>Email quick reference</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1090"/>
+ <source>For Thunderbird, you can use the Bitmask extension. Search for &quot;Bitmask&quot; in the add-on manager or download it from &lt;a href=&apos;{0}&apos;&gt;addons.mozilla.org&lt;/a&gt;.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1094"/>
+ <source>Alternately, you can manually configure your mail client to use Bitmask Email with these options:</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1094"/>
+ <source>IMAP: localhost, port {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1095"/>
+ <source>SMTP: localhost, port {0}</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1097"/>
+ <source>Username: your full email address</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1099"/>
+ <source>Password: any non-empty text</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1108"/>
+ <source>&lt;p&gt;&lt;strong&gt;{0}&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;{1}&lt;/p&gt;&lt;p&gt;{2}&lt;ul&gt;&lt;li&gt;&amp;nbsp;{3}&lt;/li&gt;&lt;li&gt;&amp;nbsp;{4}&lt;/li&gt;&lt;li&gt;&amp;nbsp;{5}&lt;/li&gt;&lt;li&gt;&amp;nbsp;{6}&lt;/li&gt;&lt;/ul&gt;&lt;/p&gt;</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1273"/>
+ <source>Stop services</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1273"/>
+ <source>Do you want to stop all services?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1277"/>
+ <source>In order to change the provider, the running services needs to be stopped.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/mainwindow.py" line="1741"/>
+ <source>Disabled: missing helper files</source>
+ <translation type="unfinished"></translation>
+ </message>
</context>
<context>
<name>Preferences</name>
@@ -1078,47 +1151,47 @@ Export canceled.</source>
<context>
<name>PreferencesWindow</name>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="59"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="60"/>
<source>Automatic</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="168"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="170"/>
<source>Changing password...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="249"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="252"/>
<source>Password changed successfully.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="433"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="436"/>
<source>There was a problem changing the password.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="437"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="440"/>
<source>You did not enter a correct current password.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="417"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="420"/>
<source>Services settings for provider &apos;{0}&apos; saved.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="114"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="116"/>
<source>You need to enable {0} in order to change the password.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="122"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="124"/>
<source>You need to wait until {0} is ready in order to change the password.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="101"/>
+ <location filename="../src/leap/bitmask/gui/preferenceswindow.py" line="103"/>
<source>In order to change your password you need to be logged in.</source>
<translation type="unfinished"></translation>
</message>
@@ -1137,12 +1210,15 @@ Export canceled.</source>
</message>
</context>
<context>
- <name>Wizard</name>
+ <name>Providers</name>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="43"/>
- <source>Welcome</source>
+ <location filename="../src/leap/bitmask/gui/providers.py" line="57"/>
+ <source>Other...</source>
<translation type="unfinished"></translation>
</message>
+</context>
+<context>
+ <name>Wizard</name>
<message>
<location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="55"/>
<source>Log In with my credentials</source>
@@ -1154,22 +1230,12 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <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/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/bitmask/gui/ui/wizard.ui" line="340"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="367"/>
<source>Check</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="315"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="342"/>
<source>https://</source>
<translation type="unfinished"></translation>
</message>
@@ -1179,310 +1245,275 @@ Export canceled.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="159"/>
- <source>Getting provider information</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="239"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="266"/>
<source>Can we reach this provider?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="353"/>
- <source>Provider Information</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="356"/>
- <source>Description of services offered by this provider</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="365"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="392"/>
<source>Name</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="397"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="424"/>
<source>Desc</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="407"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="434"/>
<source>&lt;b&gt;Services offered:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="417"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="444"/>
<source>services</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="437"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="464"/>
<source>&lt;b&gt;Enrollment policy:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="447"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="474"/>
<source>policy</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="467"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="494"/>
<source>&lt;b&gt;URL:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="477"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="504"/>
<source>URL</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="484"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="511"/>
<source>&lt;b&gt;Description:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="495"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="522"/>
<source>Provider setup</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="498"/>
- <source>Gathering configuration options for this provider</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="520"/>
- <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/bitmask/gui/ui/wizard.ui" line="543"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="570"/>
<source>Setting up provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="593"/>
- <source>Getting info from the Certificate Authority</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="600"/>
- <source>Do we trust this Certificate Authority?</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="607"/>
- <source>Establishing a trust relationship with this provider</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="666"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="720"/>
<source>Register new user</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="669"/>
- <source>Register a new user with provider</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="684"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="738"/>
<source>&lt;b&gt;Password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="711"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="765"/>
<source>&lt;b&gt;Re-enter password:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="721"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="775"/>
<source>Register</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="767"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="821"/>
<source>Remember my username and password</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="791"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="845"/>
<source>Service selection</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="794"/>
- <source>Please select the services you would like to have</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="131"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="136"/>
<source>&amp;Next &gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="133"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="138"/>
<source>Connect</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="289"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="328"/>
<source>Starting registration...</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="333"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="373"/>
<source>User %s successfully registered.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="503"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="543"/>
<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/bitmask/gui/wizard.py" line="522"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="562"/>
<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/bitmask/gui/wizard.py" line="551"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="590"/>
<source>Unable to load provider configuration</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="557"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="596"/>
<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/bitmask/gui/wizard.py" line="662"/>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="701"/>
<source>Something went wrong while trying to load service %s</source>
<translation type="unfinished"></translation>
</message>
<message>
- <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="166"/>
<source>Can we establish a secure connection?</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="754"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="808"/>
<source>&lt;b&gt;Username:&lt;/b&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="269"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="296"/>
<source>Configure or select a provider</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="275"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="302"/>
<source>Configure new provider:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="305"/>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="332"/>
<source>Use existing one:</source>
<translation type="unfinished"></translation>
</message>
<message>
- <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;Bitmask -&amp;gt; Create new account...&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>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="400"/>
+ <source>Something has gone wrong. Please try again.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="360"/>
- <source>Something has gone wrong. Please try again.</source>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="414"/>
+ <source>The requested username is taken, choose another.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="688"/>
- <source>Gathering configuration options for {0}</source>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="682"/>
+ <source>Services by {0}</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="374"/>
- <source>The requested username is taken, choose another.</source>
+ <location filename="../src/leap/bitmask/gui/wizard.py" line="743"/>
+ <source>Register a new user with {0}</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="643"/>
- <source>Services by {0}</source>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="26"/>
+ <source>Bitmask Provider Setup</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="696"/>
- <source>Description of services offered by {0}</source>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="43"/>
+ <source>Welcome to Bitmask</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/gui/wizard.py" line="711"/>
- <source>Register a new user with {0}</source>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="112"/>
+ <source>Choose a provider</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="159"/>
+ <source>Getting provider information.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="380"/>
+ <source>About this provider</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="547"/>
+ <source>Bitmask is attempting to establish a secure connection with this provider for the first time.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="638"/>
+ <source>Fetching provider credentials.</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="645"/>
+ <source>Do we trust these credentials?</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../src/leap/bitmask/gui/ui/wizard.ui" line="652"/>
+ <source>Connecting to provider.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>msg</name>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="200"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="219"/>
<source>TAP Driver</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="207"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="226"/>
<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/bitmask/platform_init/initializers.py" line="325"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="344"/>
<source>TUN Driver</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="333"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="352"/>
<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="142"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="153"/>
<source>Problem installing files</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="143"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="154"/>
<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="328"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="347"/>
<source>Bitmask 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/bitmask/platform_init/initializers.py" line="92"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="102"/>
<source>Missing helper files</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="380"/>
- <source>Missing resolvconf framework</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="432"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="427"/>
<source>Missing Bitmask helpers</source>
<translation type="unfinished"></translation>
</message>
@@ -1490,25 +1521,12 @@ Export canceled.</source>
<context>
<name>msgstr</name>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="375"/>
- <source>Could not find &lt;b&gt;resolvconf&lt;/b&gt; installed in your system.
-Do you want to quit Bitmask now?</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="379"/>
- <source>Encrypted Internet needs resolvconf installed to work properly.
-Please use your package manager to install it.
-</source>
- <translation type="unfinished"></translation>
- </message>
- <message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="401"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="396"/>
<source>Some essential helper files are missing in your system.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/platform_init/initializers.py" line="404"/>
+ <location filename="../src/leap/bitmask/platform_init/initializers.py" line="399"/>
<source>Reinstall your debian packages, or make sure you place them by hand.</source>
<translation type="unfinished"></translation>
</message>
@@ -1516,17 +1534,17 @@ Please use your package manager to install it.
<context>
<name>self._eip_status</name>
<message>
- <location filename="../src/leap/bitmask/services/eip/conductor.py" line="184"/>
+ <location filename="../src/leap/bitmask/services/eip/conductor.py" line="196"/>
<source>{0} is restarting</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/services/eip/conductor.py" line="295"/>
+ <location filename="../src/leap/bitmask/services/eip/conductor.py" line="307"/>
<source>{0} could not be launched because you did not authenticate properly.</source>
<translation type="unfinished"></translation>
</message>
<message>
- <location filename="../src/leap/bitmask/services/eip/conductor.py" line="307"/>
+ <location filename="../src/leap/bitmask/services/eip/conductor.py" line="321"/>
<source>{0} finished in an unexpected manner!</source>
<translation type="unfinished"></translation>
</message>
diff --git a/docs/man/bitmask-root.1.rst b/docs/man/bitmask-root.1.rst
index c18cc4d6..dde303ae 100644
--- a/docs/man/bitmask-root.1.rst
+++ b/docs/man/bitmask-root.1.rst
@@ -49,6 +49,19 @@ firewall
**stop** Stops the firewall.
+**isup** Check if the firewall is up.
+
+
+fw-email
+---------
+
+**start** UID Starts the email firewall. UID is the user name or unix
+ id that will have access to the email.
+
+**stop** Stops the email firewall.
+
+**isup** Check if the email firewall is up.
+
version
--------
diff --git a/pkg/linux/bitmask-root b/pkg/linux/bitmask-root
index c9034b0d..ee195e3b 100755
--- a/pkg/linux/bitmask-root
+++ b/pkg/linux/bitmask-root
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
#
# Copyright (C) 2014 LEAP
@@ -25,6 +25,8 @@ USAGE:
bitmask-root firewall start [restart] GATEWAY1 GATEWAY2 ...
bitmask-root openvpn stop
bitmask-root openvpn start CONFIG1 CONFIG1 ...
+ bitmask-root fw-email stop
+ bitmask-root fw-email start uid
All actions return exit code 0 for success, non-zero otherwise.
@@ -49,12 +51,17 @@ cmdcheck = subprocess.check_output
# CONSTANTS
#
-VERSION = "2"
+VERSION = "3"
SCRIPT = "bitmask-root"
NAMESERVER = "10.42.0.1"
BITMASK_CHAIN = "bitmask"
BITMASK_CHAIN_NAT_OUT = "bitmask"
BITMASK_CHAIN_NAT_POST = "bitmask_postrouting"
+BITMASK_CHAIN_EMAIL = "bitmask_email"
+BITMASK_CHAIN_EMAIL_OUT = "bitmask_email_output"
+LOCAL_INTERFACE = "lo"
+IMAP_PORT = "1984"
+SMTP_PORT = "2013"
IP = "/bin/ip"
IPTABLES = "/sbin/iptables"
@@ -101,7 +108,8 @@ PARAM_FORMATS = {
"^[a-zA-Z0-9_\.\@][a-zA-Z0-9_\-\.\@]*\$?$", s), # IEEE Std 1003.1-2001
"FILE": lambda s: os.path.isfile(s),
"DIR": lambda s: os.path.isdir(os.path.split(s)[0]),
- "UNIXSOCKET": lambda s: s == "unix"
+ "UNIXSOCKET": lambda s: s == "unix",
+ "UID": lambda s: re.match("^[a-zA-Z0-9]+$", s)
}
@@ -740,6 +748,119 @@ def firewall_stop():
"Please try `firewall stop` again.")
+def fw_email_start(args):
+ """
+ Bring up the email firewall.
+
+ :param args: the user uid of the bitmask process
+ :type args: list
+ """
+ # add custom chain "bitmask_email" to front of INPUT chain
+ if not ipv4_chain_exists(BITMASK_CHAIN_EMAIL):
+ ip4tables("--new-chain", BITMASK_CHAIN_EMAIL)
+ if not ipv6_chain_exists(BITMASK_CHAIN_EMAIL):
+ ip6tables("--new-chain", BITMASK_CHAIN_EMAIL)
+ iptables("--insert", "INPUT", "--jump", BITMASK_CHAIN_EMAIL)
+
+ # add custom chain "bitmask_email_output" to front of OUTPUT chain
+ if not ipv4_chain_exists(BITMASK_CHAIN_EMAIL_OUT):
+ ip4tables("--new-chain", BITMASK_CHAIN_EMAIL_OUT)
+ if not ipv6_chain_exists(BITMASK_CHAIN_EMAIL_OUT):
+ ip6tables("--new-chain", BITMASK_CHAIN_EMAIL_OUT)
+ iptables("--insert", "OUTPUT", "--jump", BITMASK_CHAIN_EMAIL_OUT)
+
+ # Disable the access to imap and smtp from outside
+ iptables("--append", BITMASK_CHAIN_EMAIL,
+ "--in-interface", LOCAL_INTERFACE, "--protocol", "tcp",
+ "--dport", IMAP_PORT, "--jump", "ACCEPT")
+ iptables("--append", BITMASK_CHAIN_EMAIL,
+ "--in-interface", LOCAL_INTERFACE, "--protocol", "tcp",
+ "--dport", SMTP_PORT, "--jump", "ACCEPT")
+ iptables("--append", BITMASK_CHAIN_EMAIL,
+ "--protocol", "tcp", "--dport", IMAP_PORT, "--jump", "REJECT")
+ iptables("--append", BITMASK_CHAIN_EMAIL,
+ "--protocol", "tcp", "--dport", SMTP_PORT, "--jump", "REJECT")
+
+ if not args or not PARAM_FORMATS["UID"](args[0]):
+ raise Exception("No uid given")
+ uid = args[0]
+
+ # Only the unix 'uid' have access to the email imap and smtp ports
+ iptables("--append", BITMASK_CHAIN_EMAIL_OUT,
+ "--out-interface", LOCAL_INTERFACE,
+ "--match", "owner", "--uid-owner", uid, "--protocol", "tcp",
+ "--dport", IMAP_PORT, "--jump", "ACCEPT")
+ iptables("--append", BITMASK_CHAIN_EMAIL_OUT,
+ "--out-interface", LOCAL_INTERFACE,
+ "--match", "owner", "--uid-owner", uid, "--protocol", "tcp",
+ "--dport", SMTP_PORT, "--jump", "ACCEPT")
+ iptables("--append", BITMASK_CHAIN_EMAIL_OUT,
+ "--out-interface", LOCAL_INTERFACE,
+ "--protocol", "tcp", "--dport", IMAP_PORT, "--jump", "REJECT")
+ iptables("--append", BITMASK_CHAIN_EMAIL_OUT,
+ "--out-interface", LOCAL_INTERFACE,
+ "--protocol", "tcp", "--dport", SMTP_PORT, "--jump", "REJECT")
+
+
+def fw_email_stop():
+ """
+ Stop the email firewall.
+ """
+ ok = True
+
+ try:
+ iptables("--delete", "INPUT", "--jump", BITMASK_CHAIN_EMAIL,
+ throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to remove bitmask email firewall from INPUT "
+ "chain (maybe it is already removed?)", exc)
+ ok = False
+
+ try:
+ iptables("--delete", "OUTPUT", "--jump", BITMASK_CHAIN_EMAIL_OUT,
+ throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to remove bitmask email firewall from OUTPUT "
+ "chain (maybe it is already removed?)", exc)
+ ok = False
+
+ try:
+ ip4tables("--flush", BITMASK_CHAIN_EMAIL, throw=True)
+ ip4tables("--delete-chain", BITMASK_CHAIN_EMAIL, throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to flush and delete bitmask ipv4 email firewall "
+ "chain (maybe it is already destroyed?)", exc)
+ ok = False
+
+ try:
+ ip6tables("--flush", BITMASK_CHAIN_EMAIL, throw=True)
+ ip6tables("--delete-chain", BITMASK_CHAIN_EMAIL, throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to flush and delete bitmask ipv6 email firewall "
+ "chain (maybe it is already destroyed?)", exc)
+ ok = False
+
+ try:
+ ip4tables("--flush", BITMASK_CHAIN_EMAIL_OUT, throw=True)
+ ip4tables("--delete-chain", BITMASK_CHAIN_EMAIL_OUT, throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to flush and delete bitmask ipv4 email firewall "
+ "chain (maybe it is already destroyed?)", exc)
+ ok = False
+
+ try:
+ ip6tables("--flush", BITMASK_CHAIN_EMAIL_OUT, throw=True)
+ ip6tables("--delete-chain", BITMASK_CHAIN_EMAIL_OUT, throw=True)
+ except subprocess.CalledProcessError as exc:
+ debug("INFO: not able to flush and delete bitmask ipv6 email firewall "
+ "chain (maybe it is already destroyed?)", exc)
+ ok = False
+
+ if not (ok or ipv4_chain_exists or ipv6_chain_exists):
+ raise Exception("email firewall might still be left up. "
+ "Please try `fw-email stop` again.")
+
+
#
# MAIN
#
@@ -793,6 +914,26 @@ def main():
else:
bail("INFO: bitmask firewall is down")
+ elif command == "fw-email_start":
+ try:
+ fw_email_start(args)
+ except Exception as ex:
+ if not is_restart:
+ fw_email_stop()
+ bail("ERROR: could not start email firewall", ex)
+
+ elif command == "fw-email_stop":
+ try:
+ fw_email_stop()
+ except Exception as ex:
+ bail("ERROR: could not stop email firewall", ex)
+
+ elif command == "fw-email_isup":
+ if ipv4_chain_exists(BITMASK_CHAIN_EMAIL):
+ log("%s: INFO: bitmask email firewall is up" % (SCRIPT,))
+ else:
+ bail("INFO: bitmask email firewall is down")
+
else:
bail("ERROR: No such command")
else:
diff --git a/pkg/postmkvenv.sh b/pkg/postmkvenv.sh
index 04f8d372..7b06fa6d 100755
--- a/pkg/postmkvenv.sh
+++ b/pkg/postmkvenv.sh
@@ -27,7 +27,13 @@ LIB_VIRTUALENV_PATH=$(python -c "$GET_PYTHON_LIB_CMD")
if [[ $platform == 'linux' ]]; then
LIB_SYSTEM_PATH=$(${VAR[-1]} -c "$GET_PYTHON_LIB_CMD")
elif [[ $platform == 'darwin' ]]; then
- LIB_SYSTEM_PATH=$(/opt/local/bin/python2.6 -c "$GET_PYTHON_LIB_CMD")
+ ORIGINAL_PATH=$PATH
+ #change first colon of path to | because path substitution is greedy
+ PATH=${PATH/:/|}
+ #remove everything up to | from path
+ PATH=${PATH/*|/}
+ LIB_SYSTEM_PATH=$(python -c "$GET_PYTHON_LIB_CMD")
+ PATH=$ORIGINAL_PATH
else
echo "unsupported platform; not doing symlinks"
fi
diff --git a/pkg/requirements-dev.pip b/pkg/requirements-dev.pip
index 8b5a8d85..799376d2 100644
--- a/pkg/requirements-dev.pip
+++ b/pkg/requirements-dev.pip
@@ -10,8 +10,12 @@
# NOTE: you have to run pip install -r pkg/requirements.pip for pip
# to install it. (do it after python setup.py develop and it
# will only install this)
-
+#
+wheel
sphinx
+ipdb
--e git+https://github.com/leapcode/leap_pycommon.git@develop#egg=leap.common
--e git+https://github.com/leapcode/soledad.git@develop#egg=leap.soledad
+# in case you want to install a package from a git source, you can use this:
+# Useful to test pre-release branches together.
+#-e git+https://github.com/leapcode/leap_pycommon.git@develop#egg=leap.common
+#-e git+https://github.com/leapcode/soledad.git@develop#egg=leap.soledad
diff --git a/pkg/requirements.pip b/pkg/requirements.pip
index bf05aa28..9f49bf03 100644
--- a/pkg/requirements.pip
+++ b/pkg/requirements.pip
@@ -9,7 +9,10 @@ argparse
requests>=1.1.0
srp>=1.0.2
pyopenssl
-python-dateutil
+
+# This won't be needed after we refactor leap.common.events
+# to use zmq.
+python-dateutil==1.4 # See https://leap.se/code/issues/6099
psutil
@@ -19,6 +22,8 @@ python-daemon # this should not be needed for Windows.
keyring
zope.proxy
+# You will want to install this bundled if you don't have sodium in your system:
+# pip install pyzmq --install-option="--zmq=bundled"
pyzmq
leap.common>=0.3.7
diff --git a/pkg/scripts/bootstrap_develop.sh b/pkg/scripts/bootstrap_develop.sh
index 7027a908..68edcd43 100755
--- a/pkg/scripts/bootstrap_develop.sh
+++ b/pkg/scripts/bootstrap_develop.sh
@@ -159,6 +159,32 @@ update() {
finish
}
+helpers() {
+ if [[ "$1" == "cleanup" ]]; then
+ status="removing helper files"
+ echo "${cc_green}Status: $status...${cc_normal}"
+ set -x
+ sudo rm -f /usr/sbin/bitmask-root
+ sudo rm -f /usr/share/polkit-1/actions/se.leap.bitmask.policy
+ set +x
+ else
+ status="installing helper files"
+ echo "${cc_green}Status: $status...${cc_normal}"
+ set -x
+ sudo cp bitmask_client/pkg/linux/bitmask-root /usr/sbin/
+ sudo cp bitmask_client/pkg/linux/polkit/se.leap.bitmask.policy /usr/share/polkit-1/actions/
+ set +x
+ fi
+}
+
+install_dependencies() {
+ status="installing system dependencies"
+ echo "${cc_green}Status: $status...${cc_normal}"
+ set -x
+ sudo apt-get install -y git python-dev python-setuptools python-virtualenv python-pip libssl-dev python-openssl libsqlite3-dev g++ openvpn pyside-tools python-pyside libffi-dev
+ set +x
+}
+
run() {
shift # remove 'run' from arg list
passthrough_args=$@
@@ -174,13 +200,17 @@ help() {
echo "Bootstraps the environment to start developing the bitmask client"
echo "with all the needed repositories and dependencies."
echo
- echo "Usage: $0 {init | update | run | help}"
+ echo "Usage: $0 {init | update | run | help | deps | helpers}"
echo
- echo " init : Initialize repositories, create virtualenv and \`python setup.py develop\` all."
- echo " You can use \`init ro\` in order to use the https remotes if you don't have rw access."
- echo " update : Update the repositories and install new deps (if needed)."
- echo " run : Runs the client (any extra parameters will be sent to the app)."
- echo " help : Show this help"
+ echo " init : Initialize repositories, create virtualenv and \`python setup.py develop\` all."
+ echo " You can use \`init ro\` in order to use the https remotes if you don't have rw access."
+ echo " update : Update the repositories and install new deps (if needed)."
+ echo " run : Runs the client (any extra parameters will be sent to the app)."
+ echo " help : Show this help"
+ echo " -- system helpers --"
+ echo " deps : Install the system dependencies needed for bitmask dev (Debian based Linux only)."
+ echo " helpers : Install the helper files needed to use bitmask (Linux only)."
+ echo " You can use \`helpers cleanup\` to remove those files."
echo
}
@@ -191,6 +221,12 @@ case "$1" in
update)
update
;;
+ helpers)
+ helpers $2
+ ;;
+ deps)
+ install_dependencies
+ ;;
run)
run "$@"
;;
diff --git a/relnotes.txt b/relnotes.txt
index 183d1f86..02e53ee6 100644
--- a/relnotes.txt
+++ b/relnotes.txt
@@ -1,8 +1,8 @@
-ANNOUNCING Bitmask, the Internet Encryption Toolkit, release 0.6.1
+ANNOUNCING Bitmask, the Internet Encryption Toolkit, release 0.7.0
The LEAP team is pleased to announce the immediate availability of
version 0.6.1 of Bitmask, the Internet Encryption Toolkit, codename
-"knock knock knocking on beta's door".
+"one time download, all time updates".
https://downloads.leap.se/client/
@@ -17,15 +17,6 @@ The Encrypted Internet Proxy provides circumvention, location
anonymization, and traffic encryption in a hassle-free, automatically
self-configuring fashion.
-WARNING (LINUX ONLY): If you ever run into the situation where you
-cannot access internet, open the terminal and run the following
-command:
-
-$ pkexec /usr/local/sbin/bitmask-root firewall stop
-
-If for some reason that doesn't work, you will need to reboot your
-computer.
-
Encrypted Mail offers automatic encryption and decryption for both
outgoing and incoming email, adding public key cryptography to your
mail without you ever having to worry about key distribution or
@@ -43,17 +34,15 @@ NOT trust your life to it.
WHAT CAN THIS VERSION OF BITMASK DO FOR ME?
-Bitmask 0.6.1 is the new stable version of the client after the big
-refactor, with a little face lift of the UI while we were at
-it. Encrypted Email is still not stable though, so don't use it for
-high security. Encrypted Internet is the first service we are calling
-stable, although its security level is just a bit higher than plain
-OpenSSL, so use accordingly. You can refer to the CHANGELOG for the
-meat.
+Bitmask 0.7.0 brings with tremendous joy automatic and secure updates
+through The Update Framework. Right beside TUF there are some bug
+fixes and a new settings panel.
-Encrypted Internet on Linux now helps you don't shoot yourself in the
-foot by leaking traffic outside of the secure connection it
-establishes. This will be added to other platforms in the future.
+You can read more about TUF in http://theupdateframework.com/
+
+Encrypted Internet on Linux avoids leaking traffic outside of the
+secure connection it establishes. This will be added to other
+platforms in the future.
The Encrypted Mail services will run local SMTP and IMAP proxies that,
once you configure the mail client of your choice, will automatically
@@ -89,8 +78,8 @@ repository to your apt sources:
deb http://deb.leap.se/debian wheezy main
-We will love to hear if you are interested in help making packages
-available for any other system.
+We will love to hear if you want to make packages available for any
+other system.
BUGS
@@ -98,6 +87,16 @@ You can send the bugs our way by pointing your telnet session to port
443 on https://leap.se/code. We will do our best to make them follow
our intensive bug-reeducation program.
+
+LINUX ONLY: If you ever run into the situation where you cannot
+access internet, open the terminal and run the following command:
+
+$ pkexec /usr/local/sbin/bitmask-root firewall stop
+
+If for some reason that doesn't work, you will need to reboot your
+computer.
+
+
HACKING
You can find us in the #leap channel on the freenode network.
@@ -108,6 +107,6 @@ beyond any border.
The LEAP team,
-August 15, 2014
+Setptember 26, 2014
Somewhere in the middle of the intertubes.
EOF
diff --git a/src/leap/bitmask/backend/components.py b/src/leap/bitmask/backend/components.py
index f721086b..5ef6befd 100644
--- a/src/leap/bitmask/backend/components.py
+++ b/src/leap/bitmask/backend/components.py
@@ -452,20 +452,14 @@ class EIP(object):
else:
logger.debug('EIP: no errors')
- def _do_stop(self, shutdown=False, restart=False):
+ def stop(self, shutdown=False, restart=False):
"""
- Stop the service. This is run in a thread to avoid blocking.
+ Stop the service.
"""
self._vpn.terminate(shutdown, restart)
if IS_LINUX:
self._wait_for_firewall_down()
- def stop(self, shutdown=False, restart=False):
- """
- Stop the service.
- """
- return threads.deferToThread(self._do_stop, shutdown, restart)
-
def _wait_for_firewall_down(self):
"""
Wait for the firewall to come down.
@@ -665,7 +659,7 @@ class EIP(object):
return False
client_cert_path = eip_config.\
- get_client_cert_path(provider_config, about_to_download=False)
+ get_client_cert_path(provider_config, about_to_download=True)
if leap_certs.should_redownload(client_cert_path):
logger.error("The client should redownload the certificate,"
diff --git a/src/leap/bitmask/crypto/certs.py b/src/leap/bitmask/crypto/certs.py
index 244decfd..c3ca4efb 100644
--- a/src/leap/bitmask/crypto/certs.py
+++ b/src/leap/bitmask/crypto/certs.py
@@ -46,19 +46,27 @@ def download_client_cert(provider_config, path, session):
# again.
srp_auth = SRPAuth(provider_config)
session_id = srp_auth.get_session_id()
+ token = srp_auth.get_token()
cookies = None
- if session_id:
+ if session_id is not None:
cookies = {"_session_id": session_id}
cert_uri = "%s/%s/cert" % (
provider_config.get_api_uri(),
provider_config.get_api_version())
logger.debug('getting cert from uri: %s' % cert_uri)
+ headers = {}
+
+ # API v2 will only support token auth, but in v1 we can send both
+ if token is not None:
+ headers["Authorization"] = 'Token token="{0}"'.format(token)
+
res = session.get(cert_uri,
verify=provider_config
.get_ca_cert_path(),
cookies=cookies,
- timeout=REQUEST_TIMEOUT)
+ timeout=REQUEST_TIMEOUT,
+ headers=headers)
res.raise_for_status()
client_cert = res.content
diff --git a/src/leap/bitmask/frontend_app.py b/src/leap/bitmask/frontend_app.py
index 5ea89fc9..909005f0 100644
--- a/src/leap/bitmask/frontend_app.py
+++ b/src/leap/bitmask/frontend_app.py
@@ -76,15 +76,24 @@ def run_frontend(options, flags_dict, backend_pid):
qApp = QtGui.QApplication(sys.argv)
- # To test:
- # $ LANG=es ./app.py
- locale = QtCore.QLocale.system().name()
- qtTranslator = QtCore.QTranslator()
- if qtTranslator.load("qt_%s" % locale, ":/translations"):
- qApp.installTranslator(qtTranslator)
- appTranslator = QtCore.QTranslator()
- if appTranslator.load("%s.qm" % locale[:2], ":/translations"):
- qApp.installTranslator(appTranslator)
+ # To test the app in other language you can do:
+ # shell> LANG=es bitmask
+ # or in some rare case if the code above didn't work:
+ # shell> LC_ALL=es LANG=es bitmask
+ locale = QtCore.QLocale.system().name() # en_US, es_AR, ar_SA, etc
+ locale_short = locale[:2] # en, es, ar, etc
+ rtl_languages = ('ar', ) # right now tested on 'arabic' only.
+
+ systemQtTranslator = QtCore.QTranslator()
+ if systemQtTranslator.load("qt_%s" % locale, ":/translations"):
+ qApp.installTranslator(systemQtTranslator)
+
+ bitmaskQtTranslator = QtCore.QTranslator()
+ if bitmaskQtTranslator.load("%s.qm" % locale_short, ":/translations"):
+ qApp.installTranslator(bitmaskQtTranslator)
+
+ if locale_short in rtl_languages:
+ qApp.setLayoutDirection(QtCore.Qt.LayoutDirection.RightToLeft)
# Needed for initializing qsettings it will write
# .config/leap/leap.conf top level app settings in a platform
diff --git a/src/leap/bitmask/gui/account.py b/src/leap/bitmask/gui/account.py
new file mode 100644
index 00000000..c941c3fa
--- /dev/null
+++ b/src/leap/bitmask/gui/account.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+"""
+A frontend GUI object to hold the current username and domain.
+"""
+
+from leap.bitmask.util import make_address
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.services import EIP_SERVICE, MX_SERVICE
+
+
+class Account():
+
+ def __init__(self, username, domain):
+ self._settings = LeapSettings()
+ self.username = username
+ self.domain = domain
+
+ if self.username is not None:
+ self.address = make_address(self.username, self.domain)
+ else:
+ self.address = self.domain
+
+ def services(self):
+ """
+ returns a list of service name strings
+
+ TODO: this should depend not just on the domain
+ """
+ return self._settings.get_enabled_services(self.domain)
+
+ def is_email_enabled(self):
+ MX_SERVICE in self.services()
+
+ def is_eip_enabled(self):
+ EIP_SERVICE in self.services()
diff --git a/src/leap/bitmask/gui/app.py b/src/leap/bitmask/gui/app.py
new file mode 100644
index 00000000..eb1a58d5
--- /dev/null
+++ b/src/leap/bitmask/gui/app.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+"""
+A single App instances holds the signals that are shared among different
+frontend UI components. The App also keeps a reference to the backend object
+and the signaler get signals from the backend.
+"""
+import logging
+
+from functools import partial
+from PySide import QtCore, QtGui
+
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.backend.backend_proxy import BackendProxy
+from leap.bitmask.backend.leapsignaler import LeapSignaler
+
+logger = logging.getLogger(__name__)
+
+
+class App(QtGui.QWidget):
+
+ # the user has changed which services are enabled for a particular account
+ # args: account (Account), active services (list of str)
+ service_selection_changed = QtCore.Signal(object, list)
+
+ def __init__(self):
+ QtGui.QWidget.__init__(self)
+
+ self.settings = LeapSettings()
+ self.backend = BackendProxy()
+ self.signaler = LeapSignaler()
+ self.signaler.start()
+
+ # periodically check if the backend is alive
+ self._backend_checker = QtCore.QTimer(self)
+ self._backend_checker.timeout.connect(self._check_backend_status)
+ self._backend_checker.start(2000)
+
+ @QtCore.Slot()
+ def _check_backend_status(self):
+ """
+ TRIGGERS:
+ self._backend_checker.timeout
+
+ Check that the backend is running. Otherwise show an error to the user.
+ """
+ if not self.backend.online:
+ logger.critical("Backend is not online.")
+ QtGui.QMessageBox.critical(
+ self, self.tr("Application error"),
+ self.tr("There is a problem contacting the backend, please "
+ "restart Bitmask."))
+ self._backend_checker.stop()
diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py
index 0f63972f..b5788f3c 100644
--- a/src/leap/bitmask/gui/eip_preferenceswindow.py
+++ b/src/leap/bitmask/gui/eip_preferenceswindow.py
@@ -116,11 +116,14 @@ class EIPPreferencesWindow(QtGui.QDialog):
self.ui.gbGatewaySelector.setEnabled(False)
return
+ # block signals so the currentIndexChanged slot doesn't get triggered
+ self.ui.cbProvidersGateway.blockSignals(True)
for provider, is_initialized in providers:
label = provider
if not is_initialized:
label += self.tr(" (uninitialized)")
self.ui.cbProvidersGateway.addItem(label, userData=provider)
+ self.ui.cbProvidersGateway.blockSignals(False)
# Select provider by name
domain = self._selected_domain
diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py
index abd6e2c9..a5cd03d3 100644
--- a/src/leap/bitmask/gui/eip_status.py
+++ b/src/leap/bitmask/gui/eip_status.py
@@ -97,7 +97,7 @@ class EIPStatusWidget(QtGui.QWidget):
# Action for the systray
self._eip_disabled_action = QtGui.QAction(
- "{0} is {1}".format(self._service_name, self.tr("disabled")), self)
+ u"{0} is {1}".format(self._service_name, self.tr("disabled")), self)
def connect_backend_signals(self):
"""
@@ -303,7 +303,6 @@ class EIPStatusWidget(QtGui.QWidget):
"""
# XXX this name is unfortunate. "disable" is also applied to a
# pushbutton being grayed out.
-
logger.debug('Hiding EIP start button')
# you might be tempted to change this for a .setEnabled(False).
# it won't work. it's under the claws of the state machine.
@@ -334,7 +333,7 @@ class EIPStatusWidget(QtGui.QWidget):
Triggered after a successful login.
Enables the start button.
"""
- # logger.debug('Showing EIP start button')
+ logger.debug('Showing EIP start button')
self.eip_button.show()
self.hide_eip_cancel_button()
diff --git a/src/leap/bitmask/gui/flashable.py b/src/leap/bitmask/gui/flashable.py
new file mode 100644
index 00000000..a26d1ec6
--- /dev/null
+++ b/src/leap/bitmask/gui/flashable.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+
+
+class Flashable(object):
+
+ """
+ An abstract super class to give a QWidget handy methods for diplaying
+ alert messages inline. The widget inheriting from this class must have
+ label named 'flash_label' available at self.ui.flash_label, or pass
+ the QLabel object in the constructor.
+ """
+
+ def __init__(self, widget=None):
+ self._setup(widget)
+
+ def _setup(self, widget=None):
+ if not hasattr(self, 'widget'):
+ if widget:
+ self.widget = widget
+ else:
+ self.widget = self.ui.flash_label
+ self.widget.setVisible(False)
+
+ def flash_error(self, message):
+ """
+ Sets string for the flash message.
+
+ :param message: the text to be displayed
+ :type message: str
+ """
+ self._setup()
+ message = "<font color='red'><b>%s</b></font>" % (message,)
+ self.widget.setVisible(True)
+ self.widget.setText(message)
+
+ def flash_success(self, message):
+ """
+ Sets string for the flash message.
+
+ :param message: the text to be displayed
+ :type message: str
+ """
+ self._setup()
+ message = "<font color='green'><b>%s</b></font>" % (message,)
+ self.widget.setVisible(True)
+ self.widget.setText(message)
+
+ def flash_message(self, message):
+ """
+ Sets string for the flash message.
+
+ :param message: the text to be displayed
+ :type message: str
+ """
+ self._setup()
+ message = "<b>%s</b>" % (message,)
+ self.widget.setVisible(True)
+ self.widget.setText(message)
+
+ def hide_flash(self):
+ self._setup()
+ self.widget.setVisible(False)
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 8ce7f2fc..cc4ede09 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -37,7 +37,6 @@ from leap.bitmask.config import flags
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement
-from leap.bitmask.gui.eip_preferenceswindow import EIPPreferencesWindow
from leap.bitmask.gui.eip_status import EIPStatusWidget
from leap.bitmask.gui.loggerwindow import LoggerWindow
from leap.bitmask.gui.login import LoginWidget
@@ -46,8 +45,11 @@ from leap.bitmask.gui.preferenceswindow import PreferencesWindow
from leap.bitmask.gui.systray import SysTray
from leap.bitmask.gui.wizard import Wizard
from leap.bitmask.gui.providers import Providers
+from leap.bitmask.gui.account import Account
+from leap.bitmask.gui.app import App
from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX
+from leap.bitmask.platform_init import locks
from leap.bitmask.platform_init.initializers import init_platform
from leap.bitmask.platform_init.initializers import init_signals
@@ -63,10 +65,6 @@ from leap.bitmask.util import autostart, make_address
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.logs.leap_log_handler import LeapLogHandler
-if IS_WIN:
- from leap.bitmask.platform_init.locks import WindowsLock
- from leap.bitmask.platform_init.locks import raise_window_ack
-
from leap.common.events import register
from leap.common.events import events_pb2 as proto
@@ -127,19 +125,10 @@ class MainWindow(QtGui.QMainWindow):
self.ui.setupUi(self)
self.menuBar().setNativeMenuBar(not IS_LINUX)
- self._backend = BackendProxy()
-
- # periodically check if the backend is alive
- self._backend_checker = QtCore.QTimer(self)
- self._backend_checker.timeout.connect(self._check_backend_status)
- self._backend_checker.start(2000)
-
- self._leap_signaler = LeapSignaler()
- self._leap_signaler.start()
-
- self._settings = LeapSettings()
- # gateway = self._settings.get_selected_gateway(provider)
- # self._backend.settings_set_selected_gateway(provider, gateway)
+ self.app = App()
+ self._backend = self.app.backend
+ self._leap_signaler = self.app.signaler
+ self._settings = self.app.settings
# Login Widget
self._login_widget = LoginWidget(self._settings, self)
@@ -155,6 +144,7 @@ class MainWindow(QtGui.QMainWindow):
# Qt Signal Connections #####################################
# TODO separate logic from ui signals.
+ self.app.service_selection_changed.connect(self._update_eip_enabled_status)
self._login_widget.login.connect(self._login)
self._login_widget.cancel_login.connect(self._cancel_login)
self._login_widget.logout.connect(self._logout)
@@ -209,12 +199,13 @@ class MainWindow(QtGui.QMainWindow):
self._finally_quitting = False
self._system_quit = False
+ # Used to differentiate between a real quit and a close to tray
+ self._close_to_tray = True
+
self._backend_connected_signals = []
self._backend_connect()
self.ui.action_preferences.triggered.connect(self._show_preferences)
- self.ui.action_eip_preferences.triggered.connect(
- self._show_eip_preferences)
self.ui.action_about_leap.triggered.connect(self._about)
self.ui.action_quit.triggered.connect(self.quit)
self.ui.action_wizard.triggered.connect(self._launch_wizard)
@@ -224,17 +215,15 @@ class MainWindow(QtGui.QMainWindow):
self.ui.action_create_new_account.triggered.connect(
self._on_provider_changed)
- self.ui.action_advanced_key_management.triggered.connect(
- self._show_AKM)
+ # Action item hidden since we don't provide stable mail yet.
+ # self.ui.action_advanced_key_management.triggered.connect(
+ # self._show_AKM)
if IS_MAC:
self.ui.menuFile.menuAction().setText(self.tr("File"))
self.raise_window.connect(self._do_raise_mainwindow)
- # Used to differentiate between real quits and close to tray
- self._really_quit = False
-
self._systray = None
# XXX separate actions into a different module.
@@ -247,10 +236,6 @@ class MainWindow(QtGui.QMainWindow):
self._action_visible = QtGui.QAction(self.tr("Show Main Window"), self)
self._action_visible.triggered.connect(self._ensure_visible)
- # disable buttons for now, may come back later.
- # self.ui.btnPreferences.clicked.connect(self._show_preferences)
- # self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences)
-
self._enabled_services = []
self._ui_mx_visible = True
self._ui_eip_visible = True
@@ -274,9 +259,7 @@ class MainWindow(QtGui.QMainWindow):
# Services signals/slots connection
self.new_updates.connect(self._react_to_new_updates)
- # XXX should connect to mail_conductor.start_mail_service instead
- self.soledad_ready.connect(self._start_smtp_bootstrapping)
- self.soledad_ready.connect(self._start_imap_service)
+ self.soledad_ready.connect(self._start_mail_service)
# ################################ end Qt Signals connection ########
init_platform()
@@ -347,23 +330,6 @@ class MainWindow(QtGui.QMainWindow):
logger.error("Bad call to the backend:")
logger.error(data)
- @QtCore.Slot()
- def _check_backend_status(self):
- """
- TRIGGERS:
- self._backend_checker.timeout
-
- Check that the backend is running. Otherwise show an error to the user.
- """
- online = self._backend.online
- if not online:
- logger.critical("Backend is not online.")
- QtGui.QMessageBox.critical(
- self, self.tr("Application error"),
- self.tr("There is a problem contacting the backend, please "
- "restart Bitmask."))
- self._backend_checker.stop()
-
def _backend_connect(self, only_tracked=False):
"""
Connect to backend signals.
@@ -413,11 +379,6 @@ class MainWindow(QtGui.QMainWindow):
"Invalid username or password."))
conntrack(sig.srp_auth_bad_user_or_password, auth_bad_user_or_password)
- # Logout signals
- conntrack(sig.srp_logout_ok, self._logout_ok)
- conntrack(sig.srp_logout_error, self._logout_error)
- conntrack(sig.srp_not_logged_in_error, self._not_logged_in_error)
-
# EIP bootstrap signals
conntrack(sig.eip_config_ready, self._eip_intermediate_stage)
conntrack(sig.eip_client_certificate_ready, self._finish_eip_bootstrap)
@@ -436,8 +397,12 @@ class MainWindow(QtGui.QMainWindow):
sig.prov_unsupported_api.connect(self._incompatible_api)
sig.prov_get_all_services.connect(self._provider_get_all_services)
- # EIP start signals ==============================================
+ # Logout signals =================================================
+ sig.srp_logout_ok.connect(self._logout_ok)
+ sig.srp_logout_error.connect(self._logout_error)
+ sig.srp_not_logged_in_error.connect(self._not_logged_in_error)
+ # EIP start signals ==============================================
self._eip_conductor.connect_backend_signals()
sig.eip_can_start.connect(self._backend_can_start_eip)
sig.eip_cannot_start.connect(self._backend_cannot_start_eip)
@@ -604,24 +569,16 @@ class MainWindow(QtGui.QMainWindow):
Display the preferences window.
"""
- user = self._logged_user
- domain = self._providers.get_selected_provider()
- mx_provided = False
- if self._provider_details is not None:
- mx_provided = MX_SERVICE in self._provider_details['services']
- preferences = PreferencesWindow(self, user, domain, self._backend,
- self._soledad_started, mx_provided,
- self._leap_signaler)
+ account = Account(self._logged_user,
+ self._providers.get_selected_provider())
+ pref_win = PreferencesWindow(self, account, self.app)
+ pref_win.show()
- self.soledad_ready.connect(preferences.set_soledad_ready)
- preferences.show()
- preferences.preferences_saved.connect(self._update_eip_enabled_status)
-
- @QtCore.Slot()
- def _update_eip_enabled_status(self):
+ @QtCore.Slot(object, list)
+ def _update_eip_enabled_status(self, account=None, services=None):
"""
TRIGGER:
- PreferencesWindow.preferences_saved
+ App.service_selection_changed
Enable or disable the EIP start/stop actions and stop EIP if the user
disabled that service.
@@ -629,24 +586,35 @@ class MainWindow(QtGui.QMainWindow):
:returns: if the eip actions were enabled or disabled
:rtype: bool
"""
- settings = self._settings
- default_provider = settings.get_defaultprovider()
+ if account is not None:
+ domain = account.domain
+ else:
+ # I am not sure why, but asking for the currently selected
+ # provider here give you the WRONG provider
+ domain = self.app.settings.get_defaultprovider()
- if default_provider is None:
+ if domain is None:
logger.warning("Trying to update eip enabled status but there's no"
" default provider. Disabling EIP for the time"
" being...")
self._backend_cannot_start_eip()
return
- self._trying_to_start_eip = settings.get_autostart_eip()
- self._backend.eip_can_start(domain=default_provider)
+ if not EIP_SERVICE in self.app.settings.get_enabled_services(domain):
+ self._eip_conductor.terminate()
+ def hide():
+ self.app.backend.eip_can_start(domain=domain)
+ QtDelayedCall(100, hide)
+ # ^^ VERY VERY Hacky, but with the simple state machine,
+ # there is no way to signal 'disconnect and then disable'
+
+ else:
+ self._trying_to_start_eip = self.app.settings.get_autostart_eip()
+ if not self._trying_to_start_eip:
+ self._backend.eip_setup(provider=domain, skip_network=True)
+ # check if EIP can start (will trigger widget update)
+ self.app.backend.eip_can_start(domain=domain)
- # If we don't want to start eip, we leave everything
- # initialized to quickly start it
- if not self._trying_to_start_eip:
- self._backend.eip_setup(provider=default_provider,
- skip_network=True)
def _backend_can_start_eip(self):
"""
@@ -657,12 +625,15 @@ class MainWindow(QtGui.QMainWindow):
to do so, start it. Otherwise it leaves everything in place
for the user to click Turn ON.
"""
+ if self._eip_status.missing_helpers:
+ self._eip_status.disable_eip_start()
+ return
+
settings = self._settings
default_provider = settings.get_defaultprovider()
enabled_services = []
if default_provider is not None:
enabled_services = settings.get_enabled_services(default_provider)
-
eip_enabled = False
if EIP_SERVICE in enabled_services:
eip_enabled = True
@@ -701,7 +672,13 @@ class MainWindow(QtGui.QMainWindow):
self._eip_status.disable_eip_start()
else:
self._eip_status.disable_eip_start()
- self._eip_status.set_eip_status(self.tr("Disabled"))
+ # NOTE: we shouldn't be setting the message here.
+ if not self._eip_status.missing_helpers:
+ self._eip_status.set_eip_status(self.tr("Disabled"))
+
+ # this state flag is responsible for deferring the login
+ # so we must update it, otherwise we're in a deadlock.
+ self._trying_to_start_eip = False
@QtCore.Slot()
def _disable_eip_missing_helpers(self):
@@ -713,20 +690,6 @@ class MainWindow(QtGui.QMainWindow):
"""
self._eip_status.missing_helpers = True
- @QtCore.Slot()
- def _show_eip_preferences(self):
- """
- TRIGGERS:
- self.ui.btnEIPPreferences.clicked
- self.ui.action_eip_preferences (disabled for now)
-
- Display the EIP preferences window.
- """
- domain = self._providers.get_selected_provider()
- pref = EIPPreferencesWindow(self, domain,
- self._backend, self._leap_signaler)
- pref.show()
-
#
# updates
#
@@ -809,6 +772,15 @@ class MainWindow(QtGui.QMainWindow):
self._show_hide_unsupported_services()
+ # XXX - HACK, kind of...
+ # With the 1ms QTimer.singleShot call we schedule the call right after
+ # other signals waiting for the qt reactor to take control.
+ # That way, the method is called right after the EIP machines' signals.
+ # We need to wait until that happens because the state-machine
+ # controlled widget shows the 'Turn On' button and we want to do the
+ # changes to that button right after, not before.
+ QtDelayedCall(1, self._update_eip_enabled_status)
+
if self._wizard:
possible_username = self._wizard.get_username()
possible_password = self._wizard.get_password()
@@ -833,8 +805,6 @@ class MainWindow(QtGui.QMainWindow):
self._wizard = None
self._backend_connect(only_tracked=True)
else:
- self._update_eip_enabled_status()
-
domain = self._settings.get_provider()
if domain is not None:
self._providers.select_provider_by_name(domain)
@@ -923,7 +893,7 @@ class MainWindow(QtGui.QMainWindow):
systrayMenu.addAction(self._action_visible)
systrayMenu.addSeparator()
- eip_status_label = "{0}: {1}".format(
+ eip_status_label = u"{0}: {1}".format(
self._eip_conductor.eip_name, self.tr("OFF"))
self._eip_menu = eip_menu = systrayMenu.addMenu(eip_status_label)
eip_menu.addAction(self._action_eip_startstop)
@@ -1131,7 +1101,7 @@ class MainWindow(QtGui.QMainWindow):
return
if QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \
- not self._really_quit:
+ self._close_to_tray:
self._ensure_invisible()
e.ignore()
return
@@ -1245,9 +1215,15 @@ class MainWindow(QtGui.QMainWindow):
# TODO: we need to add a check for the mail status (smtp/imap/soledad)
something_runing = (self._logged_user is not None or
self._already_started_eip)
+ provider = self._providers.get_selected_provider()
+
if not something_runing:
if wizard:
self._launch_wizard()
+ else:
+ self._settings.set_provider(provider)
+ self._settings.set_defaultprovider(provider)
+ self._update_eip_enabled_status()
return
title = self.tr("Stop services")
@@ -1265,7 +1241,11 @@ class MainWindow(QtGui.QMainWindow):
res = msg.exec_()
if res == QtGui.QMessageBox.Yes:
+ self._settings.set_provider(provider)
+ self._settings.set_defaultprovider(provider)
+ self._settings.set_autostart_eip(False)
self._stop_services()
+ self._update_eip_enabled_status()
self._eip_conductor.qtsigs.do_disconnect_signal.emit()
if wizard:
self._launch_wizard()
@@ -1540,37 +1520,12 @@ class MainWindow(QtGui.QMainWindow):
self.soledad_ready.emit()
###################################################################
- # Service control methods: smtp
-
+ # Service control methods: mail
@QtCore.Slot()
- def _start_smtp_bootstrapping(self):
- """
- TRIGGERS:
- self.soledad_ready
- """
- if flags.OFFLINE is True:
- logger.debug("not starting smtp in offline mode")
- return
-
- if self._provides_mx_and_enabled():
- self._mail_conductor.start_smtp_service(download_if_needed=True)
-
- ###################################################################
- # Service control methods: imap
-
- @QtCore.Slot()
- def _start_imap_service(self):
- """
- TRIGGERS:
- self.soledad_ready
- """
- # TODO in the OFFLINE mode we should also modify the rules
- # in the mail state machine so it shows that imap is active
- # (but not smtp since it's not yet ready for offline use)
+ def _start_mail_service(self):
if self._provides_mx_and_enabled() or flags.OFFLINE:
- self._mail_conductor.start_imap_service()
-
- # end service control methods (imap)
+ self._mail_conductor.start_mail_service(download_if_needed=True,
+ offline=flags.OFFLINE)
###################################################################
# Service control methods: eip
@@ -1839,7 +1794,7 @@ class MainWindow(QtGui.QMainWindow):
Callback for the raise window event
"""
if IS_WIN:
- raise_window_ack()
+ locks.raise_window_ack()
self.raise_window.emit()
@QtCore.Slot()
@@ -1866,10 +1821,9 @@ class MainWindow(QtGui.QMainWindow):
"""
Stop services and cancel ongoing actions (if any).
"""
- logger.debug('About to quit, doing cleanup.')
+ logger.debug('Stopping services...')
self._cancel_ongoing_defers()
-
self._services_being_stopped = set(('imap', 'eip'))
imap_stopped = lambda: self._remove_service('imap')
@@ -1880,8 +1834,7 @@ class MainWindow(QtGui.QMainWindow):
self._leap_signaler.eip_stopped.connect(eip_stopped)
logger.debug('Stopping mail services')
- self._backend.imap_stop_service()
- self._backend.smtp_stop_service()
+ self._mail_conductor.stop_mail_services()
if self._logged_user is not None:
logger.debug("Doing logout")
@@ -1898,9 +1851,10 @@ class MainWindow(QtGui.QMainWindow):
if self._quitting:
return
- autostart.set_autostart(False)
-
self._quitting = True
+ self._close_to_tray = False
+ logger.debug('Quitting...')
+ autostart.set_autostart(False)
# first thing to do quitting, hide the mainwindow and show tooltip.
self.hide()
@@ -1919,8 +1873,6 @@ class MainWindow(QtGui.QMainWindow):
# Set this in case that the app is hidden
QtGui.QApplication.setQuitOnLastWindowClosed(True)
- self._really_quit = True
-
if not self._backend.online:
self.final_quit()
return
@@ -1996,7 +1948,6 @@ class MainWindow(QtGui.QMainWindow):
# Remove lockfiles on a clean shutdown.
logger.debug('Cleaning pidfiles')
- if IS_WIN:
- WindowsLock.release_all_locks()
+ locks.release_lock()
self.close()
diff --git a/src/leap/bitmask/gui/passwordwindow.py b/src/leap/bitmask/gui/passwordwindow.py
new file mode 100644
index 00000000..f7ef079e
--- /dev/null
+++ b/src/leap/bitmask/gui/passwordwindow.py
@@ -0,0 +1,273 @@
+# -*- coding: utf-8 -*-
+# passwordwindow.py
+# Copyright (C) 2014 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/>.
+
+"""
+Change password dialog window
+"""
+
+from PySide import QtCore, QtGui
+from leap.bitmask.util.credentials import password_checks
+
+from leap.bitmask.gui.ui_password_change import Ui_PasswordChange
+from leap.bitmask.gui.flashable import Flashable
+
+import logging
+logger = logging.getLogger(__name__)
+
+
+class PasswordWindow(QtGui.QDialog, Flashable):
+
+ _current_window = None # currently visible password window
+
+ def __init__(self, parent, account, app):
+ """
+ :param parent: parent object of the PreferencesWindow.
+ :parent type: QWidget
+
+ :param account: the user set in the login widget
+ :type account: Account
+
+ :param app: App instance
+ :type app: App
+ """
+ QtGui.QDialog.__init__(self, parent)
+
+ self.account = account
+ self.app = app
+ self._backend_connect()
+
+ self.ui = Ui_PasswordChange()
+ self.ui.setupUi(self)
+
+ self.hide_flash()
+ self.ui.ok_button.clicked.connect(self._change_password)
+ self.ui.cancel_button.clicked.connect(self.close)
+ self.ui.username_lineedit.setText(account.address)
+
+ if PasswordWindow._current_window is not None:
+ PasswordWindow._current_window.close()
+ PasswordWindow._current_window = self
+
+ self._disabled = False # if set to True, never again enable widgets.
+
+ if account.username is None:
+ # should not ever happen, but just in case
+ self._disabled = True
+ self._enable_password_widgets(False)
+ self.ui.cancel_button.setEnabled(True)
+ self.flash_error(self.tr("Please log in to change your password."))
+
+ if self.is_soledad_needed() and not self._soledad_ready:
+ self._enable_password_widgets(False)
+ self.ui.cancel_button.setEnabled(True)
+ self.flash_message(
+ self.tr("Please wait for data storage to be ready."))
+
+ def is_soledad_needed(self):
+ """
+ Returns true if the current account needs to change the soledad
+ password as well as the SRP password.
+ """
+ return self.account.is_email_enabled()
+
+ #
+ # MANAGE WIDGETS
+ #
+
+ def _enable_password_widgets(self, enabled):
+ """
+ Enables or disables the widgets in the password change group box.
+
+ :param enabled: True if the widgets should be enabled.
+ False if widgets should be disabled and
+ display the status label that shows that is
+ changing the password.
+ :type enabled: bool
+ """
+ if self._disabled:
+ return
+
+ if enabled:
+ self.hide_flash()
+ else:
+ self.flash_message(self.tr("Changing password..."))
+
+ self.ui.current_password_lineedit.setEnabled(enabled)
+ self.ui.new_password_lineedit.setEnabled(enabled)
+ self.ui.new_password_confirmation_lineedit.setEnabled(enabled)
+ self.ui.ok_button.setEnabled(enabled)
+ self.ui.cancel_button.setEnabled(enabled)
+
+ def _change_password_success(self):
+ """
+ Callback used to display a successfully changed password.
+ """
+ logger.debug("Password changed successfully.")
+ self._clear_password_inputs()
+ self._enable_password_widgets(True)
+ self.flash_success(self.tr("Password changed successfully."))
+
+ def _clear_password_inputs(self):
+ """
+ Clear the contents of the inputs.
+ """
+ self.ui.current_password_lineedit.setText("")
+ self.ui.new_password_lineedit.setText("")
+ self.ui.new_password_confirmation_lineedit.setText("")
+
+ #
+ # SLOTS
+ #
+
+ def _backend_connect(self):
+ """
+ Helper to connect to backend signals
+ """
+ sig = self.app.signaler
+ sig.srp_password_change_ok.connect(self._srp_change_password_ok)
+ sig.srp_password_change_error.connect(self._srp_password_change_error)
+ sig.srp_password_change_badpw.connect(self._srp_password_change_badpw)
+ sig.soledad_password_change_ok.connect(
+ self._soledad_change_password_ok)
+ sig.soledad_password_change_error.connect(
+ self._soledad_change_password_problem)
+
+ self._soledad_ready = False
+ sig.soledad_bootstrap_finished.connect(self._on_soledad_ready)
+
+ @QtCore.Slot()
+ def _change_password(self):
+ """
+ TRIGGERS:
+ self.ui.buttonBox.accepted
+
+ Changes the user's password if the inputboxes are correctly filled.
+ """
+ current_password = self.ui.current_password_lineedit.text()
+ new_password = self.ui.new_password_lineedit.text()
+ new_password2 = self.ui.new_password_confirmation_lineedit.text()
+
+ self._enable_password_widgets(True)
+
+ if len(current_password) == 0:
+ self.flash_error(self.tr("Password is empty."))
+ self.ui.current_password_lineedit.setFocus()
+ return
+
+ ok, msg, field = password_checks(self.account.username, new_password,
+ new_password2)
+ if not ok:
+ self.flash_error(msg)
+ if field == 'new_password':
+ self.ui.new_password_lineedit.setFocus()
+ elif field == 'new_password_confirmation':
+ self.ui.new_password_confirmation_lineedit.setFocus()
+ return
+
+ self._enable_password_widgets(False)
+ self.app.backend.user_change_password(
+ current_password=current_password,
+ new_password=new_password)
+
+ def closeEvent(self, event=None):
+ """
+ TRIGGERS:
+ cancel_button (indirectly via self.close())
+ or when window is closed
+
+ Close this dialog & delete ourselves to clean up signals.
+ """
+ PasswordWindow._current_window = None
+ self.deleteLater()
+
+ @QtCore.Slot()
+ def _srp_change_password_ok(self):
+ """
+ TRIGGERS:
+ self._backend.signaler.srp_password_change_ok
+
+ Callback used to display a successfully changed password.
+ """
+ new_password = self.ui.new_password_lineedit.text()
+ logger.debug("SRP password changed successfully.")
+
+ if self.is_soledad_needed():
+ self._backend.soledad_change_password(new_password=new_password)
+ else:
+ self._change_password_success()
+
+ @QtCore.Slot()
+ def _srp_password_change_error(self):
+ """
+ TRIGGERS:
+ self._backend.signaler.srp_password_change_error
+
+ Unknown problem changing password
+ """
+ msg = self.tr("There was a problem changing the password.")
+ logger.error(msg)
+ self._enable_password_widgets(True)
+ self.flash_error(msg)
+
+ @QtCore.Slot()
+ def _srp_password_change_badpw(self):
+ """
+ TRIGGERS:
+ self._backend.signaler.srp_password_change_badpw
+
+ The password the user entered was wrong.
+ """
+ msg = self.tr("You did not enter a correct current password.")
+ logger.error(msg)
+ self._enable_password_widgets(True)
+ self.flash_error(msg)
+ self.ui.current_password_lineedit.setFocus()
+
+ @QtCore.Slot()
+ def _soledad_change_password_ok(self):
+ """
+ TRIGGERS:
+ Signaler.soledad_password_change_ok
+
+ Soledad password change went OK.
+ """
+ logger.debug("Soledad password changed successfully.")
+ self._change_password_success()
+
+ @QtCore.Slot(unicode)
+ def _soledad_change_password_problem(self, msg):
+ """
+ TRIGGERS:
+ Signaler.soledad_password_change_error
+
+ Callback used to display an error on changing password.
+
+ :param msg: the message to show to the user.
+ :type msg: unicode
+ """
+ logger.error("Error changing soledad password: %s" % (msg,))
+ self._enable_password_widgets(True)
+ self.flash_error(msg)
+
+ @QtCore.Slot()
+ def _on_soledad_ready(self):
+ """
+ TRIGGERS:
+ Signaler.soledad_bootstrap_finished
+ """
+ self._enable_password_widgets(True)
+ self._soledad_ready = True
diff --git a/src/leap/bitmask/gui/preferences_account_page.py b/src/leap/bitmask/gui/preferences_account_page.py
new file mode 100644
index 00000000..ec6a7716
--- /dev/null
+++ b/src/leap/bitmask/gui/preferences_account_page.py
@@ -0,0 +1,139 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+"""
+Widget for "account" preferences
+"""
+import logging
+
+from functools import partial
+
+from PySide import QtCore, QtGui
+from leap.bitmask.gui.ui_preferences_account_page import Ui_PreferencesAccountPage
+from leap.bitmask.gui.passwordwindow import PasswordWindow
+from leap.bitmask.services import get_service_display_name
+
+logger = logging.getLogger(__name__)
+
+
+class PreferencesAccountPage(QtGui.QWidget):
+
+ def __init__(self, parent, account, app):
+ """
+ :param parent: parent object of the PreferencesWindow.
+ :parent type: QWidget
+
+ :param account: user account (user + provider or just provider)
+ :type account: Account
+
+ :param app: the current App object
+ :type app: App
+ """
+ QtGui.QWidget.__init__(self, parent)
+ self.ui = Ui_PreferencesAccountPage()
+ self.ui.setupUi(self)
+
+ self.account = account
+ self.app = app
+
+ self._selected_services = set()
+ self.ui.change_password_label.setVisible(False)
+ self.ui.provider_services_label.setVisible(False)
+
+ self.ui.change_password_button.clicked.connect(
+ self._show_change_password)
+ app.signaler.prov_get_supported_services.connect(self._load_services)
+ app.backend.provider_get_supported_services(domain=account.domain)
+
+ if account.username is None:
+ self.ui.change_password_label.setText(
+ self.tr('You must be logged in to change your password.'))
+ self.ui.change_password_label.setVisible(True)
+ self.ui.change_password_button.setEnabled(False)
+
+ @QtCore.Slot(str, int)
+ def _service_selection_changed(self, service, state):
+ """
+ TRIGGERS:
+ service_checkbox.stateChanged
+
+ Adds the service to the state if the state is checked, removes
+ it otherwise
+
+ :param service: service to handle
+ :type service: str
+ :param state: state of the checkbox
+ :type state: int
+ """
+ if state == QtCore.Qt.Checked:
+ self._selected_services = \
+ self._selected_services.union(set([service]))
+ else:
+ self._selected_services = \
+ self._selected_services.difference(set([service]))
+ services = list(self._selected_services)
+
+ # We hide the maybe-visible status label after a change
+ self.ui.provider_services_label.setVisible(False)
+
+ # write to config
+ self.app.settings.set_enabled_services(self.account.domain, services)
+
+ # emit signal alerting change
+ self.app.service_selection_changed.emit(self.account, services)
+
+ @QtCore.Slot(str)
+ def _load_services(self, services):
+ """
+ TRIGGERS:
+ prov_get_supported_services
+
+ Loads the services that the provider provides into the UI for
+ the user to enable or disable.
+
+ :param services: list of supported service names
+ :type services: list of str
+ """
+ services_conf = self.account.services()
+
+ self._selected_services = set()
+
+ # Remove existing checkboxes
+ # (the new widget is deleted when its parent is deleted.
+ # We need to loop backwards because removing things from the
+ # beginning shifts items and changes the order of items in the layout.
+ # Using `QObject.deleteLater` doesn't seem to work.)
+ layout = self.ui.provider_services_layout
+ for i in reversed(range(layout.count())):
+ layout.itemAt(i).widget().setParent(None)
+
+ # add one checkbox per service and set the current value
+ # from what is saved in settings.
+ for service in services:
+ try:
+ checkbox = QtGui.QCheckBox(
+ get_service_display_name(service), self)
+ self.ui.provider_services_layout.addWidget(checkbox)
+ checkbox.stateChanged.connect(
+ partial(self._service_selection_changed, service))
+ checkbox.setChecked(service in services_conf)
+ except ValueError:
+ logger.error("Something went wrong while trying to "
+ "load service %s" % (service,))
+
+ @QtCore.Slot()
+ def _show_change_password(self):
+ change_password_window = PasswordWindow(self, self.account, self.app)
+ change_password_window.show()
diff --git a/src/leap/bitmask/gui/preferences_email_page.py b/src/leap/bitmask/gui/preferences_email_page.py
new file mode 100644
index 00000000..80e8d93e
--- /dev/null
+++ b/src/leap/bitmask/gui/preferences_email_page.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+"""
+Widget for "email" preferences
+"""
+import logging
+
+from PySide import QtCore, QtGui
+from leap.bitmask.gui.ui_preferences_email_page import Ui_PreferencesEmailPage
+
+logger = logging.getLogger(__name__)
+
+
+class PreferencesEmailPage(QtGui.QWidget):
+
+ def __init__(self, parent, account, app):
+ QtGui.QWidget.__init__(self, parent)
+ self.ui = Ui_PreferencesEmailPage()
+ self.ui.setupUi(self)
+
+ self.account = account
+ self.app = app
diff --git a/src/leap/bitmask/gui/preferences_vpn_page.py b/src/leap/bitmask/gui/preferences_vpn_page.py
new file mode 100644
index 00000000..901116b4
--- /dev/null
+++ b/src/leap/bitmask/gui/preferences_vpn_page.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2014 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/>.
+"""
+Widget for "vpn" preferences
+"""
+
+from PySide import QtCore, QtGui
+from leap.bitmask.gui.ui_preferences_vpn_page import Ui_PreferencesVpnPage
+
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.gui.flashable import Flashable
+
+
+class PreferencesVpnPage(QtGui.QWidget, Flashable):
+
+ """
+ Page in the preferences window that shows VPN settings
+ """
+
+ def __init__(self, parent, account, app):
+ """
+ :param parent: parent object of the EIPPreferencesWindow.
+ :type parent: QWidget
+
+ :param account: the currently active account
+ :type account: Account
+
+ :param app: shared App instance
+ :type app: App
+ """
+ QtGui.QWidget.__init__(self, parent)
+ self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
+
+ self.account = account
+ self.app = app
+
+ # Load UI
+ self.ui = Ui_PreferencesVpnPage()
+ self.ui.setupUi(self)
+ self.ui.flash_label.setVisible(False)
+ self.hide_flash()
+
+ # Connections
+ self.ui.gateways_list.clicked.connect(self._save_selected_gateway)
+ sig = self.app.signaler
+ sig.eip_get_gateways_list.connect(self._update_gateways_list)
+ sig.eip_get_gateways_list_error.connect(self._gateways_list_error)
+ sig.eip_uninitialized_provider.connect(
+ self._gateways_list_uninitialized)
+
+ # Trigger update
+ self.app.backend.eip_get_gateways_list(domain=self.account.domain)
+
+ @QtCore.Slot(str)
+ def _save_selected_gateway(self, index):
+ """
+ TRIGGERS:
+ self.ui.gateways_list.clicked
+
+ Saves the new gateway setting to the configuration file.
+
+ :param index: the current index of the selection.
+ :type current_item: QModelIndex
+ """
+ item = self.ui.gateways_list.currentItem()
+
+ if item.text() == self.AUTOMATIC_GATEWAY_LABEL:
+ gateway = self.app.settings.GATEWAY_AUTOMATIC
+ else:
+ gateway = item.data(QtCore.Qt.UserRole)
+ self.app.settings.set_selected_gateway(self.account.domain, gateway)
+ self.app.backend.settings_set_selected_gateway(
+ provider=self.account.domain,
+ gateway=gateway)
+
+ @QtCore.Slot(list)
+ def _update_gateways_list(self, gateways):
+ """
+ TRIGGERS:
+ Signaler.eip_get_gateways_list
+
+ :param gateways: a list of gateways
+ :type gateways: list of unicode
+
+ Add the available gateways and select the one stored in
+ configuration file.
+ """
+ self.ui.gateways_list.clear()
+ self.ui.gateways_list.addItem(self.AUTOMATIC_GATEWAY_LABEL)
+
+ selected_gateway = self.app.settings.get_selected_gateway(
+ self.account.domain)
+
+ index = 0
+ for idx, (gw_name, gw_ip, gw_country) in enumerate(gateways):
+ gateway_text = "{0} ({1})".format(gw_name, gw_ip)
+ item = QtGui.QListWidgetItem(self.ui.gateways_list)
+ item.setText(gateway_text)
+ item.setIcon(QtGui.QIcon(
+ ":/images/countries/%s.png" % (gw_country.lower(),)))
+ item.setData(QtCore.Qt.UserRole, gw_ip)
+ if gw_ip == selected_gateway:
+ index = idx + 1
+ self.ui.gateways_list.setCurrentRow(index)
+
+ @QtCore.Slot()
+ def _gateways_list_error(self):
+ """
+ TRIGGERS:
+ Signaler.eip_get_gateways_list_error
+
+ An error has occurred retrieving the gateway list
+ so we inform the user.
+ """
+ self.flash_error(
+ self.tr("Error loading configuration file."))
+ self.ui.gateways_list.setEnabled(False)
+
+ @QtCore.Slot()
+ def _gateways_list_uninitialized(self):
+ """
+ TRIGGERS:
+ Signaler.eip_uninitialized_provider
+
+ The requested provider in not initialized yet, so we give the user an
+ error msg.
+ """
+ self.flash_error(
+ self.tr("This is an uninitialized provider, please log in first."))
+ self.ui.gateways_list.setEnabled(False)
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index 3c9cd5d0..f1252301 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -20,428 +20,167 @@ Preferences window
"""
import logging
-from functools import partial
-
from PySide import QtCore, QtGui
-from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.services import EIP_SERVICE, MX_SERVICE
+
from leap.bitmask.gui.ui_preferences import Ui_Preferences
-from leap.bitmask.util.credentials import password_checks
-from leap.bitmask.services import get_service_display_name, MX_SERVICE
+from leap.bitmask.gui.preferences_account_page import PreferencesAccountPage
+from leap.bitmask.gui.preferences_vpn_page import PreferencesVpnPage
+from leap.bitmask.gui.preferences_email_page import PreferencesEmailPage
logger = logging.getLogger(__name__)
class PreferencesWindow(QtGui.QDialog):
+
"""
Window that displays the preferences.
"""
- preferences_saved = QtCore.Signal()
- def __init__(self, parent, username, domain, backend, soledad_started, mx,
- leap_signaler):
+ _current_window = None # currently visible preferences window
+
+ def __init__(self, parent, account, app):
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
- :param username: the user set in the login widget
- :type username: unicode
- :param domain: the selected domain in the login widget
- :type domain: unicode
- :param backend: Backend being used
- :type backend: Backend
- :param soledad_started: whether soledad has started or not
- :type soledad_started: bool
- :param mx: whether the current provider provides mx or not.
- :type mx: bool
+
+ :param account: the user or provider
+ :type account: Account
+
+ :param app: the current App object
+ :type app: App
"""
QtGui.QDialog.__init__(self, parent)
- self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
-
- self._username = username
- self._domain = domain
- self._leap_signaler = leap_signaler
- self._backend = backend
- self._soledad_started = soledad_started
- self._mx_provided = mx
- self._settings = LeapSettings()
- self._backend_connect()
+ self.account = account
+ self.app = app
- # Load UI
self.ui = Ui_Preferences()
self.ui.setupUi(self)
- self.ui.lblPasswordChangeStatus.setVisible(False)
- self.ui.lblProvidersServicesStatus.setVisible(False)
- self._selected_services = set()
-
- # Connections
- self.ui.pbChangePassword.clicked.connect(self._change_password)
- self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
- self._populate_services)
-
- if not self._settings.get_configured_providers():
- self.ui.gbEnabledServices.setEnabled(False)
- else:
- self._add_configured_providers()
-
- if self._username is None:
- self._not_logged_in()
- else:
- self.ui.gbPasswordChange.setEnabled(True)
- if self._mx_provided:
- self._provides_mx()
-
- self._select_provider_by_name(domain)
-
- def _not_logged_in(self):
- """
- Actions to perform if the user is not logged in.
- """
- msg = self.tr(
- "In order to change your password you need to be logged in.")
- self._set_password_change_status(msg)
- self.ui.gbPasswordChange.setEnabled(False)
-
- def _provides_mx(self):
- """
- Actions to perform if the provider provides MX.
- """
- pw_enabled = True
- enabled_services = self._settings.get_enabled_services(self._domain)
- mx_name = get_service_display_name(MX_SERVICE)
-
- if MX_SERVICE not in enabled_services:
- msg = self.tr("You need to enable {0} in order to change "
- "the password.".format(mx_name))
- self._set_password_change_status(msg, error=True)
- pw_enabled = False
- else:
- # check if Soledad is bootstrapped
- if not self._soledad_started:
- msg = self.tr(
- "You need to wait until {0} is ready in "
- "order to change the password.".format(mx_name))
- self._set_password_change_status(msg)
- pw_enabled = False
-
- self.ui.gbPasswordChange.setEnabled(pw_enabled)
-
- @QtCore.Slot()
- def set_soledad_ready(self):
+ self.ui.close_button.clicked.connect(self.close)
+ self.ui.account_label.setText(account.address)
+
+ self.app.service_selection_changed.connect(self._update_icons)
+
+ self._add_icons()
+ self._add_pages()
+ self._update_icons(self.account, self.account.services())
+
+ # only allow a single preferences window at a time.
+ if PreferencesWindow._current_window is not None:
+ PreferencesWindow._current_window.close_window()
+ PreferencesWindow._current_window = self
+
+ def _add_icons(self):
+ """
+ Adds all the icons for the different configuration categories.
+ Icons are QListWidgetItems added to the nav_widget on the side
+ of the preferences window.
+
+ A note on sizing of QListWidgetItems
+ icon_width = list_widget.width - (2 x nav_widget.spacing) - 2
+ icon_height = 56 seems to look ok
+ """
+ account_item = QtGui.QListWidgetItem(self.ui.nav_widget)
+ account_item.setIcon(QtGui.QIcon(":/images/black/32/user.png"))
+ account_item.setText(self.tr("Account"))
+ account_item.setTextAlignment(
+ QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+ account_item.setFlags(
+ QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+ account_item.setSizeHint(QtCore.QSize(98, 56))
+ self._account_item = account_item
+
+ vpn_item = QtGui.QListWidgetItem(self.ui.nav_widget)
+ vpn_item.setHidden(True)
+ vpn_item.setIcon(QtGui.QIcon(":/images/black/32/earth.png"))
+ vpn_item.setText(self.tr("VPN"))
+ vpn_item.setTextAlignment(
+ QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+ vpn_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+ vpn_item.setSizeHint(QtCore.QSize(98, 56))
+ self._vpn_item = vpn_item
+
+ email_item = QtGui.QListWidgetItem(self.ui.nav_widget)
+ email_item.setHidden(True)
+ email_item.setIcon(QtGui.QIcon(":/images/black/32/email.png"))
+ email_item.setText(self.tr("Email"))
+ email_item.setTextAlignment(
+ QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
+ email_item.setFlags(
+ QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
+ email_item.setSizeHint(QtCore.QSize(98, 56))
+ self._email_item = email_item
+
+ self.ui.nav_widget.currentItemChanged.connect(self._change_page)
+ self.ui.nav_widget.setCurrentRow(0)
+
+ def _add_pages(self):
+ """
+ Adds the pages for the different configuration categories.
+ """
+ self._account_page = PreferencesAccountPage(self, self.account, self.app)
+ self._vpn_page = PreferencesVpnPage(self, self.account, self.app)
+ self._email_page = PreferencesEmailPage(self, self.account, self.app)
+
+ self.ui.pages_widget.addWidget(self._account_page)
+ self.ui.pages_widget.addWidget(self._vpn_page)
+ self.ui.pages_widget.addWidget(self._email_page)
+
+ #
+ # Slots
+ #
+
+ def closeEvent(self, e):
"""
TRIGGERS:
- parent.soledad_ready
+ self.ui.close_button.clicked
+ (since self.close() will trigger closeEvent)
+ whenever the window is closed
- It notifies when the soledad object as ready to use.
+ Close this dialog and destroy it.
"""
- self.ui.lblPasswordChangeStatus.setVisible(False)
- self.ui.gbPasswordChange.setEnabled(True)
+ PreferencesWindow._current_window = None
- def _set_password_change_status(self, status, error=False, success=False):
- """
- Sets the status label for the password change.
-
- :param status: status message to display, can be HTML
- :type status: str
- """
- if error:
- status = "<font color='red'><b>%s</b></font>" % (status,)
- elif success:
- status = "<font color='green'><b>%s</b></font>" % (status,)
-
- if not self.ui.gbPasswordChange.isEnabled():
- status = "<font color='black'>%s</font>" % (status,)
-
- self.ui.lblPasswordChangeStatus.setVisible(True)
- self.ui.lblPasswordChangeStatus.setText(status)
-
- def _set_changing_password(self, disable):
- """
- Enables or disables the widgets in the password change group box.
-
- :param disable: True if the widgets should be disabled and
- it displays the status label that shows that is
- changing the password.
- False if they should be enabled.
- :type disable: bool
- """
- if disable:
- self._set_password_change_status(self.tr("Changing password..."))
-
- self.ui.leCurrentPassword.setEnabled(not disable)
- self.ui.leNewPassword.setEnabled(not disable)
- self.ui.leNewPassword2.setEnabled(not disable)
- self.ui.pbChangePassword.setEnabled(not disable)
+ # deleteLater does not seem to cascade to items in stackLayout
+ # (even with QtCore.Qt.WA_DeleteOnClose attribute).
+ # so, here we call deleteLater() explicitly:
+ self._account_page.deleteLater()
+ self._vpn_page.deleteLater()
+ self._email_page.deleteLater()
+ self.deleteLater()
@QtCore.Slot()
- def _change_password(self):
+ def _change_page(self, current, previous):
"""
TRIGGERS:
- self.ui.pbChangePassword.clicked
-
- Changes the user's password if the inputboxes are correctly filled.
- """
- username = self._username
- current_password = self.ui.leCurrentPassword.text()
- new_password = self.ui.leNewPassword.text()
- new_password2 = self.ui.leNewPassword2.text()
-
- ok, msg = password_checks(username, new_password, new_password2)
-
- if not ok:
- self._set_changing_password(False)
- self._set_password_change_status(msg, error=True)
- self.ui.leNewPassword.setFocus()
- return
-
- self._set_changing_password(True)
- self._backend.user_change_password(current_password=current_password,
- new_password=new_password)
-
- @QtCore.Slot()
- def _srp_change_password_ok(self):
- """
- TRIGGERS:
- self._backend.signaler.srp_password_change_ok
-
- Callback used to display a successfully changed password.
- """
- new_password = self.ui.leNewPassword.text()
- logger.debug("SRP password changed successfully.")
-
- if self._mx_provided:
- self._backend.soledad_change_password(new_password=new_password)
- else:
- self._change_password_success()
+ self.ui.nav_widget.currentItemChanged
- @QtCore.Slot(unicode)
- def _srp_change_password_problem(self, msg):
- """
- TRIGGERS:
- self._backend.signaler.srp_password_change_error
- self._backend.signaler.srp_password_change_badpw
+ Changes what page is displayed.
- Callback used to display an error on changing password.
+ :param current: the currently selected item (might be None?)
+ :type current: PySide.QtGui.QListWidgetItem
- :param msg: the message to show to the user.
- :type msg: unicode
+ :param previous: the previously selected item (might be None)
+ :type previous: PySide.QtGui.QListWidgetItem
"""
- logger.error("Error changing password")
- self._set_password_change_status(msg, error=True)
- self._set_changing_password(False)
+ if not current:
+ current = previous
+ self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current))
- @QtCore.Slot()
- def _soledad_change_password_ok(self):
+ @QtCore.Slot(object, list)
+ def _update_icons(self, account, services):
"""
TRIGGERS:
- Signaler.soledad_password_change_ok
-
- Soledad password change went OK.
- """
- logger.debug("Soledad password changed successfully.")
- self._change_password_success()
+ self.app.service_selection_changed
- def _change_password_success(self):
+ Change which icons are visible.
"""
- Callback used to display a successfully changed password.
- """
- logger.debug("Soledad password changed successfully.")
-
- self._set_password_change_status(
- self.tr("Password changed successfully."), success=True)
- self._clear_password_inputs()
- self._set_changing_password(False)
-
- @QtCore.Slot(unicode)
- def _soledad_change_password_problem(self, msg):
- """
- TRIGGERS:
- Signaler.soledad_password_change_error
-
- Callback used to display an error on changing password.
-
- :param msg: the message to show to the user.
- :type msg: unicode
- """
- logger.error("Error changing soledad password")
- self._set_password_change_status(msg, error=True)
- self._set_changing_password(False)
-
- def _clear_password_inputs(self):
- """
- Clear the contents of the inputs.
- """
- self.ui.leCurrentPassword.setText("")
- self.ui.leNewPassword.setText("")
- self.ui.leNewPassword2.setText("")
-
- def _set_providers_services_status(self, status, success=False):
- """
- Sets the status label for the password change.
-
- :param status: status message to display, can be HTML
- :type status: str
- :param success: is set to True if we should display the
- message as green
- :type success: bool
- """
- if success:
- status = "<font color='green'><b>%s</b></font>" % (status,)
-
- self.ui.lblProvidersServicesStatus.setVisible(True)
- self.ui.lblProvidersServicesStatus.setText(status)
-
- def _add_configured_providers(self):
- """
- Add the client's configured providers to the providers combo boxes.
- """
- self.ui.cbProvidersServices.clear()
- for provider in self._settings.get_configured_providers():
- self.ui.cbProvidersServices.addItem(provider)
-
- def _select_provider_by_name(self, name):
- """
- Given a provider name/domain, selects it in the combobox.
-
- :param name: name or domain for the provider
- :type name: str
- """
- provider_index = self.ui.cbProvidersServices.findText(name)
- self.ui.cbProvidersServices.setCurrentIndex(provider_index)
-
- @QtCore.Slot(str, int)
- def _service_selection_changed(self, service, state):
- """
- TRIGGERS:
- service_checkbox.stateChanged
-
- Adds the service to the state if the state is checked, removes
- it otherwise
-
- :param service: service to handle
- :type service: str
- :param state: state of the checkbox
- :type state: int
- """
- if state == QtCore.Qt.Checked:
- self._selected_services = \
- self._selected_services.union(set([service]))
- else:
- self._selected_services = \
- self._selected_services.difference(set([service]))
-
- # We hide the maybe-visible status label after a change
- self.ui.lblProvidersServicesStatus.setVisible(False)
-
- @QtCore.Slot(str)
- def _populate_services(self, domain):
- """
- TRIGGERS:
- self.ui.cbProvidersServices.currentIndexChanged[unicode]
-
- Fill the services list with the selected provider's services.
-
- :param domain: the domain of the provider to load services from.
- :type domain: str
- """
- # We hide the maybe-visible status label after a change
- self.ui.lblProvidersServicesStatus.setVisible(False)
-
- if not domain:
+ if account != self.account:
return
- # set the proper connection for the 'save' button
- try:
- self.ui.pbSaveServices.clicked.disconnect()
- except RuntimeError:
- pass # Signal was not connected
-
- save_services = partial(self._save_enabled_services, domain)
- self.ui.pbSaveServices.clicked.connect(save_services)
-
- self._backend.provider_get_supported_services(domain=domain)
-
- @QtCore.Slot(str)
- def _load_services(self, services):
- """
- TRIGGERS:
- self.ui.cbProvidersServices.currentIndexChanged[unicode]
-
- Loads the services that the provider provides into the UI for
- the user to enable or disable.
-
- :param domain: the domain of the provider to load services from.
- :type domain: str
- """
- domain = self.ui.cbProvidersServices.currentText()
- services_conf = self._settings.get_enabled_services(domain)
-
- # discard changes if other provider is selected
- self._selected_services = set()
-
- # from: http://stackoverflow.com/a/13103617/687989
- # remove existing checkboxes
- layout = self.ui.vlServices
- for i in reversed(range(layout.count())):
- layout.itemAt(i).widget().setParent(None)
-
- # add one checkbox per service and set the current configured value
- for service in services:
- try:
- checkbox = QtGui.QCheckBox(self)
- service_label = get_service_display_name(service)
- checkbox.setText(service_label)
-
- self.ui.vlServices.addWidget(checkbox)
- checkbox.stateChanged.connect(
- partial(self._service_selection_changed, service))
-
- checkbox.setChecked(service in services_conf)
- except ValueError:
- logger.error("Something went wrong while trying to "
- "load service %s" % (service,))
-
- @QtCore.Slot(str)
- def _save_enabled_services(self, provider):
- """
- TRIGGERS:
- self.ui.pbSaveServices.clicked
-
- Saves the new enabled services settings to the configuration file.
-
- :param provider: the provider config that we need to save.
- :type provider: str
- """
- services = list(self._selected_services)
- self._settings.set_enabled_services(provider, services)
-
- msg = self.tr(
- "Services settings for provider '{0}' saved.".format(provider))
- logger.debug(msg)
- self._set_providers_services_status(msg, success=True)
- self.preferences_saved.emit()
-
- def _backend_connect(self):
- """
- Helper to connect to backend signals
- """
- sig = self._leap_signaler
-
- sig.prov_get_supported_services.connect(self._load_services)
-
- sig.srp_password_change_ok.connect(self._srp_change_password_ok)
-
- pwd_change_error = lambda: self._srp_change_password_problem(
- self.tr("There was a problem changing the password."))
- sig.srp_password_change_error.connect(pwd_change_error)
-
- pwd_change_badpw = lambda: self._srp_change_password_problem(
- self.tr("You did not enter a correct current password."))
- sig.srp_password_change_badpw.connect(pwd_change_badpw)
-
- sig.soledad_password_change_ok.connect(
- self._soledad_change_password_ok)
-
- sig.soledad_password_change_error.connect(
- self._soledad_change_password_problem)
+ self._vpn_item.setHidden(not EIP_SERVICE in services)
+ # self._email_item.setHidden(not MX_SERVICE in services)
+ # ^^ disable email for now, there is nothing there yet.
diff --git a/src/leap/bitmask/gui/providers.py b/src/leap/bitmask/gui/providers.py
index b3eb8620..6954411f 100644
--- a/src/leap/bitmask/gui/providers.py
+++ b/src/leap/bitmask/gui/providers.py
@@ -109,6 +109,6 @@ class Providers(QtCore.QObject):
"""
self._providers_indexes.append(idx)
is_wizard = idx == (self._combo.count() - 1)
- self._provider_changed.emit(is_wizard)
if is_wizard:
self.restore_previous_provider()
+ self._provider_changed.emit(is_wizard)
diff --git a/src/leap/bitmask/gui/ui/eippreferences.ui b/src/leap/bitmask/gui/ui/eippreferences.ui
deleted file mode 100644
index 1a5fcd24..00000000
--- a/src/leap/bitmask/gui/ui/eippreferences.ui
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>EIPPreferences</class>
- <widget class="QDialog" name="EIPPreferences">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>435</width>
- <height>144</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Encrypted Internet Preferences</string>
- </property>
- <property name="windowIcon">
- <iconset resource="../../../../../data/resources/icons.qrc">
- <normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0">
- <widget class="QGroupBox" name="gbGatewaySelector">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="title">
- <string>Select gateway for provider</string>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QLabel" name="lblSelectProvider">
- <property name="text">
- <string>Select &amp;provider:</string>
- </property>
- <property name="buddy">
- <cstring>cbProvidersGateway</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1" colspan="2">
- <widget class="QComboBox" name="cbProvidersGateway">
- <item>
- <property name="text">
- <string>&lt;Select provider&gt;</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="7" column="2">
- <widget class="QPushButton" name="pbSaveGateway">
- <property name="text">
- <string>&amp;Save this provider settings</string>
- </property>
- </widget>
- </item>
- <item row="4" column="0" colspan="3">
- <widget class="QLabel" name="lblProvidersGatewayStatus">
- <property name="text">
- <string>&lt; Providers Gateway Status &gt;</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Select &amp;gateway:</string>
- </property>
- <property name="buddy">
- <cstring>cbGateways</cstring>
- </property>
- </widget>
- </item>
- <item row="1" column="1" colspan="2">
- <widget class="QComboBox" name="cbGateways">
- <item>
- <property name="text">
- <string>Automatic</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </widget>
- <tabstops>
- <tabstop>cbProvidersGateway</tabstop>
- <tabstop>cbGateways</tabstop>
- <tabstop>pbSaveGateway</tabstop>
- </tabstops>
- <resources>
- <include location="../../../../../data/resources/icons.qrc"/>
- </resources>
- <connections/>
-</ui>
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index 92c13d15..b1d68c4a 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -75,7 +75,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
- <height>540</height>
+ <height>549</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -306,7 +306,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
- <height>25</height>
+ <height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -317,7 +317,6 @@
<addaction name="action_advanced_key_management"/>
<addaction name="separator"/>
<addaction name="action_preferences"/>
- <addaction name="action_eip_preferences"/>
<addaction name="separator"/>
<addaction name="action_quit"/>
</widget>
@@ -338,12 +337,7 @@
<bool>true</bool>
</property>
<property name="text">
- <string>Account Preferences...</string>
- </property>
- </action>
- <action name="action_eip_preferences">
- <property name="text">
- <string>Internet Preferences...</string>
+ <string>Pr&amp;eferences...</string>
</property>
</action>
<action name="action_quit">
@@ -378,11 +372,14 @@
</action>
<action name="action_advanced_key_management">
<property name="enabled">
- <bool>true</bool>
+ <bool>false</bool>
</property>
<property name="text">
<string>Advanced Key Management</string>
</property>
+ <property name="visible">
+ <bool>false</bool>
+ </property>
</action>
</widget>
<resources>
diff --git a/src/leap/bitmask/gui/ui/password_change.ui b/src/leap/bitmask/gui/ui/password_change.ui
new file mode 100644
index 00000000..b7ceac38
--- /dev/null
+++ b/src/leap/bitmask/gui/ui/password_change.ui
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PasswordChange</class>
+ <widget class="QDialog" name="PasswordChange">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>459</width>
+ <height>231</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Change Password</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="grid_layout">
+ <item row="0" column="0">
+ <widget class="QLabel" name="username_label">
+ <property name="text">
+ <string>Username:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="new_password_label">
+ <property name="text">
+ <string>New password:</string>
+ </property>
+ <property name="buddy">
+ <cstring>new_password_lineedit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="new_password_lineedit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="new_password_confirmation_label">
+ <property name="text">
+ <string>Re-enter new password:</string>
+ </property>
+ <property name="buddy">
+ <cstring>new_password_confirmation_lineedit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="current_password_label">
+ <property name="text">
+ <string>Current password:</string>
+ </property>
+ <property name="buddy">
+ <cstring>current_password_lineedit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="new_password_confirmation_lineedit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="current_password_lineedit">
+ <property name="echoMode">
+ <enum>QLineEdit::Password</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <spacer name="spacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>10</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="1">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLineEdit" name="username_lineedit">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QLabel" name="flash_label">
+ <property name="text">
+ <string>&lt;flash_label&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacer2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="button_layout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancel_button">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="ok_button">
+ <property name="text">
+ <string>OK</string>
+ </property>
+ <property name="autoDefault">
+ <bool>false</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>username_lineedit</tabstop>
+ <tabstop>current_password_lineedit</tabstop>
+ <tabstop>new_password_lineedit</tabstop>
+ <tabstop>new_password_confirmation_lineedit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui
index cd4d3a77..5e30ea57 100644
--- a/src/leap/bitmask/gui/ui/preferences.ui
+++ b/src/leap/bitmask/gui/ui/preferences.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>503</width>
- <height>401</height>
+ <width>520</width>
+ <height>439</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,159 +17,97 @@
<iconset resource="../../../../../data/resources/icons.qrc">
<normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
</property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="4" column="0">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1,0">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="account_label">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
+ <property name="text">
+ <string>user@example.org</string>
</property>
- </spacer>
+ </widget>
</item>
- <item row="0" column="0">
- <widget class="QGroupBox" name="gbPasswordChange">
- <property name="enabled">
- <bool>false</bool>
- </property>
- <property name="title">
- <string>Password Change</string>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
</property>
- <layout class="QFormLayout" name="formLayout">
- <property name="fieldGrowthPolicy">
- <enum>QFormLayout::ExpandingFieldsGrow</enum>
- </property>
- <item row="0" column="0">
- <widget class="QLabel" name="lblCurrentPassword">
- <property name="text">
- <string>&amp;Current password:</string>
- </property>
- <property name="buddy">
- <cstring>leCurrentPassword</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QLineEdit" name="leCurrentPassword">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="lblNewPassword">
- <property name="text">
- <string>&amp;New password:</string>
- </property>
- <property name="buddy">
- <cstring>leNewPassword</cstring>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QLineEdit" name="leNewPassword">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QLabel" name="lblNewPassword2">
- <property name="text">
- <string>&amp;Re-enter new password:</string>
- </property>
- <property name="buddy">
- <cstring>leNewPassword2</cstring>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLineEdit" name="leNewPassword2">
- <property name="echoMode">
- <enum>QLineEdit::Password</enum>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QPushButton" name="pbChangePassword">
- <property name="text">
- <string>Change</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0" colspan="2">
- <widget class="QLabel" name="lblPasswordChangeStatus">
- <property name="text">
- <string>&lt;Password change status&gt;</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- </layout>
</widget>
</item>
- <item row="3" column="0">
- <widget class="QGroupBox" name="gbEnabledServices">
- <property name="title">
- <string>Enabled services</string>
+ <item>
+ <layout class="QHBoxLayout" name="horizontal_layout" stretch="0,0">
+ <property name="spacing">
+ <number>12</number>
</property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="3" column="1">
- <widget class="QPushButton" name="pbSaveServices">
- <property name="text">
- <string>Save this provider settings</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="2">
- <widget class="QGroupBox" name="gbServices">
- <property name="title">
- <string>Services</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="vlServices"/>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="cbProvidersServices">
- <item>
- <property name="text">
- <string>&lt;Select provider&gt;</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="0" column="0">
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Select provider:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0" colspan="2">
- <widget class="QLabel" name="lblProvidersServicesStatus">
- <property name="text">
- <string>&lt; Providers Services Status &gt;</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
+ <item>
+ <widget class="QListWidget" name="nav_widget">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>120</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="iconSize">
+ <size>
+ <width>32</width>
+ <height>32</height>
+ </size>
+ </property>
+ <property name="movement">
+ <enum>QListView::Static</enum>
+ </property>
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="viewMode">
+ <enum>QListView::IconMode</enum>
+ </property>
+ <property name="uniformItemSizes">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QStackedWidget" name="pages_widget"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="button_layout" stretch="0,0">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="close_button">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
diff --git a/src/leap/bitmask/gui/ui/preferences_account_page.ui b/src/leap/bitmask/gui/ui/preferences_account_page.ui
new file mode 100644
index 00000000..9b6d885b
--- /dev/null
+++ b/src/leap/bitmask/gui/ui/preferences_account_page.ui
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreferencesAccountPage</class>
+ <widget class="QWidget" name="PreferencesAccountPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>462</width>
+ <height>371</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QGroupBox" name="provider_services_box">
+ <property name="title">
+ <string>Services</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="provider_services_layout"/>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="provider_services_label">
+ <property name="text">
+ <string>&lt;provider_services_label&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Preferred</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>15</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox">
+ <property name="title">
+ <string>Password</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QPushButton" name="change_password_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Change Password</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="change_password_label">
+ <property name="text">
+ <string>&lt;change_password_label&gt;</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/leap/bitmask/gui/ui/preferences_email_page.ui b/src/leap/bitmask/gui/ui/preferences_email_page.ui
new file mode 100644
index 00000000..41b3c28d
--- /dev/null
+++ b/src/leap/bitmask/gui/ui/preferences_email_page.ui
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreferencesEmailPage</class>
+ <widget class="QWidget" name="PreferencesEmailPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <widget class="QPushButton" name="pushButton">
+ <property name="geometry">
+ <rect>
+ <x>250</x>
+ <y>210</y>
+ <width>98</width>
+ <height>27</height>
+ </rect>
+ </property>
+ <property name="text">
+ <string>PushButton</string>
+ </property>
+ </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/leap/bitmask/gui/ui/preferences_vpn_page.ui b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui
new file mode 100644
index 00000000..1bf3a060
--- /dev/null
+++ b/src/leap/bitmask/gui/ui/preferences_vpn_page.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PreferencesVpnPage</class>
+ <widget class="QWidget" name="PreferencesVpnPage">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>362</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QLabel" name="flash_label">
+ <property name="text">
+ <string>&lt;flash_label&gt;</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="heading_label">
+ <property name="text">
+ <string>Default VPN Gateway:</string>
+ </property>
+ <property name="buddy">
+ <cstring>gateways_list</cstring>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QListWidget" name="gateways_list">
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="tip_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>You must reconnect for changes to take effect.</string>
+ </property>
+ <property name="scaledContents">
+ <bool>false</bool>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="buddy">
+ <cstring>gateways_list</cstring>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="../../../../../data/resources/flags.qrc"/>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index 0223c053..ff9cae55 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -42,6 +42,7 @@ logger = logging.getLogger(__name__)
class Wizard(QtGui.QWizard):
+
"""
First run wizard to register a user and setup a provider
"""
@@ -96,11 +97,6 @@ class Wizard(QtGui.QWizard):
# this details are set when the provider download is complete.
self._provider_details = None
- # We will store a reference to the defers for eventual use
- # (eg, to cancel them) but not doing anything with them right now.
- self._provider_select_defer = None
- self._provider_setup_defer = None
-
self._connect_and_track(self.currentIdChanged,
self._current_id_changed)
@@ -322,7 +318,8 @@ class Wizard(QtGui.QWizard):
user_ok, msg = username_checks(username)
if user_ok:
- pass_ok, msg = password_checks(username, password, password2)
+ pass_ok, msg, field = password_checks(
+ username, password, password2)
if user_ok and pass_ok:
self._set_register_status(self.tr("Starting registration..."))
@@ -479,8 +476,7 @@ class Wizard(QtGui.QWizard):
self.button(QtGui.QWizard.BackButton).clearFocus()
self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON)
- self._provider_select_defer = self._backend.\
- provider_setup(provider=self._domain)
+ self._backend.provider_setup(provider=self._domain)
@QtCore.Slot(bool)
def _skip_provider_checks(self, skip):
@@ -678,8 +674,9 @@ class Wizard(QtGui.QWizard):
Loads the services that the provider provides into the UI for
the user to enable or disable.
"""
- self.ui.grpServices.setTitle(
- self.tr("Services by {0}").format(self._provider_details['domain']))
+ title = self.tr("Services by {0}").format(
+ self._provider_details['domain'])
+ self.ui.grpServices.setTitle(title)
services = get_supported(self._provider_details['services'])
@@ -724,8 +721,7 @@ class Wizard(QtGui.QWizard):
if not self._provider_setup_ok:
self._reset_provider_setup()
self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON)
- self._provider_setup_defer = self._backend.\
- provider_bootstrap(provider=self._domain)
+ self._backend.provider_bootstrap(provider=self._domain)
if pageId == self.PRESENT_PROVIDER_PAGE:
details = self._provider_details
diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py
index f56b9330..1d6bb1d0 100644
--- a/src/leap/bitmask/platform_init/initializers.py
+++ b/src/leap/bitmask/platform_init/initializers.py
@@ -29,9 +29,9 @@ from PySide import QtGui, QtCore
from leap.bitmask.config import flags
from leap.bitmask.config.leapsettings import LeapSettings
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.privilege_policies import LinuxPolicyChecker
logger = logging.getLogger(__name__)
@@ -127,12 +127,15 @@ def check_missing():
complain_missing = True
launcher = get_vpn_launcher()
- missing_scripts = launcher.missing_updown_scripts
- missing_other = launcher.missing_other_files
+ missing_scripts = launcher.missing_updown_scripts()
+ missing_other = launcher.missing_other_files()
- logger.debug("MISSING OTHER: %s" % (str(missing_other())))
+ if missing_scripts:
+ logger.warning("Missing scripts: %s" % (missing_scripts))
+ if missing_other:
+ logger.warning("Missing other files: %s" % (missing_other))
- missing_some = missing_scripts() or missing_other()
+ missing_some = missing_scripts or missing_other
if alert_missing and missing_some:
msg = get_missing_helpers_dialog()
ret = msg.exec_()
@@ -164,13 +167,20 @@ def check_missing():
logger.debug(
"Setting alert_missing_scripts to False, we will not "
"ask again")
+ init_signals.eip_missing_helpers.emit()
config.set_alert_missing_scripts(False)
if complain_missing and missing_some:
- missing = missing_scripts() + missing_other()
+ missing = missing_scripts + missing_other
msg = _get_missing_complain_dialog(missing)
ret = msg.exec_()
+ # If there is some missing file and we don't want to complain, we emit the
+ # 'missing helpers' signal so the eip status can show that some files are
+ # missing.
+ if missing_some and not alert_missing and not complain_missing:
+ init_signals.eip_missing_helpers.emit()
+
#
# windows initializers
#
@@ -435,7 +445,6 @@ def _linux_install_missing_scripts(badexec, notfound):
success = False
installer_path = os.path.abspath(
os.path.join(os.getcwd(), "apps", "eip", "files"))
- launcher = LinuxVPNLauncher
install_helper = "leap-install-helper.sh"
install_helper_path = os.path.join(installer_path, install_helper)
@@ -446,7 +455,8 @@ def _linux_install_missing_scripts(badexec, notfound):
if os.path.isdir(installer_path):
try:
- pkexec = first(launcher.maybe_pkexec())
+ policyChecker = LinuxPolicyChecker()
+ pkexec = first(policyChecker.maybe_pkexec())
cmdline = ["%s %s %s" % (
pkexec, install_helper_path, install_opts)]
diff --git a/src/leap/bitmask/platform_init/locks.py b/src/leap/bitmask/platform_init/locks.py
index 78ebf4cd..ac45a5ce 100644
--- a/src/leap/bitmask/platform_init/locks.py
+++ b/src/leap/bitmask/platform_init/locks.py
@@ -22,11 +22,11 @@ import errno
import os
import platform
-from leap.bitmask import platform_init
+from leap.bitmask.platform_init import IS_WIN, IS_UNIX
from leap.common.events import signal as signal_event
from leap.common.events import events_pb2 as proto
-if platform_init.IS_UNIX:
+if IS_UNIX:
from fcntl import flock, LOCK_EX, LOCK_NB
else: # WINDOWS
import datetime
@@ -40,7 +40,7 @@ else: # WINDOWS
logger = logging.getLogger(__name__)
-if platform_init.IS_UNIX:
+if IS_UNIX:
class UnixLock(object):
"""
@@ -48,14 +48,13 @@ if platform_init.IS_UNIX:
See man 2 flock
"""
- def __init__(self, path):
+ _LOCK_FILE = '/tmp/bitmask.lock'
+
+ def __init__(self):
"""
- iniializes t he UnixLock with the path of the
- desired lockfile
+ Initialize the UnixLock.
"""
-
self._fd = None
- self.path = path
def get_lock(self):
"""
@@ -77,7 +76,7 @@ if platform_init.IS_UNIX:
:rtype: bool
"""
- self._fd = os.open(self.path, os.O_CREAT | os.O_RDWR)
+ self._fd = os.open(self._LOCK_FILE, os.O_CREAT | os.O_RDWR)
try:
flock(self._fd, LOCK_EX | LOCK_NB)
@@ -102,6 +101,21 @@ if platform_init.IS_UNIX:
gotit, pid = self._get_lock_and_pid()
return pid == os.getpid()
+ @classmethod
+ def release_lock(self):
+ """
+ Release the lock.
+
+ :return: True if the lock was released, False otherwise
+ :rtype: bool
+ """
+ try:
+ os.remove(self._LOCK_FILE)
+ return True
+ except Exception as e:
+ logger.debug("Problem removing lock, {0!r}".format(e))
+ return False
+
def _get_lock_and_pid(self):
"""
Tries to get a lock over the file.
@@ -109,7 +123,6 @@ if platform_init.IS_UNIX:
:rtype: tuple
"""
-
if self._get_lock():
self._write_to_pidfile()
return True, None
@@ -121,9 +134,7 @@ if platform_init.IS_UNIX:
Tries to read pid from the pidfile,
returns False if no content found.
"""
-
- pidfile = os.read(
- self._fd, 16)
+ pidfile = os.read(self._fd, 16)
if not pidfile:
return False
@@ -144,7 +155,7 @@ if platform_init.IS_UNIX:
os.fsync(fd)
-if platform_init.IS_WIN:
+if IS_WIN:
# Time to wait (in secs) before assuming a raise window signal has not been
# ack-ed.
@@ -348,17 +359,15 @@ def we_are_the_one_and_only():
:rtype: bool
"""
- _sys = platform.system()
-
- if _sys in ("Linux", "Darwin"):
- locker = UnixLock('/tmp/bitmask.lock')
+ if IS_UNIX:
+ locker = UnixLock()
locker.get_lock()
we_are_the_one = locker.locked_by_us
if not we_are_the_one:
signal_event(proto.RAISE_WINDOW)
return we_are_the_one
- elif _sys == "Windows":
+ elif IS_WIN:
locker = WindowsLock()
locker.get_lock()
we_are_the_one = locker.locked_by_us
@@ -398,6 +407,16 @@ def we_are_the_one_and_only():
else:
logger.warning("Multi-instance checker "
- "not implemented for %s" % (_sys))
+ "not implemented for %s" % (platform.system()))
# lies, lies, lies...
return True
+
+
+def release_lock():
+ """
+ Release the acquired lock.
+ """
+ if IS_WIN:
+ WindowsLock.release_all_locks()
+ elif IS_UNIX:
+ UnixLock.release_lock()
diff --git a/src/leap/bitmask/services/eip/conductor.py b/src/leap/bitmask/services/eip/conductor.py
index 0ee56628..01dd7449 100644
--- a/src/leap/bitmask/services/eip/conductor.py
+++ b/src/leap/bitmask/services/eip/conductor.py
@@ -124,6 +124,12 @@ class EIPConductor(object):
"""
self._backend.tear_fw_down()
+ def terminate(self):
+ """
+ Turn off VPN
+ """
+ self.qtsigs.do_disconnect_signal.emit()
+
@QtCore.Slot()
def _start_eip(self):
"""
diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py
index 37c0c8ae..5b51d12e 100644
--- a/src/leap/bitmask/services/eip/eipconfig.py
+++ b/src/leap/bitmask/services/eip/eipconfig.py
@@ -113,39 +113,38 @@ class VPNGatewaySelector(object):
"""
Return the existing gateways, sorted by timezone proximity.
- :rtype: list of tuples (location, ip)
- (str, IPv4Address or IPv6Address object)
+ :rtype: list of tuples (label, ip, country_code)
+ (str, IPv4Address or IPv6Address object, str)
"""
gateways_timezones = []
locations = self._eipconfig.get_locations()
gateways = self._eipconfig.get_gateways()
for idx, gateway in enumerate(gateways):
- gateway_location = gateway.get('location')
- gateway_distance = 99 # if hasn't location -> should go last
-
- if gateway_location is not None:
- timezone = locations[gateway['location']]['timezone']
- gateway_name = locations[gateway['location']].get('name', None)
- if gateway_name is not None:
- gateway_location = gateway_name
-
- gw_offset = int(timezone)
- if gw_offset in self.equivalent_timezones:
- gw_offset = self.equivalent_timezones[gw_offset]
-
- gateway_distance = self._get_timezone_distance(gw_offset)
+ distance = 99 # if hasn't location -> should go last
+ location = locations.get(gateway.get('location'))
+ label = gateway.get('location', 'Unknown')
+ country = 'XX'
+ if location is not None:
+ country = location.get('country_code', 'XX')
+ label = location.get('name', label)
+ timezone = location.get('timezone')
+ if timezone is not None:
+ offset = int(timezone)
+ if offset in self.equivalent_timezones:
+ offset = self.equivalent_timezones[offset]
+ distance = self._get_timezone_distance(offset)
ip = self._eipconfig.get_gateway_ip(idx)
- gateways_timezones.append((ip, gateway_distance, gateway_location))
+ gateways_timezones.append((ip, distance, label, country))
gateways_timezones = sorted(gateways_timezones, key=lambda gw: gw[1])
- gateways = []
- for ip, distance, location in gateways_timezones:
- gateways.append((location, ip))
+ result = []
+ for ip, distance, label, country in gateways_timezones:
+ result.append((label, ip, country))
- return gateways
+ return result
def get_gateways(self):
"""
@@ -153,7 +152,7 @@ class VPNGatewaySelector(object):
:rtype: list of IPv4Address or IPv6Address object.
"""
- gateways = [ip for location, ip in self.get_gateways_list()][:4]
+ gateways = [gateway[1] for gateway in self.get_gateways_list()][:4]
return gateways
def get_gateways_country_code(self):
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
index b6e47f25..a3ab408b 100644
--- a/src/leap/bitmask/services/eip/linuxvpnlauncher.py
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -20,17 +20,15 @@ Linux VPN launcher implementation.
import commands
import logging
import os
-import subprocess
import sys
-import time
from leap.bitmask.config import flags
from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
-from leap.common.files import which
+from leap.bitmask.util.privilege_policies import NoPolkitAuthAgentAvailable
+from leap.bitmask.util.privilege_policies import NoPkexecAvailable
from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
from leap.bitmask.util import get_path_prefix, force_eval
-from leap.common.check import leap_assert
from leap.bitmask.util import first
logger = logging.getLogger(__name__)
@@ -46,66 +44,11 @@ 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
- """
- # Note that gnome-shell does not uses a separate process for the
- # polkit-agent, it uses a polkit-agent within its own process so we can't
- # ps-grep a polkit process, we can ps-grep gnome-shell itself.
-
- # the [x] thing is to avoid grep match itself
- polkit_options = [
- 'ps aux | grep "polkit-[g]nome-authentication-agent-1"',
- 'ps aux | grep "polkit-[k]de-authentication-agent-1"',
- 'ps aux | grep "polkit-[m]ate-authentication-agent-1"',
- 'ps aux | grep "[l]xpolkit"',
- 'ps aux | grep "[g]nome-shell"',
- 'ps aux | grep "[f]ingerprint-polkit-agent"',
- ]
- is_running = [commands.getoutput(cmd) for cmd in polkit_options]
-
- return any(is_running)
-
-
-def _try_to_launch_agent():
- """
- Tries to launch a polkit daemon.
- """
- env = None
- if flags.STANDALONE:
- 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.
- logger.debug("Trying to launch polkit agent")
- subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
- shell=True, env=env)
- except Exception as exc:
- logger.exception(exc)
-
-
SYSTEM_CONFIG = "/etc/leap"
leapfile = lambda f: "%s/%s" % (SYSTEM_CONFIG, f)
class LinuxVPNLauncher(VPNLauncher):
- PKEXEC_BIN = 'pkexec'
# The following classes depend on force_eval to be called against
# the classes, to get the evaluation of the standalone flag on runtine.
@@ -130,36 +73,6 @@ class LinuxVPNLauncher(VPNLauncher):
OTHER_FILES = (POLKIT_PATH, BITMASK_ROOT, OPENVPN_BIN_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(2)
- 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 get_vpn_command(kls, eipconfig, providerconfig, socket_host,
socket_port="unix", openvpn_verb=1):
"""
@@ -194,7 +107,13 @@ class LinuxVPNLauncher(VPNLauncher):
command.insert(1, "openvpn")
command.insert(2, "start")
- pkexec = kls.maybe_pkexec()
+ policyChecker = LinuxPolicyChecker()
+ try:
+ pkexec = policyChecker.maybe_pkexec()
+ except NoPolkitAuthAgentAvailable:
+ raise EIPNoPolkitAuthAgentAvailable()
+ except NoPkexecAvailable:
+ raise EIPNoPkexecAvailable()
if pkexec:
command.insert(0, first(pkexec))
diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py
index c7159a93..8dc6021f 100644
--- a/src/leap/bitmask/services/eip/vpnprocess.py
+++ b/src/leap/bitmask/services/eip/vpnprocess.py
@@ -50,8 +50,7 @@ from leap.common.check import leap_assert, leap_assert_type
logger = logging.getLogger(__name__)
vpnlog = logging.getLogger('leap.openvpn')
-from twisted.internet import protocol
-from twisted.internet import defer
+from twisted.internet import defer, protocol, reactor
from twisted.internet import error as internet_error
from twisted.internet.task import LoopingCall
@@ -157,10 +156,8 @@ class VPN(object):
of a QObject containing the QSignals that we will pass along
to the VPNManager.
"""
- from twisted.internet import reactor
self._vpnproc = None
self._pollers = []
- self._reactor = reactor
self._signaler = kwargs['signaler']
self._openvpn_verb = flags.OPENVPN_VERBOSITY
@@ -217,14 +214,15 @@ class VPN(object):
# and abstract us away from anything else.
try:
cmd = vpnproc.getCommand()
- except Exception:
- logger.error("Error while getting vpn command...")
+ except Exception as e:
+ logger.error("Error while getting vpn command... {0!r}".format(e))
raise
+
env = os.environ
for key, val in vpnproc.vpn_env.items():
env[key] = val
- self._reactor.spawnProcess(vpnproc, cmd[0], cmd, env)
+ reactor.spawnProcess(vpnproc, cmd[0], cmd, env)
self._vpnproc = vpnproc
# add pollers for status and state
@@ -300,7 +298,6 @@ class VPN(object):
:param tries: counter of tries, used in recursion
:type tries: int
"""
- from twisted.internet import reactor
while tries < self.TERMINATE_MAXTRIES:
if self._vpnproc.transport.pid is None:
logger.debug("Process has been happily terminated.")
@@ -351,7 +348,6 @@ class VPN(object):
:param restart: whether this stop is part of a hard restart.
:type restart: bool
"""
- from twisted.internet import reactor
self._stop_pollers()
# First we try to be polite and send a SIGTERM...
@@ -375,6 +371,8 @@ class VPN(object):
logger.debug("Firewall down")
else:
logger.warning("Could not tear firewall down")
+ else:
+ logger.debug("VPN is not running.")
def _start_pollers(self):
"""
@@ -424,8 +422,6 @@ class VPNManager(object):
backend
:type signaler: backend.Signaler
"""
- from twisted.internet import reactor
- self._reactor = reactor
self._tn = None
self._signaler = signaler
self._aborted = False
@@ -602,7 +598,7 @@ class VPNManager(object):
logger.debug('trying to connect to management')
if not self.aborted and not self.is_connected():
self.connect_to_management(self._socket_host, self._socket_port)
- self._reactor.callLater(
+ reactor.callLater(
self.CONNECTION_RETRY_TIME,
self.try_to_connect_to_management, retry + 1)
diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py
index 5e85368f..416aff34 100644
--- a/src/leap/bitmask/services/mail/conductor.py
+++ b/src/leap/bitmask/services/mail/conductor.py
@@ -22,6 +22,7 @@ import logging
from leap.bitmask.config import flags
from leap.bitmask.gui import statemachines
from leap.bitmask.services.mail import connection as mail_connection
+from leap.bitmask.services.mail.emailfirewall import get_email_firewall
from leap.common.events import events_pb2 as leap_events
from leap.common.events import register as leap_register
@@ -211,6 +212,11 @@ class MailConductor(IMAPControl, SMTPControl):
self._mail_connection = mail_connection.MailConnection()
self._userid = None
+ try:
+ self._firewall = get_email_firewall()
+ except NotImplementedError:
+ self._firewall = None
+ logger.info("Email firewall is not implemented in this platform")
@property
def userid(self):
@@ -247,12 +253,25 @@ class MailConductor(IMAPControl, SMTPControl):
self._smtp_machine = smtp
self._smtp_machine.start()
+ def start_mail_service(self, download_if_needed=False, offline=False):
+ """
+ Start the IMAP and SMTP servcies.
+ """
+ if self._firewall is not None:
+ self._firewall.start()
+ if not offline:
+ logger.debug("not starting smtp in offline mode")
+ self.start_smtp_service(download_if_needed=download_if_needed)
+ self.start_imap_service()
+
def stop_mail_services(self):
"""
Stop the IMAP and SMTP services.
"""
self.stop_imap_service()
self.stop_smtp_service()
+ if self._firewall is not None:
+ self._firewall.stop()
def connect_mail_signals(self, widget):
"""
diff --git a/src/leap/bitmask/services/mail/emailfirewall.py b/src/leap/bitmask/services/mail/emailfirewall.py
new file mode 100644
index 00000000..2cd2ec31
--- /dev/null
+++ b/src/leap/bitmask/services/mail/emailfirewall.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+# emailfirewall.py
+# Copyright (C) 2014 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/>.
+"""
+Email firewall implementation.
+"""
+
+import os
+import subprocess
+
+from abc import ABCMeta, abstractmethod
+
+from leap.bitmask.config import flags
+from leap.bitmask.platform_init import IS_LINUX
+from leap.bitmask.util import first, force_eval
+from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
+from leap.common.check import leap_assert
+
+
+def get_email_firewall():
+ """
+ Return the email firewall handler for the current platform.
+ """
+ if not (IS_LINUX):
+ error_msg = "Email firewall not implemented for this platform."
+ raise NotImplementedError(error_msg)
+
+ firewall = None
+ if IS_LINUX:
+ firewall = LinuxEmailFirewall
+
+ leap_assert(firewall is not None)
+
+ return firewall()
+
+
+class EmailFirewall(object):
+ """
+ Abstract email firwall class
+ """
+ __metaclass__ = ABCMeta
+
+ @abstractmethod
+ def start(self):
+ """
+ Start email firewall
+ """
+ return False
+
+ @abstractmethod
+ def stop(self):
+ """
+ Stop email firewall
+ """
+ return False
+
+
+class EmailFirewallException(Exception):
+ pass
+
+
+class LinuxEmailFirewall(EmailFirewall):
+
+ class BITMASK_ROOT(object):
+ def __call__(self):
+ return ("/usr/local/sbin/bitmask-root" if flags.STANDALONE else
+ "/usr/sbin/bitmask-root")
+
+ def start(self):
+ uid = str(os.getuid())
+ return True if self._run(["start", uid]) is 0 else False
+
+ def stop(self):
+ return True if self._run(["stop"]) is 0 else False
+
+ def _run(self, cmd):
+ """
+ Run an email firewall command with bitmask-root
+
+ Might raise:
+ NoPkexecAvailable,
+ NoPolkitAuthAgentAvailable,
+
+ :param cmd: command to send to bitmask-root fw-email
+ :type cmd: [str]
+ :returns: exit code of bitmask-root
+ :rtype: int
+ """
+ command = []
+
+ policyChecker = LinuxPolicyChecker()
+ pkexec = policyChecker.maybe_pkexec()
+ if pkexec:
+ command.append(first(pkexec))
+
+ command.append(force_eval(self.BITMASK_ROOT))
+ command.append("fw-email")
+ command += cmd
+
+ # XXX: will be nice to use twisted ProcessProtocol instead of
+ # subprocess to avoid blocking until it finish
+ return subprocess.call(command)
diff --git a/src/leap/bitmask/services/mail/smtpbootstrapper.py b/src/leap/bitmask/services/mail/smtpbootstrapper.py
index 3ef755e8..9dd61488 100644
--- a/src/leap/bitmask/services/mail/smtpbootstrapper.py
+++ b/src/leap/bitmask/services/mail/smtpbootstrapper.py
@@ -173,3 +173,5 @@ class SMTPBootstrapper(AbstractBootstrapper):
logger.debug('Stopping SMTP service.')
self._smtp_port.stopListening()
self._smtp_service.doStop()
+ else:
+ logger.debug('SMTP service not running.')
diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py
index caa94ec7..e8eddd64 100644
--- a/src/leap/bitmask/util/__init__.py
+++ b/src/leap/bitmask/util/__init__.py
@@ -151,6 +151,6 @@ def flags_to_dict():
:rtype: dict.
"""
items = [i for i in dir(flags) if i[0] != '_']
- values = {i: getattr(flags, i) for i in items}
+ values = dict((i, getattr(flags, i)) for i in items)
return values
diff --git a/src/leap/bitmask/util/credentials.py b/src/leap/bitmask/util/credentials.py
index 757ce10c..dfc78a09 100644
--- a/src/leap/bitmask/util/credentials.py
+++ b/src/leap/bitmask/util/credentials.py
@@ -38,7 +38,7 @@ def username_checks(username):
valid = USERNAME_VALIDATOR.validate(username, 0)
valid_username = valid[0] == QtGui.QValidator.State.Acceptable
if message is None and not valid_username:
- message = _tr("Invalid username")
+ message = _tr("That username is not allowed. Try another.")
return message is None, message
@@ -54,28 +54,34 @@ def password_checks(username, password, password2):
:param password2: second password from the registration form
:type password: str
- :returns: True and empty message if all the checks pass,
- False and an error message otherwise
- :rtype: tuple(bool, str)
+ :returns: (True, None, None) if all the checks pass,
+ (False, message, field name) otherwise
+ :rtype: tuple(bool, str, str)
"""
# translation helper
_tr = QtCore.QObject().tr
message = None
+ field = None
if message is None and password != password2:
message = _tr("Passwords don't match")
+ field = 'new_password_confirmation'
if message is None and not password:
- message = _tr("You can't use an empty password")
+ message = _tr("Password is empty")
+ field = 'new_password'
if message is None and len(password) < 8:
- message = _tr("Password too short")
+ message = _tr("Password is too short")
+ field = 'new_password'
if message is None and password in WEAK_PASSWORDS:
- message = _tr("Password too easy")
+ message = _tr("Password is too easy")
+ field = 'new_password'
if message is None and username == password:
- message = _tr("Password equal to username")
+ message = _tr("Password can't be the same as username")
+ field = 'new_password'
- return message is None, message
+ return message is None, message, field
diff --git a/src/leap/bitmask/util/privilege_policies.py b/src/leap/bitmask/util/privilege_policies.py
index f894d73b..2016e67b 100644
--- a/src/leap/bitmask/util/privilege_policies.py
+++ b/src/leap/bitmask/util/privilege_policies.py
@@ -18,17 +18,30 @@
Helpers to determine if the needed policies for privilege escalation
are operative under this client run.
"""
+import commands
import logging
import os
+import subprocess
import platform
+import time
from abc import ABCMeta, abstractmethod
from leap.bitmask.config import flags
+from leap.common.check import leap_assert
+from leap.common.files import which
logger = logging.getLogger(__name__)
+class NoPolkitAuthAgentAvailable(Exception):
+ pass
+
+
+class NoPkexecAvailable(Exception):
+ pass
+
+
def is_missing_policy_permissions():
"""
Returns True if we do not have implemented a policy checker for this
@@ -75,6 +88,7 @@ class LinuxPolicyChecker(PolicyChecker):
"se.leap.bitmask.policy")
LINUX_POLKIT_FILE_BUNDLE = ("/usr/share/polkit-1/actions/"
"se.leap.bitmask.bundle.policy")
+ PKEXEC_BIN = 'pkexec'
@classmethod
def get_polkit_path(self):
@@ -97,3 +111,87 @@ class LinuxPolicyChecker(PolicyChecker):
"""
path = self.get_polkit_path()
return not os.path.isfile(path)
+
+ @classmethod
+ def maybe_pkexec(self):
+ """
+ Checks whether pkexec is available in the system, and
+ returns the path if found.
+
+ Might raise:
+ NoPkexecAvailable,
+ NoPolkitAuthAgentAvailable.
+
+ :returns: a list of the paths where pkexec is to be found
+ :rtype: list
+ """
+ if self._is_pkexec_in_system():
+ if not self.is_up():
+ self.launch()
+ time.sleep(2)
+ if self.is_up():
+ pkexec_possibilities = which(self.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 NoPolkitAuthAgentAvailable()
+ else:
+ logger.warning("System has no pkexec")
+ raise NoPkexecAvailable()
+
+ @classmethod
+ def launch(self):
+ """
+ Tries to launch policykit
+ """
+ env = None
+ if flags.STANDALONE:
+ 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.
+ logger.debug("Trying to launch polkit agent")
+ subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
+ shell=True, env=env)
+ except Exception as exc:
+ logger.exception(exc)
+
+ @classmethod
+ def is_up(self):
+ """
+ Checks if a polkit daemon is running.
+
+ :return: True if it's running, False if it's not.
+ :rtype: boolean
+ """
+ # Note that gnome-shell does not uses a separate process for the
+ # polkit-agent, it uses a polkit-agent within its own process so we
+ # can't ps-grep a polkit process, we can ps-grep gnome-shell itself.
+
+ # the [x] thing is to avoid grep match itself
+ polkit_options = [
+ 'ps aux | grep "polkit-[g]nome-authentication-agent-1"',
+ 'ps aux | grep "polkit-[k]de-authentication-agent-1"',
+ 'ps aux | grep "polkit-[m]ate-authentication-agent-1"',
+ 'ps aux | grep "[l]xpolkit"',
+ 'ps aux | grep "[g]nome-shell"',
+ 'ps aux | grep "[f]ingerprint-polkit-agent"',
+ ]
+ is_running = [commands.getoutput(cmd) for cmd in polkit_options]
+
+ return any(is_running)
+
+ @classmethod
+ def _is_pkexec_in_system(self):
+ """
+ Checks the existence of the pkexec binary in system.
+ """
+ pkexec_path = which('pkexec')
+ if len(pkexec_path) == 0:
+ return False
+ return True