summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/gui
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2014-04-04 16:58:18 -0300
committerTomás Touceda <chiiph@leap.se>2014-04-04 16:58:18 -0300
commit81715dc47d77934c4f67d2527a56c28f58f0345d (patch)
tree6e66940af735e089803c5ce05ad1ce1df16c9d1e /src/leap/bitmask/gui
parent496036f15cf257d16b6594770812da64a249280c (diff)
parenteb4cdab9c6b8ff66bb4667cc6195d2c366122540 (diff)
Merge branch 'release-0.5.0'
Diffstat (limited to 'src/leap/bitmask/gui')
-rw-r--r--src/leap/bitmask/gui/advanced_key_management.py38
-rw-r--r--src/leap/bitmask/gui/eip_preferenceswindow.py21
-rw-r--r--src/leap/bitmask/gui/eip_status.py10
-rw-r--r--src/leap/bitmask/gui/loggerwindow.py82
-rw-r--r--src/leap/bitmask/gui/login.py52
-rw-r--r--src/leap/bitmask/gui/mail_status.py55
-rw-r--r--src/leap/bitmask/gui/mainwindow.py725
-rw-r--r--src/leap/bitmask/gui/preferenceswindow.py10
-rw-r--r--src/leap/bitmask/gui/twisted_main.py18
-rw-r--r--src/leap/bitmask/gui/ui/advanced_key_management.ui79
-rw-r--r--src/leap/bitmask/gui/ui/eip_status.ui66
-rw-r--r--src/leap/bitmask/gui/ui/loggerwindow.ui15
-rw-r--r--src/leap/bitmask/gui/ui/login.ui45
-rw-r--r--src/leap/bitmask/gui/ui/mainwindow.ui195
-rw-r--r--src/leap/bitmask/gui/ui/wizard.ui90
-rw-r--r--src/leap/bitmask/gui/wizard.py244
16 files changed, 1113 insertions, 632 deletions
diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py
index 2c0fa034..cbc8c3e3 100644
--- a/src/leap/bitmask/gui/advanced_key_management.py
+++ b/src/leap/bitmask/gui/advanced_key_management.py
@@ -48,20 +48,25 @@ class AdvancedKeyManagement(QtGui.QWidget):
self.ui = Ui_AdvancedKeyManagement()
self.ui.setupUi(self)
+ # XXX: Temporarily disable the key import.
+ self.ui.pbImportKeys.setVisible(False)
+
# if Soledad is not started yet
if sameProxiedObjects(soledad, None):
- self.ui.container.setEnabled(False)
+ self.ui.gbMyKeyPair.setEnabled(False)
+ self.ui.gbStoredPublicKeys.setEnabled(False)
msg = self.tr("<span style='color:#0000FF;'>NOTE</span>: "
"To use this, you need to enable/start {0}.")
msg = msg.format(get_service_display_name(MX_SERVICE))
self.ui.lblStatus.setText(msg)
return
- else:
- msg = self.tr(
- "<span style='color:#ff0000;'>WARNING</span>:<br>"
- "This is an experimental feature, you can lose access to "
- "existing e-mails.")
- self.ui.lblStatus.setText(msg)
+ # XXX: since import is disabled this is no longer a dangerous feature.
+ # else:
+ # msg = self.tr(
+ # "<span style='color:#ff0000;'>WARNING</span>:<br>"
+ # "This is an experimental feature, you can lose access to "
+ # "existing e-mails.")
+ # self.ui.lblStatus.setText(msg)
self._keymanager = keymanager
self._soledad = soledad
@@ -79,6 +84,12 @@ class AdvancedKeyManagement(QtGui.QWidget):
self.ui.pbImportKeys.clicked.connect(self._import_keys)
self.ui.pbExportKeys.clicked.connect(self._export_keys)
+ # Stretch columns to content
+ self.ui.twPublicKeys.horizontalHeader().setResizeMode(
+ 0, QtGui.QHeaderView.Stretch)
+
+ self._list_keys()
+
def _import_keys(self):
"""
Imports the user's key pair.
@@ -183,3 +194,16 @@ class AdvancedKeyManagement(QtGui.QWidget):
return
else:
logger.debug('Export canceled by the user.')
+
+ def _list_keys(self):
+ """
+ Loads all the public keys stored in the local db to the keys table.
+ """
+ keys = self._keymanager.get_all_keys_in_local_db()
+
+ keys_table = self.ui.twPublicKeys
+ for key in keys:
+ row = keys_table.rowCount()
+ keys_table.insertRow(row)
+ keys_table.setItem(row, 0, QtGui.QTableWidgetItem(key.address))
+ keys_table.setItem(row, 1, QtGui.QTableWidgetItem(key.key_id))
diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py
index 504d1cf1..dcaa8b1e 100644
--- a/src/leap/bitmask/gui/eip_preferenceswindow.py
+++ b/src/leap/bitmask/gui/eip_preferenceswindow.py
@@ -22,7 +22,7 @@ import os
import logging
from functools import partial
-from PySide import QtGui
+from PySide import QtCore, QtGui
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
@@ -37,10 +37,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
"""
Window that displays the EIP preferences.
"""
- def __init__(self, parent):
+ def __init__(self, parent, domain):
"""
:param parent: parent object of the EIPPreferencesWindow.
- :parent type: QWidget
+ :type parent: QWidget
+ :param domain: the selected by default domain.
+ :type domain: unicode
"""
QtGui.QDialog.__init__(self, parent)
self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
@@ -59,7 +61,7 @@ class EIPPreferencesWindow(QtGui.QDialog):
self.ui.cbGateways.currentIndexChanged[unicode].connect(
lambda x: self.ui.lblProvidersGatewayStatus.setVisible(False))
- self._add_configured_providers()
+ self._add_configured_providers(domain)
def _set_providers_gateway_status(self, status, success=False,
error=False):
@@ -83,9 +85,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
self.ui.lblProvidersGatewayStatus.setVisible(True)
self.ui.lblProvidersGatewayStatus.setText(status)
- def _add_configured_providers(self):
+ def _add_configured_providers(self, domain=None):
"""
Add the client's configured providers to the providers combo boxes.
+
+ :param domain: the domain to be selected by default.
+ :type domain: unicode
"""
self.ui.cbProvidersGateway.clear()
providers = self._settings.get_configured_providers()
@@ -100,6 +105,12 @@ class EIPPreferencesWindow(QtGui.QDialog):
label = provider + self.tr(" (uninitialized)")
self.ui.cbProvidersGateway.addItem(label, userData=provider)
+ # Select provider by name
+ if domain is not None:
+ provider_index = self.ui.cbProvidersGateway.findText(
+ domain, QtCore.Qt.MatchStartsWith)
+ self.ui.cbProvidersGateway.setCurrentIndex(provider_index)
+
def _save_selected_gateway(self, provider):
"""
SLOT
diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py
index 4b4d360f..19942d9d 100644
--- a/src/leap/bitmask/gui/eip_status.py
+++ b/src/leap/bitmask/gui/eip_status.py
@@ -41,8 +41,8 @@ class EIPStatusWidget(QtGui.QWidget):
EIP Status widget that displays the current state of the EIP service
"""
DISPLAY_TRAFFIC_RATES = True
- RATE_STR = "%14.2f KB/s"
- TOTAL_STR = "%14.2f Kb"
+ RATE_STR = "%1.2f KB/s"
+ TOTAL_STR = "%1.2f Kb"
eip_connection_connected = QtCore.Signal()
@@ -248,10 +248,10 @@ class EIPStatusWidget(QtGui.QWidget):
Triggered when a default provider_config has not been found.
Disables the start button and adds instructions to the user.
"""
- logger.debug('Hiding EIP start button')
+ #logger.debug('Hiding EIP start button')
# you might be tempted to change this for a .setEnabled(False).
# it won't work. it's under the claws of the state machine.
- # probably the best thing would be to make a transitional
+ # probably the best thing would be to make a conditional
# transition there, but that's more involved.
self.eip_button.hide()
msg = self.tr("You must login to use {0}".format(self._service_name))
@@ -272,7 +272,7 @@ class EIPStatusWidget(QtGui.QWidget):
Triggered after a successful login.
Enables the start button.
"""
- logger.debug('Showing EIP start button')
+ #logger.debug('Showing EIP start button')
self.eip_button.show()
# Restore the eip action menu
diff --git a/src/leap/bitmask/gui/loggerwindow.py b/src/leap/bitmask/gui/loggerwindow.py
index 6ef58558..9f396574 100644
--- a/src/leap/bitmask/gui/loggerwindow.py
+++ b/src/leap/bitmask/gui/loggerwindow.py
@@ -22,10 +22,13 @@ import logging
import cgi
from PySide import QtGui
+from twisted.internet import threads
from ui_loggerwindow import Ui_LoggerWindow
+from leap.bitmask.util.constants import PASTEBIN_API_DEV_KEY
from leap.bitmask.util.leap_log_handler import LeapLogHandler
+from leap.bitmask.util.pastebin import PastebinAPI, PastebinError
from leap.common.check import leap_assert, leap_assert_type
logger = logging.getLogger(__name__)
@@ -42,6 +45,9 @@ class LoggerWindow(QtGui.QDialog):
:param handler: Custom handler that supports history and signal.
:type handler: LeapLogHandler.
"""
+ from twisted.internet import reactor
+ self.reactor = reactor
+
QtGui.QDialog.__init__(self)
leap_assert(handler, "We need a handler for the logger window")
leap_assert_type(handler, LeapLogHandler)
@@ -59,8 +65,10 @@ class LoggerWindow(QtGui.QDialog):
self.ui.btnCritical.toggled.connect(self._load_history)
self.ui.leFilterBy.textEdited.connect(self._filter_by)
self.ui.cbCaseInsensitive.stateChanged.connect(self._load_history)
+ self.ui.btnPastebin.clicked.connect(self._pastebin_this)
self._current_filter = ""
+ self._current_history = ""
# Load logging history and connect logger with the widget
self._logging_handler = handler
@@ -116,8 +124,13 @@ class LoggerWindow(QtGui.QDialog):
self._set_logs_to_display()
self.ui.txtLogHistory.clear()
history = self._logging_handler.log_history
+ current_history = []
for line in history:
self._add_log_line(line)
+ message = line[LeapLogHandler.MESSAGE_KEY]
+ current_history.append(message)
+
+ self._current_history = "\n".join(current_history)
def _set_logs_to_display(self):
"""
@@ -164,3 +177,72 @@ class LoggerWindow(QtGui.QDialog):
logger.error("Error saving log file: %r" % (e, ))
else:
logger.debug('Log not saved!')
+
+ def _set_pastebin_sending(self, sending):
+ """
+ Define the status of the pastebin button.
+ Change the text and enable/disable according to the current action.
+
+ :param sending: if we are sending to pastebin or not.
+ :type sending: bool
+ """
+ if sending:
+ self.ui.btnPastebin.setText(self.tr("Sending to pastebin..."))
+ self.ui.btnPastebin.setEnabled(False)
+ else:
+ self.ui.btnPastebin.setText(self.tr("Send to Pastebin.com"))
+ self.ui.btnPastebin.setEnabled(True)
+
+ def _pastebin_this(self):
+ """
+ Send the current log history to pastebin.com and gives the user a link
+ to see it.
+ """
+ def do_pastebin():
+ """
+ Send content to pastebin and return the link.
+ """
+ content = self._current_history
+ pb = PastebinAPI()
+ link = pb.paste(PASTEBIN_API_DEV_KEY, content,
+ paste_name="Bitmask log",
+ paste_expire_date='1W')
+
+ # convert to 'raw' link
+ link = "http://pastebin.com/raw.php?i=" + link.split('/')[-1]
+
+ return link
+
+ def pastebin_ok(link):
+ """
+ Callback handler for `do_pastebin`.
+
+ :param link: the recently created pastebin link.
+ :type link: str
+ """
+ msg = self.tr("Your pastebin link <a href='{0}'>{0}</a>")
+ msg = msg.format(link)
+ show_info = lambda: QtGui.QMessageBox.information(
+ self, self.tr("Pastebin OK"), msg)
+ self._set_pastebin_sending(False)
+ self.reactor.callLater(0, show_info)
+
+ def pastebin_err(failure):
+ """
+ Errback handler for `do_pastebin`.
+
+ :param failure: the failure that triggered the errback.
+ :type failure: twisted.python.failure.Failure
+ """
+ logger.error(repr(failure))
+ msg = self.tr("Sending logs to Pastebin failed!")
+ show_err = lambda: QtGui.QMessageBox.critical(
+ self, self.tr("Pastebin Error"), msg)
+ self._set_pastebin_sending(False)
+ self.reactor.callLater(0, show_err)
+ failure.trap(PastebinError)
+
+ self._set_pastebin_sending(True)
+ d = threads.deferToThread(do_pastebin)
+ d.addCallback(pastebin_ok)
+ d.addErrback(pastebin_err)
diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py
index b21057f0..4a483c32 100644
--- a/src/leap/bitmask/gui/login.py
+++ b/src/leap/bitmask/gui/login.py
@@ -19,12 +19,13 @@ Login widget implementation
"""
import logging
-import keyring
-
from PySide import QtCore, QtGui
from ui_login import Ui_LoginWidget
+from leap.bitmask.config import flags
+from leap.bitmask.util import make_address
from leap.bitmask.util.keyring_helpers import has_keyring
+from leap.bitmask.util.keyring_helpers import get_keyring
from leap.common.check import leap_assert_type
logger = logging.getLogger(__name__)
@@ -221,6 +222,15 @@ class LoginWidget(QtGui.QWidget):
self._set_cancel(not enabled)
+ def set_logout_btn_enabled(self, enabled):
+ """
+ Enables or disables the logout button.
+
+ :param enabled: wether they should be enabled or not
+ :type enabled: bool
+ """
+ self.ui.btnLogout.setEnabled(enabled)
+
def _set_cancel(self, enabled=False):
"""
Enables or disables the cancel action in the "log in" process.
@@ -304,14 +314,15 @@ class LoginWidget(QtGui.QWidget):
if self.get_remember() and has_keyring():
# in the keyring and in the settings
# we store the value 'usename@provider'
- username_domain = (username + '@' + provider).encode("utf8")
+ full_user_id = make_address(username, provider).encode("utf8")
try:
+ keyring = get_keyring()
keyring.set_password(self.KEYRING_KEY,
- username_domain,
+ full_user_id,
password.encode("utf8"))
# Only save the username if it was saved correctly in
# the keyring
- self._settings.set_user(username_domain)
+ self._settings.set_user(full_user_id)
except Exception as e:
logger.exception("Problem saving data to keyring. %r"
% (e,))
@@ -323,15 +334,19 @@ class LoginWidget(QtGui.QWidget):
"""
self.ui.login_widget.hide()
self.ui.logged_widget.show()
- self.ui.lblUser.setText("%s@%s" % (self.get_user(),
- self.get_selected_provider()))
- self.set_login_status("")
- self.logged_in_signal.emit()
+ self.ui.lblUser.setText(make_address(
+ self.get_user(), self.get_selected_provider()))
+
+ if flags.OFFLINE is False:
+ self.logged_in_signal.emit()
def logged_out(self):
"""
Sets the widgets to the logged out state
"""
+ # TODO consider "logging out offline" too...
+ # how that would be ???
+
self.ui.login_widget.show()
self.ui.logged_widget.hide()
@@ -339,27 +354,11 @@ class LoginWidget(QtGui.QWidget):
self.set_enabled(True)
self.set_status("", error=False)
- def set_login_status(self, msg, error=False):
- """
- Sets the status label for the logged in state.
-
- :param msg: status message
- :type msg: str or unicode
- :param error: if the status is an erroneous one, then set this
- to True
- :type error: bool
- """
- leap_assert_type(error, bool)
- if error:
- msg = "<font color='red'><b>%s</b></font>" % (msg,)
- self.ui.lblLoginStatus.setText(msg)
- self.ui.lblLoginStatus.show()
-
def start_logout(self):
"""
Sets the widgets to the logging out state
"""
- self.ui.btnLogout.setText(self.tr("Loggin out..."))
+ self.ui.btnLogout.setText(self.tr("Logging out..."))
self.ui.btnLogout.setEnabled(False)
def done_logout(self):
@@ -396,6 +395,7 @@ class LoginWidget(QtGui.QWidget):
saved_password = None
try:
+ keyring = get_keyring()
saved_password = keyring.get_password(self.KEYRING_KEY,
saved_user
.encode("utf8"))
diff --git a/src/leap/bitmask/gui/mail_status.py b/src/leap/bitmask/gui/mail_status.py
index 3c933c9a..44a138e2 100644
--- a/src/leap/bitmask/gui/mail_status.py
+++ b/src/leap/bitmask/gui/mail_status.py
@@ -112,6 +112,10 @@ class MailStatusWidget(QtGui.QWidget):
callback=self._mail_handle_imap_events,
reqcbk=lambda req, resp: None)
+ register(signal=proto.SOLEDAD_INVALID_AUTH_TOKEN,
+ callback=self.set_soledad_invalid_auth_token,
+ reqcbk=lambda req, resp: None)
+
self._soledad_event.connect(
self._mail_handle_soledad_events_slot)
self._imap_event.connect(
@@ -191,6 +195,17 @@ class MailStatusWidget(QtGui.QWidget):
msg = self.tr("There was an unexpected problem with Soledad.")
self._set_mail_status(msg, ready=-1)
+ def set_soledad_invalid_auth_token(self):
+ """
+ SLOT
+ TRIGGER:
+ SoledadBootstrapper.soledad_invalid_token
+
+ This method is called when the auth token is invalid
+ """
+ msg = self.tr("Invalid auth token, try logging in again.")
+ self._set_mail_status(msg, ready=-1)
+
def _set_mail_status(self, status, ready=0):
"""
Sets the Mail status in the label and in the tray icon.
@@ -213,7 +228,7 @@ class MailStatusWidget(QtGui.QWidget):
self._service_name))
elif ready == 1:
icon = self.CONNECTING_ICON
- self._mx_status = self.tr('Starting..')
+ self._mx_status = self.tr('Starting…')
tray_status = self.tr('Mail is starting')
elif ready >= 2:
icon = self.CONNECTED_ICON
@@ -362,10 +377,19 @@ class MailStatusWidget(QtGui.QWidget):
ext_status = None
if req.event == proto.IMAP_UNREAD_MAIL:
+ # By now, the semantics of the UNREAD_MAIL event are
+ # limited to mails with the Unread flag *in the Inbox".
+ # We could make this configurable to include all unread mail
+ # or all unread mail in subscribed folders.
if self._started:
- if req.content != "0":
- self._set_mail_status(self.tr("%s Unread Emails") %
- (req.content,), ready=2)
+ count = req.content
+ if count != "0":
+ status = self.tr("{0} Unread Emails "
+ "in your Inbox").format(count)
+ if count == "1":
+ status = self.tr("1 Unread Email in your Inbox")
+
+ self._set_mail_status(status, ready=2)
else:
self._set_mail_status("", ready=2)
elif req.event == proto.IMAP_SERVICE_STARTED:
@@ -375,7 +399,7 @@ class MailStatusWidget(QtGui.QWidget):
def about_to_start(self):
"""
- Displays the correct UI for the point where mail components
+ Display the correct UI for the point where mail components
haven't really started, but they are about to in a second.
"""
self._set_mail_status(self.tr("About to start, please wait..."),
@@ -383,7 +407,7 @@ class MailStatusWidget(QtGui.QWidget):
def set_disabled(self):
"""
- Displays the correct UI for disabled mail.
+ Display the correct UI for disabled mail.
"""
self._set_mail_status(self.tr("Disabled"), -1)
@@ -394,7 +418,7 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_disconnected(self):
"""
- Displays the correct UI for the disconnected state.
+ Display the correct UI for the disconnected state.
"""
# XXX this should handle the disabled state better.
self._started = False
@@ -406,7 +430,7 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_connecting(self):
"""
- Displays the correct UI for the connecting state.
+ Display the correct UI for the connecting state.
"""
self._disabled = False
self._started = True
@@ -415,23 +439,32 @@ class MailStatusWidget(QtGui.QWidget):
@QtCore.Slot()
def mail_state_disconnecting(self):
"""
- Displays the correct UI for the connecting state.
+ Display the correct UI for the connecting state.
"""
self._set_mail_status(self.tr("Disconnecting..."), 1)
@QtCore.Slot()
def mail_state_connected(self):
"""
- Displays the correct UI for the connected state.
+ Display the correct UI for the connected state.
"""
self._set_mail_status(self.tr("ON"), 2)
@QtCore.Slot()
def mail_state_disabled(self):
"""
- Displays the correct UI for the disabled state.
+ Display the correct UI for the disabled state.
"""
self._disabled = True
status = self.tr("You must be logged in to use {0}.").format(
self._service_name)
self._set_mail_status(status, -1)
+
+ @QtCore.Slot()
+ def soledad_invalid_auth_token(self):
+ """
+ Display the correct UI for the invalid token state
+ """
+ self._disabled = True
+ status = self.tr("Invalid auth token, try logging in again.")
+ self._set_mail_status(status, -1)
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 929919ac..5abfaa67 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# mainwindow.py
-# Copyright (C) 2013 LEAP
+# Copyright (C) 2013, 2014 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -18,16 +18,25 @@
Main window for Bitmask.
"""
import logging
-import os
+import socket
+
+from threading import Condition
+from datetime import datetime
from PySide import QtCore, QtGui
-from twisted.internet import threads
from zope.proxy import ProxyBase, setProxiedObject
+from twisted.internet import reactor, threads
+from twisted.internet.defer import CancelledError
from leap.bitmask import __version__ as VERSION
+from leap.bitmask import __version_hash__ as VERSION_HASH
+from leap.bitmask.config import flags
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
+
+from leap.bitmask.crypto import srpauth
from leap.bitmask.crypto.srpauth import SRPAuth
+
from leap.bitmask.gui.loggerwindow import LoggerWindow
from leap.bitmask.gui.advanced_key_management import AdvancedKeyManagement
from leap.bitmask.gui.login import LoginWidget
@@ -40,11 +49,12 @@ from leap.bitmask.gui.wizard import Wizard
from leap.bitmask.gui.systray import SysTray
from leap.bitmask import provider
-from leap.bitmask.platform_init import IS_WIN, IS_MAC
+from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX
from leap.bitmask.platform_init.initializers import init_platform
-from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_service_display_name, EIP_SERVICE
+from leap.bitmask import backend
+
+from leap.bitmask.services import get_service_display_name
from leap.bitmask.services.mail import conductor as mail_conductor
@@ -66,6 +76,7 @@ from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
+from leap.bitmask.util import make_address
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -77,6 +88,8 @@ from leap.common.check import leap_assert
from leap.common.events import register
from leap.common.events import events_pb2 as proto
+from leap.mail.imap.service.imap import IMAP_PORT
+
from ui_mainwindow import Ui_MainWindow
logger = logging.getLogger(__name__)
@@ -93,6 +106,7 @@ class MainWindow(QtGui.QMainWindow):
# Signals
eip_needs_login = QtCore.Signal([])
+ offline_mode_bypass_login = QtCore.Signal([])
new_updates = QtCore.Signal(object)
raise_window = QtCore.Signal([])
soledad_ready = QtCore.Signal([])
@@ -102,6 +116,12 @@ class MainWindow(QtGui.QMainWindow):
# We use this flag to detect abnormal terminations
user_stopped_eip = False
+ # We give EIP some time to come up before starting soledad anyway
+ EIP_TIMEOUT = 60000 # in milliseconds
+
+ # We give each service some time to come to a halt before forcing quit
+ SERVICE_STOP_TIMEOUT = 20
+
def __init__(self, quit_callback,
openvpn_verb=1,
bypass_checks=False):
@@ -132,11 +152,14 @@ class MainWindow(QtGui.QMainWindow):
# end register leap events ####################################
self._quit_callback = quit_callback
-
self._updates_content = ""
+ # setup UI
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
+ self.menuBar().setNativeMenuBar(not IS_LINUX)
+ self._backend = backend.Backend(bypass_checks)
+ self._backend.start()
self._settings = LeapSettings()
@@ -173,6 +196,11 @@ class MainWindow(QtGui.QMainWindow):
self._eip_status.eip_connection_connected.connect(
self._on_eip_connected)
+ self._eip_status.eip_connection_connected.connect(
+ self._maybe_run_soledad_setup_checks)
+ self.offline_mode_bypass_login.connect(
+ self._maybe_run_soledad_setup_checks)
+
self.eip_needs_login.connect(
self._eip_status.disable_eip_start)
self.eip_needs_login.connect(
@@ -180,36 +208,23 @@ class MainWindow(QtGui.QMainWindow):
# This is loaded only once, there's a bug when doing that more
# than once
- self._provider_config = ProviderConfig()
+ # XXX HACK!! But we need it as long as we are using
+ # provider_config in here
+ self._provider_config = (
+ self._backend._components["provider"]._provider_config)
# Used for automatic start of EIP
self._provisional_provider_config = ProviderConfig()
self._eip_config = eipconfig.EIPConfig()
self._already_started_eip = False
+ self._already_started_soledad = False
# This is created once we have a valid provider config
self._srp_auth = None
self._logged_user = None
+ self._logged_in_offline = False
- # This thread is always running, although it's quite
- # lightweight when it's done setting up provider
- # configuration and certificate.
- self._provider_bootstrapper = ProviderBootstrapper(bypass_checks)
-
- # Intermediate stages, only do something if there was an error
- self._provider_bootstrapper.name_resolution.connect(
- self._intermediate_stage)
- self._provider_bootstrapper.https_connection.connect(
- self._intermediate_stage)
- self._provider_bootstrapper.download_ca_cert.connect(
- self._intermediate_stage)
-
- # Important stages, loads the provider config and checks
- # certificates
- self._provider_bootstrapper.download_provider_info.connect(
- self._load_provider_config)
- self._provider_bootstrapper.check_api_certificate.connect(
- self._provider_config_loaded)
+ self._backend_connect()
# This thread is similar to the provider bootstrapper
self._eip_bootstrapper = EIPBootstrapper()
@@ -243,15 +258,23 @@ class MainWindow(QtGui.QMainWindow):
self._soledad_intermediate_stage)
self._soledad_bootstrapper.gen_key.connect(
self._soledad_bootstrapped_stage)
+ self._soledad_bootstrapper.local_only_ready.connect(
+ self._soledad_bootstrapped_stage)
self._soledad_bootstrapper.soledad_timeout.connect(
self._retry_soledad_connection)
+ self._soledad_bootstrapper.soledad_invalid_auth_token.connect(
+ self._mail_status.set_soledad_invalid_auth_token)
self._soledad_bootstrapper.soledad_failed.connect(
self._mail_status.set_soledad_failed)
+ self.ui.action_preferences.triggered.connect(self._show_preferences)
+ self.ui.action_eip_preferences.triggered.connect(
+ self._show_eip_preferences)
self.ui.action_about_leap.triggered.connect(self._about)
self.ui.action_quit.triggered.connect(self.quit)
self.ui.action_wizard.triggered.connect(self._launch_wizard)
self.ui.action_show_logs.triggered.connect(self._show_logger_window)
+ self.ui.action_help.triggered.connect(self._help)
self.ui.action_create_new_account.triggered.connect(
self._launch_wizard)
@@ -279,13 +302,15 @@ class MainWindow(QtGui.QMainWindow):
self._action_visible = QtGui.QAction(self.tr("Hide Main Window"), self)
self._action_visible.triggered.connect(self._toggle_visible)
- self.ui.btnPreferences.clicked.connect(self._show_preferences)
- self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences)
+ # disable buttons for now, may come back later.
+ # self.ui.btnPreferences.clicked.connect(self._show_preferences)
+ # self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences)
self._enabled_services = []
- self._center_window()
+ # last minute UI manipulations
+ self._center_window()
self.ui.lblNewUpdates.setVisible(False)
self.ui.btnMore.setVisible(False)
#########################################
@@ -294,6 +319,8 @@ class MainWindow(QtGui.QMainWindow):
self.ui.btnMore.resize(0, 0)
#########################################
self.ui.btnMore.clicked.connect(self._updates_details)
+ if flags.OFFLINE is True:
+ self._set_label_offline()
# Services signals/slots connection
self.new_updates.connect(self._react_to_new_updates)
@@ -323,7 +350,7 @@ class MainWindow(QtGui.QMainWindow):
self._keymanager = ProxyBase(None)
self._login_defer = None
- self._download_provider_defer = None
+ self._soledad_defer = None
self._mail_conductor = mail_conductor.MailConductor(
self._soledad, self._keymanager)
@@ -345,7 +372,9 @@ class MainWindow(QtGui.QMainWindow):
if self._first_run():
self._wizard_firstrun = True
- self._wizard = Wizard(bypass_checks=bypass_checks)
+ self._backend_disconnect()
+ self._wizard = Wizard(backend=self._backend,
+ bypass_checks=bypass_checks)
# Give this window time to finish init and then show the wizard
QtCore.QTimer.singleShot(1, self._launch_wizard)
self._wizard.accepted.connect(self._finish_init)
@@ -355,6 +384,42 @@ class MainWindow(QtGui.QMainWindow):
# so this has to be done after eip_machine is started
self._finish_init()
+ def _backend_connect(self):
+ """
+ Helper to connect to backend signals
+ """
+ sig = self._backend.signaler
+ sig.prov_name_resolution.connect(self._intermediate_stage)
+ sig.prov_https_connection.connect(self._intermediate_stage)
+ sig.prov_download_ca_cert.connect(self._intermediate_stage)
+
+ sig.prov_download_provider_info.connect(self._load_provider_config)
+ sig.prov_check_api_certificate.connect(self._provider_config_loaded)
+
+ # Only used at login, no need to disconnect this like we do
+ # with the other
+ sig.prov_problem_with_provider.connect(self._login_problem_provider)
+
+ sig.prov_unsupported_client.connect(self._needs_update)
+ sig.prov_unsupported_api.connect(self._incompatible_api)
+
+ sig.prov_cancelled_setup.connect(self._set_login_cancelled)
+
+ def _backend_disconnect(self):
+ """
+ Helper to disconnect from backend signals.
+
+ Some signals are emitted from the wizard, and we want to
+ ignore those.
+ """
+ sig = self._backend.signaler
+ sig.prov_name_resolution.disconnect(self._intermediate_stage)
+ sig.prov_https_connection.disconnect(self._intermediate_stage)
+ sig.prov_download_ca_cert.disconnect(self._intermediate_stage)
+
+ sig.prov_download_provider_info.disconnect(self._load_provider_config)
+ sig.prov_check_api_certificate.disconnect(self._provider_config_loaded)
+
def _rejected_wizard(self):
"""
SLOT
@@ -375,7 +440,9 @@ class MainWindow(QtGui.QMainWindow):
# This happens if the user finishes the provider
# setup but does not register
self._wizard = None
- self._finish_init()
+ self._backend_connect()
+ if self._wizard_firstrun:
+ self._finish_init()
def _launch_wizard(self):
"""
@@ -390,9 +457,11 @@ class MainWindow(QtGui.QMainWindow):
there.
"""
if self._wizard is None:
- self._wizard = Wizard(bypass_checks=self._bypass_checks)
+ self._backend_disconnect()
+ self._wizard = Wizard(backend=self._backend,
+ bypass_checks=self._bypass_checks)
self._wizard.accepted.connect(self._finish_init)
- self._wizard.rejected.connect(self._wizard.close)
+ self._wizard.rejected.connect(self._rejected_wizard)
self.setVisible(False)
# Do NOT use exec_, it will use a child event loop!
@@ -467,26 +536,66 @@ class MainWindow(QtGui.QMainWindow):
"""
SLOT
TRIGGERS:
- self.ui.btnPreferences.clicked
+ self.ui.btnPreferences.clicked (disabled for now)
+ self.ui.action_preferences
Displays the preferences window.
"""
- preferences_window = PreferencesWindow(
+ preferences = PreferencesWindow(
self, self._srp_auth, self._provider_config, self._soledad,
self._login_widget.get_selected_provider())
- self.soledad_ready.connect(preferences_window.set_soledad_ready)
- preferences_window.show()
+ self.soledad_ready.connect(preferences.set_soledad_ready)
+ preferences.show()
+ preferences.preferences_saved.connect(self._update_eip_enabled_status)
+
+ def _update_eip_enabled_status(self):
+ """
+ SLOT
+ TRIGGER:
+ PreferencesWindow.preferences_saved
+
+ Enable or disable the EIP start/stop actions and stop EIP if the user
+ disabled that service.
+
+ :returns: if the eip actions were enabled or disabled
+ :rtype: bool
+ """
+ settings = self._settings
+ default_provider = settings.get_defaultprovider()
+ enabled_services = []
+ if default_provider is not None:
+ enabled_services = settings.get_enabled_services(default_provider)
+
+ eip_enabled = False
+ if EIP_SERVICE in enabled_services:
+ should_autostart = settings.get_autostart_eip()
+ if should_autostart and default_provider is not None:
+ self._eip_status.enable_eip_start()
+ self._eip_status.set_eip_status("")
+ eip_enabled = True
+ else:
+ # we don't have an usable provider
+ # so the user needs to log in first
+ self._eip_status.disable_eip_start()
+ else:
+ self._stop_eip()
+ self._eip_status.disable_eip_start()
+ self._eip_status.set_eip_status(self.tr("Disabled"))
+
+ return eip_enabled
def _show_eip_preferences(self):
"""
SLOT
TRIGGERS:
self.ui.btnEIPPreferences.clicked
+ self.ui.action_eip_preferences (disabled for now)
Displays the EIP preferences window.
"""
- EIPPreferencesWindow(self).show()
+ domain = self._login_widget.get_selected_provider()
+ EIPPreferencesWindow(self, domain).show()
#
# updates
@@ -585,6 +694,7 @@ class MainWindow(QtGui.QMainWindow):
self.eip_needs_login.emit()
self._wizard = None
+ self._backend_connect()
else:
self._try_autostart_eip()
@@ -626,6 +736,19 @@ class MainWindow(QtGui.QMainWindow):
self.ui.eipWidget.setVisible(EIP_SERVICE in services)
self.ui.mailWidget.setVisible(MX_SERVICE in services)
+ def _set_label_offline(self):
+ """
+ Set the login label to reflect offline status.
+ """
+ if self._logged_in_offline:
+ provider = ""
+ else:
+ provider = self.ui.lblLoginProvider.text()
+
+ self.ui.lblLoginProvider.setText(
+ provider +
+ self.tr(" (offline mode)"))
+
#
# systray
#
@@ -748,10 +871,13 @@ class MainWindow(QtGui.QMainWindow):
Display the About Bitmask dialog
"""
+ today = datetime.now().date()
+ greet = ("Happy New 1984!... or not ;)<br><br>"
+ if today.month == 1 and today.day < 15 else "")
QtGui.QMessageBox.about(
self, self.tr("About Bitmask - %s") % (VERSION,),
- self.tr("Version: <b>%s</b><br>"
- "<br>"
+ self.tr("Version: <b>%s</b> (%s)<br>"
+ "<br>%s"
"Bitmask is the Desktop client application for "
"the LEAP platform, supporting encrypted internet "
"proxy, secure email, and secure chat (coming soon).<br>"
@@ -763,7 +889,58 @@ class MainWindow(QtGui.QMainWindow):
"and widely available. <br>"
"<br>"
"<a href='https://leap.se'>More about LEAP"
- "</a>") % (VERSION,))
+ "</a>") % (VERSION, VERSION_HASH[:10], greet))
+
+ def _help(self):
+ """
+ SLOT
+ TRIGGERS: self.ui.action_help.triggered
+
+ Display the Bitmask help dialog.
+ """
+ # TODO: don't hardcode!
+ smtp_port = 2013
+
+ url = ("<a href='https://addons.mozilla.org/es/thunderbird/"
+ "addon/bitmask/'>bitmask addon</a>")
+
+ msg = self.tr(
+ "<strong>Instructions to use mail:</strong><br>"
+ "If you use Thunderbird you can use the Bitmask extension helper. "
+ "Search for 'Bitmask' in the add-on manager or download it "
+ "from: {0}.<br><br>"
+ "You can configure Bitmask manually with these options:<br>"
+ "<em>"
+ " Incoming -> IMAP, port: {1}<br>"
+ " Outgoing -> SMTP, port: {2}<br>"
+ " Username -> your bitmask username.<br>"
+ " Password -> does not matter, use any text. "
+ " Just don't leave it empty and don't use your account's password."
+ "</em>").format(url, IMAP_PORT, smtp_port)
+ QtGui.QMessageBox.about(self, self.tr("Bitmask Help"), msg)
+
+ def _needs_update(self):
+ """
+ Display a warning dialog to inform the user that the app needs update.
+ """
+ url = "https://dl.bitmask.net/"
+ msg = self.tr(
+ "The current client version is not supported "
+ "by this provider.<br>"
+ "Please update to latest version.<br><br>"
+ "You can get the latest version from "
+ "<a href='{0}'>{1}</a>").format(url, url)
+ QtGui.QMessageBox.warning(self, self.tr("Update Needed"), msg)
+
+ def _incompatible_api(self):
+ """
+ Display a warning dialog to inform the user that the provider has an
+ incompatible API.
+ """
+ msg = self.tr(
+ "This provider is not compatible with the client.<br><br>"
+ "Error: API version incompatible.")
+ QtGui.QMessageBox.warning(self, self.tr("Incompatible Provider"), msg)
def changeEvent(self, e):
"""
@@ -812,51 +989,36 @@ class MainWindow(QtGui.QMainWindow):
"""
# XXX should rename this provider, name clash.
provider = self._login_widget.get_selected_provider()
-
- pb = self._provider_bootstrapper
- d = pb.run_provider_select_checks(provider, download_if_needed=True)
- self._download_provider_defer = d
+ self._backend.setup_provider(provider)
def _load_provider_config(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.download_provider_info
+ TRIGGER: self._backend.signaler.prov_download_provider_info
Once the provider config has been downloaded, this loads the
self._provider_config instance with it and starts the second
part of the bootstrapping sequence
:param data: result from the last stage of the
- run_provider_select_checks
+ run_provider_select_checks
:type data: dict
"""
- if data[self._provider_bootstrapper.PASSED_KEY]:
- # XXX should rename this provider, name clash.
- provider = self._login_widget.get_selected_provider()
-
- # If there's no loaded provider or
- # we want to connect to other provider...
- if (not self._provider_config.loaded() or
- self._provider_config.get_domain() != provider):
- self._provider_config.load(
- os.path.join("leap", "providers",
- provider, "provider.json"))
-
- if self._provider_config.loaded():
- self._provider_bootstrapper.run_provider_setup_checks(
- self._provider_config,
- download_if_needed=True)
- else:
- self._login_widget.set_status(
- self.tr("Unable to login: Problem with provider"))
- logger.error("Could not load provider configuration.")
- self._login_widget.set_enabled(True)
+ if data[self._backend.PASSED_KEY]:
+ selected_provider = self._login_widget.get_selected_provider()
+ self._backend.provider_bootstrap(selected_provider)
else:
- self._login_widget.set_status(
- self.tr("Unable to login: Problem with provider"))
- logger.error(data[self._provider_bootstrapper.ERROR_KEY])
+ logger.error(data[self._backend.ERROR_KEY])
self._login_widget.set_enabled(True)
+ def _login_problem_provider(self):
+ """
+ Warns the user about a problem with the provider during login.
+ """
+ self._login_widget.set_status(
+ self.tr("Unable to login: Problem with provider"))
+ self._login_widget.set_enabled(True)
+
def _login(self):
"""
SLOT
@@ -868,10 +1030,57 @@ class MainWindow(QtGui.QMainWindow):
start the SRP authentication, and as the last step
bootstrapping the EIP service
"""
- leap_assert(self._provider_config, "We need a provider config")
+ # TODO most of this could ve handled by the login widget,
+ # but we'd have to move lblLoginProvider into the widget itself,
+ # instead of having it as a top-level attribute.
+ if flags.OFFLINE is True:
+ logger.debug("OFFLINE mode! bypassing remote login")
+ # TODO reminder, we're not handling logout for offline
+ # mode.
+ self._login_widget.logged_in()
+ self._logged_in_offline = True
+ self._set_label_offline()
+ self.offline_mode_bypass_login.emit()
+ else:
+ leap_assert(self._provider_config, "We need a provider config")
+ if self._login_widget.start_login():
+ self._download_provider_config()
- if self._login_widget.start_login():
- self._download_provider_config()
+ def _login_errback(self, failure):
+ """
+ Error handler for the srpauth.authenticate method.
+
+ :param failure: failure object that Twisted generates
+ :type failure: twisted.python.failure.Failure
+ """
+ # NOTE: this behavior needs to be managed through the signaler,
+ # as we are doing with the prov_cancelled_setup signal.
+ # After we move srpauth to the backend, we need to update this.
+ logger.error("Error logging in, {0!r}".format(failure))
+
+ if failure.check(CancelledError):
+ logger.debug("Defer cancelled.")
+ failure.trap(Exception)
+ self._set_login_cancelled()
+ return
+ elif failure.check(srpauth.SRPAuthBadUserOrPassword):
+ msg = self.tr("Invalid username or password.")
+ elif failure.check(srpauth.SRPAuthBadStatusCode,
+ srpauth.SRPAuthenticationError,
+ srpauth.SRPAuthVerificationFailed,
+ srpauth.SRPAuthNoSessionId,
+ srpauth.SRPAuthNoSalt, srpauth.SRPAuthNoB,
+ srpauth.SRPAuthBadDataFromServer,
+ srpauth.SRPAuthJSONDecodeError):
+ msg = self.tr("There was a server problem with authentication.")
+ elif failure.check(srpauth.SRPAuthConnectionError):
+ msg = self.tr("Could not establish a connection.")
+ else:
+ # this shouldn't happen, but just in case.
+ msg = self.tr("Unknown error: {0!r}".format(failure.value))
+
+ self._login_widget.set_status(msg)
+ self._login_widget.set_enabled(True)
def _cancel_login(self):
"""
@@ -882,28 +1091,48 @@ class MainWindow(QtGui.QMainWindow):
Stops the login sequence.
"""
logger.debug("Cancelling log in.")
+ self._cancel_ongoing_defers()
- if self._download_provider_defer:
- logger.debug("Cancelling download provider defer.")
- self._download_provider_defer.cancel()
+ def _cancel_ongoing_defers(self):
+ """
+ Cancel the running defers to avoid app blocking.
+ """
+ self._backend.cancel_setup_provider()
- if self._login_defer:
+ if self._login_defer is not None:
logger.debug("Cancelling login defer.")
self._login_defer.cancel()
+ self._login_defer = None
+ if self._soledad_defer is not None:
+ logger.debug("Cancelling soledad defer.")
+ self._soledad_defer.cancel()
+ self._soledad_defer = None
+
+ def _set_login_cancelled(self):
+ """
+ SLOT
+ TRIGGERS:
+ Signaler.prov_cancelled_setup fired by
+ self._backend.cancel_setup_provider()
+
+ This method re-enables the login widget and display a message for
+ the cancelled operation.
+ """
self._login_widget.set_status(self.tr("Log in cancelled by the user."))
+ self._login_widget.set_enabled(True)
def _provider_config_loaded(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.check_api_certificate
+ TRIGGER: self._backend.signaler.prov_check_api_certificate
Once the provider configuration is loaded, this starts the SRP
authentication
"""
leap_assert(self._provider_config, "We need a provider config!")
- if data[self._provider_bootstrapper.PASSED_KEY]:
+ if data[self._backend.PASSED_KEY]:
username = self._login_widget.get_user()
password = self._login_widget.get_password()
@@ -913,18 +1142,18 @@ class MainWindow(QtGui.QMainWindow):
self._srp_auth = SRPAuth(self._provider_config)
self._srp_auth.authentication_finished.connect(
self._authentication_finished)
- self._srp_auth.logout_finished.connect(
- self._done_logging_out)
+ self._srp_auth.logout_ok.connect(self._logout_ok)
+ self._srp_auth.logout_error.connect(self._logout_error)
- # TODO Add errback!
self._login_defer = self._srp_auth.authenticate(username, password)
+ self._login_defer.addErrback(self._login_errback)
else:
self._login_widget.set_status(
"Unable to login: Problem with provider")
- logger.error(data[self._provider_bootstrapper.ERROR_KEY])
+ logger.error(data[self._backend.ERROR_KEY])
self._login_widget.set_enabled(True)
- def _authentication_finished(self, ok, message):
+ def _authentication_finished(self):
"""
SLOT
TRIGGER: self._srp_auth.authentication_finished
@@ -932,22 +1161,23 @@ class MainWindow(QtGui.QMainWindow):
Once the user is properly authenticated, try starting the EIP
service
"""
- # In general we want to "filter" likely complicated error
- # messages, but in this case, the messages make more sense as
- # they come. Since they are "Unknown user" or "Unknown
- # password"
- self._login_widget.set_status(message, error=not ok)
-
- if ok:
- self._logged_user = self._login_widget.get_user()
- user = self._logged_user
- domain = self._provider_config.get_domain()
- userid = "%s@%s" % (user, domain)
- self._mail_conductor.userid = userid
- self._login_defer = None
- self._start_eip_bootstrap()
- else:
- self._login_widget.set_enabled(True)
+ self._login_widget.set_status(self.tr("Succeeded"), error=False)
+
+ self._logged_user = self._login_widget.get_user()
+ user = self._logged_user
+ domain = self._provider_config.get_domain()
+ full_user_id = make_address(user, domain)
+ self._mail_conductor.userid = full_user_id
+ self._login_defer = None
+ self._start_eip_bootstrap()
+
+ # if soledad/mail is enabled:
+ if MX_SERVICE in self._enabled_services:
+ btn_enabled = self._login_widget.set_logout_btn_enabled
+ btn_enabled(False)
+ self.soledad_ready.connect(lambda: btn_enabled(True))
+ self._soledad_bootstrapper.soledad_failed.connect(
+ lambda: btn_enabled(True))
def _start_eip_bootstrap(self):
"""
@@ -956,28 +1186,83 @@ class MainWindow(QtGui.QMainWindow):
"""
self._login_widget.logged_in()
- self.ui.lblLoginProvider.setText(self._provider_config.get_name())
+ provider = self._provider_config.get_domain()
+ self.ui.lblLoginProvider.setText(provider)
self._enabled_services = self._settings.get_enabled_services(
self._provider_config.get_domain())
# TODO separate UI from logic.
- # TODO soledad should check if we want to run only over EIP.
- if self._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
+ if self._provides_mx_and_enabled():
self._mail_status.about_to_start()
-
- self._soledad_bootstrapper.run_soledad_setup_checks(
- self._provider_config,
- self._login_widget.get_user(),
- self._login_widget.get_password(),
- download_if_needed=True)
else:
self._mail_status.set_disabled()
- # XXX the config should be downloaded from the start_eip
- # method.
- self._download_eip_config()
+ self._maybe_start_eip()
+
+ def _provides_mx_and_enabled(self):
+ """
+ Defines if the current provider provides mx and if we have it enabled.
+
+ :returns: True if provides and is enabled, False otherwise
+ :rtype: bool
+ """
+ provider_config = self._get_best_provider_config()
+ return (provider_config.provides_mx() and
+ MX_SERVICE in self._enabled_services)
+
+ def _provides_eip_and_enabled(self):
+ """
+ Defines if the current provider provides eip and if we have it enabled.
+
+ :returns: True if provides and is enabled, False otherwise
+ :rtype: bool
+ """
+ provider_config = self._get_best_provider_config()
+ return (provider_config.provides_eip() and
+ EIP_SERVICE in self._enabled_services)
+
+ def _maybe_run_soledad_setup_checks(self):
+ """
+ Conditionally start Soledad.
+ """
+ # TODO split.
+ if self._already_started_soledad is True:
+ return
+
+ if not self._provides_mx_and_enabled():
+ return
+
+ username = self._login_widget.get_user()
+ password = unicode(self._login_widget.get_password())
+ provider_domain = self._login_widget.get_selected_provider()
+
+ sb = self._soledad_bootstrapper
+ if flags.OFFLINE is True:
+ provider_domain = self._login_widget.get_selected_provider()
+ sb._password = password
+
+ self._provisional_provider_config.load(
+ provider.get_provider_path(provider_domain))
+
+ full_user_id = make_address(username, provider_domain)
+ uuid = self._settings.get_uuid(full_user_id)
+ self._mail_conductor.userid = full_user_id
+
+ if uuid is None:
+ # We don't need more visibility at the moment,
+ # this is mostly for internal use/debug for now.
+ logger.warning("Sorry! Log-in at least one time.")
+ return
+ fun = sb.load_offline_soledad
+ fun(full_user_id, password, uuid)
+ else:
+ provider_config = self._provider_config
+
+ if self._logged_user is not None:
+ self._soledad_defer = sb.run_soledad_setup_checks(
+ provider_config, username, password,
+ download_if_needed=True)
###################################################################
# Service control methods: soledad
@@ -1010,8 +1295,9 @@ class MainWindow(QtGui.QMainWindow):
logger.debug("Retrying soledad connection.")
if self._soledad_bootstrapper.should_retry_initialization():
self._soledad_bootstrapper.increment_retries_count()
- threads.deferToThread(
- self._soledad_bootstrapper.load_and_sync_soledad)
+ # XXX should cancel the existing socket --- this
+ # is avoiding a clean termination.
+ self._maybe_run_soledad_setup_checks()
else:
logger.warning("Max number of soledad initialization "
"retries reached.")
@@ -1021,6 +1307,7 @@ class MainWindow(QtGui.QMainWindow):
SLOT
TRIGGERS:
self._soledad_bootstrapper.gen_key
+ self._soledad_bootstrapper.local_only_ready
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
@@ -1047,6 +1334,7 @@ class MainWindow(QtGui.QMainWindow):
# Ok, now soledad is ready, so we can allow other things that
# depend on soledad to start.
+ self._soledad_defer = None
# this will trigger start_imap_service
# and start_smtp_boostrapping
@@ -1062,10 +1350,13 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.soledad_ready
"""
+ if flags.OFFLINE is True:
+ logger.debug("not starting smtp in offline mode")
+ return
+
# TODO for simmetry, this should be called start_smtp_service
# (and delegate all the checks to the conductor)
- if self._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
+ if self._provides_mx_and_enabled():
self._mail_conductor.smtp_bootstrapper.run_smtp_setup_checks(
self._provider_config,
self._mail_conductor.smtp_config,
@@ -1093,9 +1384,22 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.soledad_ready
"""
- if self._provider_config.provides_mx() and \
- self._enabled_services.count(MX_SERVICE) > 0:
- self._mail_conductor.start_imap_service()
+ # TODO in the OFFLINE mode we should also modify the rules
+ # in the mail state machine so it shows that imap is active
+ # (but not smtp since it's not yet ready for offline use)
+ start_fun = self._mail_conductor.start_imap_service
+ if flags.OFFLINE is True:
+ provider_domain = self._login_widget.get_selected_provider()
+ self._provider_config.load(
+ provider.get_provider_path(provider_domain))
+ provides_mx = self._provider_config.provides_mx()
+
+ if flags.OFFLINE is True and provides_mx:
+ start_fun()
+ return
+
+ if self._provides_mx_and_enabled():
+ start_fun()
def _on_mail_client_logged_in(self, req):
"""
@@ -1120,8 +1424,13 @@ class MainWindow(QtGui.QMainWindow):
TRIGGERS:
self.logout
"""
+ cv = Condition()
+ cv.acquire()
# TODO call stop_mail_service
- self._mail_conductor.stop_imap_service()
+ threads.deferToThread(self._mail_conductor.stop_imap_service, cv)
+ # and wait for it to be stopped
+ logger.debug('Waiting for imap service to stop.')
+ cv.wait(self.SERVICE_STOP_TIMEOUT)
# end service control methods (imap)
@@ -1171,27 +1480,65 @@ class MainWindow(QtGui.QMainWindow):
"""
self._eip_connection.qtsigs.connected_signal.emit()
+ # check for connectivity
+ provider_config = self._get_best_provider_config()
+ domain = provider_config.get_domain()
+ self._check_name_resolution(domain)
+
+ def _check_name_resolution(self, domain):
+ """
+ Check if we can resolve the given domain name.
+
+ :param domain: the domain to check.
+ :type domain: str
+ """
+ def do_check():
+ """
+ Try to resolve the domain name.
+ """
+ socket.gethostbyname(domain.encode('idna'))
+
+ def check_err(failure):
+ """
+ Errback handler for `do_check`.
+
+ :param failure: the failure that triggered the errback.
+ :type failure: twisted.python.failure.Failure
+ """
+ logger.error(repr(failure))
+ logger.error("Can't resolve hostname.")
+
+ msg = self.tr(
+ "The server at {0} can't be found, because the DNS lookup "
+ "failed. DNS is the network service that translates a "
+ "website's name to its Internet address. Either your computer "
+ "is having trouble connecting to the network, or you are "
+ "missing some helper files that are needed to securely use "
+ "DNS while {1} is active. To install these helper files, quit "
+ "this application and start it again."
+ ).format(domain, self._eip_name)
+
+ show_err = lambda: QtGui.QMessageBox.critical(
+ self, self.tr("Connection Error"), msg)
+ reactor.callLater(0, show_err)
+
+ # python 2.7.4 raises socket.error
+ # python 2.7.5 raises socket.gaierror
+ failure.trap(socket.gaierror, socket.error)
+
+ d = threads.deferToThread(do_check)
+ d.addErrback(check_err)
+
def _try_autostart_eip(self):
"""
Tries to autostart EIP
"""
settings = self._settings
- should_autostart = settings.get_autostart_eip()
- if not should_autostart:
- logger.debug('Will not autostart EIP since it is setup '
- 'to not to do it')
- self.eip_needs_login.emit()
+ if not self._update_eip_enabled_status():
return
default_provider = settings.get_defaultprovider()
-
- if default_provider is None:
- logger.info("Cannot autostart Encrypted Internet because there is "
- "no default provider configured")
- self.eip_needs_login.emit()
- return
-
self._enabled_services = settings.get_enabled_services(
default_provider)
@@ -1202,7 +1549,7 @@ class MainWindow(QtGui.QMainWindow):
# it adds some delay.
# Maybe if it's the first run in a session,
# or we can try only if it fails.
- self._download_eip_config()
+ self._maybe_start_eip()
else:
# XXX: Display a proper message to the user
self.eip_needs_login.emit()
@@ -1336,8 +1683,9 @@ class MainWindow(QtGui.QMainWindow):
if self._logged_user:
self._eip_status.set_provider(
- "%s@%s" % (self._logged_user,
- self._get_best_provider_config().get_domain()))
+ make_address(
+ self._logged_user,
+ self._get_best_provider_config().get_domain()))
self._eip_status.eip_stopped()
@QtCore.Slot()
@@ -1434,18 +1782,16 @@ class MainWindow(QtGui.QMainWindow):
# eip boostrapping, config etc...
- def _download_eip_config(self):
+ def _maybe_start_eip(self):
"""
- Starts the EIP bootstrapping sequence
+ Start the EIP bootstrapping sequence if the client is configured to
+ do so.
"""
leap_assert(self._eip_bootstrapper, "We need an eip bootstrapper!")
provider_config = self._get_best_provider_config()
- if provider_config.provides_eip() and \
- self._enabled_services.count(EIP_SERVICE) > 0 and \
- not self._already_started_eip:
-
+ if self._provides_eip_and_enabled() and not self._already_started_eip:
# XXX this should be handled by the state machine.
self._eip_status.set_eip_status(
self.tr("Starting..."))
@@ -1453,14 +1799,22 @@ class MainWindow(QtGui.QMainWindow):
provider_config,
download_if_needed=True)
self._already_started_eip = True
- elif not self._already_started_eip:
- if self._enabled_services.count(EIP_SERVICE) > 0:
- self._eip_status.set_eip_status(
- self.tr("Not supported"),
- error=True)
- else:
- self._eip_status.disable_eip_start()
- self._eip_status.set_eip_status(self.tr("Disabled"))
+ # we want to start soledad anyway after a certain timeout if eip
+ # fails to come up
+ QtCore.QTimer.singleShot(
+ self.EIP_TIMEOUT,
+ self._maybe_run_soledad_setup_checks)
+ else:
+ if not self._already_started_eip:
+ if EIP_SERVICE in self._enabled_services:
+ self._eip_status.set_eip_status(
+ self.tr("Not supported"),
+ error=True)
+ else:
+ self._eip_status.disable_eip_start()
+ self._eip_status.set_eip_status(self.tr("Disabled"))
+ # eip will not start, so we start soledad anyway
+ self._maybe_run_soledad_setup_checks()
def _finish_eip_bootstrap(self, data):
"""
@@ -1506,11 +1860,11 @@ class MainWindow(QtGui.QMainWindow):
This is used for intermediate bootstrapping stages, in case
they fail.
"""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if not passed:
self._login_widget.set_status(
self.tr("Unable to connect: Problem with provider"))
- logger.error(data[self._provider_bootstrapper.ERROR_KEY])
+ logger.error(data[self._backend.ERROR_KEY])
self._already_started_eip = False
# end of EIP methods ---------------------------------------------
@@ -1547,20 +1901,35 @@ class MainWindow(QtGui.QMainWindow):
Starts the logout sequence
"""
-
self._soledad_bootstrapper.cancel_bootstrap()
setProxiedObject(self._soledad, None)
+ self._cancel_ongoing_defers()
+
+ # reset soledad status flag
+ self._already_started_soledad = False
+
# 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):
- # TODO missing params in docstring
+ def _logout_error(self):
+ """
+ SLOT
+ TRIGGER: self._srp_auth.logout_error
+
+ Inform the user about a logout error.
+ """
+ self._login_widget.done_logout()
+ self.ui.lblLoginProvider.setText(self.tr("Login"))
+ self._login_widget.set_status(
+ self.tr("Something went wrong with the logout."))
+
+ def _logout_ok(self):
"""
SLOT
- TRIGGER: self._srp_auth.logout_finished
+ TRIGGER: self._srp_auth.logout_ok
Switches the stackedWidget back to the login stage after
logging out
@@ -1568,36 +1937,30 @@ class MainWindow(QtGui.QMainWindow):
self._login_widget.done_logout()
self.ui.lblLoginProvider.setText(self.tr("Login"))
- if ok:
- self._logged_user = None
- self._login_widget.logged_out()
- self._mail_status.mail_state_disabled()
-
- else:
- self._login_widget.set_login_status(
- self.tr("Something went wrong with the logout."),
- error=True)
+ self._logged_user = None
+ self._login_widget.logged_out()
+ self._mail_status.mail_state_disabled()
def _intermediate_stage(self, data):
# TODO this method name is confusing as hell.
"""
SLOT
TRIGGERS:
- self._provider_bootstrapper.name_resolution
- self._provider_bootstrapper.https_connection
- self._provider_bootstrapper.download_ca_cert
+ self._backend.signaler.prov_name_resolution
+ self._backend.signaler.prov_https_connection
+ self._backend.signaler.prov_download_ca_cert
self._eip_bootstrapper.download_config
If there was a problem, displays it, otherwise it does nothing.
This is used for intermediate bootstrapping stages, in case
they fail.
"""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if not passed:
+ msg = self.tr("Unable to connect: Problem with provider")
+ self._login_widget.set_status(msg)
self._login_widget.set_enabled(True)
- self._login_widget.set_status(
- self.tr("Unable to connect: Problem with provider"))
- logger.error(data[self._provider_bootstrapper.ERROR_KEY])
+ logger.error(data[self._backend.ERROR_KEY])
#
# window handling methods
@@ -1647,7 +2010,7 @@ class MainWindow(QtGui.QMainWindow):
"""
logger.debug('About to quit, doing cleanup...')
- self._mail_conductor.stop_imap_service()
+ self._stop_imap_service()
if self._srp_auth is not None:
if self._srp_auth.get_session_id() is not None or \
@@ -1664,13 +2027,7 @@ class MainWindow(QtGui.QMainWindow):
logger.debug('Terminating vpn')
self._vpn.terminate(shutdown=True)
- if self._login_defer:
- logger.debug("Cancelling login defer.")
- self._login_defer.cancel()
-
- if self._download_provider_defer:
- logger.debug("Cancelling download provider defer.")
- self._download_provider_defer.cancel()
+ self._cancel_ongoing_defers()
# TODO missing any more cancels?
@@ -1687,8 +2044,8 @@ class MainWindow(QtGui.QMainWindow):
# Set this in case that the app is hidden
QtGui.QApplication.setQuitOnLastWindowClosed(True)
+ self._backend.stop()
self._cleanup_and_quit()
-
self._really_quit = True
if self._wizard:
diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py
index b4bddef2..b2cc2236 100644
--- a/src/leap/bitmask/gui/preferenceswindow.py
+++ b/src/leap/bitmask/gui/preferenceswindow.py
@@ -18,7 +18,6 @@
"""
Preferences window
"""
-import os
import logging
from functools import partial
@@ -26,6 +25,7 @@ from functools import partial
from PySide import QtCore, QtGui
from zope.proxy import sameProxiedObjects
+from leap.bitmask.provider import get_provider_path
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.soledad.client import NoStorageSecret
@@ -42,6 +42,8 @@ class PreferencesWindow(QtGui.QDialog):
"""
Window that displays the preferences.
"""
+ preferences_saved = QtCore.Signal()
+
def __init__(self, parent, srp_auth, provider_config, soledad, domain):
"""
:param parent: parent object of the PreferencesWindow.
@@ -369,6 +371,7 @@ class PreferencesWindow(QtGui.QDialog):
"Services settings for provider '{0}' saved.".format(provider))
logger.debug(msg)
self._set_providers_services_status(msg, success=True)
+ self.preferences_saved.emit()
def _get_provider_config(self, domain):
"""
@@ -380,10 +383,7 @@ class PreferencesWindow(QtGui.QDialog):
: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):
+ if not provider_config.load(get_provider_path(domain)):
provider_config = None
return provider_config
diff --git a/src/leap/bitmask/gui/twisted_main.py b/src/leap/bitmask/gui/twisted_main.py
index e11af7bd..1e876c57 100644
--- a/src/leap/bitmask/gui/twisted_main.py
+++ b/src/leap/bitmask/gui/twisted_main.py
@@ -27,24 +27,6 @@ from twisted.internet import error
logger = logging.getLogger(__name__)
-def start(app):
- """
- Start the mainloop.
-
- :param app: the main qt QApplication instance.
- :type app: QtCore.QApplication
- """
- from twisted.internet import reactor
- logger.debug('starting twisted reactor')
-
- # this seems to be troublesome under some
- # unidentified settings.
- #reactor.run()
-
- reactor.runReturn()
- app.exec_()
-
-
def quit(app):
"""
Stop the mainloop.
diff --git a/src/leap/bitmask/gui/ui/advanced_key_management.ui b/src/leap/bitmask/gui/ui/advanced_key_management.ui
index d61aa87e..1112670f 100644
--- a/src/leap/bitmask/gui/ui/advanced_key_management.ui
+++ b/src/leap/bitmask/gui/ui/advanced_key_management.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>431</width>
- <height>188</height>
+ <width>504</width>
+ <height>546</height>
</rect>
</property>
<property name="windowTitle">
@@ -17,10 +17,13 @@
<iconset resource="../../../../../data/resources/mainwindow.qrc">
<normaloff>:/images/mask-icon.png</normaloff>:/images/mask-icon.png</iconset>
</property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <widget class="QWidget" name="container" native="true">
- <layout class="QGridLayout" name="gridLayout_2">
+ <layout class="QGridLayout" name="gridLayout_4">
+ <item row="1" column="0" colspan="2">
+ <widget class="QGroupBox" name="gbMyKeyPair">
+ <property name="title">
+ <string>My key pair</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
@@ -90,20 +93,7 @@
</property>
</widget>
</item>
- <item row="3" column="1">
- <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="4" column="0" colspan="2">
+ <item row="3" column="0" colspan="2">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QPushButton" name="pbExportKeys">
@@ -135,9 +125,56 @@
</layout>
</item>
</layout>
+ <zorder>leKeyID</zorder>
+ <zorder>leUser</zorder>
+ <zorder>leFingerprint</zorder>
+ <zorder>label_3</zorder>
+ <zorder>label_5</zorder>
+ <zorder>label</zorder>
+ </widget>
+ </item>
+ <item row="2" column="0" colspan="2">
+ <widget class="QGroupBox" name="gbStoredPublicKeys">
+ <property name="title">
+ <string>Stored public keys</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_2">
+ <item row="0" column="0">
+ <widget class="QTableWidget" name="twPublicKeys">
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="selectionMode">
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior">
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideRight</enum>
+ </property>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Email</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Key ID</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ </layout>
</widget>
</item>
- <item row="1" column="0">
+ <item row="3" column="0" colspan="2">
<widget class="QLabel" name="lblStatus">
<property name="text">
<string/>
diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui
index d078ca0c..64821ad6 100644
--- a/src/leap/bitmask/gui/ui/eip_status.ui
+++ b/src/leap/bitmask/gui/ui/eip_status.ui
@@ -25,6 +25,9 @@
</property>
<item>
<layout class="QGridLayout" name="gridLayout">
+ <property name="verticalSpacing">
+ <number>0</number>
+ </property>
<item row="0" column="2">
<widget class="QPushButton" name="btnEipStartStop">
<property name="text">
@@ -75,13 +78,6 @@
<verstretch>0</verstretch>
</sizepolicy>
</property>
- <property name="font">
- <font>
- <pointsize>14</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="text">
<string>Traffic is being routed in the clear</string>
</property>
@@ -124,12 +120,6 @@
</item>
<item row="2" column="1" colspan="3">
<widget class="QWidget" name="eip_bandwidth" native="true">
- <property name="maximumSize">
- <size>
- <width>16777215</width>
- <height>32</height>
- </size>
- </property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
@@ -146,16 +136,6 @@
<enum>QLayout::SetDefaultConstraint</enum>
</property>
<item>
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/down-arrow.png</pixmap>
- </property>
- </widget>
- </item>
- <item>
<widget class="QPushButton" name="btnDownload">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@@ -175,25 +155,18 @@
<height>16777215</height>
</size>
</property>
- <property name="font">
- <font>
- <pointsize>11</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
- <property name="styleSheet">
- <string notr="true">text-align: left;</string>
- </property>
<property name="text">
<string>0.0 KB/s</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
+ <property name="icon">
+ <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/down-arrow.png</pixmap>
+ </property>
</widget>
</item>
<item>
@@ -206,22 +179,12 @@
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>10</width>
+ <width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
- <item>
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string/>
- </property>
- <property name="pixmap">
- <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/up-arrow.png</pixmap>
- </property>
- </widget>
- </item>
<item alignment="Qt::AlignLeft">
<widget class="QPushButton" name="btnUpload">
<property name="sizePolicy">
@@ -242,25 +205,18 @@
<height>16777215</height>
</size>
</property>
- <property name="font">
- <font>
- <pointsize>11</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
<property name="cursor">
<cursorShape>PointingHandCursor</cursorShape>
</property>
- <property name="styleSheet">
- <string notr="true">text-align: left;</string>
- </property>
<property name="text">
<string>0.0 KB/s</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
+ <property name="icon">
+ <pixmap resource="../../../../../data/resources/icons.qrc">:/images/light/16/up-arrow.png</pixmap>
+ </property>
</widget>
</item>
<item>
@@ -271,7 +227,7 @@
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
- <height>20</height>
+ <height>0</height>
</size>
</property>
</spacer>
diff --git a/src/leap/bitmask/gui/ui/loggerwindow.ui b/src/leap/bitmask/gui/ui/loggerwindow.ui
index 3de786f7..b19ed91a 100644
--- a/src/leap/bitmask/gui/ui/loggerwindow.ui
+++ b/src/leap/bitmask/gui/ui/loggerwindow.ui
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
- <width>648</width>
- <height>469</height>
+ <width>769</width>
+ <height>464</height>
</rect>
</property>
<property name="windowTitle">
@@ -154,6 +154,17 @@
</property>
</widget>
</item>
+ <item>
+ <widget class="QPushButton" name="btnPastebin">
+ <property name="text">
+ <string>Send to Pastebin.com</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../../../../data/resources/loggerwindow.qrc">
+ <normaloff>:/images/pastebin.png</normaloff>:/images/pastebin.png</iconset>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
</layout>
diff --git a/src/leap/bitmask/gui/ui/login.ui b/src/leap/bitmask/gui/ui/login.ui
index e7ca1652..f5725d5a 100644
--- a/src/leap/bitmask/gui/ui/login.ui
+++ b/src/leap/bitmask/gui/ui/login.ui
@@ -215,28 +215,8 @@
<number>0</number>
</property>
<property name="bottomMargin">
- <number>24</number>
+ <number>0</number>
</property>
- <item row="1" column="1">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="0">
- <widget class="QPushButton" name="btnLogout">
- <property name="text">
- <string>Logout</string>
- </property>
- </widget>
- </item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="lblUser">
<property name="font">
@@ -251,17 +231,26 @@
</property>
</widget>
</item>
- <item row="2" column="0" colspan="2">
- <widget class="QLabel" name="lblLoginStatus">
- <property name="styleSheet">
- <string notr="true">color: rgb(132, 132, 132);
-font: 75 12pt &quot;Lucida Grande&quot;;</string>
- </property>
+ <item row="1" column="0">
+ <widget class="QPushButton" name="btnLogout">
<property name="text">
- <string/>
+ <string>Logout</string>
</property>
</widget>
</item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui
index 3b83788e..d755115a 100644
--- a/src/leap/bitmask/gui/ui/mainwindow.ui
+++ b/src/leap/bitmask/gui/ui/mainwindow.ui
@@ -75,7 +75,7 @@
<x>0</x>
<y>0</y>
<width>524</width>
- <height>651</height>
+ <height>667</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@@ -86,108 +86,6 @@
<number>0</number>
</property>
<item>
- <widget class="QWidget" name="eipWidget" native="true">
- <layout class="QVBoxLayout" name="verticalLayout_2">
- <property name="spacing">
- <number>0</number>
- </property>
- <property name="margin">
- <number>0</number>
- </property>
- <item>
- <widget class="QFrame" name="frame_2">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="styleSheet">
- <string notr="true">QFrame{background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0));}</string>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
- <property name="leftMargin">
- <number>24</number>
- </property>
- <property name="rightMargin">
- <number>24</number>
- </property>
- <item>
- <widget class="QLabel" name="label_2">
- <property name="font">
- <font>
- <pointsize>16</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="styleSheet">
- <string notr="true">background-color: rgba(255, 255, 255, 0);</string>
- </property>
- <property name="text">
- <string>Encrypted Internet</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="btnEIPPreferences">
- <property name="maximumSize">
- <size>
- <width>48</width>
- <height>20</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
- </property>
- <property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="../../../../../data/resources/mainwindow.qrc">
- <normaloff>:/images/black/32/gear.png</normaloff>:/images/black/32/gear.png</iconset>
- </property>
- <property name="autoDefault">
- <bool>false</bool>
- </property>
- <property name="default">
- <bool>false</bool>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <layout class="QVBoxLayout" name="eipLayout">
- <property name="leftMargin">
- <number>12</number>
- </property>
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="rightMargin">
- <number>12</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="Line" name="line">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- <item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -199,9 +97,7 @@
<bool>false</bool>
</property>
<property name="styleSheet">
- <string notr="true">QFrame{
-background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0));
-}</string>
+ <string notr="true">background-color: rgba(0,0,0,20); border-bottom: 1px solid rgba(0,0,0,30);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
@@ -214,36 +110,15 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
<widget class="QLabel" name="lblLoginProvider">
<property name="font">
<font>
- <pointsize>16</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
- <string notr="true">background-color: rgba(255, 255, 255, 0);</string>
- </property>
- <property name="text">
- <string>Login</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="btnPreferences">
- <property name="maximumSize">
- <size>
- <width>48</width>
- <height>20</height>
- </size>
- </property>
- <property name="styleSheet">
- <string notr="true"/>
+ <string notr="true">background-color: rgba(255, 255, 255, 0); border: none;</string>
</property>
<property name="text">
- <string/>
- </property>
- <property name="icon">
- <iconset resource="../../../../../data/resources/mainwindow.qrc">
- <normaloff>:/images/black/32/gear.png</normaloff>:/images/black/32/gear.png</iconset>
+ <string>Please Log In</string>
</property>
</widget>
</item>
@@ -258,7 +133,42 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
</layout>
</item>
<item>
- <widget class="Line" name="line_2">
+ <widget class="Line" name="lineUnderLogin">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="eipWidget" native="true">
+ <layout class="QVBoxLayout" name="eipVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="eipLayout">
+ <property name="leftMargin">
+ <number>12</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>12</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="Line" name="lineUnderEIP">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
@@ -287,6 +197,13 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
</widget>
</item>
<item>
+ <widget class="Line" name="lineUnderEmail">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
@@ -380,7 +297,7 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
<x>0</x>
<y>0</y>
<width>524</width>
- <height>21</height>
+ <height>23</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -390,11 +307,14 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
<addaction name="action_create_new_account"/>
<addaction name="action_advanced_key_management"/>
<addaction name="separator"/>
+ <addaction name="action_preferences"/>
+ <addaction name="action_eip_preferences"/>
+ <addaction name="separator"/>
<addaction name="action_quit"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
- <string>Help</string>
+ <string>&amp;Help</string>
</property>
<addaction name="action_help"/>
<addaction name="action_show_logs"/>
@@ -404,10 +324,17 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
- <widget class="QStatusBar" name="statusbar"/>
<action name="action_preferences">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Account Preferences...</string>
+ </property>
+ </action>
+ <action name="action_eip_preferences">
<property name="text">
- <string>Preferences...</string>
+ <string>Internet Preferences...</string>
</property>
</action>
<action name="action_quit">
diff --git a/src/leap/bitmask/gui/ui/wizard.ui b/src/leap/bitmask/gui/ui/wizard.ui
index cf591470..6c592522 100644
--- a/src/leap/bitmask/gui/ui/wizard.ui
+++ b/src/leap/bitmask/gui/ui/wizard.ui
@@ -59,7 +59,7 @@
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
- <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;/p&gt;&lt;p&gt;If you ever need to modify these options again, you can find the wizard in the &lt;span style=&quot; font-style:italic;&quot;&gt;'Settings'&lt;/span&gt; menu from the main window.&lt;/p&gt;&lt;p&gt;Do you want to &lt;span style=&quot; font-weight:600;&quot;&gt;sign up&lt;/span&gt; for a new account, or &lt;span style=&quot; font-weight:600;&quot;&gt;log in&lt;/span&gt; with an already existing username?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Now we will guide you through some configuration that is needed before you can connect for the first time.&lt;/p&gt;&lt;p&gt;If you ever need to modify these options again, you can find the wizard in the &lt;span style=&quot; font-style:italic;&quot;&gt;'Bitmask -&amp;gt; Create new account...'&lt;/span&gt; menu from the main window.&lt;/p&gt;&lt;p&gt;Do you want to &lt;span style=&quot; font-weight:600;&quot;&gt;sign up&lt;/span&gt; for a new account, or &lt;span style=&quot; font-weight:600;&quot;&gt;log in&lt;/span&gt; with an already existing username?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
@@ -269,7 +269,7 @@
<string>Configure or select a provider</string>
</property>
<layout class="QGridLayout" name="gridLayout_5">
- <item row="0" column="0">
+ <item row="1" column="0">
<widget class="QRadioButton" name="rbNewProvider">
<property name="text">
<string>Configure new provider:</string>
@@ -279,14 +279,14 @@
</property>
</widget>
</item>
- <item row="2" column="0">
- <widget class="QRadioButton" name="rbExistingProvider">
- <property name="text">
- <string>Use existing one:</string>
+ <item row="0" column="2">
+ <widget class="QComboBox" name="cbProviders">
+ <property name="enabled">
+ <bool>false</bool>
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="1" column="1">
<widget class="QLabel" name="label">
<property name="text">
<string>https://</string>
@@ -296,17 +296,20 @@
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="1" column="2">
<widget class="QLineEdit" name="lnProvider"/>
</item>
- <item row="1" column="2">
- <widget class="QPushButton" name="btnCheck">
+ <item row="0" column="0">
+ <widget class="QRadioButton" name="rbExistingProvider">
<property name="text">
- <string>Check</string>
+ <string>Use existing one:</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
</property>
</widget>
</item>
- <item row="3" column="0">
+ <item row="0" column="1">
<widget class="QLabel" name="label_8">
<property name="text">
<string>https://</string>
@@ -316,12 +319,29 @@
</property>
</widget>
</item>
- <item row="3" column="1">
- <widget class="QComboBox" name="cbProviders">
- <property name="enabled">
- <bool>false</bool>
- </property>
- </widget>
+ <item row="2" column="2">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnCheck">
+ <property name="text">
+ <string>Check</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
</item>
</layout>
</widget>
@@ -820,8 +840,8 @@
<slot>setFocus()</slot>
<hints>
<hint type="sourcelabel">
- <x>167</x>
- <y>192</y>
+ <x>174</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
<x>265</x>
@@ -836,12 +856,12 @@
<slot>setFocus()</slot>
<hints>
<hint type="sourcelabel">
- <x>171</x>
- <y>164</y>
+ <x>174</x>
+ <y>227</y>
</hint>
<hint type="destinationlabel">
- <x>246</x>
- <y>164</y>
+ <x>425</x>
+ <y>254</y>
</hint>
</hints>
</connection>
@@ -852,12 +872,12 @@
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>169</x>
- <y>196</y>
+ <x>174</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
- <x>327</x>
- <y>163</y>
+ <x>450</x>
+ <y>266</y>
</hint>
</hints>
</connection>
@@ -868,8 +888,8 @@
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>169</x>
- <y>162</y>
+ <x>174</x>
+ <y>227</y>
</hint>
<hint type="destinationlabel">
<x>269</x>
@@ -881,15 +901,15 @@
<sender>rbExistingProvider</sender>
<signal>toggled(bool)</signal>
<receiver>btnCheck</receiver>
- <slot>setDisabled(bool)</slot>
+ <slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
- <x>154</x>
- <y>193</y>
+ <x>169</x>
+ <y>174</y>
</hint>
<hint type="destinationlabel">
- <x>498</x>
- <y>170</y>
+ <x>520</x>
+ <y>255</y>
</hint>
</hints>
</connection>
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index 5f5224ae..e2c1a16e 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -17,22 +17,17 @@
"""
First run wizard
"""
-import os
import logging
-import json
import random
from functools import partial
from PySide import QtCore, QtGui
-from twisted.internet import threads
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.crypto.srpregister import SRPRegister
-from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.provider import get_provider_path
from leap.bitmask.services import get_service_display_name, get_supported
-from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
@@ -55,12 +50,15 @@ class Wizard(QtGui.QWizard):
BARE_USERNAME_REGEX = r"^[A-Za-z\d_]+$"
- def __init__(self, bypass_checks=False):
+ def __init__(self, backend, bypass_checks=False):
"""
Constructor for the main Wizard.
+ :param backend: Backend being used
+ :type backend: Backend
:param bypass_checks: Set to true if the app should bypass
- first round of checks for CA certificates at bootstrap
+ first round of checks for CA
+ certificates at bootstrap
:type bypass_checks: bool
"""
QtGui.QWizard.__init__(self)
@@ -86,23 +84,14 @@ class Wizard(QtGui.QWizard):
self.ui.btnCheck.clicked.connect(self._check_provider)
self.ui.lnProvider.returnPressed.connect(self._check_provider)
- self._provider_bootstrapper = ProviderBootstrapper(bypass_checks)
- self._provider_bootstrapper.name_resolution.connect(
- self._name_resolution)
- self._provider_bootstrapper.https_connection.connect(
- self._https_connection)
- self._provider_bootstrapper.download_provider_info.connect(
- self._download_provider_info)
-
- self._provider_bootstrapper.download_ca_cert.connect(
- self._download_ca_cert)
- self._provider_bootstrapper.check_ca_fingerprint.connect(
- self._check_ca_fingerprint)
- self._provider_bootstrapper.check_api_certificate.connect(
- self._check_api_certificate)
+ self._backend = backend
+ self._backend_connect()
self._domain = None
- self._provider_config = ProviderConfig()
+ # HACK!! We need provider_config for the time being, it'll be
+ # removed
+ self._provider_config = (
+ self._backend._components["provider"]._provider_config)
# We will store a reference to the defers for eventual use
# (eg, to cancel them) but not doing anything with them right now.
@@ -114,6 +103,8 @@ class Wizard(QtGui.QWizard):
self.ui.lnProvider.textChanged.connect(self._enable_check)
self.ui.rbNewProvider.toggled.connect(
lambda x: self._enable_check())
+ self.ui.cbProviders.currentIndexChanged[int].connect(
+ self._reset_provider_check)
self.ui.lblUser.returnPressed.connect(
self._focus_password)
@@ -166,6 +157,7 @@ class Wizard(QtGui.QWizard):
self._provider_setup_ok = False
self.ui.lnProvider.setText('')
self.ui.grpCheckProvider.setVisible(False)
+ self._backend_disconnect()
def _load_configured_providers(self):
"""
@@ -199,6 +191,10 @@ class Wizard(QtGui.QWizard):
random.shuffle(pinned) # don't prioritize alphabetically
self.ui.cbProviders.addItems(pinned)
+ # We have configured providers, so by default we select the
+ # 'Use existing provider' option.
+ self.ui.rbExistingProvider.setChecked(True)
+
def get_domain(self):
return self._domain
@@ -225,7 +221,7 @@ class Wizard(QtGui.QWizard):
depending on the lnProvider content.
"""
enabled = len(self.ui.lnProvider.text()) != 0
- enabled = enabled and self.ui.rbNewProvider.isChecked()
+ enabled = enabled or self.ui.rbExistingProvider.isChecked()
self.ui.btnCheck.setEnabled(enabled)
if reset:
@@ -255,16 +251,11 @@ class Wizard(QtGui.QWizard):
ok, msg = basic_password_checks(username, password, password2)
if ok:
- register = SRPRegister(provider_config=self._provider_config)
- register.registration_finished.connect(
- self._registration_finished)
-
- threads.deferToThread(
- partial(register.register_user, username, password))
+ self._set_register_status(self.tr("Starting registration..."))
+ self._backend.register_user(self._domain, username, password)
self._username = username
self._password = password
- self._set_register_status(self.tr("Starting registration..."))
else:
self._set_register_status(msg, error=True)
self._focus_password()
@@ -291,42 +282,59 @@ class Wizard(QtGui.QWizard):
# register button
self.ui.btnRegister.setVisible(visible)
- def _registration_finished(self, ok, req):
- if ok:
- user_domain = self._username + "@" + self._domain
- message = "<font color='green'><h3>"
- message += self.tr("User %s successfully registered.") % (
- user_domain, )
- message += "</h3></font>"
- self._set_register_status(message)
-
- self.ui.lblPassword2.clearFocus()
- self._set_registration_fields_visibility(False)
-
- # Allow the user to remember his password
- if has_keyring():
- self.ui.chkRemember.setVisible(True)
- self.ui.chkRemember.setEnabled(True)
-
- self.page(self.REGISTER_USER_PAGE).set_completed()
- self.button(QtGui.QWizard.BackButton).setEnabled(False)
- else:
- old_username = self._username
- self._username = None
- self._password = None
- error_msg = self.tr("Something has gone wrong. "
- "Please try again.")
- try:
- content, _ = get_content(req)
- json_content = json.loads(content)
- error_msg = json_content.get("errors").get("login")[0]
- if not error_msg.istitle():
- error_msg = "%s %s" % (old_username, error_msg)
- except Exception as e:
- logger.error("Unknown error: %r" % (e,))
-
- self._set_register_status(error_msg, error=True)
- self.ui.btnRegister.setEnabled(True)
+ def _registration_finished(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_finished
+
+ The registration has finished successfully, so we do some final steps.
+ """
+ user_domain = self._username + "@" + self._domain
+ message = "<font color='green'><h3>"
+ message += self.tr("User %s successfully registered.") % (
+ user_domain, )
+ message += "</h3></font>"
+ self._set_register_status(message)
+
+ self.ui.lblPassword2.clearFocus()
+ self._set_registration_fields_visibility(False)
+
+ # Allow the user to remember his password
+ if has_keyring():
+ self.ui.chkRemember.setVisible(True)
+ self.ui.chkRemember.setEnabled(True)
+
+ self.page(self.REGISTER_USER_PAGE).set_completed()
+ self.button(QtGui.QWizard.BackButton).setEnabled(False)
+
+ def _registration_failed(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_failed
+
+ The registration has failed, so we report the problem.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("Something has gone wrong. Please try again.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
+
+ def _registration_taken(self):
+ """
+ SLOT
+ TRIGGERS:
+ self._backend.signaler.srp_registration_taken
+
+ The requested username is taken, warn the user about that.
+ """
+ self._username = self._password = None
+
+ error_msg = self.tr("The requested username is taken, choose another.")
+ self._set_register_status(error_msg, error=True)
+ self.ui.btnRegister.setEnabled(True)
def _set_register_status(self, status, error=False):
"""
@@ -369,8 +377,10 @@ class Wizard(QtGui.QWizard):
Starts the checks for a given provider
"""
- if len(self.ui.lnProvider.text()) == 0:
- return
+ if self.ui.rbNewProvider.isChecked():
+ self._domain = self.ui.lnProvider.text()
+ else:
+ self._domain = self.ui.cbProviders.currentText()
self._provider_checks_ok = False
@@ -382,11 +392,10 @@ class Wizard(QtGui.QWizard):
self.ui.btnCheck.setEnabled(False)
self.ui.lnProvider.setEnabled(False)
self.button(QtGui.QWizard.BackButton).clearFocus()
- self._domain = self.ui.lnProvider.text()
self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON)
- self._provider_select_defer = self._provider_bootstrapper.\
- run_provider_select_checks(self._domain)
+ self._provider_select_defer = self._backend.\
+ setup_provider(self._domain)
def _skip_provider_checks(self, skip):
"""
@@ -403,8 +412,6 @@ class Wizard(QtGui.QWizard):
if skip:
self._reset_provider_check()
- self.page(self.SELECT_PROVIDER_PAGE).set_completed(skip)
- self.button(QtGui.QWizard.NextButton).setEnabled(skip)
self._use_existing_provider = skip
def _complete_task(self, data, label, complete=False, complete_page=-1):
@@ -423,8 +430,8 @@ class Wizard(QtGui.QWizard):
:param complete_page: page id to complete
:type complete_page: int
"""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
- error = data[self._provider_bootstrapper.ERROR_KEY]
+ passed = data[self._backend.PASSED_KEY]
+ error = data[self._backend.ERROR_KEY]
if passed:
label.setPixmap(self.OK_ICON)
if complete:
@@ -437,13 +444,13 @@ class Wizard(QtGui.QWizard):
def _name_resolution(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.name_resolution
+ TRIGGER: self._backend.signaler.prov_name_resolution
Sets the status for the name resolution check
"""
self._complete_task(data, self.ui.lblNameResolution)
status = ""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if not passed:
status = self.tr("<font color='red'><b>Non-existent "
"provider</b></font>")
@@ -456,16 +463,16 @@ class Wizard(QtGui.QWizard):
def _https_connection(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.https_connection
+ TRIGGER: self._backend.signaler.prov_https_connection
Sets the status for the https connection check
"""
self._complete_task(data, self.ui.lblHTTPS)
status = ""
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if not passed:
status = self.tr("<font color='red'><b>%s</b></font>") \
- % (data[self._provider_bootstrapper.ERROR_KEY])
+ % (data[self._backend.ERROR_KEY])
self.ui.lblProviderSelectStatus.setText(status)
else:
self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON)
@@ -475,29 +482,26 @@ class Wizard(QtGui.QWizard):
def _download_provider_info(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.download_provider_info
+ TRIGGER: self._backend.signaler.prov_download_provider_info
Sets the status for the provider information download
check. Since this check is the last of this set, it also
completes the page if passed
"""
- if self._provider_config.load(os.path.join("leap",
- "providers",
- self._domain,
- "provider.json")):
+ if self._provider_config.load(get_provider_path(self._domain)):
self._complete_task(data, self.ui.lblProviderInfo,
True, self.SELECT_PROVIDER_PAGE)
self._provider_checks_ok = True
else:
new_data = {
- self._provider_bootstrapper.PASSED_KEY: False,
- self._provider_bootstrapper.ERROR_KEY:
+ self._backend.PASSED_KEY: False,
+ self._backend.ERROR_KEY:
self.tr("Unable to load provider configuration")
}
self._complete_task(new_data, self.ui.lblProviderInfo)
status = ""
- if not data[self._provider_bootstrapper.PASSED_KEY]:
+ if not data[self._backend.PASSED_KEY]:
status = self.tr("<font color='red'><b>Not a valid provider"
"</b></font>")
self.ui.lblProviderSelectStatus.setText(status)
@@ -507,31 +511,31 @@ class Wizard(QtGui.QWizard):
def _download_ca_cert(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.download_ca_cert
+ TRIGGER: self._backend.signaler.prov_download_ca_cert
Sets the status for the download of the CA certificate check
"""
self._complete_task(data, self.ui.lblDownloadCaCert)
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if passed:
self.ui.lblCheckCaFpr.setPixmap(self.QUESTION_ICON)
def _check_ca_fingerprint(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.check_ca_fingerprint
+ TRIGGER: self._backend.signaler.prov_check_ca_fingerprint
Sets the status for the CA fingerprint check
"""
self._complete_task(data, self.ui.lblCheckCaFpr)
- passed = data[self._provider_bootstrapper.PASSED_KEY]
+ passed = data[self._backend.PASSED_KEY]
if passed:
self.ui.lblCheckApiCert.setPixmap(self.QUESTION_ICON)
def _check_api_certificate(self, data):
"""
SLOT
- TRIGGER: self._provider_bootstrapper.check_api_certificate
+ TRIGGER: self._backend.signaler.prov_check_api_certificate
Sets the status for the API certificate check. Also finishes
the provider bootstrapper thread since it's not needed anymore
@@ -597,6 +601,7 @@ class Wizard(QtGui.QWizard):
Prepares the pages when they appear
"""
if pageId == self.SELECT_PROVIDER_PAGE:
+ self._clear_register_widgets()
skip = self.ui.rbExistingProvider.isChecked()
if not self._provider_checks_ok:
self._enable_check()
@@ -611,8 +616,8 @@ class Wizard(QtGui.QWizard):
sub_title = sub_title.format(self._provider_config.get_name())
self.page(pageId).setSubTitle(sub_title)
self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON)
- self._provider_setup_defer = self._provider_bootstrapper.\
- run_provider_setup_checks(self._provider_config)
+ self._provider_setup_defer = self._backend.\
+ provider_bootstrap(self._domain)
if pageId == self.PRESENT_PROVIDER_PAGE:
self.page(pageId).setSubTitle(self.tr("Description of services "
@@ -670,3 +675,50 @@ class Wizard(QtGui.QWizard):
return self.SERVICES_PAGE
return QtGui.QWizard.nextId(self)
+
+ def _clear_register_widgets(self):
+ """
+ Clears the widgets that my be filled and a possible error message.
+ """
+ self._set_register_status("")
+ self.ui.lblUser.setText("")
+ self.ui.lblPassword.setText("")
+ self.ui.lblPassword2.setText("")
+
+ def _backend_connect(self):
+ """
+ Connects all the backend signals with the wizard.
+ """
+ sig = self._backend.signaler
+ sig.prov_name_resolution.connect(self._name_resolution)
+ sig.prov_https_connection.connect(self._https_connection)
+ sig.prov_download_provider_info.connect(self._download_provider_info)
+
+ sig.prov_download_ca_cert.connect(self._download_ca_cert)
+ sig.prov_check_ca_fingerprint.connect(self._check_ca_fingerprint)
+ sig.prov_check_api_certificate.connect(self._check_api_certificate)
+
+ sig.srp_registration_finished.connect(self._registration_finished)
+ sig.srp_registration_failed.connect(self._registration_failed)
+ sig.srp_registration_taken.connect(self._registration_taken)
+
+ def _backend_disconnect(self):
+ """
+ This method is called when the wizard dialog is closed.
+ We disconnect all the backend signals in here.
+ """
+ sig = self._backend.signaler
+ try:
+ # disconnect backend signals
+ sig.prov_name_resolution.disconnect(self._name_resolution)
+ sig.prov_https_connection.disconnect(self._https_connection)
+ sig.prov_download_provider_info.disconnect(
+ self._download_provider_info)
+
+ sig.prov_download_ca_cert.disconnect(self._download_ca_cert)
+ sig.prov_check_ca_fingerprint.disconnect(
+ self._check_ca_fingerprint)
+ sig.prov_check_api_certificate.disconnect(
+ self._check_api_certificate)
+ except RuntimeError:
+ pass # Signal was not connected