summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore12
-rw-r--r--CHANGELOG20
-rw-r--r--LICENSE6
-rw-r--r--README.rst30
-rw-r--r--changes/VERSION_COMPAT10
-rw-r--r--changes/bug-3778_fix-path-prefix-helper1
-rw-r--r--changes/bug_3803-do-not-install-resolv-update-globally1
-rw-r--r--changes/bug_fix-first1
-rw-r--r--data/images/Globe.pngbin22470 -> 0 bytes
-rw-r--r--data/images/favicon.icobin318 -> 1150 bytes
-rw-r--r--data/images/mask-small.pngbin0 -> 18172 bytes
-rw-r--r--data/images/watermark.pngbin22819 -> 0 bytes
-rw-r--r--docs/Makefile1
-rw-r--r--docs/checklist_for_leap_client_release.wiki45
-rw-r--r--docs/conf.py37
-rw-r--r--docs/dev/workflow.rst45
-rw-r--r--docs/index.rst16
-rw-r--r--docs/release_checklist.wiki40
-rw-r--r--docs/testers/howto.rst8
-rw-r--r--docs/user/install.rst64
-rw-r--r--docs/user/intro.rst18
-rw-r--r--docs/user/leap-color-small.png (renamed from data/images/leap-color-small.png)bin10100 -> 10100 bytes
-rw-r--r--docs/user/running.rst9
-rwxr-xr-xpkg/linux/build_bundle.sh107
-rwxr-xr-xpkg/linux/resolv-update6
-rwxr-xr-xpkg/osx/build_bundle.sh123
-rw-r--r--pkg/osx/build_bundle_from_linux.sh84
-rw-r--r--pkg/requirements-docs.pip1
-rw-r--r--pkg/requirements-testing.pip2
-rw-r--r--pkg/requirements.pip8
-rwxr-xr-x[-rw-r--r--]pkg/scripts/bitmask_bootstrap.sh (renamed from pkg/scripts/leap_client_bootstrap.sh)25
-rw-r--r--relnotes.txt80
-rwxr-xr-xsetup.py33
-rw-r--r--src/leap/bitmask/__init__.py75
-rw-r--r--src/leap/bitmask/app.py13
-rw-r--r--src/leap/bitmask/config/leapsettings.py76
-rw-r--r--src/leap/bitmask/config/providerconfig.py10
-rw-r--r--src/leap/bitmask/config/tests/test_leapsettings.py71
-rw-r--r--src/leap/bitmask/gui/loggerwindow.py3
-rw-r--r--src/leap/bitmask/gui/mainwindow.py167
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py337
-rw-r--r--src/leap/bitmask/gui/statuspanel.py55
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui2
-rw-r--r--src/leap/bitmask/gui/ui/preferences.ui211
-rw-r--r--src/leap/bitmask/gui/ui/wizard.ui93
-rw-r--r--src/leap/bitmask/gui/wizard.py69
-rw-r--r--src/leap/bitmask/platform_init/locks.py4
-rw-r--r--src/leap/bitmask/services/__init__.py39
-rw-r--r--src/leap/bitmask/services/eip/eipconfig.py43
-rw-r--r--src/leap/bitmask/services/eip/vpnlaunchers.py83
-rw-r--r--src/leap/bitmask/services/soledad/soledadbootstrapper.py3
-rw-r--r--src/leap/bitmask/util/__init__.py52
-rw-r--r--src/leap/bitmask/util/leap_argparse.py6
-rw-r--r--src/leap/bitmask/util/leap_log_handler.py3
-rw-r--r--src/leap/bitmask/util/log_silencer.py95
-rw-r--r--src/leap/bitmask/util/password.py58
-rw-r--r--src/leap/bitmask/util/polkit_agent.py52
-rw-r--r--src/leap/bitmask/util/reqs.txt14
-rw-r--r--src/leap/bitmask/util/requirement_checker.py5
-rw-r--r--src/leap/bitmask/util/tests/test_is_release_version.py2
60 files changed, 1856 insertions, 618 deletions
diff --git a/.gitignore b/.gitignore
index 103b16d5..84dc0541 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
ui_*.py
!.coveragerc
!.tx
+!.gitattributes
bin/
build/
core
@@ -18,15 +19,12 @@ include/
lib/
local/
share/
-src/leap/util/reqs.txt
-src/leap.egg-info/
-src/leap_client.egg-info
-src/leap/_branding.py
-src/leap/certs/*.pem
-src/*.egg-info
-src/pysqlcipher
pkg/osx/dist
pkg/osx/build
+
+src/*.egg-info
+src/pysqlcipher
+src/leap/bitmask/util/reqs.txt
MANIFEST
_trial_temp*
config/*
diff --git a/CHANGELOG b/CHANGELOG
index f8def7cb..577bb6ca 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,23 @@
+0.3.2 Sep 9:
+ o Fix up script in non-bundle linuces. Closes: #3450
+ o Logout stops imap and smtp services. Closes: #3553
+ o Properly daemonize polkit-gnome-authentication-agent. Closes: #3554
+ o Set appropiate error on login cancel. Closes #3582.
+ o Fix gateway selection problem. Closes 3595.
+ o Fix typo in wizard: stablish -> establish. Closes #3615.
+ o Display Encrypted Mail instead of mx in wizard. Closes #3657.
+ o Fix save logs to file dialog freezing. Closes #3675.
+ o Complain if setup.py is run with python3. Closes: #3711
+ o Enable preferences option in systray. Closes #3717.
+ o Make soledad emit failed signal for all kinds of socket error.
+ o Allow to selectively silence logs from different leap components. Closes: #3504
+ o Add option to select gateway manually in the preferences panel. Closes #3505.
+ o Add preferences option to select the enabled services of a provider. Closes #3534.
+ o Refactor basic password checks. Closes #3552.
+ o Use dirspec instead of plain xdg. Closes #3574.
+ o Remove last page from wizard. Closes #3616.
+ o Display encrypted mail status in the tray. Closes #3659.
+
0.3.1 Aug 23:
o Replace wizard images with the rainbow mask. Closes #3425.
o Update leap.common minimum version needed.
diff --git a/LICENSE b/LICENSE
index bfd516bd..bca70598 100644
--- a/LICENSE
+++ b/LICENSE
@@ -686,12 +686,6 @@ License: GNU General Public License - http://en.wikipedia.org/wiki/GNU_General_P
WebSite: http://wefunction.com/
IconPackage: WooFunction icon pack - http://www.iconspedia.com/pack/woofunction-icons-4136/
---
-data/images/Globe.png
-
-Author: Everaldo Coelho
-License: LGPL - http://www.gnu.org/licenses/lgpl.html
-WebSite: http://www.everaldo.com/
----
data/images/oxygen-icons/
The following icons were created based on 'mail-unread.png' from oxygen:
diff --git a/README.rst b/README.rst
index d5d6c946..f8308566 100644
--- a/README.rst
+++ b/README.rst
@@ -1,10 +1,26 @@
-The LEAP Encryption Access Project Client
-=========================================
-
+Bitmask
+=======
*your internet encryption toolkit*
-.. image:: https://pypip.in/v/bitmask/badge.png
- :target: https://crate.io/packages/bitmask
+.. image:: https://pypip.in/v/leap.bitmask/badge.png
+ :target: https://crate.io/packages/leap.bitmask
+
+**Bitmask** is the multiplatform desktop client for the services offered by
+`the LEAP Platform`_.
+It is written in python using `PySide`_ and licensed under the GPL3.
+Currently we distribute pre-compiled bundles for Linux and OSX, with Windows
+bundles following soon.
+
+.. _`PySide`: http://qt-project.org/wiki/PySide
+.. _`the LEAP Platform`: https://github.com/leapcode/leap_platform
+
+
+Read the Docs!
+------------------
+
+The latest documentation is available at `Read The Docs`_.
+
+.. _`Read The Docs`: http://bitmask.rtfd.org
Dependencies
------------------
@@ -55,7 +71,7 @@ Hacking
The Bitmask git repository is available at::
- git://leap.se/bitmask
+ git://leap.se/leap_client
Some steps need to be run when setting a development environment for the first time.
@@ -101,7 +117,7 @@ which the first time should automagically install all the needed dependencies in
License
=======
-.. image:: https://raw.github.com/leapcode/bitmask/develop/docs/user/gpl.png
+.. image:: https://raw.github.com/leapcode/leap_client/develop/docs/user/gpl.png
Bitmask is released under the terms of the `GNU GPL version 3`_ or later.
diff --git a/changes/VERSION_COMPAT b/changes/VERSION_COMPAT
new file mode 100644
index 00000000..cc00ecf7
--- /dev/null
+++ b/changes/VERSION_COMPAT
@@ -0,0 +1,10 @@
+#################################################
+# This file keeps track of the recent changes
+# introduced in internal leap dependencies.
+# Add your changes here so we can properly update
+# requirements.pip during the release process.
+# (leave header when resetting)
+#################################################
+#
+# BEGIN DEPENDENCY LIST -------------------------
+# leap.foo.bar>=x.y.z
diff --git a/changes/bug-3778_fix-path-prefix-helper b/changes/bug-3778_fix-path-prefix-helper
new file mode 100644
index 00000000..e7cec539
--- /dev/null
+++ b/changes/bug-3778_fix-path-prefix-helper
@@ -0,0 +1 @@
+ o Fix path prefix helper for the bundle and add regresion tests. Closes #3778.
diff --git a/changes/bug_3803-do-not-install-resolv-update-globally b/changes/bug_3803-do-not-install-resolv-update-globally
new file mode 100644
index 00000000..f6e06d5f
--- /dev/null
+++ b/changes/bug_3803-do-not-install-resolv-update-globally
@@ -0,0 +1 @@
+ o Do not try to install resolv-update globally. Closes: #3803
diff --git a/changes/bug_fix-first b/changes/bug_fix-first
new file mode 100644
index 00000000..0ef5188c
--- /dev/null
+++ b/changes/bug_fix-first
@@ -0,0 +1 @@
+ o Catch IndexError on `first` utility.
diff --git a/data/images/Globe.png b/data/images/Globe.png
deleted file mode 100644
index 7549433b..00000000
--- a/data/images/Globe.png
+++ /dev/null
Binary files differ
diff --git a/data/images/favicon.ico b/data/images/favicon.ico
index b5f3505a..7f41dd1d 100644
--- a/data/images/favicon.ico
+++ b/data/images/favicon.ico
Binary files differ
diff --git a/data/images/mask-small.png b/data/images/mask-small.png
new file mode 100644
index 00000000..5f5f1b41
--- /dev/null
+++ b/data/images/mask-small.png
Binary files differ
diff --git a/data/images/watermark.png b/data/images/watermark.png
deleted file mode 100644
index d8e3f965..00000000
--- a/data/images/watermark.png
+++ /dev/null
Binary files differ
diff --git a/docs/Makefile b/docs/Makefile
index 16aa258b..5c2c4145 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -4,6 +4,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
+#SPHINXBUILD = $(VIRTUAL_ENV)/bin/sphinx-build
PAPER =
BUILDDIR = _build
diff --git a/docs/checklist_for_leap_client_release.wiki b/docs/checklist_for_leap_client_release.wiki
deleted file mode 100644
index d3bdf1ee..00000000
--- a/docs/checklist_for_leap_client_release.wiki
+++ /dev/null
@@ -1,45 +0,0 @@
-= LEAP CLient Release Checklist (*) =
-
- * [ ] validate rc
- * [ ] all rc-critical closed!
- * [ ] all bbots green
- * [ ] uploaded translations: make translations
- * [ ] re-generate pyside resources
-
- * [ ] update docs
- * [ ] CREDITS
- * [ ] relnotes.txt
- * [ ] docs/known_issues.rst
- * [ ] NEWS.rst: Add release name and date to top-most item in NEWS.
-
- * [ ] change docs/quickstart.rst to point to just the current
- leap-client-X.Y.Z.deb binaries and .tar.gz source code files
- * [ ] on release/vX.Y.Z branch: git pull
- * [ ] git tag X.Y.Z
- * [ ] build locally to make sure the release is reporting itself as the
- intended version (FIXME!)
- * [ ] make sure buildbot is green
- * [ ] make sure other people aren't committing at that moment
- * [ ] FUTURE: push tag along with some other documentation-only patch (typically to
- relnotes.txt) to trigger buildslaves
- * [ ] git push --tags official; git push official
- * [ ] that will build tarballs
- * [ ] make sure buildbot is green (in a parallel universe, he)
- * [ ] download tarballs, sign with "gpg -ba -u deadbeef TAR", upload *.asc
- * [ ] symlink the release tarball on leap.se downloads page:
- /var/www/source/leap-client/releases/ CHANGEME XXX
-
- * [ ] update news pages. release notes.
- * [ ] send out relnotes.txt to internal list.
- * [ ] wait ...?
-
- * [ ] PYPI UPLOAD: with "python ./setup.py sdist upload register"
-
- * [ ] make an "announcement of new release" on leap.se
- * [ ] close the Milestone on the chili Roadmap
- * [ ] send out relnotes.txt to:
- * [ ] mailing lists...
-
-notes
------
-(*) this checklist kindly borrowed from tahoe-lafs documentation =)
diff --git a/docs/conf.py b/docs/conf.py
index 39f17d9b..3c908b2c 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
#
-# LEAP documentation build configuration file, created by
+# Bitmask documentation build configuration file, created by
# sphinx-quickstart on Sun Jul 22 18:32:05 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
@@ -18,16 +18,20 @@ import sys, os
# documentation root, use os.path.abspath to make it absolute, like shown here.
sys.path.insert(0, os.path.abspath('../src'))
sys.path.insert(0, os.path.abspath('../src/leap'))
-sys.path.insert(0, os.path.abspath('../src/leap/crypto'))
-sys.path.insert(0, os.path.abspath('../src/leap/keymanager'))
-sys.path.insert(0, os.path.abspath('../src/leap/services'))
-sys.path.insert(0, os.path.abspath('../src/leap/services/eip'))
-sys.path.insert(0, os.path.abspath('../src/leap/util'))
-
-sys.path.insert(0, os.path.abspath(
- os.path.expanduser(
- '~/Virtualenvs/leap-client/local/lib/python2.7/'
- 'site-packages/leap/common')))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask'))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask/crypto'))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask/keymanager'))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask/services'))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask/services/eip'))
+sys.path.insert(0, os.path.abspath('../src/leap/bitmask/util'))
+
+try:
+ sys.path.insert(0, os.path.abspath(
+ os.path.expanduser(
+ '~/Virtualenvs/leap-bitmask/local/lib/python2.7/'
+ 'site-packages/leap/common')))
+except:
+ pass
# TODO: should add all the virtualenv site-packages to the path
# as a workaround, install all in your path.
@@ -58,17 +62,18 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = u'LEAP'
-copyright = u'2012, The LEAP Encryption Access Project'
+project = u'Bitmask'
+copyright = u'2012-2013, The LEAP Encryption Access Project'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '0.2.1-dev1'
+import leap.bitmask
+version = leap.bitmask.__short_version__
# The full version, including alpha/beta/rc tags.
-release = '0.2.1'
+release = leap.bitmask.__version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
@@ -128,7 +133,7 @@ html_theme = 'default'
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
-html_logo = "../data/images/leap-color-small.png"
+html_logo = "../data/images/mask-small.png"
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
diff --git a/docs/dev/workflow.rst b/docs/dev/workflow.rst
index e36431ff..1e3bd4af 100644
--- a/docs/dev/workflow.rst
+++ b/docs/dev/workflow.rst
@@ -5,6 +5,11 @@ Development Workflow
This section documents the workflow that the LEAP project team follows and expects for the code contributions.
+While reading this guide, you should have in mind the two rules of contributing code:
+
+* The first rule of code contribution is: Nobody will push unreviewed code to the mainline branches.
+* The second rule of code contribution is: Nobody will push unreviewed code to the mainline branches.
+
Code formatting
---------------
In one word: `PEP8`_.
@@ -24,7 +29,7 @@ We are basing our workflow on what is described in `A successful git branching m
.. image:: https://leap.se/code/attachments/13/git-branching-model.png
-The author of the aforementioned post has also a handy pdf version of it: `branching_model.pdf`_
+The author of the aforementioned post has also a handy pdf version of it: `branching_model.pdf`_
However, we use a setup in which each developer maintains her own feature branch in her private repo. After a code review, this feature branch is rebased onto the authoritative integration branch. Thus, the leapcode repo in leap.se (mirrored in github) only maintains the master and develop branches.
@@ -41,3 +46,41 @@ All code ready to be merged into the integration branch is expected to:
* Have tests
* Be documented
* Pass existing tests: do **run_tests.sh** and **tox -v**. All feature branches are automagically built by our `buildbot farm <http://lemur.leap.se:8010/grid>`_. So please check your branch is green before merging it it to `develop`. Rebasing against the current tip of the integration when possible is preferred in order to keep a clean history.
+
+Using Github
+------------
+
+Particularly for the Bitmask client, we are using Github. So you should fork the repo from `github`_ . Depending on what kind of work you are going to do (bug or feature) you should create a branch with the following name:
+
+`bug/some_descriptive_text`
+
+or
+
+`feature/some_descriptive_text`
+
+Do your work there, push it, and create a pull request against the develop branch in the leapcode owned repo. Either you should post the pull request in `#leap-dev` at `Freenode` or we will just notice it when it's created.
+
+Your code will get reviewed/discussed by someone else on the team, and say that you need to make some changes. What you would do is the following:
+
+ git checkout <your branch>
+
+ # edit what you need here ...
+
+ # Simple commit, this doesn't need a good commit message
+ git commit -avm "Fix"
+
+ # This will help you reorder your commits and squash them (so that the
+ # final commit list has good representative messages)
+ git rebase -i develop
+
+ # Since you've rewritten your history, you'll need a force push
+ git push <your remote> +<your branch>
+
+This will update your pull request automatically, but it won't notify us about the update, so you should add a comment saying so, or re-pingthe reviewer.
+
+.. _`github`: https://github.com/leapcode/
+
+Other methods
+-------------
+
+Feel free to use any other methods like format-patch and mail or whatever method you prefer, although we recommend you follow the same workflow as we do.
diff --git a/docs/index.rst b/docs/index.rst
index e3078929..d0b0ff22 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,15 +1,20 @@
-.. LEAP documentation master file, created by
+.. Bitmask documentation master file, created by
sphinx-quickstart on Sun Jul 22 18:32:05 2012.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
-LEAP Client
+Bitmask
=====================================
-Release v\ |version|. (`Impatient? jump to the` :ref:`Installation <install>` `section!`)
+Release v \ |version|. (`Impatient? jump to the` :ref:`Installation <install>` `section!`)
.. if you change this paragraph, change it in user/intro too
-The **LEAP Encryption Access Project Client** is a :ref:`GPL3 Licensed <gpl3>` multiplatform client, written in python using PySide, that supports the features offered by :ref:`the LEAP Platform <leapplatform>`. Currently is being tested on Linux, support for OSX and Windows will come soon.
+**Bitmask** is the multiplatform desktop client for the services offered by :ref:`the LEAP Platform <leapplatform>`.
+It is written in python using `PySide`_ and :ref:`licensed under the GPL3 <gpl3>`.
+Currently we distribute pre-compiled bundles for Linux and OSX, with Windows
+bundles following soon.
+
+.. _`PySide`: http://qt-project.org/wiki/PySide
User Guide
----------
@@ -48,7 +53,6 @@ If you want to contribute to the project, we wrote this for you.
.. dev/internals
dev/authors
dev/todo
- dev/workflow
Packager Guide
---------------
@@ -87,3 +91,5 @@ If you are looking for a reference to specific classes or functions, you are lik
:maxdepth: 2
api/leap
+
+
diff --git a/docs/release_checklist.wiki b/docs/release_checklist.wiki
new file mode 100644
index 00000000..e6467048
--- /dev/null
+++ b/docs/release_checklist.wiki
@@ -0,0 +1,40 @@
+= Bitmask Release Checklist (*) =
+ * [ ] Check that all tests are passing!
+ * [ ] Tag everything
+ * Should be done for the following packages, in order:
+ 1. leap.common
+ 2. leap.keymanager
+ 3. leap.soledad
+ 4. leap.mail
+ 5. leap.bitmask
+ 6. leap.mx
+ * NOTE: It's assumed that origin is the leap.se repo
+ * [ ] git fetch origin
+ * [ ] git tag -l, and see the latest tagged version (unless it's not a minor version bump, in which case, just bump to it)
+ * [ ] Checkout release-X.Y.Z (locally, never pushed)
+ * [ ] Fold in changes files into the CHANGELOG
+ - NOTE: For leap.soledad, the CHANGELOG entries should be divided per package (common, client, server). See older releases for reference.
+ - Helper bash line: for i in $(ls changes); do cat changes/$i; echo; done
+ * [ ] Update relnotes.txt if needed.
+ * [ ] git rm changes/*
+ * [ ] git commit -av
+ * [ ] Review pkg/requirements.pip for everything and update if needed (that's why the order).
+ - See whatever has been introduced in changes/VERSION_COMPAT
+ - Reset changes/VERSION_COMPAT
+ * [ ] git tag -s X.Y.Z (note the -s so that it's a signed tag) The message should be something like: Tag <package> version X.Y.Z
+ * [ ] git push origin X.Y.Z
+ * [ ] git checkout master && git pull origin master && git merge release-X.Y.Z && git push origin master
+ * [ ] git checkout develop && git pull origin develop && git merge release-X.Y.Z && git push origin develop
+ * [ ] Build and upload bundles
+ * [ ] Use the scripts under pkg/<os>/ to build the the bundles.
+ * [ ] Sign them with gpg -a <path/to/bundle>
+ * [ ] Upload bundle and signature to web-uploads@salmon.leap.se:~/public/client/<os>/Bitmask-<os>-<ver>.(tar.bz2,dmg,zip)
+ * [ ] Update symbolic link for latest upload and signature:
+ * [ ] ~/public/client/Bitmask-<os>-latest
+ * [ ] ~/public/client/Bitmask-<os>-latest.asc
+ * [ ] Announce
+ * [ ] Mail leap@lists.riseup.net
+
+Notes
+-----
+(*) this checklist kindly borrowed from tahoe-lafs documentation =)
diff --git a/docs/testers/howto.rst b/docs/testers/howto.rst
index dde893d1..9c6561ed 100644
--- a/docs/testers/howto.rst
+++ b/docs/testers/howto.rst
@@ -19,7 +19,7 @@ To allow rapid testing in different platforms, we have put together a quick scri
.. note::
- In the near future, we will be using ``standalone bundles`` with the ability to self-update.
+ In the near future, we will be using :ref:`standalone bundles <standalone-bundle>` with the ability to self-update.
Install dependencies
^^^^^^^^^^^^^^^^^^^^
@@ -41,8 +41,8 @@ Download and source the following script in the parent folder where you want you
.. code-block:: bash
cd /tmp
- wget https://raw.github.com/leapcode/bitmask/develop/pkg/scripts/leap_client_bootstrap.sh
- source leap_client_bootstrap.sh
+ wget https://raw.github.com/leapcode/leap_client/develop/pkg/scripts/bitmask_bootstrap.sh
+ source bitmask_bootstrap.sh
Tada! If everything went well, you should be able to run bitmask by typing::
@@ -52,7 +52,7 @@ Noticed that your prompt changed? That was *virtualenv*. Keep reading...
Activating the virtualenv
^^^^^^^^^^^^^^^^^^^^^^^^^
-The above bootstrap script has fetched latest code inside a virtualenv, which is an isolated, *virtual* python local environment that avoids messing with your global paths. You will notice you are *inside* a virtualenv because you will see a modified prompt reminding it to you (*leap-client-testbuild* in this case).
+The above bootstrap script has fetched latest code inside a virtualenv, which is an isolated, *virtual* python local environment that avoids messing with your global paths. You will notice you are *inside* a virtualenv because you will see a modified prompt reminding it to you (*bitmask-testbuild* in this case).
Thus, if you forget to *activate your virtualenv*, bitmask will not run from the local path, and it will be looking for something else in your global path. So, **you have to remember to activate your virtualenv** each time that you open a new shell and want to execute the code you are testing. You can do this by typing::
diff --git a/docs/user/install.rst b/docs/user/install.rst
index da1d914c..81807a43 100644
--- a/docs/user/install.rst
+++ b/docs/user/install.rst
@@ -4,28 +4,53 @@ Installation
============
This part of the documentation covers the installation of Bitmask.
-We assume that you want to get it properly installed before being able to use it.
+We assume that you want to get it properly installed before being able to use it. But we can we wrong.
+.. _standalone-bundle:
+
+Standalone bundle
+-----------------
+
+Maybe the quickest way of running Bitmask in your machine is using the standalone bundle. That is the recommended way to use Bitmask for the time being.
+
+You can get the latest bundles, and their matching signatures at `the downloads page <https://downloads.leap.se/client/>`_.
+
+Linux
+^^^^^
+- `Linux 32 bits bundle`_ (`signature <https://downloads.leap.se/client/linux/Bitmask-linux32-latest.tar.bz2.asc>`_)
+- `Linux 64 bits bundle`_ (`signature <https://downloads.leap.se/client/linux/Bitmask-linux64-latest.tar.bz2.asc>`_)
+
+OSX
+^^^
+- `OSX bundle`_ (`signature <https://downloads.leap.se/client/osx/Bitmask-OSX-latest.dmg.asc>`_)
+
+Windows
+^^^^^^^
.. note::
- The recommended way of installing in the near future will be the standalone bundles, but those are not quite ready yet. Methods described in this page assume you are familiar with python code, and you can find your way through the process of dependencies install. You can refer to the sections :ref:`setting up a working environment <environment>` or :ref:`fetching latest code for testing <fetchinglatest>`.
+ The release of the bundles for Windows is delayed right now. We should resume
+ producing them shortly, keep tuned.
+Signature verification
+^^^^^^^^^^^^^^^^^^^^^^
-Distribute & Pip
-----------------
+For the signature verification you can use ::
-.. warning:: The package in the cheese shop is from the stable, `0.2.0` release, which is now outdated. You are encouraged to install the development version instead.
+ $ gpg --verify Bitmask-linux64-latest.tar.bz2.asc
-Installing Bitmask is as simple as using `pip <http://www.pip-installer.org/>`_ for the already released versions ::
+Asuming that you downloaded the linux 64 bits bundle.
- $ pip install bitmask
+.. _`Linux 64 bits bundle`: https://downloads.leap.se/client/linux/Bitmask-linux64-latest.tar.bz2
+.. _`Linux 32 bits bundle`: https://downloads.leap.se/client/linux/Bitmask-linux32-latest.tar.bz2
+.. _`OSX bundle`: https://downloads.leap.se/client/osx/Bitmask-OSX-latest.dmg
+.. _`Windows bundle`: https://downloads.leap.se/client/windows/Bitmask-windows-latest.zip
Debian package
--------------
.. warning::
- The debian package in the leap repositories is from the stable, `0.2.0` release, which is now outdated. You are encouraged to install the development version instead,
+ The debian package that you can currently find in the leap repositories is from the stable, `0.2.0` release, which is now outdated. You are encouraged to install the development version or the standalone bundles while we upload the newest packages.
First, you need to bootstrap your apt-key::
@@ -44,18 +69,35 @@ And then you can happily install bitmask::
apt-get install bitmask
+Distribute & Pip
+----------------
+
+.. note::
+
+ The rest of the methods described below in this page assume you are familiar with python code, and you can find your way through the process of dependencies install. For more insight, you can also refer to the sections :ref:`setting up a working environment <environment>` or :ref:`fetching latest code for testing <fetchinglatest>`.
+
+.. image:: https://pypip.in/v/leap.bitmask/badge.png
+ :target: https://crate.io/packages/leap.bitmask
+
+
+Installing Bitmask is as simple as using `pip <http://www.pip-installer.org/>`_ for the already released versions ::
+
+ $ pip install leap.bitmask
+
+
Show me the code!
-----------------
+.. XXX UPDATE REPO NAMES AS SOON AS #3417 is DONE
+
You can get the code from LEAP public git repository ::
- $ git clone git://leap.se/bitmask
+ $ git clone git://leap.se/leap_client
Or from the github mirror ::
- $ git clone git://github.com/leapcode/bitmask.git
+ $ git clone git://github.com/leapcode/leap_client.git
Once you have grabbed a copy of the sources, you can install it into your site-packages easily ::
$ pyton setup.py install
-
diff --git a/docs/user/intro.rst b/docs/user/intro.rst
index 22ad9356..b93df12b 100644
--- a/docs/user/intro.rst
+++ b/docs/user/intro.rst
@@ -6,26 +6,34 @@ Introduction
Bitmask
-------
.. if yoy change this, change it also in the index.rst
-**Bitmask** is a :ref:`GPL3 Licensed <gpl3>` multiplatform client, written in python using PySide, that supports the features offered by :ref:`the LEAP Platform <leapplatform>`. Currently is being tested on Linux, support for OSX and Windows will come soon.
+**Bitmask** is the multiplatform desktop client for the services offered by :ref:`the LEAP Platform <leapplatform>`.
+It is written in python using `PySide`_ and :ref:`licensed under the GPL3 <gpl3>`.
+Currently we distribute pre-compiled bundles for Linux and OSX, with Windows
+bundles following soon.
Features
^^^^^^^^
Bitmask allows to easily secure communications.
-- Provider selection
-- User registration
+- Provider selection.
+- User registration.
- Encrypted Internet Proxy support (autoconfigured service using openvpn).
+- Encrypted email.
Coming soon
^^^^^^^^^^^^
-- Encrypted email
+- Encrypted chat.
+
.. _leapplatform:
The LEAP Platform
^^^^^^^^^^^^^^^^^
+
+.. image:: leap-color-small.*
+
The LEAP Provider Platform is the server-side part of LEAP that is run by service providers. It consists of a set of complementary packages and recipes to automate the maintenance of LEAP services in a hardened GNU/Linux environment. Our goal is to make it painless for service providers and ISPs to deploy a secure communications platform.
Read `more about the LEAP Platform <https://leap.se/en/technology/platform>`_ or `check out the code <https://github.com/leapcode/leap_platform>`_.
@@ -97,5 +105,7 @@ Bitmask is released under the terms of the `GNU GPL version 3`_ or later.
.. _`GNU GPL version 3`: http://www.gnu.org/licenses/gpl.txt
+.. _`PySide`: http://qt-project.org/wiki/PySide
+
.. ??? include whole version?
.. include:: ../COPYING
diff --git a/data/images/leap-color-small.png b/docs/user/leap-color-small.png
index bc9d4e7f..bc9d4e7f 100644
--- a/data/images/leap-color-small.png
+++ b/docs/user/leap-color-small.png
Binary files differ
diff --git a/docs/user/running.rst b/docs/user/running.rst
index da83e9ef..0a93204c 100644
--- a/docs/user/running.rst
+++ b/docs/user/running.rst
@@ -3,7 +3,8 @@
Running
==================
-This document covers how to launch Bitmask.
+This document covers how to launch Bitmask. Also know as, where the magic
+happens.
Launching Bitmask
-----------------
@@ -11,7 +12,7 @@ After a successful installation, there should be a launcher called `bitmask` som
% bitmask
-The first time you launch it, it should launch the first run wizard that will guide you through the setup of the LEAP Services.
+The first time you launch it, it should launch the first run wizard that will guide you through the mostly automatic configuration of the LEAP Services.
.. note::
@@ -37,8 +38,8 @@ If you ask for it, you can also have all that debug info in a beautiful file rea
.. If you want to increment the level of verbosity passed to openvpn, you can do::
.. $ bitmask --openvpn-verbosity 4
-Options
-------------
+I want all the options!
+-----------------------
To see all the available command line options::
$ bitmask --help
diff --git a/pkg/linux/build_bundle.sh b/pkg/linux/build_bundle.sh
new file mode 100755
index 00000000..e6a1043f
--- /dev/null
+++ b/pkg/linux/build_bundle.sh
@@ -0,0 +1,107 @@
+REPOS_ROOT=$1
+VERSION=$2
+TEMPLATE_BUNDLE=$3
+JOINT_CHANGELOG=$4
+DEST=$5
+
+# clean template
+
+rm $TEMPLATE_BUNDLE/CHANGELOG
+rm $TEMPLATE_BUNDLE/relnotes.txt
+rm -rf $TEMPLATE_BUNDLE/apps/leap
+rm -rf $TEMPLATE_BUNDLE/lib/leap/{common,keymanager,soledad,mail}
+
+# checkout VERSION in all repos
+
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git fetch
+ git checkout $VERSION
+ done
+
+# make ui in client
+
+cd $REPOS_ROOT/leap_client
+make
+
+# cp client
+
+cp -r $REPOS_ROOT/leap_client/src/leap $TEMPLATE_BUNDLE/apps/leap
+
+# setup sdist client
+
+cd $REPOS_ROOT/leap_client
+python setup.py sdist
+
+# extract $VERSION and copy _version.py to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/_version.py
+
+cd dist
+rm -rf leap.bitmask-$VERSION
+tar xzf leap.bitmask-$VERSION.tar.gz
+cp leap.bitmask-$VERSION/src/leap/bitmask/_version.py $TEMPLATE_BUNDLE/apps/leap/bitmask/_version.py
+cp leap.bitmask-$VERSION/src/leap/bitmask/util/reqs.txt $TEMPLATE_BUNDLE/apps/leap/bitmask/util/reqs.txt
+
+# cp common, soledad(client and common), mail and keymanager in TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/lib/leap/
+
+LEAP_LIB=$TEMPLATE_BUNDLE/lib/leap/
+
+cp -r $REPOS_ROOT/leap_pycommon/src/leap/common $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/common/src/leap/soledad $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/client/src/leap/soledad/client $LEAP_LIB/soledad
+cp -r $REPOS_ROOT/leap_mail/src/leap/mail $LEAP_LIB
+cp -r $REPOS_ROOT/keymanager/src/leap/keymanager $LEAP_LIB
+
+# cp leap_client launcher to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/Bitmask
+
+BITMASK_BIN=$TEMPLATE_BUNDLE/bitmask
+
+cd $REPOS_ROOT/leap_client_launcher/build/
+make
+cp src/launcher $BITMASK_BIN
+
+# cp launcher.py to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/
+
+cd $REPOS_ROOT/leap_client_launcher/src/
+cp launcher.py $TEMPLATE_BUNDLE/apps/
+
+# cp relnotes to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/relnotes.txt $TEMPLATE_BUNDLE
+
+# cp joint_chglog to TEMPLATE_BUNDLE
+
+cp $JOINT_CHANGELOG $TEMPLATE_BUNDLE/CHANGELOG
+
+# cp LICENSE to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/LICENSE $TEMPLATE_BUNDLE/LICENSE
+
+# clean pyc$
+
+cd $TEMPLATE_BUNDLE
+for i in $(find . | grep pyc$);
+ do
+ rm $i
+ done
+
+# create tarball
+
+ARCH=$(uname -m | sed 's/x86_//;s/i[3-6]86/32/')
+BUNDLE_NAME=Bitmask-linux$ARCH-$VERSION
+TMP=/tmp/$BUNDLE_NAME
+
+rm -rf $TMP
+mkdir -p $TMP
+cp -R $TEMPLATE_BUNDLE/* $TMP
+cd /tmp
+tar cjf $DEST/$BUNDLE_NAME.tar.bz2 $BUNDLE_NAME
+cd
+rm -rf $TMP
+
+# go back to develop in all repos
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git checkout develop
+ done
diff --git a/pkg/linux/resolv-update b/pkg/linux/resolv-update
index a54802e3..601d3bd2 100755
--- a/pkg/linux/resolv-update
+++ b/pkg/linux/resolv-update
@@ -20,13 +20,13 @@ function up() {
comment=$(
cat <<SETVAR
#
-# This is a temporary resolv.conf set by the LEAP Client in order to
+# This is a temporary resolv.conf set by the Bitmask in order to
# strictly enforce that DNS lookups are secured by the VPN.
#
-# When the LEAP Client quits or the VPN connection it manages is dropped,
+# When Bitmask quits or the VPN connection it manages is dropped,
# this file will be replace with the regularly scheduled /etc/resolv.conf
#
-# If you want custom entries to appear in this file while LEAP is running,
+# If you want custom entries to appear in this file while Bitmask is running,
# put them in /etc/leap/resolv-head or /etc/leap/resolv-tail. These files
# should only be writable by root.
#
diff --git a/pkg/osx/build_bundle.sh b/pkg/osx/build_bundle.sh
new file mode 100755
index 00000000..a13746bf
--- /dev/null
+++ b/pkg/osx/build_bundle.sh
@@ -0,0 +1,123 @@
+REPOS_ROOT=$1
+VERSION=$2
+TEMPLATE_BUNDLE=$3
+JOINT_CHANGELOG=$4
+DEST=$5
+
+# clean template
+
+rm $TEMPLATE_BUNDLE/CHANGELOG.txt
+rm $TEMPLATE_BUNDLE/relnotes.txt
+rm -rf $TEMPLATE_BUNDLE/Bitmask.app/Contentes/MacOS/apps/leap
+rm $TEMPLATE_BUNDLE/Bitmask.app/Contentes/MacOS/lib/leap/{common,keymanager,soledad,mail}
+
+# checkout VERSION in all repos
+
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git checkout $VERSION
+ done
+
+# make ui in client
+
+cd $REPOS_ROOT/leap_client
+make
+
+# cp client
+
+cp -r $REPOS_ROOT/leap_client/src/leap $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap
+
+# setup sdist client
+
+cd $REPOS_ROOT/leap_client
+python setup.py sdist
+
+# extract $VERSION and copy _version.py to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/_version.py
+
+cd dist
+rm -rf leap.bitmask-$VERSION
+tar xzf leap.bitmask-$VERSION.tar.gz
+cp leap.bitmask-$VERSION/src/leap/bitmask/_version.py $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/_version.py
+cp leap.bitmask-$VERSION/src/leap/bitmask/util/reqs.txt $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/util/reqs.txt
+
+# cp common, soledad(client and common), mail and keymanager in TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/lib/leap/
+
+LEAP_LIB=$TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/lib/leap/
+
+cp -r $REPOS_ROOT/leap_pycommon/src/leap/common $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/common/src/leap/soledad $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/client/src/leap/soledad/client $LEAP_LIB/soledad
+cp -r $REPOS_ROOT/leap_mail/src/leap/mail $LEAP_LIB
+cp -r $REPOS_ROOT/keymanager/src/leap/keymanager $LEAP_LIB
+
+# cp leap_client launcher to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/Bitmask
+
+BITMASK_BIN=$TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/Bitmask
+
+cd $REPOS_ROOT/leap_client_launcher/build/
+make
+cp src/launcher $BITMASK_BIN
+
+# cp launcher.py to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/
+
+cd $REPOS_ROOT/leap_client_launcher/src/
+cp launcher.py $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/
+
+# install_name_tool it
+
+install_name_tool -change libboost_python.dylib lib/libboost_python.dylib $BITMASK_BIN
+install_name_tool -change libboost_filesystem.dylib lib/libboost_filesystem.dylib $BITMASK_BIN
+install_name_tool -change libboost_system.dylib lib/libboost_system.dylib $BITMASK_BIN
+
+# cp relnotes to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/relnotes.txt $TEMPLATE_BUNDLE
+
+# cp joint_chglog to TEMPLATE_BUNDLE
+
+cp $JOINT_CHANGELOG $TEMPLATE_BUNDLE/CHANGELOG.txt
+
+# cp LICENSE to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/LICENSE $TEMPLATE_BUNDLE/LICENSE.txt
+
+# clean pyc$
+
+cd $TEMPLATE_BUNDLE
+for i in $(find . | grep pyc$);
+ do
+ rm $i
+ done
+
+# create dmg
+
+TMP=/tmp/Bitmask
+VOLUME_NAME=Bitmask
+DMG_FILE=Bitmask-OSX-$VERSION.dmg
+
+rm -rf $TMP
+mkdir -p $TMP
+cp -R $TEMPLATE_BUNDLE/* $TMP
+cp $REPOS_ROOT/leap_assets/mac/bitmask.icns $TMP/.VolumeIcon.icns
+SetFile -c icnC $TMP/.VolumeIcon.icns
+hdiutil create -srcfolder $TMP -volname $VOLUME_NAME -format UDRW -ov $DEST/raw-$DMG_FILE
+
+rm -rf $TMP
+mkdir -p $TMP
+hdiutil attach $DEST/raw-$DMG_FILE -mountpoint $TMP
+
+SetFile -a C $TMP
+hdiutil detach $TMP
+
+rm -rf $TMP
+rm -f $DEST/$DMG_FILE
+hdiutil convert $DEST/raw-$DMG_FILE -format UDZO -o $DEST/$DMG_FILE
+rm -f $DEST/raw-$DMG_FILE
+
+# go back to develop in all repos
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git checkout develop
+ done
diff --git a/pkg/osx/build_bundle_from_linux.sh b/pkg/osx/build_bundle_from_linux.sh
new file mode 100644
index 00000000..c98e1b7a
--- /dev/null
+++ b/pkg/osx/build_bundle_from_linux.sh
@@ -0,0 +1,84 @@
+REPOS_ROOT=$1
+VERSION=$2
+TEMPLATE_BUNDLE=$3
+JOINT_CHANGELOG=$4
+DEST=$5
+
+# clean template
+
+rm $TEMPLATE_BUNDLE/CHANGELOG.txt
+rm $TEMPLATE_BUNDLE/relnotes.txt
+rm -rf $TEMPLATE_BUNDLE/Bitmask.app/Contentes/MacOS/apps/leap
+rm $TEMPLATE_BUNDLE/Bitmask.app/Contentes/MacOS/lib/leap/{common,keymanager,soledad,mail}
+
+# checkout VERSION in all repos
+
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git checkout $VERSION
+ done
+
+# make ui in client
+
+cd $REPOS_ROOT/leap_client
+make
+
+# cp client
+
+cp -r $REPOS_ROOT/leap_client/src/leap $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap
+
+# setup sdist client
+
+cd $REPOS_ROOT/leap_client
+python setup.py sdist
+
+# extract $VERSION and copy _version.py to TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/_version.py
+
+cd dist
+rm -rf leap.bitmask-$VERSION
+tar xzf leap.bitmask-$VERSION.tar.gz
+cp leap.bitmask-$VERSION/src/leap/bitmask/_version.py $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/_version.py
+cp leap.bitmask-$VERSION/src/leap/bitmask/util/reqs.txt $TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/apps/leap/bitmask/util/reqs.txt
+
+# cp common, soledad(client and common), mail and keymanager in TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/lib/leap/
+
+LEAP_LIB=$TEMPLATE_BUNDLE/Bitmask.app/Contents/MacOS/lib/leap/
+
+cp -r $REPOS_ROOT/leap_pycommon/src/leap/common $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/common/src/leap/soledad $LEAP_LIB
+cp -r $REPOS_ROOT/soledad/client/src/leap/soledad/client $LEAP_LIB/soledad
+cp -r $REPOS_ROOT/leap_mail/src/leap/mail $LEAP_LIB
+cp -r $REPOS_ROOT/keymanager/src/leap/keymanager $LEAP_LIB
+
+# cp relnotes to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/relnotes.txt $TEMPLATE_BUNDLE
+
+# cp joint_chglog to TEMPLATE_BUNDLE
+
+cp $JOINT_CHANGELOG $TEMPLATE_BUNDLE/CHANGELOG.txt
+
+# cp LICENSE to TEMPLATE_BUNDLE
+
+cp $REPOS_ROOT/leap_client/LICENSE $TEMPLATE_BUNDLE/LICENSE.txt
+
+# clean pyc$
+
+cd $TEMPLATE_BUNDLE
+for i in $(find . | grep pyc$);
+ do
+ rm $i
+ done
+
+# create dmg
+
+genisoimage -D -V "Bitmask" -no-pad -r -apple -o raw-Bitmask-OSX-$VERSION.dmg $TEMPLATE_BUNDLE
+dmg dmg raw-Bitmask-OSX-$VERSION.dmg Bitmask-OSX-$VERSION.dmg
+
+# go back to develop in all repos
+for i in {leap_client,leap_pycommon,soledad,keymanager,leap_mail}
+ do
+ cd $REPOS_ROOT/$i
+ git checkout develop
+ done
diff --git a/pkg/requirements-docs.pip b/pkg/requirements-docs.pip
new file mode 100644
index 00000000..6966869c
--- /dev/null
+++ b/pkg/requirements-docs.pip
@@ -0,0 +1 @@
+sphinx
diff --git a/pkg/requirements-testing.pip b/pkg/requirements-testing.pip
index 2df5fe56..e789664a 100644
--- a/pkg/requirements-testing.pip
+++ b/pkg/requirements-testing.pip
@@ -1,6 +1,7 @@
nose
nose-exclude
nose-progressive
+mock
unittest2 # TODO we should include this dep only for python2.6
@@ -13,6 +14,5 @@ tox
# double reqs
# (the client already includes, which gives some errors)
# -----------
-# mock # re-add XXX
#twisted
#zope.interface
diff --git a/pkg/requirements.pip b/pkg/requirements.pip
index e04127b7..081f3ba9 100644
--- a/pkg/requirements.pip
+++ b/pkg/requirements.pip
@@ -9,18 +9,20 @@ argparse
requests
srp>=1.0.2
pyopenssl
-keyring
python-dateutil
psutil
ipaddr
twisted
qt4reactor
python-gnupg
+python-daemon # this should not be needed for Windows.
-leap.common>=0.3.0
+keyring<=2.9 # See #3759
+
+leap.common>=0.3.2
leap.soledad.client>=0.3.0
leap.keymanager>=0.2.0
-leap.mail>=0.3.0
+leap.mail>=0.3.2
# Remove this when u1db fixes its dependency on oauth
oauth
diff --git a/pkg/scripts/leap_client_bootstrap.sh b/pkg/scripts/bitmask_bootstrap.sh
index dcde64f9..42eb0af9 100644..100755
--- a/pkg/scripts/leap_client_bootstrap.sh
+++ b/pkg/scripts/bitmask_bootstrap.sh
@@ -17,34 +17,33 @@ cc_red="${esc}[0;31m"
cc_normal=`echo -en "${esc}[m\017"`
echo "${cc_yellow}"
-echo "~~~~~~~~~~~~~~~~~~~~~~"
-echo "LEAP "
-echo "client bootstrapping "
-echo "~~~~~~~~~~~~~~~~~~~~~~"
+echo "~~~~~~~~~~~~~~~~~~~~~~~"
+echo " Bitmask bootstrapping "
+echo "~~~~~~~~~~~~~~~~~~~~~~~"
echo ""
echo "${cc_green}Creating virtualenv...${cc_normal}"
-mkdir leap-client-testbuild
-virtualenv leap-client-testbuild
-source leap-client-testbuild/bin/activate
+mkdir bitmask-testbuild
+virtualenv bitmask-testbuild
+source bitmask-testbuild/bin/activate
-echo "${cc_green}Installing leap client...${cc_normal}"
+echo "${cc_green}Installing bitmask...${cc_normal}"
# Clone latest git (develop branch)
# change "develop" for any other branch you want.
-pip install -e 'git://leap.se/leap_client@develop#egg=leap-client'
+pip install -e 'git://leap.se/leap_client@develop#egg=leap.bitmask'
-cd leap-client-testbuild
+cd bitmask-testbuild
# symlink the pyside libraries to the system libs
-./src/leap-client/pkg/postmkvenv.sh
+./src/leap.bitmask/pkg/postmkvenv.sh
-echo "${cc_green}leap-client installed! =)"
+echo "${cc_green}bitmask installed! =)"
echo "${cc_yellow}"
echo "Launch it with: "
echo "~~~~~~~~~~~~~~~~~~~~~~"
-echo "bin/leap-client"
+echo "bin/bitmask"
echo "~~~~~~~~~~~~~~~~~~~~~~"
echo "${cc_normal}"
diff --git a/relnotes.txt b/relnotes.txt
index 5f711005..349b430c 100644
--- a/relnotes.txt
+++ b/relnotes.txt
@@ -1,42 +1,57 @@
-ANNOUNCING Bitmask, the internet encryption toolkit, v0.3.0
+ANNOUNCING Bitmask, the internet encryption toolkit, release 0.3.2
The LEAP team is pleased to announce the immediate availability of
-version 0.3.0 of Bitmask
+version 0.3.2 of Bitmask, the Internet Encryption Toolkit.
https://downloads.leap.se/client/
LEAP (LEAP Encryption Access Project) develops a plan to secure
everyday communication, breaking down into discrete services.
-The client for the current phase gives support to the EIP Service and
-the first beta release of Encrypted Mail.
-EIP (the Encrypted Internet Proxy) provides circumvention,
-location anonymization, and traffic encryption in a hassle-free,
-automatically self-configuring fashion.
-Encrypted Mail
+Bitmask is the desktop client to connect to the services offered
+by the LEAP Platform. In the current phase the supported services are
+Encrypted Internet Proxy and Encrypted Mail.
-You can read the user manual and the developer notes online at:
+The Encrypted Internet Proxy provides circumvention, location anonymization,
+and traffic encryption in a hassle-free, automatically self-configuring
+fashion.
-http://bitmask.readthedocs.org/
+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 signature verification.
-WARNING: This is still a beta release of our services, a lot of
-testing and audits are still needed so DO NOT use this for strong
-security.
+You can read about this and many other cool things in the user manual and the
+developer notes, which can be found online at:
+http://bitmask.rtfd.org/
-WHAT CAN THIS VERSION OF THE CLIENT DO FOR ME?
+WARNING: This is still part of a beta release of our software, a lot of testing and
+auditing is still needed, so indeed use it, and feed us back, fork it and contribute
+to its development, but by any means DO NOT trust your life to it (yet!).
-You can connect to the EIP service offered by a provider of your
-choice, and enjoy a encrypted internet connection.
-The first run wizard allows to register an user with the selected
-provider, downloading all the config files needed to connect to the
-eip service. There are also some minimal network checks in place.
+WHAT CAN THIS VERSION OF BITMASK DO FOR ME?
+Bitmask 0.3.2 is mostly a bugfix release, with some minor improvements. Mail
+service is a bit more polished, and we are slowly making our potential packagers
+happy. Refer to the CHANGELOG for the funny details.
-LICENCE
+You can connect to the Encrypted Internet Proxy service offered by a provider of
+your choice, and enjoy a encrypted internet connection that the spying eyes can only
+track back to your provider.
-You may use this package under the GNU General Public License,
+The Encrypted Mail services will run local SMTP and IMAP proxies that, once you
+configure the mail client of your choice, will automatically encrypt and decrypt
+your email using GPG encryption under the hood.
+
+The first run wizard will help you registering an user with your selected
+provider, downloading all the config files needed to connect to the various LEAP
+services.
+
+
+LICENSE
+
+You may use Bitmask under the GNU General Public License,
version 3 or, at your option, any later version. See the file
"COPYING.GPL" for the terms of the GNU General Public
License, version 3.
@@ -50,15 +65,18 @@ including the two.
INSTALLATION
-The current version of the LEAP Client has been tested on GNU/Linux
-and OSX, but it is likely that you are able to run it under other
-systems, specially if you are skillful and patient is one of your
+We distribute the current version of Bitmask as standalone bundles
+for GNU/Linux and OSX, but it is likely that you are able to run it under
+other systems, specially if you are skillful and patience is one of your
virtues.
Have a look at "docs/user/install.rst".
-Packages are provided for debian and ubuntu. OSX and win installers
-will be following soon.
+Packages will be soon provided for debian and ubuntu, and the release of
+windows bundles will be resumed shortly.
+
+We will love to hear if you are interested in help making packages available for
+any other system.
BUGS
@@ -71,11 +89,13 @@ intensive bug-reeducation program.
HACKING
You can find us in the #leap-dev channel on the freenode network.
-If you are lucky enough, you can spot us sleepless in night trains,
-rooftops, rainforests, and beyond any border.
+
+If you are lucky enough, you can also spot us drinking mate, sleepless in
+night trains, rooftops, rainforests, lonely islands and, always, beyond
+any border.
-The LEAP team.
+The LEAP team,
-Aug 9, 2013
+Sep 06, 2013
Somewhere in the middle of the intertubes.
diff --git a/setup.py b/setup.py
index 7f2a5011..88cb0e40 100755
--- a/setup.py
+++ b/setup.py
@@ -1,10 +1,33 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+# setup.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Setup file for bitmask.
+"""
from __future__ import print_function
import sys
+if not sys.version_info[0] == 2:
+ print("[ERROR] Sorry, Python 3 is not supported (yet). "
+ "Try running with python2: python2 setup.py ...")
+ exit()
+
try:
from setuptools import setup, find_packages
except ImportError:
@@ -112,17 +135,17 @@ import platform
_system = platform.system()
IS_LINUX = True if _system == "Linux" else False
+data_files = []
+
if IS_LINUX:
+ # XXX use check_for_permissions to install data
+ # globally. See #3805
data_files = [
- # ("share/man/man1",
- # ["docs/man/bitmask.1"]),
("share/polkit-1/actions",
["pkg/linux/polkit/net.openvpn.gui.leap.policy"]),
- ("/etc/leap/",
+ ("etc/leap/",
["pkg/linux/resolv-update"]),
]
-else:
- data_files = []
setup(
name="leap.bitmask",
diff --git a/src/leap/bitmask/__init__.py b/src/leap/bitmask/__init__.py
index e69de29b..ebdd53c4 100644
--- a/src/leap/bitmask/__init__.py
+++ b/src/leap/bitmask/__init__.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+# __init__.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Init file for leap.bitmask
+
+Initializes version and app info.
+"""
+import re
+
+from pkg_resources import parse_version
+
+from leap.bitmask.util import first
+
+
+def _is_release_version(version):
+ """
+ Helper to determine whether a version is a final release or not.
+ The release needs to be of the form: w.x.y.z containing only numbers
+ and dots.
+
+ :param version: the version string
+ :type version: str
+ :returns: if the version is a release version or not.
+ :rtype: bool
+ """
+ parsed_version = parse_version(version)
+ not_number = 0
+ for x in parsed_version:
+ try:
+ int(x)
+ except:
+ not_number += 1
+
+ return not_number == 1
+
+
+__version__ = "unknown"
+IS_RELEASE_VERSION = False
+
+__short_version__ = "unknown"
+
+try:
+ from leap.bitmask._version import get_versions
+ __version__ = get_versions()['version']
+ IS_RELEASE_VERSION = _is_release_version(__version__)
+ del get_versions
+except ImportError:
+ #running on a tree that has not run
+ #the setup.py setver
+ pass
+
+__appname__ = "unknown"
+try:
+ from leap._appname import __appname__
+except ImportError:
+ #running on a tree that has not run
+ #the setup.py setver
+ pass
+
+__short_version__ = first(re.findall('\d\.\d\.\d', __version__))
+__full_version__ = __appname__ + '/' + str(__version__)
diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py
index 6ffa1d25..158f1afe 100644
--- a/src/leap/bitmask/app.py
+++ b/src/leap/bitmask/app.py
@@ -25,6 +25,7 @@ from functools import partial
from PySide import QtCore, QtGui
from leap.bitmask.util import leap_argparse
+from leap.bitmask.util import log_silencer
from leap.bitmask.util.leap_log_handler import LeapLogHandler
from leap.bitmask.util.streamtologger import StreamToLogger
from leap.common.events import server as event_server
@@ -51,7 +52,7 @@ def install_qtreactor(logger):
logger.debug("Qt4 reactor installed")
-def add_logger_handlers(debug=False, logfile=None):
+def add_logger_handlers(debug=False, logfile=None, standalone=False):
"""
Create the logger and attach the handlers.
@@ -71,6 +72,7 @@ def add_logger_handlers(debug=False, logfile=None):
# Create logger and formatter
logger = logging.getLogger(name='leap')
logger.setLevel(level)
+
log_format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(log_format)
@@ -78,12 +80,16 @@ def add_logger_handlers(debug=False, logfile=None):
console = logging.StreamHandler()
console.setLevel(level)
console.setFormatter(formatter)
+
+ silencer = log_silencer.SelectiveSilencerFilter(standalone=standalone)
+ console.addFilter(silencer)
logger.addHandler(console)
logger.debug('Console handler plugged!')
# LEAP custom handler
leap_handler = LeapLogHandler()
leap_handler.setLevel(level)
+ leap_handler.addFilter(silencer)
logger.addHandler(leap_handler)
logger.debug('Leap handler plugged!')
@@ -93,6 +99,7 @@ def add_logger_handlers(debug=False, logfile=None):
fileh = logging.FileHandler(logfile)
fileh.setLevel(logging.DEBUG)
fileh.setFormatter(formatter)
+ fileh.addFilter(silencer)
logger.addHandler(fileh)
logger.debug('File handler plugged!')
@@ -149,13 +156,13 @@ def main():
from leap.bitmask.gui.mainwindow import MainWindow
from leap.bitmask.platform_init import IS_MAC
from leap.bitmask.platform_init.locks import we_are_the_one_and_only
- from leap.bitmask.util import __version__ as VERSION
+ from leap.bitmask import __version__ as VERSION
from leap.bitmask.util.requirement_checker import check_requirements
# pylint: avoid unused import
assert(locale_rc)
- logger = add_logger_handlers(debug, logfile)
+ logger = add_logger_handlers(debug, logfile, standalone)
replace_stdout_stderr_with_logging(logger)
if not we_are_the_one_and_only():
diff --git a/src/leap/bitmask/config/leapsettings.py b/src/leap/bitmask/config/leapsettings.py
index 35010280..7d8b5977 100644
--- a/src/leap/bitmask/config/leapsettings.py
+++ b/src/leap/bitmask/config/leapsettings.py
@@ -24,7 +24,7 @@ import logging
from PySide import QtCore
from leap.common.check import leap_assert, leap_assert_type
-from leap.common.config.prefixers import get_platform_prefixer
+from leap.common.config import get_path_prefix
logger = logging.getLogger(__name__)
@@ -66,20 +66,22 @@ class LeapSettings(object):
REMEMBER_KEY = "RememberUserAndPass"
DEFAULTPROVIDER_KEY = "DefaultProvider"
ALERTMISSING_KEY = "AlertMissingScripts"
+ GATEWAY_KEY = "Gateway"
+
+ # values
+ GATEWAY_AUTOMATIC = "Automatic"
def __init__(self, standalone=False):
"""
Constructor
- :param standalone: parameter used to define the location of
- the config
+ :param standalone: parameter used to define the location of the config.
:type standalone: bool
"""
+ self._path_prefix = get_path_prefix(standalone=standalone)
+ settings_path = os.path.join(self._path_prefix,
+ "leap", self.CONFIG_NAME)
- settings_path = os.path.join(get_platform_prefixer()
- .get_path_prefix(standalone=standalone),
- "leap",
- self.CONFIG_NAME)
self._settings = QtCore.QSettings(settings_path,
QtCore.QSettings.IniFormat)
@@ -119,6 +121,58 @@ class LeapSettings(object):
leap_assert(windowstate, "We need a window state")
self._settings.setValue(self.WINDOWSTATE_KEY, windowstate)
+ def get_configured_providers(self):
+ """
+ Returns the configured providers based on the file structure in the
+ settings directory.
+
+ :rtype: list of str
+ """
+ # TODO: check which providers have a valid certificate among
+ # other things, not just the directories
+ providers = []
+ try:
+ providers_path = os.path.join(self._path_prefix,
+ "leap", "providers")
+ providers = os.listdir(providers_path)
+ except Exception as e:
+ logger.debug("Error listing providers, assume there are none. %r"
+ % (e,))
+
+ return providers
+
+ def get_selected_gateway(self, provider):
+ """
+ Returns the configured gateway for the given provider.
+
+ :param provider: provider domain
+ :type provider: str
+
+ :rtype: str
+ """
+ leap_assert(len(provider) > 0, "We need a nonempty provider")
+ gateway_key = "{0}/{1}".format(provider, self.GATEWAY_KEY)
+ gateway = self._settings.value(gateway_key, self.GATEWAY_AUTOMATIC)
+
+ return gateway
+
+ def set_selected_gateway(self, provider, gateway):
+ """
+ Saves the configured gateway for the given provider
+
+ :param provider: provider domain
+ :type provider: str
+
+ :param gateway: gateway to use as default
+ :type gateway: str
+ """
+
+ leap_assert(len(provider) > 0, "We need a nonempty provider")
+ leap_assert_type(gateway, (str, unicode))
+
+ gateway_key = "{0}/{1}".format(provider, self.GATEWAY_KEY)
+ self._settings.setValue(gateway_key, gateway)
+
def get_enabled_services(self, provider):
"""
Returns a list of enabled services for the given provider
@@ -151,8 +205,12 @@ class LeapSettings(object):
leap_assert(len(provider) > 0, "We need a nonempty provider")
leap_assert_type(services, list)
- self._settings.setValue("%s/Services" % (provider,),
- services)
+ key = "{0}/Services".format(provider)
+ if not services:
+ # if there are no enabled services we don't need that key
+ self._settings.remove(key)
+ else:
+ self._settings.setValue(key, services)
def get_user(self):
"""
diff --git a/src/leap/bitmask/config/providerconfig.py b/src/leap/bitmask/config/providerconfig.py
index c65932be..a7808399 100644
--- a/src/leap/bitmask/config/providerconfig.py
+++ b/src/leap/bitmask/config/providerconfig.py
@@ -24,6 +24,7 @@ import os
from leap.bitmask.config.provider_spec import leap_provider_spec
from leap.common.check import leap_check
from leap.common.config.baseconfig import BaseConfig, LocalizedKey
+from leap.bitmask.services import get_service_display_name
logger = logging.getLogger(__name__)
@@ -130,9 +131,11 @@ class ProviderConfig(BaseConfig):
Returns a string with the available services in the current
provider, ready to be shown to the user.
"""
- services_str = ", ".join(self.get_services())
- services_str = services_str.replace(
- "openvpn", "Encrypted Internet")
+ services = []
+ for service in self.get_services():
+ services.append(get_service_display_name(service))
+
+ services_str = ", ".join(services)
return services_str
def get_ca_cert_path(self, about_to_download=False):
@@ -216,3 +219,4 @@ if __name__ == "__main__":
print provider.get_languages()
print provider.get_name()
print provider.get_services()
+ print provider.get_services_string()
diff --git a/src/leap/bitmask/config/tests/test_leapsettings.py b/src/leap/bitmask/config/tests/test_leapsettings.py
new file mode 100644
index 00000000..18166923
--- /dev/null
+++ b/src/leap/bitmask/config/tests/test_leapsettings.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+# test_leapsettings.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Tests for leapsettings module.
+"""
+
+try:
+ import unittest2 as unittest
+except ImportError:
+ import unittest
+
+import os
+import mock
+
+from leap.common.testing.basetest import BaseLeapTest
+from leap.bitmask.config.leapsettings import LeapSettings
+
+
+class LeapSettingsTest(BaseLeapTest):
+ """Tests for LeapSettings"""
+
+ def setUp(self):
+ pass
+
+ def tearDown(self):
+ pass
+
+ def test_get_configured_providers(self):
+ """
+ Test that the config file IS NOT stored under the CWD.
+ """
+ self._leapsettings = LeapSettings()
+ with mock.patch('os.listdir') as os_listdir:
+ # use this method only to spy where LeapSettings is looking for
+ self._leapsettings.get_configured_providers()
+ args, kwargs = os_listdir.call_args
+ config_dir = args[0]
+ self.assertFalse(config_dir.startswith(os.getcwd()))
+ self.assertFalse(config_dir.endswith('config'))
+
+ def test_get_configured_providers_in_bundle(self):
+ """
+ Test that the config file IS stored under the CWD.
+ """
+ self._leapsettings = LeapSettings(standalone=True)
+ with mock.patch('os.listdir') as os_listdir:
+ # use this method only to spy where LeapSettings is looking for
+ self._leapsettings.get_configured_providers()
+ args, kwargs = os_listdir.call_args
+ config_dir = args[0]
+ self.assertTrue(config_dir.startswith(os.getcwd()))
+ self.assertFalse(config_dir.endswith('config'))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py
index 9b4ba55d..ece4cad6 100644
--- a/src/leap/bitmask/gui/loggerwindow.py
+++ b/src/leap/bitmask/gui/loggerwindow.py
@@ -146,7 +146,8 @@ class LoggerWindow(QtGui.QDialog):
Lets the user save the current log to a file
"""
fileName, filtr = QtGui.QFileDialog.getSaveFileName(
- self, self.tr("Save As"))
+ self, self.tr("Save As"),
+ options=QtGui.QFileDialog.DontUseNativeDialog)
if fileName:
try:
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index c832887a..0950462b 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -14,9 +14,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
-Main window for the leap client
+Main window for Bitmask.
"""
import logging
import os
@@ -59,7 +58,7 @@ from leap.bitmask.services.eip.vpnlaunchers import \
EIPNoPolkitAuthAgentAvailable
from leap.bitmask.services.eip.vpnlaunchers import EIPNoTunKextLoaded
-from leap.bitmask.util import __version__ as VERSION
+from leap.bitmask import __version__ as VERSION
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -102,6 +101,7 @@ class MainWindow(QtGui.QMainWindow):
raise_window = QtCore.Signal([])
soledad_ready = QtCore.Signal([])
mail_client_logged_in = QtCore.Signal([])
+ logout = QtCore.Signal([])
# We use this flag to detect abnormal terminations
user_stopped_eip = False
@@ -254,14 +254,20 @@ class MainWindow(QtGui.QMainWindow):
self._action_eip_provider = QtGui.QAction(
self.tr("No default provider"), self)
self._action_eip_provider.setEnabled(False)
+
self._action_eip_status = QtGui.QAction(
self.tr("Encrypted internet is OFF"),
self)
self._action_eip_status.setEnabled(False)
-
self._status_panel.set_action_eip_status(
self._action_eip_status)
+ self._action_mail_status = QtGui.QAction(
+ self.tr("Encrypted Mail is OFF"), self)
+ self._action_mail_status.setEnabled(False)
+ self._status_panel.set_action_mail_status(
+ self._action_mail_status)
+
self._action_eip_startstop = QtGui.QAction(
self.tr("Turn OFF"), self)
self._action_eip_startstop.triggered.connect(
@@ -270,6 +276,9 @@ class MainWindow(QtGui.QMainWindow):
self._status_panel.set_action_eip_startstop(
self._action_eip_startstop)
+ self._action_preferences = QtGui.QAction(self.tr("Preferences"), self)
+ self._action_preferences.triggered.connect(self._show_preferences)
+
self._action_visible = QtGui.QAction(self.tr("Hide Main Window"), self)
self._action_visible.triggered.connect(self._toggle_visible)
@@ -284,26 +293,26 @@ class MainWindow(QtGui.QMainWindow):
# Services signals/slots connection
self.new_updates.connect(self._react_to_new_updates)
self.soledad_ready.connect(self._start_imap_service)
+ self.soledad_ready.connect(self._set_soledad_ready)
self.mail_client_logged_in.connect(self._fetch_incoming_mail)
+ self.logout.connect(self._stop_imap_service)
+ self.logout.connect(self._stop_smtp_service)
################################# end Qt Signals connection ########
- # Enable the password change when soledad is ready
- self.soledad_ready.connect(
- partial(self.ui.btnPreferences.setEnabled, True))
-
init_platform()
self._wizard = None
self._wizard_firstrun = False
self._logger_window = None
- self._preferences_window = None
self._bypass_checks = bypass_checks
self._soledad = None
+ self._soledad_ready = False
self._keymanager = None
+ self._smtp_service = None
self._imap_service = None
self._login_defer = None
@@ -419,7 +428,26 @@ class MainWindow(QtGui.QMainWindow):
Displays the preferences window.
"""
- PreferencesWindow(self, self._srp_auth, self._soledad).show()
+ preferences_window = PreferencesWindow(
+ self, self._srp_auth, self._settings, self._standalone)
+
+ if self._soledad_ready:
+ preferences_window.set_soledad_ready(self._soledad)
+ else:
+ self.soledad_ready.connect(
+ lambda: preferences_window.set_soledad_ready(self._soledad))
+
+ preferences_window.show()
+
+ def _set_soledad_ready(self):
+ """
+ SLOT
+ TRIGGERS:
+ self.soledad_ready
+
+ It sets the soledad object as ready to use.
+ """
+ self._soledad_ready = True
def _uncheck_logger_button(self):
"""
@@ -491,7 +519,8 @@ class MainWindow(QtGui.QMainWindow):
"""
# XXX: May be this can be divided into two methods?
- self._login_widget.set_providers(self._configured_providers())
+ providers = self._settings.get_configured_providers()
+ self._login_widget.set_providers(providers)
self._show_systray()
self.show()
if IS_MAC:
@@ -589,10 +618,8 @@ class MainWindow(QtGui.QMainWindow):
self._systray.setVisible(True)
return
- # Placeholder actions
- # They are temporary to display the tray as designed
- preferences_action = QtGui.QAction(self.tr("Preferences"), self)
- preferences_action.setEnabled(False)
+ # Placeholder action
+ # It is temporary to display the tray as designed
help_action = QtGui.QAction(self.tr("Help"), self)
help_action.setEnabled(False)
@@ -602,8 +629,9 @@ class MainWindow(QtGui.QMainWindow):
systrayMenu.addAction(self._action_eip_provider)
systrayMenu.addAction(self._action_eip_status)
systrayMenu.addAction(self._action_eip_startstop)
+ systrayMenu.addAction(self._action_mail_status)
systrayMenu.addSeparator()
- systrayMenu.addAction(preferences_action)
+ systrayMenu.addAction(self._action_preferences)
systrayMenu.addAction(help_action)
systrayMenu.addSeparator()
systrayMenu.addAction(self.ui.action_log_out)
@@ -736,34 +764,14 @@ class MainWindow(QtGui.QMainWindow):
QtGui.QMainWindow.closeEvent(self, e)
- def _configured_providers(self):
- """
- Returns the available providers based on the file structure
-
- :rtype: list
- """
-
- # TODO: check which providers have a valid certificate among
- # other things, not just the directories
- providers = []
- try:
- providers = os.listdir(
- os.path.join(self._provider_config.get_path_prefix(),
- "leap",
- "providers"))
- except Exception as e:
- logger.debug("Error listing providers, assume there are none. %r"
- % (e,))
-
- return providers
-
def _first_run(self):
"""
Returns True if there are no configured providers. False otherwise
:rtype: bool
"""
- has_provider_on_disk = len(self._configured_providers()) != 0
+ providers = self._settings.get_configured_providers()
+ has_provider_on_disk = len(providers) != 0
is_proper_provider = self._settings.get_properprovider()
return not (has_provider_on_disk and is_proper_provider)
@@ -891,6 +899,8 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Cancelling login defer.")
self._login_defer.cancel()
+ self._login_widget.set_status(self.tr("Log in cancelled by the user."))
+
def _provider_config_loaded(self, data):
"""
SLOT
@@ -912,7 +922,7 @@ class MainWindow(QtGui.QMainWindow):
self._srp_auth.logout_finished.connect(
self._done_logging_out)
- # TODO: Add errback!
+ # TODO Add errback!
self._login_defer = self._srp_auth.authenticate(username, password)
else:
self._login_widget.set_status(
@@ -979,7 +989,7 @@ class MainWindow(QtGui.QMainWindow):
"""
passed = data[self._soledad_bootstrapper.PASSED_KEY]
if not passed:
- # TODO: display in the GUI:
+ # TODO display in the GUI:
# should pass signal to a slot in status_panel
# that sets the global status
logger.error("Soledad failed to start: %s" %
@@ -1040,13 +1050,13 @@ class MainWindow(QtGui.QMainWindow):
True)
else:
if self._enabled_services.count(self.MX_SERVICE) > 0:
- pass # TODO: show MX status
+ pass # TODO show MX status
#self._status_panel.set_eip_status(
# self.tr("%s does not support MX") %
# (self._provider_config.get_domain(),),
# error=True)
else:
- pass # TODO: show MX status
+ pass # TODO show MX status
#self._status_panel.set_eip_status(
# self.tr("MX is disabled"))
@@ -1073,25 +1083,43 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Done bootstrapping SMTP")
hosts = self._smtp_config.get_hosts()
- # TODO: handle more than one host and define how to choose
+ # TODO handle more than one host and define how to choose
if len(hosts) > 0:
hostname = hosts.keys()[0]
logger.debug("Using hostname %s for SMTP" % (hostname,))
host = hosts[hostname][self.IP_KEY].encode("utf-8")
port = hosts[hostname][self.PORT_KEY]
- # TODO: pick local smtp port in a better way
- # TODO: Make the encrypted_only configurable
+ # TODO move the start to _start_smtp_service
+
+ # TODO Make the encrypted_only configurable
+ # TODO pick local smtp port in a better way
+ # TODO remove hard-coded port and let leap.mail set
+ # the specific default.
from leap.mail.smtp import setup_smtp_relay
client_cert = self._eip_config.get_client_cert_path(
self._provider_config)
- setup_smtp_relay(port=2013,
- keymanager=self._keymanager,
- smtp_host=host,
- smtp_port=port,
- smtp_cert=client_cert,
- smtp_key=client_cert,
- encrypted_only=False)
+ self._smtp_service = setup_smtp_relay(
+ port=2013,
+ keymanager=self._keymanager,
+ smtp_host=host,
+ smtp_port=port,
+ smtp_cert=client_cert,
+ smtp_key=client_cert,
+ encrypted_only=False)
+
+ def _stop_smtp_service(self):
+ """
+ SLOT
+ TRIGGERS:
+ self.logout
+ """
+ # There is a subtle difference here:
+ # we are stopping the factory for the smtp service here,
+ # but in the imap case we are just stopping the fetcher.
+ if self._smtp_service is not None:
+ logger.debug('Stopping smtp service.')
+ self._smtp_service.doStop()
###################################################################
# Service control methods: imap
@@ -1100,7 +1128,7 @@ class MainWindow(QtGui.QMainWindow):
"""
SLOT
TRIGGERS:
- soledad_ready
+ self.soledad_ready
"""
if self._provider_config.provides_mx() and \
self._enabled_services.count(self.MX_SERVICE) > 0:
@@ -1109,17 +1137,6 @@ class MainWindow(QtGui.QMainWindow):
self._imap_service = imap.start_imap_service(
self._soledad,
self._keymanager)
- else:
- if self._enabled_services.count(self.MX_SERVICE) > 0:
- pass # TODO: show MX status
- #self._status_panel.set_eip_status(
- # self.tr("%s does not support MX") %
- # (self._provider_config.get_domain(),),
- # error=True)
- else:
- pass # TODO: show MX status
- #self._status_panel.set_eip_status(
- # self.tr("MX is disabled"))
def _on_mail_client_logged_in(self, req):
"""
@@ -1131,13 +1148,27 @@ class MainWindow(QtGui.QMainWindow):
"""
SLOT
TRIGGERS:
- mail_client_logged_in
+ self.mail_client_logged_in
"""
# TODO have a mutex over fetch operation.
if self._imap_service:
logger.debug('Client connected, fetching mail...')
self._imap_service.fetch()
+ def _stop_imap_service(self):
+ """
+ SLOT
+ TRIGGERS:
+ self.logout
+ """
+ # There is a subtle difference here:
+ # we are just stopping the fetcher here,
+ # but in the smtp case we are stopping the factory.
+ # We should homogenize both services.
+ if self._imap_service is not None:
+ logger.debug('Stopping imap service.')
+ self._imap_service.stop()
+
# end service control methods (imap)
###################################################################
@@ -1149,7 +1180,8 @@ class MainWindow(QtGui.QMainWindow):
:rtype: tuple (str, str) (host, port)
"""
- # TODO: make this properly multiplatform
+ # TODO make this properly multiplatform
+ # TODO get this out of gui/
if platform.system() == "Windows":
host = "localhost"
@@ -1402,6 +1434,7 @@ class MainWindow(QtGui.QMainWindow):
# XXX: If other defers are doing authenticated stuff, this
# might conflict with those. CHECK!
threads.deferToThread(self._srp_auth.logout)
+ self.logout.emit()
def _done_logging_out(self, ok, message):
"""
@@ -1576,7 +1609,7 @@ class MainWindow(QtGui.QMainWindow):
"""
Cleanup and tidely close the main window before quitting.
"""
- # TODO: separate the shutting down of services from the
+ # TODO separate the shutting down of services from the
# UI stuff.
self._cleanup_and_quit()
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index 67448768..1becfb18 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -18,14 +18,20 @@
"""
Preferences window
"""
+import os
import logging
from functools import partial
-from PySide import QtGui
+from PySide import QtCore, QtGui
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
from leap.bitmask.crypto.srpauth import SRPAuthBadPassword
+from leap.bitmask.util.password import basic_password_checks
+from leap.bitmask.services import get_supported
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.services import get_service_display_name
logger = logging.getLogger(__name__)
@@ -37,58 +43,62 @@ class PreferencesWindow(QtGui.QDialog):
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password")
- def __init__(self, parent, srp_auth, soledad):
+ def __init__(self, parent, srp_auth, leap_settings, standalone):
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
:param srp_auth: SRPAuth object configured in the main app.
:type srp_auth: SRPAuth
- :param soledad: Soledad object configured in the main app.
- :type soledad: Soledad
+ :param standalone: If True, the application is running as standalone
+ and the preferences dialog should display some
+ messages according to this.
+ :type standalone: bool
"""
QtGui.QDialog.__init__(self, parent)
+ self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
self._srp_auth = srp_auth
- self._soledad = soledad
+ self._settings = leap_settings
+ self._standalone = standalone
+ self._soledad = None
# Load UI
self.ui = Ui_Preferences()
self.ui.setupUi(self)
self.ui.lblPasswordChangeStatus.setVisible(False)
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+ self.ui.lblProvidersGatewayStatus.setVisible(False)
+
+ self._selected_services = set()
# Connections
self.ui.pbChangePassword.clicked.connect(self._change_password)
+ self.ui.cbProvidersServices.currentIndexChanged[unicode].connect(
+ self._populate_services)
+ self.ui.cbProvidersGateway.currentIndexChanged[unicode].connect(
+ self._populate_gateways)
- def _basic_password_checks(self, username, password, password2):
- """
- Performs basic password checks to avoid really easy passwords.
+ self.ui.cbGateways.currentIndexChanged[unicode].connect(
+ lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))
- :param username: username provided at the registrarion form
- :type username: str
- :param password: password from the registration form
- :type password: str
- :param password2: second password from the registration form
- :type password: str
+ if not self._settings.get_configured_providers():
+ self.ui.gbEnabledServices.setEnabled(False)
+ else:
+ self._add_configured_providers()
- :returns: True and empty message if all the checks pass,
- False and an error message otherwise
- :rtype: tuple(bool, str)
+ def set_soledad_ready(self, soledad):
"""
- message = None
-
- if message is None and password != password2:
- message = self.tr("Passwords don't match")
+ SLOT
+ TRIGGERS:
+ parent.soledad_ready
- if message is None and len(password) < 6:
- message = self.tr("Password too short")
+ It sets the soledad object as ready to use.
- if message is None and password in self.WEAK_PASSWORDS:
- message = self.tr("Password too easy")
-
- if message is None and username == password:
- message = self.tr("Password equal to username")
-
- return message is None, message
+ :param soledad: Soledad object configured in the main app.
+ :type soledad: Soledad
+ """
+ self._soledad = soledad
+ self.ui.gbPasswordChange.setEnabled(True)
def _set_password_change_status(self, status, error=False, success=False):
"""
@@ -116,7 +126,7 @@ class PreferencesWindow(QtGui.QDialog):
:type disable: bool
"""
if disable:
- self._set_password_change_disable(self.tr("Changing password..."))
+ self._set_password_change_status(self.tr("Changing password..."))
self.ui.leCurrentPassword.setEnabled(not disable)
self.ui.leNewPassword.setEnabled(not disable)
@@ -125,6 +135,10 @@ class PreferencesWindow(QtGui.QDialog):
def _change_password(self):
"""
+ SLOT
+ TRIGGERS:
+ self.ui.pbChangePassword.clicked
+
Changes the user's password if the inputboxes are correctly filled.
"""
username = self._srp_auth.get_username()
@@ -132,8 +146,7 @@ class PreferencesWindow(QtGui.QDialog):
new_password = self.ui.leNewPassword.text()
new_password2 = self.ui.leNewPassword2.text()
- ok, msg = self._basic_password_checks(
- username, new_password, new_password2)
+ ok, msg = basic_password_checks(username, new_password, new_password2)
if not ok:
self._set_changing_password(False)
@@ -165,7 +178,7 @@ class PreferencesWindow(QtGui.QDialog):
self._set_password_change_status(
self.tr("Password changed successfully."), success=True)
- self._clear_inputs()
+ self._clear_password_inputs()
self._set_changing_password(False)
def _change_password_problem(self, failure):
@@ -187,10 +200,264 @@ class PreferencesWindow(QtGui.QDialog):
self._set_changing_password(False)
failure.trap(Exception)
- def _clear_inputs(self):
+ 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 _set_providers_gateway_status(self, status, success=False,
+ error=False):
+ """
+ Sets the status label for the gateway 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
+ :param error: is set to True if we should display the
+ message as red
+ :type error: bool
+ """
+ if success:
+ status = "<font color='green'><b>%s</b></font>" % (status,)
+ elif error:
+ status = "<font color='red'><b>%s</b></font>" % (status,)
+
+ self.ui.lblProvidersGatewayStatus.setVisible(True)
+ self.ui.lblProvidersGatewayStatus.setText(status)
+
+ def _add_configured_providers(self):
+ """
+ Add the client's configured providers to the providers combo boxes.
+ """
+ self.ui.cbProvidersServices.clear()
+ self.ui.cbProvidersGateway.clear()
+ for provider in self._settings.get_configured_providers():
+ self.ui.cbProvidersServices.addItem(provider)
+ self.ui.cbProvidersGateway.addItem(provider)
+
+ def _service_selection_changed(self, service, state):
+ """
+ SLOT
+ TRIGGER: 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)
+
+ def _populate_services(self, domain):
+ """
+ SLOT
+ 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
+ """
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersServicesStatus.setVisible(False)
+
+ if not domain:
+ return
+
+ provider_config = self._get_provider_config(domain)
+ if provider_config is None:
+ 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)
+
+ services = get_supported(provider_config.get_services())
+ 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, self._standalone)
+ 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,))
+
+ def _save_enabled_services(self, provider):
+ """
+ SLOT
+ 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)
+
+ def _get_provider_config(self, domain):
+ """
+ Helper to return a valid Provider Config from the domain name.
+
+ :param domain: the domain name of the provider.
+ :type domain: str
+
+ :rtype: ProviderConfig or None if there is a problem loading the config
+ """
+ provider_config = ProviderConfig()
+ provider_config_path = os.path.join(
+ "leap", "providers", domain, "provider.json")
+
+ if not provider_config.load(provider_config_path):
+ provider_config = None
+
+ return provider_config
+
+ def _save_selected_gateway(self, provider):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.pbSaveGateway.clicked
+
+ Saves the new gateway setting to the configuration file.
+
+ :param provider: the provider config that we need to save.
+ :type provider: str
+ """
+ gateway = self.ui.cbGateways.currentText()
+
+ if gateway == self.AUTOMATIC_GATEWAY_LABEL:
+ gateway = self._settings.GATEWAY_AUTOMATIC
+ else:
+ idx = self.ui.cbGateways.currentIndex()
+ gateway = self.ui.cbGateways.itemData(idx)
+
+ self._settings.set_selected_gateway(provider, gateway)
+
+ msg = self.tr(
+ "Gateway settings for provider '{0}' saved.".format(provider))
+ logger.debug(msg)
+ self._set_providers_gateway_status(msg, success=True)
+
+ def _populate_gateways(self, domain):
+ """
+ SLOT
+ TRIGGERS:
+ self.ui.cbProvidersGateway.currentIndexChanged[unicode]
+
+ Loads the gateways that the provider provides into the UI for
+ the user to select.
+
+ :param domain: the domain of the provider to load gateways from.
+ :type domain: str
+ """
+ # We hide the maybe-visible status label after a change
+ self.ui.lblProvidersGatewayStatus.setVisible(False)
+
+ if not domain:
+ return
+
+ try:
+ # disconnect prevoiusly connected save method
+ self.ui.pbSaveGateway.clicked.disconnect()
+ except RuntimeError:
+ pass # Signal was not connected
+
+ # set the proper connection for the 'save' button
+ save_gateway = partial(self._save_selected_gateway, domain)
+ self.ui.pbSaveGateway.clicked.connect(save_gateway)
+
+ eip_config = EIPConfig()
+ provider_config = self._get_provider_config(domain)
+
+ eip_config_path = os.path.join("leap", "providers",
+ domain, "eip-service.json")
+ api_version = provider_config.get_api_version()
+ eip_config.set_api_version(api_version)
+ eip_loaded = eip_config.load(eip_config_path)
+
+ if not eip_loaded or provider_config is None:
+ self._set_providers_gateway_status(
+ self.tr("There was a problem with configuration files."),
+ error=True)
+ return
+
+ gateways = VPNGatewaySelector(eip_config).get_gateways_list()
+ logger.debug(gateways)
+
+ self.ui.cbGateways.clear()
+ self.ui.cbGateways.addItem(self.AUTOMATIC_GATEWAY_LABEL)
+
+ # Add the available gateways and
+ # select the one stored in configuration file.
+ selected_gateway = self._settings.get_selected_gateway(domain)
+ index = 0
+ for idx, (gw_name, gw_ip) in enumerate(gateways):
+ gateway = "{0} ({1})".format(gw_name, gw_ip)
+ self.ui.cbGateways.addItem(gateway, gw_ip)
+ if gw_ip == selected_gateway:
+ index = idx + 1
+
+ self.ui.cbGateways.setCurrentIndex(index)
diff --git a/src/leap/bitmask/gui/statuspanel.py b/src/leap/bitmask/gui/statuspanel.py
index 9352eb04..3a91f08e 100644
--- a/src/leap/bitmask/gui/statuspanel.py
+++ b/src/leap/bitmask/gui/statuspanel.py
@@ -356,6 +356,16 @@ class StatusPanelWidget(QtGui.QWidget):
leap_assert_type(action_eip_status, QtGui.QAction)
self._action_eip_status = action_eip_status
+ def set_action_mail_status(self, action_mail_status):
+ """
+ Sets the action_mail_status to use.
+
+ :param action_mail_status: action_mail_status to be used
+ :type action_mail_status: QtGui.QAction
+ """
+ leap_assert_type(action_mail_status, QtGui.QAction)
+ self._action_mail_status = action_mail_status
+
def set_global_status(self, status, error=False):
"""
Sets the global status label.
@@ -538,6 +548,27 @@ class StatusPanelWidget(QtGui.QWidget):
def set_provider(self, provider):
self.ui.lblProvider.setText(provider)
+ def _set_mail_status(self, status, ready=False):
+ """
+ Sets the Encrypted Mail status in the label and in the tray icon.
+
+ :param status: the status text to display
+ :type status: unicode
+ :param ready: if mx is ready or not.
+ :type ready: bool
+ """
+ self.ui.lblMailStatus.setText(status)
+
+ tray_status = self.tr('Encrypted Mail is OFF')
+
+ icon = QtGui.QPixmap(self.MAIL_OFF_ICON)
+ if ready:
+ icon = QtGui.QPixmap(self.MAIL_ON_ICON)
+ tray_status = self.tr('Encrypted Mail is ON')
+
+ self.ui.lblMailIcon.setPixmap(icon)
+ self._action_mail_status.setText(tray_status)
+
def _mail_handle_soledad_events(self, req):
"""
Callback for ...
@@ -557,7 +588,7 @@ class StatusPanelWidget(QtGui.QWidget):
:param req: Request type
:type req: leap.common.events.events_pb2.SignalRequest
"""
- self.ui.lblMailStatus.setText(self.tr("Starting..."))
+ self._set_mail_status(self.tr("Starting..."))
ext_status = ""
@@ -591,7 +622,12 @@ class StatusPanelWidget(QtGui.QWidget):
:param req: Request type
:type req: leap.common.events.events_pb2.SignalRequest
"""
- self.ui.lblMailStatus.setText(self.tr("Starting..."))
+ # We want to ignore this kind of events once everything has
+ # started
+ if self._smtp_started and self._imap_started:
+ return
+
+ self._set_mail_status(self.tr("Starting..."))
ext_status = ""
@@ -639,14 +675,11 @@ class StatusPanelWidget(QtGui.QWidget):
ext_status = self.tr("SMTP has started...")
self._smtp_started = True
if self._smtp_started and self._imap_started:
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
- self.ui.lblMailIcon.setPixmap(
- QtGui.QPixmap(":/images/mail-locked.png"))
+ self._set_mail_status(self.tr("ON"), ready=True)
ext_status = ""
elif req.event == proto.SMTP_SERVICE_FAILED_TO_START:
ext_status = self.tr("SMTP failed to start, check the logs.")
- self.ui.lblMailStatus.setText(self.tr("Failed"))
+ self._set_mail_status(self.tr("Failed"))
else:
leap_assert(False,
"Don't know how to handle this state: %s"
@@ -679,19 +712,17 @@ class StatusPanelWidget(QtGui.QWidget):
ext_status = self.tr("IMAP has started...")
self._imap_started = True
if self._smtp_started and self._imap_started:
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
+ self._set_mail_status(self.tr("ON"), ready=True)
ext_status = ""
elif req.event == proto.IMAP_SERVICE_FAILED_TO_START:
ext_status = self.tr("IMAP failed to start, check the logs.")
- self.ui.lblMailStatus.setText(self.tr("Failed"))
+ self._set_mail_status(self.tr("Failed"))
elif req.event == proto.IMAP_UNREAD_MAIL:
if self._smtp_started and self._imap_started:
self.ui.lblUnread.setText(
self.tr("%s Unread Emails") % (req.content))
self.ui.lblUnread.setVisible(req.content != "0")
- self.ui.lblMailStatus.setText(self.tr("ON"))
- self.ui.lblMailIcon.setPixmap(QtGui.QPixmap(self.MAIL_ON_ICON))
+ self._set_mail_status(self.tr("ON"), ready=True)
else:
leap_assert(False,
"Don't know how to handle this state: %s"
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index 834a562e..17837642 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -217,7 +217,7 @@
<item>
<widget class="QPushButton" name="btnPreferences">
<property name="enabled">
- <bool>false</bool>
+ <bool>true</bool>
</property>
<property name="text">
<string>Preferences</string>
diff --git a/src/leap/bitmask/gui/ui/preferences.ui b/src/leap/bitmask/gui/ui/preferences.ui
index 8c63ccad..e66a2d68 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>451</width>
- <height>267</height>
+ <width>503</width>
+ <height>529</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,9 +17,149 @@
<iconset resource="../../../../../data/resources/mainwindow.qrc">
<normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
</property>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="5" column="0">
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" 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="1" colspan="2">
+ <widget class="QComboBox" name="cbProvidersGateway">
+ <item>
+ <property name="text">
+ <string>&lt;Select provider&gt;</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="lblSelectProvider">
+ <property name="text">
+ <string>&amp;Select provider:</string>
+ </property>
+ <property name="buddy">
+ <cstring>cbProvidersGateway</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Select gateway:</string>
+ </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>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="pbSaveGateway">
+ <property name="text">
+ <string>Save this provider settings</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" 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>
+ </layout>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QGroupBox" name="gbEnabledServices">
+ <property name="title">
+ <string>Enabled services</string>
+ </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>
+ <item row="0" column="0">
<widget class="QGroupBox" name="gbPasswordChange">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
<property name="title">
<string>Password Change</string>
</property>
@@ -98,69 +238,6 @@
</layout>
</widget>
</item>
- <item>
- <widget class="QGroupBox" name="gbGatewaySelector">
- <property name="enabled">
- <bool>false</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>&amp;Select provider:</string>
- </property>
- <property name="buddy">
- <cstring>cbProviders</cstring>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QComboBox" name="cbProviders">
- <item>
- <property name="text">
- <string>&lt;Select provider&gt;</string>
- </property>
- </item>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Select gateway:</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="comboBox">
- <item>
- <property name="text">
- <string>Automatic</string>
- </property>
- </item>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <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>
diff --git a/src/leap/bitmask/gui/ui/wizard.ui b/src/leap/bitmask/gui/ui/wizard.ui
index 3b8f1215..2a412784 100644
--- a/src/leap/bitmask/gui/ui/wizard.ui
+++ b/src/leap/bitmask/gui/ui/wizard.ui
@@ -183,7 +183,7 @@
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
- <string>Can we stablish a secure connection?</string>
+ <string>Can we establish a secure connection?</string>
</property>
</widget>
</item>
@@ -740,97 +740,6 @@
</item>
</layout>
</widget>
- <widget class="QWizardPage" name="finish_page">
- <property name="title">
- <string>Congratulations!</string>
- </property>
- <property name="subTitle">
- <string>You have successfully configured Bitmask.</string>
- </property>
- <attribute name="pageId">
- <string notr="true">6</string>
- </attribute>
- <layout class="QGridLayout" name="gridLayout_10">
- <item row="1" column="0">
- <spacer name="horizontalSpacer_4">
- <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 row="0" column="1">
- <spacer name="verticalSpacer_9">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_23">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/mainwindow.qrc">:/images/mask-icon.png</pixmap>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QLabel" name="label_25">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/mainwindow.qrc">:/images/Globe.png</pixmap>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <spacer name="verticalSpacer_10">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
</widget>
<customwidgets>
<customwidget>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index ed6c1da0..e004e6cf 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -32,8 +32,9 @@ from leap.bitmask.crypto.srpregister import SRPRegister
from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
+from leap.bitmask.util.password import basic_password_checks
from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_supported
+from leap.bitmask.services import get_service_display_name, get_supported
from ui_wizard import Ui_Wizard
@@ -51,7 +52,6 @@ class Wizard(QtGui.QWizard):
SETUP_PROVIDER_PAGE = 3
REGISTER_USER_PAGE = 4
SERVICES_PAGE = 5
- FINISH_PAGE = 6
WEAK_PASSWORDS = ("123456", "qweasd", "qwerty",
"password")
@@ -83,23 +83,6 @@ class Wizard(QtGui.QWizard):
self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png")
self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png")
- # Correspondence for services and their name to display
- EIP_LABEL = self.tr("Encrypted Internet")
- MX_LABEL = self.tr("Encrypted Mail")
-
- if self._is_need_eip_password_warning():
- EIP_LABEL += " " + self.tr(
- "(will need admin password to start)")
-
- self.SERVICE_DISPLAY = [
- EIP_LABEL,
- MX_LABEL
- ]
- self.SERVICE_CONFIG = [
- "openvpn",
- "mx"
- ]
-
self._selected_services = set()
self._shown_services = set()
@@ -160,7 +143,7 @@ class Wizard(QtGui.QWizard):
self.page(self.REGISTER_USER_PAGE).setButtonText(
QtGui.QWizard.CommitButton, self.tr("&Next >"))
- self.page(self.FINISH_PAGE).setButtonText(
+ self.page(self.SERVICES_PAGE).setButtonText(
QtGui.QWizard.FinishButton, self.tr("Connect"))
# XXX: Temporary removal for enrollment policy
@@ -199,41 +182,6 @@ class Wizard(QtGui.QWizard):
"""
self.ui.lblPassword2.setFocus()
- def _basic_password_checks(self, username, password, password2):
- """
- Performs basic password checks to avoid really easy passwords.
-
- :param username: username provided at the registrarion form
- :type username: str
- :param password: password from the registration form
- :type password: str
- :param password2: second password from the registration form
- :type password: str
-
- :return: returns True if all the checks pass, False otherwise
- :rtype: bool
- """
- message = None
-
- if message is None and password != password2:
- message = self.tr("Passwords don't match")
-
- if message is None and len(password) < 6:
- message = self.tr("Password too short")
-
- if message is None and password in self.WEAK_PASSWORDS:
- message = self.tr("Password too easy")
-
- if message is None and username == password:
- message = self.tr("Password equal to username")
-
- if message is not None:
- self._set_register_status(message, error=True)
- self._focus_password()
- return False
-
- return True
-
def _register(self):
"""
Performs the registration based on the values provided in the form
@@ -244,7 +192,8 @@ class Wizard(QtGui.QWizard):
password = self.ui.lblPassword.text()
password2 = self.ui.lblPassword2.text()
- if self._basic_password_checks(username, password, password2):
+ ok, msg = basic_password_checks(username, password, password2)
+ if ok:
register = SRPRegister(provider_config=self._provider_config)
register.registration_finished.connect(
self._registration_finished)
@@ -258,6 +207,8 @@ class Wizard(QtGui.QWizard):
self._password = password
self._set_register_status(self.tr("Starting registration..."))
else:
+ self._set_register_status(msg, error=True)
+ self._focus_password()
self.ui.btnRegister.setEnabled(True)
def _set_registration_fields_visibility(self, visible):
@@ -538,8 +489,10 @@ class Wizard(QtGui.QWizard):
try:
if service not in self._shown_services:
checkbox = QtGui.QCheckBox(self)
- service_index = self.SERVICE_CONFIG.index(service)
- checkbox.setText(self.SERVICE_DISPLAY[service_index])
+ service_label = get_service_display_name(
+ service, self.standalone)
+ checkbox.setText(service_label)
+
self.ui.serviceListLayout.addWidget(checkbox)
checkbox.stateChanged.connect(
partial(self._service_selection_changed, service))
diff --git a/src/leap/bitmask/platform_init/locks.py b/src/leap/bitmask/platform_init/locks.py
index ecfe3b1f..34f884dc 100644
--- a/src/leap/bitmask/platform_init/locks.py
+++ b/src/leap/bitmask/platform_init/locks.py
@@ -185,7 +185,7 @@ if platform_init.IS_WIN:
Creates a lock based on the atomic nature of mkdir on Windows
system calls.
"""
- LOCKBASE = os.path.join(gettempdir(), "leap-client-lock")
+ LOCKBASE = os.path.join(gettempdir(), "bitmask-lock")
def __init__(self):
"""
@@ -353,7 +353,7 @@ def we_are_the_one_and_only():
_sys = platform.system()
if _sys in ("Linux", "Darwin"):
- locker = UnixLock('/tmp/leap-client.lock')
+ locker = UnixLock('/tmp/bitmask.lock')
locker.get_lock()
we_are_the_one = locker.locked_by_us
if not we_are_the_one:
diff --git a/src/leap/bitmask/services/__init__.py b/src/leap/bitmask/services/__init__.py
index 253359cd..339f9cc6 100644
--- a/src/leap/bitmask/services/__init__.py
+++ b/src/leap/bitmask/services/__init__.py
@@ -17,9 +17,48 @@
"""
Services module.
"""
+from PySide import QtCore
+from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
+
DEPLOYED = ["openvpn", "mx"]
+def get_service_display_name(service, standalone=False):
+ """
+ Returns the name to display of the given service.
+ If there is no configured name for that service, then returns the same
+ parameter
+
+ :param service: the 'machine' service name
+ :type service: str
+ :param standalone: True if the app is running in a standalone mode, used
+ to display messages according that.
+ :type standalone: bool
+
+ :rtype: str
+ """
+ # qt translator method helper
+ _tr = QtCore.QObject().tr
+
+ # Correspondence for services and their name to display
+ EIP_LABEL = _tr("Encrypted Internet")
+ MX_LABEL = _tr("Encrypted Mail")
+
+ service_display = {
+ "openvpn": EIP_LABEL,
+ "mx": MX_LABEL
+ }
+
+ # If we need to add a warning about eip needing
+ # administrative permissions to start. That can be either
+ # because we are running in standalone mode, or because we could
+ # not find the needed privilege escalation mechanisms being operative.
+ if standalone or is_missing_policy_permissions():
+ EIP_LABEL += " " + _tr("(will need admin password to start)")
+
+ return service_display.get(service, service)
+
+
def get_supported(services):
"""
Returns a list of the available services.
diff --git a/src/leap/bitmask/services/eip/eipconfig.py b/src/leap/bitmask/services/eip/eipconfig.py
index 843e7397..1cb7419e 100644
--- a/src/leap/bitmask/services/eip/eipconfig.py
+++ b/src/leap/bitmask/services/eip/eipconfig.py
@@ -62,11 +62,12 @@ class VPNGatewaySelector(object):
self._eipconfig = eipconfig
- def get_gateways(self):
+ def get_gateways_list(self):
"""
- Returns the 4 best gateways, sorted by timezone proximity.
+ Returns the existing gateways, sorted by timezone proximity.
- :rtype: list of IPv4Address or IPv6Address object.
+ :rtype: list of tuples (location, ip)
+ (str, IPv4Address or IPv6Address object)
"""
gateways_timezones = []
locations = self._eipconfig.get_locations()
@@ -77,19 +78,35 @@ class VPNGatewaySelector(object):
gateway_distance = 99 # if hasn't location -> should go last
if gateway_location is not None:
- gw_offset = int(locations[gateway['location']]['timezone'])
+ 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)
ip = self._eipconfig.get_gateway_ip(idx)
- gateways_timezones.append((ip, gateway_distance))
+ gateways_timezones.append((ip, gateway_distance, gateway_location))
- gateways_timezones = sorted(gateways_timezones,
- key=lambda gw: gw[1])[:4]
+ gateways_timezones = sorted(gateways_timezones, key=lambda gw: gw[1])
+
+ gateways = []
+ for ip, distance, location in gateways_timezones:
+ gateways.append((location, ip))
+
+ return gateways
- gateways = [ip for ip, dist in gateways_timezones]
+ def get_gateways(self):
+ """
+ Returns the 4 best gateways, sorted by timezone proximity.
+
+ :rtype: list of IPv4Address or IPv6Address object.
+ """
+ gateways = [ip for location, ip in self.get_gateways_list()][:4]
return gateways
def _get_timezone_distance(self, offset):
@@ -124,7 +141,7 @@ class VPNGatewaySelector(object):
if time.daylight:
local_offset = time.altzone
- return local_offset / 3600
+ return -local_offset / 3600
class EIPConfig(BaseConfig):
@@ -246,7 +263,8 @@ if __name__ == "__main__":
console.setFormatter(formatter)
logger.addHandler(console)
- eipconfig = EIPConfig('1')
+ eipconfig = EIPConfig()
+ eipconfig.set_api_version('1')
try:
eipconfig.get_clusters()
@@ -255,9 +273,14 @@ if __name__ == "__main__":
print "Safe value getting is working"
if eipconfig.load("leap/providers/bitmask.net/eip-service.json"):
+ print "EIPConfig methods"
print eipconfig.get_clusters()
print eipconfig.get_gateways()
print eipconfig.get_locations()
print eipconfig.get_openvpn_configuration()
print eipconfig.get_serial()
print eipconfig.get_version()
+ print "VPNGatewaySelector methods"
+ gws = VPNGatewaySelector(eipconfig)
+ print gws.get_gateways()
+ print gws.get_gateways_list()
diff --git a/src/leap/bitmask/services/eip/vpnlaunchers.py b/src/leap/bitmask/services/eip/vpnlaunchers.py
index f8c51ad8..a50da8b9 100644
--- a/src/leap/bitmask/services/eip/vpnlaunchers.py
+++ b/src/leap/bitmask/services/eip/vpnlaunchers.py
@@ -23,8 +23,8 @@ import logging
import getpass
import os
import platform
-import subprocess
import stat
+import subprocess
try:
import grp
except ImportError:
@@ -32,6 +32,9 @@ except ImportError:
from abc import ABCMeta, abstractmethod
from functools import partial
+from time import sleep
+
+from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
@@ -217,19 +220,23 @@ def _is_auth_agent_running():
return any(is_running)
-def _try_to_launch_agent():
+def _try_to_launch_agent(standalone=False):
"""
Tries to launch a polkit daemon.
"""
- opts = [
- "/usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1",
- # XXX add kde thing here
- ]
- for cmd in opts:
- try:
- subprocess.Popen([cmd], shell=True)
- except:
- pass
+ env = None
+ if standalone is True:
+ env = {
+ "PYTHONPATH": os.path.abspath('../../../../lib/')}
+ try:
+ # We need to quote the command because subprocess call
+ # will do "sh -c 'foo'", so if we do not quoute it we'll end
+ # up with a invocation to the python interpreter. And that
+ # is bad.
+ subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
+ shell=True, env=env)
+ except Exception as exc:
+ logger.exception(exc)
class LinuxVPNLauncher(VPNLauncher):
@@ -313,7 +320,8 @@ class LinuxVPNLauncher(VPNLauncher):
"""
if _is_pkexec_in_system():
if not _is_auth_agent_running():
- _try_to_launch_agent()
+ _try_to_launch_agent(ProviderConfig.standalone)
+ sleep(0.5)
if _is_auth_agent_running():
pkexec_possibilities = which(kls.PKEXEC_BIN)
leap_assert(len(pkexec_possibilities) > 0,
@@ -414,14 +422,22 @@ class LinuxVPNLauncher(VPNLauncher):
if openvpn_verb is not None:
args += ['--verb', '%d' % (openvpn_verb,)]
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
+ gateways = []
+ leap_settings = LeapSettings(ProviderConfig.standalone)
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
if not gateways:
logger.error('No gateway was found!')
raise VPNLauncherException(self.tr('No gateway was found!'))
- logger.debug("Using gateways ips: {}".format(', '.join(gateways)))
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
for gw in gateways:
args += ['--remote', gw, '1194', 'udp']
@@ -669,11 +685,22 @@ class DarwinVPNLauncher(VPNLauncher):
if openvpn_verb is not None:
args += ['--verb', '%d' % (openvpn_verb,)]
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
+ gateways = []
+ leap_settings = LeapSettings(ProviderConfig.standalone)
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException(self.tr('No gateway was found!'))
- logger.debug("Using gateways ips: {gw}".format(
- gw=', '.join(gateways)))
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
for gw in gateways:
args += ['--remote', gw, '1194', 'udp']
@@ -841,10 +868,22 @@ class WindowsVPNLauncher(VPNLauncher):
if openvpn_verb is not None:
args += ['--verb', '%d' % (openvpn_verb,)]
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
+ gateways = []
+ leap_settings = LeapSettings(ProviderConfig.standalone)
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException(self.tr('No gateway was found!'))
- logger.debug("Using gateways ips: {}".format(', '.join(gateways)))
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
for gw in gateways:
args += ['--remote', gw, '1194', 'udp']
diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
index 2419fc0d..3bbfea85 100644
--- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py
+++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py
@@ -159,8 +159,7 @@ class SoledadBootstrapper(AbstractBootstrapper):
self.soledad_timeout.emit()
except socket.error as exc:
logger.error("Socket error while initializing soledad")
- if exc.errno in (111, ):
- self.soledad_failed.emit()
+ self.soledad_failed.emit()
except u1db_errors.Unauthorized:
logger.error("Error while initializing soledad "
"(unauthorized).")
diff --git a/src/leap/bitmask/util/__init__.py b/src/leap/bitmask/util/__init__.py
index ce8323cd..78efcb6e 100644
--- a/src/leap/bitmask/util/__init__.py
+++ b/src/leap/bitmask/util/__init__.py
@@ -15,59 +15,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
-Initializes version and app info, plus some small and handy functions.
+Some small and handy functions.
"""
import datetime
import os
-from pkg_resources import parse_version
-
-
-def _is_release_version(version):
- """
- Helper to determine whether a version is a final release or not.
- The release needs to be of the form: w.x.y.z containing only numbers
- and dots.
-
- :param version: the version string
- :type version: str
- :returns: if the version is a release version or not.
- :rtype: bool
- """
- parsed_version = parse_version(version)
- not_number = 0
- for x in parsed_version:
- try:
- int(x)
- except:
- not_number += 1
-
- return not_number == 1
-
-
-__version__ = "unknown"
-IS_RELEASE_VERSION = False
-
-try:
- from leap.bitmask._version import get_versions
- __version__ = get_versions()['version']
- IS_RELEASE_VERSION = _is_release_version(__version__)
- del get_versions
-except ImportError:
- #running on a tree that has not run
- #the setup.py setver
- pass
-
-__appname__ = "unknown"
-try:
- from leap._appname import __appname__
-except ImportError:
- #running on a tree that has not run
- #the setup.py setver
- pass
-
-__full_version__ = __appname__ + '/' + str(__version__)
-
def first(things):
"""
@@ -75,7 +27,7 @@ def first(things):
"""
try:
return things[0]
- except TypeError:
+ except (IndexError, TypeError):
return None
diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py
index 71f5163d..bc21a9cf 100644
--- a/src/leap/bitmask/util/leap_argparse.py
+++ b/src/leap/bitmask/util/leap_argparse.py
@@ -14,10 +14,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
+"""
+Parses the command line arguments passed to the application.
+"""
import argparse
-from leap.bitmask.util import IS_RELEASE_VERSION
+from leap.bitmask import IS_RELEASE_VERSION
def build_parser():
diff --git a/src/leap/bitmask/util/leap_log_handler.py b/src/leap/bitmask/util/leap_log_handler.py
index 9adb21a5..98924c12 100644
--- a/src/leap/bitmask/util/leap_log_handler.py
+++ b/src/leap/bitmask/util/leap_log_handler.py
@@ -90,6 +90,9 @@ class HandlerAdapter(object):
def setLevel(self, *args, **kwargs):
return self._handler.setLevel(*args, **kwargs)
+ def addFilter(self, *args, **kwargs):
+ return self._handler.addFilter(*args, **kwargs)
+
def handle(self, *args, **kwargs):
return self._handler.handle(*args, **kwargs)
diff --git a/src/leap/bitmask/util/log_silencer.py b/src/leap/bitmask/util/log_silencer.py
new file mode 100644
index 00000000..09aa2cff
--- /dev/null
+++ b/src/leap/bitmask/util/log_silencer.py
@@ -0,0 +1,95 @@
+# -*- coding: utf-8 -*-
+# log_silencer.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Filter for leap logs.
+"""
+import logging
+import os
+import re
+
+from leap.common.config import get_path_prefix
+
+
+class SelectiveSilencerFilter(logging.Filter):
+ """
+ Configurable filter for root leap logger.
+
+ If you want to ignore components from the logging, just add them,
+ one by line, to ~/.config/leap/leap.dev.conf
+ """
+ # TODO we can augment this by properly parsing the log-silencer file
+ # and having different sections: ignore, levels, ...
+
+ # TODO use ConfigParser to unify sections [log-ignore] [log-debug] etc
+
+ CONFIG_NAME = "leap.dev.conf"
+
+ # Components to be completely silenced in the main bitmask logs.
+ # You probably should think twice before adding a component to
+ # the tuple below. Only very well tested components should go here, and
+ # only in those cases in which we gain more from silencing them than from
+ # having their logs into the main log file that the user will likely send
+ # to us.
+ SILENCER_RULES = (
+ 'leap.common.events',
+ )
+
+ def __init__(self, standalone=False):
+ """
+ Tries to load silencer rules from the default path,
+ or load from the SILENCER_RULES tuple if not found.
+ """
+ self.standalone = standalone
+ self.rules = None
+ if os.path.isfile(self._rules_path):
+ self.rules = self._load_rules()
+ if not self.rules:
+ self.rules = self.SILENCER_RULES
+
+ @property
+ def _rules_path(self):
+ """
+ The configuration file for custom ignore rules.
+ """
+ return os.path.join(
+ get_path_prefix(standalone=self.standalone),
+ "leap", self.CONFIG_NAME)
+
+ def _load_rules(self):
+ """
+ Loads a list of paths to be ignored from the logging.
+ """
+ lines = open(self._rules_path).readlines()
+ return map(lambda line: re.sub('\s', '', line),
+ lines)
+
+ def filter(self, record):
+ """
+ Implements the filter functionality for this Filter
+
+ :param record: the record to be examined
+ :type record: logging.LogRecord
+ :returns: a bool indicating whether the record should be logged or not.
+ :rtype: bool
+ """
+ if not self.rules:
+ return True
+ logger_path = record.name
+ for path in self.rules:
+ if logger_path.startswith(path):
+ return False
+ return True
diff --git a/src/leap/bitmask/util/password.py b/src/leap/bitmask/util/password.py
new file mode 100644
index 00000000..73659f0d
--- /dev/null
+++ b/src/leap/bitmask/util/password.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+# password.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Password utilities
+"""
+from PySide import QtCore
+
+WEAK_PASSWORDS = ("123456", "qweasd", "qwerty", "password")
+
+
+def basic_password_checks(username, password, password2):
+ """
+ Performs basic password checks to avoid really easy passwords.
+
+ :param username: username provided at the registrarion form
+ :type username: str
+ :param password: password from the registration form
+ :type password: str
+ :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)
+ """
+ # translation helper
+ _tr = QtCore.QObject().tr
+
+ message = None
+
+ if message is None and password != password2:
+ message = _tr("Passwords don't match")
+
+ if message is None and len(password) < 6:
+ message = _tr("Password too short")
+
+ if message is None and password in WEAK_PASSWORDS:
+ message = _tr("Password too easy")
+
+ if message is None and username == password:
+ message = _tr("Password equal to username")
+
+ return message is None, message
diff --git a/src/leap/bitmask/util/polkit_agent.py b/src/leap/bitmask/util/polkit_agent.py
new file mode 100644
index 00000000..6fda2f88
--- /dev/null
+++ b/src/leap/bitmask/util/polkit_agent.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# polkit_agent.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Daemonizes polkit authentication agent.
+"""
+import logging
+import subprocess
+
+import daemon
+
+logger = logging.getLogger(__name__)
+
+AUTH_FILE = "polkit-%s-authentication-agent-1"
+BASE_PATH_GNO = "/usr/lib/policykit-1-gnome/"
+BASE_PATH_KDE = "/usr/lib/kde4/libexec/"
+GNO_PATH = BASE_PATH_GNO + AUTH_FILE % ("gnome",)
+KDE_PATH = BASE_PATH_KDE + AUTH_FILE % ("kde",)
+
+
+def _launch_agent():
+ logger.debug('Launching polkit auth agent')
+ try:
+ subprocess.call(GNO_PATH)
+ except Exception as exc:
+ logger.error('Exception while running polkit authentication agent '
+ '%s' % (exc,))
+ # XXX fix KDE launch. See: #3755
+ #try:
+ #subprocess.call(KDE_PATH)
+ #except Exception as exc:
+
+
+def launch():
+ with daemon.DaemonContext():
+ _launch_agent()
+
+if __name__ == "__main__":
+ launch()
diff --git a/src/leap/bitmask/util/reqs.txt b/src/leap/bitmask/util/reqs.txt
deleted file mode 100644
index 0bcf85dc..00000000
--- a/src/leap/bitmask/util/reqs.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-requests
-srp>=1.0.2
-pyopenssl
-keyring
-python-dateutil
-psutil
-ipaddr
-twisted
-qt4reactor
-python-gnupg
-leap.common>=0.2.5
-leap.soledad>=0.1.0
-mock
-oauth \ No newline at end of file
diff --git a/src/leap/bitmask/util/requirement_checker.py b/src/leap/bitmask/util/requirement_checker.py
index 1d9b9923..37e8e693 100644
--- a/src/leap/bitmask/util/requirement_checker.py
+++ b/src/leap/bitmask/util/requirement_checker.py
@@ -51,8 +51,9 @@ def get_requirements():
# if we are running from the package
if not develop:
- requires_file_name = os.path.join('leap', 'util', 'reqs.txt')
- dist_name = Requirement.parse('leap-client')
+ requires_file_name = os.path.join(
+ 'leap', 'bitmask', 'util', 'reqs.txt')
+ dist_name = Requirement.parse('leap.bitmask')
try:
with resource_stream(dist_name, requires_file_name) as stream:
diff --git a/src/leap/bitmask/util/tests/test_is_release_version.py b/src/leap/bitmask/util/tests/test_is_release_version.py
index 088ec66d..0a0093da 100644
--- a/src/leap/bitmask/util/tests/test_is_release_version.py
+++ b/src/leap/bitmask/util/tests/test_is_release_version.py
@@ -19,7 +19,7 @@ tests for _is_release_version function
"""
import unittest
-from leap.bitmask.util import _is_release_version as is_release_version
+from leap.bitmask import _is_release_version as is_release_version
from leap.common.testing.basetest import BaseLeapTest