Unverified Commit 229f8032 authored by elijah's avatar elijah Committed by Kali Kaneko
Browse files

[feature] add email panel to preferences

parent c6db8a1f
- Add email panel to preferences window.
\ No newline at end of file
......@@ -576,8 +576,10 @@ class EIP(object):
self._signaler.eip_uninitialized_provider)
return
eip_config = eipconfig.EIPConfig()
provider_config = ProviderConfig.get_provider_config(domain)
if EIP_SERVICE not in provider_config.get_services():
return
eip_config = eipconfig.EIPConfig()
api_version = provider_config.get_api_version()
eip_config.set_api_version(api_version)
......@@ -1003,13 +1005,11 @@ class Keymanager(object):
def get_key_details(self, username):
"""
List all the keys stored in the local DB.
Get information on our primary key pair
"""
def signal_details(public_key):
# XXX: We should avoid the key-id
details = (public_key.fingerprint[-16:], public_key.fingerprint)
self._signaler.signal(self._signaler.keymanager_key_details,
details)
public_key.get_dict())
d = self._keymanager_proxy.get_key(username,
openpgp.OpenPGPKey)
......@@ -1215,15 +1215,13 @@ class Authenticate(object):
def get_logged_in_status(self):
"""
Signal if the user is currently logged in or not.
Signal if the user is currently logged in or not. If logged in,
authenticated username is passed as argument to the signal.
"""
if self._signaler is None:
return
signal = None
if self._is_logged_in():
signal = self._signaler.srp_status_logged_in
self._signaler.signal(self._signaler.srp_status_logged_in)
else:
signal = self._signaler.srp_status_not_logged_in
self._signaler.signal(signal)
self._signaler.signal(self._signaler.srp_status_not_logged_in)
......@@ -20,7 +20,7 @@ A frontend GUI object to hold the current username and domain.
from leap.bitmask.util import make_address
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.services import EIP_SERVICE, MX_SERVICE
from leap.bitmask._components import HAS_EIP, HAS_MAIL
class Account():
......@@ -42,8 +42,8 @@ class Account():
"""
return self._settings.get_enabled_services(self.domain)
def is_email_enabled(self):
return MX_SERVICE in self.services()
def has_email(self):
return HAS_MAIL and MX_SERVICE in self.services()
def is_eip_enabled(self):
return EIP_SERVICE in self.services()
def has_eip(self):
return HAS_EIP and EIP_SERVICE in self.services()
......@@ -20,6 +20,7 @@ and the signaler get signals from the backend.
"""
from PySide import QtCore, QtGui
from leap.bitmask.gui.account import Account
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.backend.backend_proxy import BackendProxy
from leap.bitmask.backend.leapsignaler import LeapSignaler
......@@ -44,12 +45,37 @@ class App(QtGui.QWidget):
self.signaler.start()
self.soledad_started = False
self.service_tokens = {}
self.login_state = None
self.providers_widget = None
# periodically check if the backend is alive
self._backend_checker = QtCore.QTimer(self)
self._backend_checker.timeout.connect(self._check_backend_status)
self._backend_checker.start(2000)
# store the service tokens for later use, once they are known.
self.signaler.soledad_got_service_token.connect(
self._set_service_tokens)
def current_account(self):
"""
Alas, the only definitive account information is buried in the memory of
QT widgets.
:returns: an object representing the current user account.
:rtype: Account
"""
if self.login_state is None or self.providers_widget is None:
return None
if self.login_state.full_logged_username is not None:
username, domain = self.login_state.full_logged_username.split('@')
return Account(username, domain)
else:
domain = self.providers_widget.get_selected_provider()
return Account(None, domain)
def _check_backend_status(self):
"""
TRIGGERS:
......@@ -64,3 +90,11 @@ class App(QtGui.QWidget):
self.tr("There is a problem contacting the backend, please "
"restart Bitmask."))
self._backend_checker.stop()
def _set_service_tokens(self, data):
"""
Triggered by signal soledad_got_service_token.
Saves the service tokens.
"""
service, token = data
self.service_tokens[service] = token
......@@ -46,7 +46,6 @@ from leap.bitmask.gui.signaltracker import SignalTracker
from leap.bitmask.gui.systray import SysTray
from leap.bitmask.gui.wizard import Wizard
from leap.bitmask.gui.providers import Providers
from leap.bitmask.gui.account import Account
from leap.bitmask.gui.app import App
from leap.bitmask.platform_init import IS_WIN, IS_MAC, IS_LINUX
......@@ -154,6 +153,14 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):
# Provider List
self._providers = Providers(self.ui.cmbProviders)
##
## tmphack: important state information about the application is stored
## in widgets. Rather than rewrite the UI, for now we simulate this
## info being stored in an application object:
##
self.app.login_state = self._login_widget._state
self.app.providers_widget = self._providers
# Qt Signal Connections #####################################
# TODO separate logic from ui signals.
......@@ -416,10 +423,6 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):
sig.soledad_invalid_auth_token.connect(
self._mail_status.set_soledad_invalid_auth_token)
self._service_tokens = {}
sig.soledad_got_service_token.connect(
self._set_service_tokens)
# TODO: connect this with something
# sig.soledad_cancelled_bootstrap.connect()
......@@ -569,15 +572,7 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):
Display the preferences window.
"""
logged_user = self._login_widget.get_logged_user()
if logged_user is not None:
user, domain = logged_user.split('@')
else:
user = None
domain = self._providers.get_selected_provider()
account = Account(user, domain)
pref_win = PreferencesWindow(self, account, self.app)
pref_win = PreferencesWindow(self, self.app)
pref_win.show()
def _show_pixelated_browser(self):
......@@ -1054,13 +1049,6 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):
msg = msg.format(ver=VERSION, ver_hash=VERSION_HASH[:10], greet=greet)
QtGui.QMessageBox.about(self, title, msg)
def _set_service_tokens(self, data):
"""
Set the received service token.
"""
service, token = data
self._service_tokens[service] = token
def _help(self):
"""
TRIGGERS:
......@@ -1095,7 +1083,7 @@ class MainWindow(QtGui.QMainWindow, SignalTracker):
# FIXME on i3, this doens't allow to mouse-select.
# Switch to a dialog in which we can set the QLabel
mail_auth_token = (
self._service_tokens.get('mail_auth', None) or
self.app.service_tokens.get('mail_auth', None) or
"??? (log in to unlock)")
mail_password = self.tr("IMAP/SMTP Password:") + " %s" % (
mail_auth_token,)
......
......@@ -83,7 +83,7 @@ class PasswordWindow(QtGui.QDialog, Flashable):
Returns true if the current account needs to change the soledad
password as well as the SRP password.
"""
return self.account.is_email_enabled()
return self.account.has_email()
#
# MANAGE WIDGETS
......
......@@ -22,6 +22,7 @@ from PySide import QtCore, QtGui
from leap.bitmask.logs.utils import get_logger
from leap.bitmask.gui import ui_preferences_account_page as ui_pref
from leap.bitmask.gui.preferences_page import PreferencesPage
from leap.bitmask.gui.passwordwindow import PasswordWindow
from leap.bitmask.services import get_service_display_name
from leap.bitmask._components import HAS_EIP
......@@ -29,7 +30,7 @@ from leap.bitmask._components import HAS_EIP
logger = get_logger()
class PreferencesAccountPage(QtGui.QWidget):
class PreferencesAccountPage(PreferencesPage):
def __init__(self, parent, account, app):
"""
......@@ -42,20 +43,15 @@ class PreferencesAccountPage(QtGui.QWidget):
:param app: the current App object
:type app: App
"""
QtGui.QWidget.__init__(self, parent)
PreferencesPage.__init__(self, parent, account, app)
self.ui = ui_pref.Ui_PreferencesAccountPage()
self.ui.setupUi(self)
self.account = account
self.app = app
self._selected_services = set()
self.ui.change_password_label.setVisible(False)
self.ui.provider_services_label.setVisible(False)
self.ui.change_password_button.clicked.connect(
self._show_change_password)
app.signaler.prov_get_supported_services.connect(self._load_services)
self.setup_connections()
app.backend.provider_get_supported_services(domain=account.domain)
if account.username is None:
......@@ -64,6 +60,25 @@ class PreferencesAccountPage(QtGui.QWidget):
self.ui.change_password_label.setVisible(True)
self.ui.change_password_button.setEnabled(False)
def setup_connections(self):
"""
connect signals
"""
self.ui.change_password_button.clicked.connect(
self._show_change_password)
self.app.signaler.prov_get_supported_services.connect(
self._load_services)
def teardown_connections(self):
"""
disconnect signals
"""
self.ui.change_password_button.clicked.disconnect(
self._show_change_password)
self.app.signaler.prov_get_supported_services.disconnect(
self._load_services)
def _service_selection_changed(self, service, state):
"""
TRIGGERS:
......
......@@ -16,20 +16,192 @@
"""
Widget for "email" preferences
"""
from PySide import QtGui
from PySide import QtCore, QtGui
from datetime import datetime
from leap.bitmask.logs.utils import get_logger
from leap.bitmask.gui.ui_preferences_email_page import Ui_PreferencesEmailPage
from leap.bitmask.gui.preferences_page import PreferencesPage
from leap.mail.imap.service.imap import IMAP_PORT
logger = get_logger()
class PreferencesEmailPage(QtGui.QWidget):
class PreferencesEmailPage(PreferencesPage):
def __init__(self, parent, account, app):
QtGui.QWidget.__init__(self, parent)
"""
:param parent: parent object of the PreferencesWindow.
:parent type: QWidget
:param account: user account (user + provider or just provider)
:type account: Account
:param app: the current App object
:type app: App
"""
PreferencesPage.__init__(self, parent, account, app)
self.ui = Ui_PreferencesEmailPage()
self.ui.setupUi(self)
self.account = account
self.app = app
# the only way to set the tab titles is to re-add them:
self.ui.email_tabs.addTab(self.ui.config_tab,
self.tr("Mail Client"))
self.ui.email_tabs.addTab(self.ui.my_key_tab,
self.tr("My Key"))
self.ui.email_tabs.addTab(self.ui.other_keys_tab,
self.tr("Other Keys"))
# set mail client configuration help text
lang = QtCore.QLocale.system().name().replace('_', '-')
thunderbird_extension_url = \
"https://addons.mozilla.org/{0}/" \
"thunderbird/addon/bitmask/".format(lang)
self.ui.thunderbird_label.setText(self.tr(
"For Thunderbird, you can use the Bitmask extension. "
"Search for \"Bitmask\" in the add-on manager or "
"download it from <a href='{0}'>addons.mozilla.org</a>.".format(
thunderbird_extension_url)))
self.ui.mail_client_label.setText(self.tr(
"Alternatively, you can manually configure your mail client to "
"use Bitmask Email with these options:"))
self.ui.keys_table.horizontalHeader().setResizeMode(
0, QtGui.QHeaderView.Stretch)
self.setup_connections()
def setup_connections(self):
"""
connect signals
"""
self.app.signaler.keymanager_key_details.connect(self._key_details)
self.app.signaler.keymanager_export_ok.connect(
self._keymanager_export_ok)
self.app.signaler.keymanager_export_error.connect(
self._keymanager_export_error)
self.ui.import_button.clicked.connect(self._import_keys)
self.ui.export_button.clicked.connect(self._export_keys)
def teardown_connections(self):
"""
disconnect signals
"""
self.app.signaler.keymanager_key_details.disconnect(self._key_details)
self.app.signaler.keymanager_export_ok.disconnect(
self._keymanager_export_ok)
self.app.signaler.keymanager_export_error.disconnect(
self._keymanager_export_error)
def showEvent(self, event):
"""
called whenever this widget is shown
"""
self.ui.keys_table.clearContents()
if self.account.username is None:
self.ui.email_tabs.setVisible(False)
self.ui.message_label.setVisible(True)
self.ui.message_label.setText(
self.tr('You must be logged in to edit email settings.'))
else:
self.ui.import_button.setVisible(False) # hide this until working
self.ui.message_label.setVisible(False)
self.ui.email_tabs.setVisible(True)
smtp_port = 2013
self.ui.imap_port_edit.setText(str(IMAP_PORT))
self.ui.imap_host_edit.setText("127.0.0.1")
self.ui.smtp_port_edit.setText(str(smtp_port))
self.ui.smtp_host_edit.setText("127.0.0.1")
self.ui.username_edit.setText(self.account.address)
self.ui.password_edit.setText(
self.app.service_tokens.get('mail_auth', ''))
self.app.backend.keymanager_list_keys()
self.app.backend.keymanager_get_key_details(
username=self.account.address)
def _key_details(self, details):
"""
Trigger by signal: keymanager_key_details
Set the current user's key details into the gui.
"""
self.ui.fingerprint_edit.setPlainText(
self._format_fingerprint(details["fingerprint"]))
self.ui.expiration_edit.setText(details["expiry_date"])
self.ui.uid_edit.setText(" ".join(details["uids"]))
self.ui.public_key_edit.setPlainText(details["key_data"])
def _format_fingerprint(self, fingerprint):
"""
formats an openpgp fingerprint in a manner similar to what gpg
produces, wrapped to two lines.
"""
fp = fingerprint.upper()
fp_list = [fp[i:i+4] for i in range(0, len(fp), 4)]
fp_wrapped = " ".join(fp_list[0:5]) + "\n" + " ".join(fp_list[5:10])
return fp_wrapped
def _export_keys(self):
"""
Exports the user's key pair.
"""
file_name, filtr = QtGui.QFileDialog.getSaveFileName(
self, self.tr("Save private key file"),
filter="*.pem",
options=QtGui.QFileDialog.DontUseNativeDialog)
if file_name:
if not file_name.endswith('.pem'):
file_name += '.pem'
self.app.backend.keymanager_export_keys(
username=self.account.address,
filename=file_name)
else:
logger.debug('Export canceled by the user.')
def _keymanager_export_ok(self):
"""
TRIGGERS:
Signaler.keymanager_export_ok
Notify the user that the key export went OK.
"""
QtGui.QMessageBox.information(
self, self.tr("Export Successful"),
self.tr("The key pair was exported successfully.\n"
"Please, store your private key in a safe place."))
def _keymanager_export_error(self):
"""
TRIGGERS:
Signaler.keymanager_export_error
Notify the user that the key export didn't go well.
"""
QtGui.QMessageBox.critical(
self, self.tr("Input/Output error"),
self.tr("There was an error accessing the file.\n"
"Export canceled."))
def _import_keys(self):
"""
not yet supported
"""
def _keymanager_keys_list(self, keys):
"""
TRIGGERS:
Signaler.keymanager_keys_list
Load the keys given as parameter in the table.
:param keys: the list of keys to load.
:type keys: list
"""
for key in keys:
row = self.ui.keys_table.rowCount()
self.ui.keys_table.insertRow(row)
self.ui.keys_table.setItem(row, 0, QtGui.QTableWidgetItem(key.address))
self.ui.keys_table.setItem(row, 1, QtGui.QTableWidgetItem(key.fingerprint))
# -*- coding: utf-8 -*-
# Copyright (C) 2014 LEAP
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
base class for preference pages
"""
from PySide import QtCore, QtGui
class PreferencesPage(QtGui.QWidget):
def __init__(self, parent, account=None, app=None):
"""
:param parent: parent object of the EIPPreferencesWindow.
:type parent: QWidget
:param account: the currently active account
:type account: Account
:param app: shared App instance
:type app: App
"""
QtGui.QWidget.__init__(self, parent)
self.app = app
self.account = account
def setup_connections(self):
"""
connect signals
must be overridden by subclass
"""
def teardown_connections(self):
"""
disconnect signals
must be overridden by subclass
"""
......@@ -22,9 +22,9 @@ from leap.bitmask.gui.ui_preferences_vpn_page import Ui_PreferencesVpnPage
from leap.bitmask.config.leapsettings import LeapSettings
from leap.bitmask.gui.flashable import Flashable
from leap.bitmask.gui.preferences_page import PreferencesPage
class PreferencesVpnPage(QtGui.QWidget, Flashable):
class PreferencesVpnPage(PreferencesPage, Flashable):
"""
Page in the preferences window that shows VPN settings
......@@ -41,19 +41,24 @@ class PreferencesVpnPage(QtGui.QWidget, Flashable):
:param app: shared App instance
:type app: App
"""
QtGui.QWidget.__init__(self, parent)
PreferencesPage.__init__(self, parent, account, app)
self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic")
self.account = account
self.app = app
# Load UI
self.ui = Ui_PreferencesVpnPage()
self.ui.setupUi(self)
self.ui.flash_label.setVisible(False)
self.hide_flash()
# Connections
self.setup_connections()
# Trigger update
self.app.backend.eip_get_gateways_list(domain=self.account.domain)
def setup_connections(self):
"""
connect signals
"""
self.ui.gateways_list.clicked.connect(self._save_selected_gateway)
sig = self.app.signaler
sig.eip_get_gateways_list.connect(self._update_gateways_list)
......@@ -61,8 +66,16 @@ class PreferencesVpnPage(QtGui.QWidget, Flashable):
sig.eip_uninitialized_provider.connect(
self._gateways_list_uninitialized)
# Trigger update
self.app.backend.eip_get_gateways_list(domain=self.account.domain)
def teardown_connections(self):
"""
disconnect signals
"""
self.ui.gateways_list.clicked.disconnect(self._save_selected_gateway)
sig = self.app.signaler
sig.eip_get_gateways_list.disconnect(self._update_gateways_list)
sig.eip_get_gateways_list_error.disconnect(self._gateways_list_error)
sig.eip_uninitialized_provider.disconnect(
self._gateways_list_uninitialized)
def _save_selected_gateway(self, index):
"""
......
......@@ -20,18 +20,15 @@ Preferences window
"""
from PySide import QtCore, QtGui
from leap.bitmask.services import EIP_SERVICE
from leap.bitmask._components import HAS_EIP
from leap.bitmask.logs.utils import get_logger
from leap.bitmask.gui.ui_preferences import Ui_Preferences
from leap.bitmask.gui.preferences_page import PreferencesPage
from leap.bitmask.gui.preferences_account_page import PreferencesAccountPage
from leap.bitmask.gui.preferences_vpn_page import PreferencesVpnPage
from leap.bitmask.gui.preferences_email_page import PreferencesEmailPage
logger = get_logger()
class PreferencesWindow(QtGui.QDialog):