From 361a18b0e727a68d6e0d1e9d03273630b9c14692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 6 Mar 2013 15:43:36 -0300 Subject: Add UI merging all the code Also add resources --- Makefile | 69 ++++ data/images/Arrow-Down-32.png | Bin 0 -> 1458 bytes data/images/Arrow-Up-32.png | Bin 0 -> 1400 bytes data/images/Blue-Arrow-Right-32.png | Bin 0 -> 1432 bytes data/images/Dialog-accept.png | Bin 0 -> 1316 bytes data/images/Dialog-error.png | Bin 0 -> 1380 bytes data/images/Emblem-question.png | Bin 0 -> 1260 bytes data/images/Globe.png | Bin 0 -> 22470 bytes data/images/conn_connected.png | Bin 0 -> 426 bytes data/images/conn_connecting.png | Bin 0 -> 712 bytes data/images/conn_error.png | Bin 0 -> 1429 bytes data/images/favicon.ico | Bin 0 -> 318 bytes data/images/leap-client.icns | Bin 0 -> 27456 bytes data/images/leap-color-big.png | Bin 0 -> 30322 bytes data/images/leap-color-small.png | Bin 0 -> 10100 bytes data/leap_client.pro | 11 + data/resources/locale.qrc | 6 + data/resources/mainwindow.qrc | 16 + data/translations/README.rst | 8 + data/ts/README.rst | 14 + data/ts/en_US.ts | 477 +++++++++++++++++++++ src/leap/gui/__init__.py | 0 src/leap/gui/mainwindow.py | 600 +++++++++++++++++++++++++++ src/leap/gui/ui/mainwindow.ui | 377 +++++++++++++++++ src/leap/gui/ui/wizard.ui | 800 ++++++++++++++++++++++++++++++++++++ src/leap/gui/wizard.py | 403 ++++++++++++++++++ src/leap/gui/wizardpage.py | 39 ++ 27 files changed, 2820 insertions(+) create mode 100644 Makefile create mode 100644 data/images/Arrow-Down-32.png create mode 100644 data/images/Arrow-Up-32.png create mode 100644 data/images/Blue-Arrow-Right-32.png create mode 100644 data/images/Dialog-accept.png create mode 100644 data/images/Dialog-error.png create mode 100644 data/images/Emblem-question.png create mode 100644 data/images/Globe.png create mode 100644 data/images/conn_connected.png create mode 100644 data/images/conn_connecting.png create mode 100644 data/images/conn_error.png create mode 100644 data/images/favicon.ico create mode 100644 data/images/leap-client.icns create mode 100644 data/images/leap-color-big.png create mode 100644 data/images/leap-color-small.png create mode 100644 data/leap_client.pro create mode 100644 data/resources/locale.qrc create mode 100644 data/resources/mainwindow.qrc create mode 100644 data/translations/README.rst create mode 100644 data/ts/README.rst create mode 100644 data/ts/en_US.ts create mode 100644 src/leap/gui/__init__.py create mode 100644 src/leap/gui/mainwindow.py create mode 100644 src/leap/gui/ui/mainwindow.ui create mode 100644 src/leap/gui/ui/wizard.ui create mode 100644 src/leap/gui/wizard.py create mode 100644 src/leap/gui/wizardpage.py diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..62e2fd80 --- /dev/null +++ b/Makefile @@ -0,0 +1,69 @@ +# ################################ +# Makefile for compiling resources +# files. +# TODO move to setup scripts +# and implement it in python +# http://die-offenbachs.homelinux.org:48888/hg/eric5/file/5072605ad4dd/compileUiFiles.py +###### EDIT ###################### + +#Directory with ui and resource files +RESOURCE_DIR = data/resources +UI_DIR = src/leap/gui/ui + +#Directory for compiled resources +COMPILED_DIR = src/leap/gui + +#Directory for (finished) translations +TRANSLAT_DIR = data/translations + +#Project file, used for translations +PROJFILE = data/leap_client.pro + +#UI files to compile +# UI_FILES = foo.ui +UI_FILES = mainwindow.ui wizard.ui +#Qt resource files to compile +#images.qrc +RESOURCES = mainwindow.qrc # locale.qrc + +#pyuic4 and pyrcc4 binaries +PYUIC = pyside-uic +PYRCC = pyside-rcc +PYLUP = pylupdate4 +LRELE = lrelease + + +################################# +# DO NOT EDIT FOLLOWING + +COMPILED_UI = $(UI_FILES:%.ui=$(COMPILED_DIR)/ui_%.py) +COMPILED_RESOURCES = $(RESOURCES:%.qrc=$(COMPILED_DIR)/%_rc.py) + +DEBVER = $(shell dpkg-parsechangelog | sed -ne 's,Version: ,,p') + +# + +all : resources ui + +resources : $(COMPILED_RESOURCES) + +ui : $(COMPILED_UI) + +translations: + $(PYLUP) $(PROJFILE) + $(LRELE) $(TRANSLAT_DIR)/*.ts + +$(COMPILED_DIR)/ui_%.py : $(UI_DIR)/%.ui + $(PYUIC) $< -o $@ + +$(COMPILED_DIR)/%_rc.py : $(RESOURCE_DIR)/%.qrc + $(PYRCC) $< -o $@ + +manpages: + rst2man docs/man/leap.1.rst docs/man/leap.1 + +apidocs: + @sphinx-apidoc -o docs/api src/leap + +clean : + $(RM) $(COMPILED_UI) $(COMPILED_RESOURCES) $(COMPILED_UI:.py=.pyc) $(COMPILED_RESOURCES:.py=.pyc) diff --git a/data/images/Arrow-Down-32.png b/data/images/Arrow-Down-32.png new file mode 100644 index 00000000..c5c607a1 Binary files /dev/null and b/data/images/Arrow-Down-32.png differ diff --git a/data/images/Arrow-Up-32.png b/data/images/Arrow-Up-32.png new file mode 100644 index 00000000..85370ac5 Binary files /dev/null and b/data/images/Arrow-Up-32.png differ diff --git a/data/images/Blue-Arrow-Right-32.png b/data/images/Blue-Arrow-Right-32.png new file mode 100644 index 00000000..66e50b0d Binary files /dev/null and b/data/images/Blue-Arrow-Right-32.png differ diff --git a/data/images/Dialog-accept.png b/data/images/Dialog-accept.png new file mode 100644 index 00000000..5a8a0bdb Binary files /dev/null and b/data/images/Dialog-accept.png differ diff --git a/data/images/Dialog-error.png b/data/images/Dialog-error.png new file mode 100644 index 00000000..51da2f5b Binary files /dev/null and b/data/images/Dialog-error.png differ diff --git a/data/images/Emblem-question.png b/data/images/Emblem-question.png new file mode 100644 index 00000000..b2163e5b Binary files /dev/null and b/data/images/Emblem-question.png differ diff --git a/data/images/Globe.png b/data/images/Globe.png new file mode 100644 index 00000000..7549433b Binary files /dev/null and b/data/images/Globe.png differ diff --git a/data/images/conn_connected.png b/data/images/conn_connected.png new file mode 100644 index 00000000..a5d20497 Binary files /dev/null and b/data/images/conn_connected.png differ diff --git a/data/images/conn_connecting.png b/data/images/conn_connecting.png new file mode 100644 index 00000000..31b6e617 Binary files /dev/null and b/data/images/conn_connecting.png differ diff --git a/data/images/conn_error.png b/data/images/conn_error.png new file mode 100644 index 00000000..85669af6 Binary files /dev/null and b/data/images/conn_error.png differ diff --git a/data/images/favicon.ico b/data/images/favicon.ico new file mode 100644 index 00000000..b5f3505a Binary files /dev/null and b/data/images/favicon.ico differ diff --git a/data/images/leap-client.icns b/data/images/leap-client.icns new file mode 100644 index 00000000..d5d52cdc Binary files /dev/null and b/data/images/leap-client.icns differ diff --git a/data/images/leap-color-big.png b/data/images/leap-color-big.png new file mode 100644 index 00000000..eafacdcd Binary files /dev/null and b/data/images/leap-color-big.png differ diff --git a/data/images/leap-color-small.png b/data/images/leap-color-small.png new file mode 100644 index 00000000..bc9d4e7f Binary files /dev/null and b/data/images/leap-color-small.png differ diff --git a/data/leap_client.pro b/data/leap_client.pro new file mode 100644 index 00000000..9ec1a43b --- /dev/null +++ b/data/leap_client.pro @@ -0,0 +1,11 @@ +# qmake file + +# is not there a f*** way of expanding this? other to template with python I mean... + +# SOURCES += ... +# where to generate ts files -- tx will pick from here + +# original file, english + +TRANSLATIONS += ts/en_US.ts + diff --git a/data/resources/locale.qrc b/data/resources/locale.qrc new file mode 100644 index 00000000..47fb5243 --- /dev/null +++ b/data/resources/locale.qrc @@ -0,0 +1,6 @@ + + +../translations/es.qm +../translations/de.qm + + diff --git a/data/resources/mainwindow.qrc b/data/resources/mainwindow.qrc new file mode 100644 index 00000000..e53e3633 --- /dev/null +++ b/data/resources/mainwindow.qrc @@ -0,0 +1,16 @@ + + + ../images/Blue-Arrow-Right-32.png + ../images/Globe.png + ../images/conn_error.png + ../images/leap-color-big.png + ../images/Arrow-Down-32.png + ../images/Arrow-Up-32.png + ../images/conn_connecting.png + ../images/conn_connected.png + ../images/leap-color-small.png + ../images/Dialog-accept.png + ../images/Dialog-error.png + ../images/Emblem-question.png + + diff --git a/data/translations/README.rst b/data/translations/README.rst new file mode 100644 index 00000000..1f3dd0b3 --- /dev/null +++ b/data/translations/README.rst @@ -0,0 +1,8 @@ +data/translations +================= + +We expect finished translations (i.e., those downloaded from ``transifex``) to live here. + +Translator object will pick them from here. + +(Actually, from the embedded locale_rc) diff --git a/data/ts/README.rst b/data/ts/README.rst new file mode 100644 index 00000000..3db2d104 --- /dev/null +++ b/data/ts/README.rst @@ -0,0 +1,14 @@ +data/ts +======= + +Here we expect the .ts files generated by typing:: + + $ make translations + +Which will generate the sources (en_US) + +For uploading a source:: + + $ tx push -s + +Translator should pick finished ``.qm`` files from ``data/translations`` instead of this folder. diff --git a/data/ts/en_US.ts b/data/ts/en_US.ts new file mode 100644 index 00000000..d2cba837 --- /dev/null +++ b/data/ts/en_US.ts @@ -0,0 +1,477 @@ + + + + ConnectionPage + + + Connecting... + + + + + Setting up a encrypted connection with the provider + + + + + Getting EIP configuration files + + + + + Authentication error: %s + + + + + Getting EIP certificate + + + + + EIPConductorAppMixin + + + &Disconnect + + + + + &Connect + + + + + EIPErrors + + + We could not find any authentication agent in your system.<br/>Make sure you have <b>polkit-gnome-authentication-agent-1</b> running and try again. + + + + + We could not find <b>pkexec</b> in your system.<br/> Do you want to try <b>setuid workaround</b>? (<i>DOES NOTHING YET</i>) + + + + + No suitable openvpn command found. <br/>(Might be a permissions problem) + + + + + there is a problem with provider certificate + + + + + an error occurred during configuratio of leap services + + + + + Server does not allow secure connections + + + + + Server certificate could not be verified + + + + + We could not find your eip certs in the expected path + + + + + Another OpenVPN Process has been detected. Please close it before starting leap-client + + + + + Errors + + + Interface not found + + + + + Looks like your computer is not connected to the internet + + + + + Looks like there are problems with your internet connection + + + + + It looks like there is no internet connection. + + + + + Domain cannot be found + + + + + The Encrypted Connection was lost. + + + + + IntroPage + + + First run wizard + + + + + Sign up for a new account + + + + + Log In with my credentials + + + + + Now we will guide you through some configuration that is needed before you can connect for the first time.<br><br>If you ever need to modify these options again, you can find the wizard in the '<i>Settings</i>' menu from the main window.<br><br>Do you want to <b>sign up</b> for a new account, or <b>log in</b> with an already existing username?<br> + + + + + LastPage + + + Connecting to Encrypted Internet Proxy service... + + + + + Click '<i>%s</i>' to end the wizard and save your settings. + + + + + You are now using an encrypted connection! + + + + + LogInPage + + + Username must be in the username@provider form. + + + + + Resolving domain name + + + + + Authentication error: %s + + + + + Credentials validated. + + + + + Log In + + + + + User &name: + + + + + &Password: + + + + + Log in + + + + + Log in with your credentials + + + + + LogPaneMixin + + + &Connect + + + + + Disconnected + + + + + ProviderInfoPage + + + Provider Information + + + + + Services offered by this provider + + + + + enrollment policy + + + + + ProviderSetupValidationPage + + + Provider setup + + + + + Fetching CA certificate + + + + + Checking CA fingerprint + + + + + Validating api certificate + + + + + Gathering configuration options for this provider + + + + + RegisterUserPage + + + Sign Up + + + + + Registration succeeded! + + + + + Password does not match.. + + + + + Password too short. + + + + + Password too obvious. + + + + + Error connecting to provider (timeout) + + + + + Error Connecting to provider (connerr). + + + + + Error during registration (%s) + + + + + Could not register (bad response) + + + + + Username not available. + + + + + User &name: + + + + + &Password: + + + + + Password (again): + + + + + &Remember username and password. + + + + + Register a new user with provider <em>%s</em> + + + + + SelectProviderPage + + + Enter Provider + + + + + chec&k! + + + + + Server certificate could not be verified. + + + + + Certificate validation + + + + + Could not get info from provider. + + + + + Could not download provider info (refused conn.). + + + + + &Trust this provider certificate. + + + + + Do you want to <b>trust this provider certificate?</b> + + + + + SHA-256 fingerprint: <i>%s</i><br> + + + + + Please enter the domain of the provider you want to use for your connection + + + + + Checking if it is a valid provider + + + + + Checking for a secure connection + + + + + Getting info from the provider + + + + + StatusAwareTrayIconMixin + + + EIP Connection Status + + + + + <b>disconnected</b> + + + + + Encryption ON turn &off + + + + + &Details... + + + + + &About + + + + + About Q&t + + + + + &Quit + + + + + Encryption ON turn o&ff + + + + + Encryption OFF turn &on + + + + + connecting... + + + + + About + + + + diff --git a/src/leap/gui/__init__.py b/src/leap/gui/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py new file mode 100644 index 00000000..1821e4a6 --- /dev/null +++ b/src/leap/gui/mainwindow.py @@ -0,0 +1,600 @@ +# -*- coding: utf-8 -*- +# mainwindow.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Main window for the leap client +""" +import os +import logging + +from PySide import QtCore, QtGui + +from ui_mainwindow import Ui_MainWindow +from leap.config.providerconfig import ProviderConfig +from leap.crypto.srpauth import SRPAuth +from leap.services.eip.vpn import VPN +from leap.services.eip.providerbootstrapper import ProviderBootstrapper +from leap.services.eip.eipbootstrapper import EIPBootstrapper +from leap.services.eip.eipconfig import EIPConfig +from leap.gui.wizard import Wizard + +logger = logging.getLogger(__name__) + + +class MainWindow(QtGui.QMainWindow): + """ + Main window for login and presenting status updates to the user + """ + + # StackedWidget indexes + LOGIN_INDEX = 0 + EIP_STATUS_INDEX = 1 + + def __init__(self): + QtGui.QMainWindow.__init__(self) + + self.CONNECTING_ICON = QtGui.QPixmap(":/images/conn_connecting.png") + self.CONNECTED_ICON = QtGui.QPixmap(":/images/conn_connected.png") + self.ERROR_ICON = QtGui.QPixmap(":/images/conn_error.png") + + self.ui = Ui_MainWindow() + self.ui.setupUi(self) + + self.ui.lnPassword.setEchoMode(QtGui.QLineEdit.Password) + + self.ui.btnLogin.clicked.connect(self._login) + self.ui.lnUser.returnPressed.connect(self._focus_password) + self.ui.lnPassword.returnPressed.connect(self._login) + + self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX) + + # This is loaded only once, there's a bug when doing that more + # than once + self._provider_config = ProviderConfig() + self._eip_config = EIPConfig() + # This is created once we have a valid provider config + self._srp_auth = None + + # This thread is always running, although it's quite + # lightweight when it's done setting up provider + # configuration and certificate. + self._provider_bootstrapper = ProviderBootstrapper() + + # TODO: add sigint handler + + # 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) + + # This thread is similar to the provider bootstrapper + self._eip_bootstrapper = EIPBootstrapper() + + self._eip_bootstrapper.download_config.connect( + self._intermediate_stage) + self._eip_bootstrapper.download_client_certificate.connect( + self._start_eip) + + self._vpn = VPN() + self._vpn.state_changed.connect(self._update_vpn_state) + self._vpn.status_changed.connect(self._update_vpn_status) + + QtCore.QCoreApplication.instance().connect( + QtCore.QCoreApplication.instance(), + QtCore.SIGNAL("aboutToQuit()"), + self._vpn.set_should_quit) + QtCore.QCoreApplication.instance().connect( + QtCore.QCoreApplication.instance(), + QtCore.SIGNAL("aboutToQuit()"), + self._provider_bootstrapper.set_should_quit) + QtCore.QCoreApplication.instance().connect( + QtCore.QCoreApplication.instance(), + QtCore.SIGNAL("aboutToQuit()"), + self._eip_bootstrapper.set_should_quit) + + self.ui.action_sign_out.setEnabled(False) + self.ui.action_sign_out.triggered.connect(self._logout) + 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) + + # Used to differentiate between real quits and close to tray + self._really_quit = False + + self._systray = None + self._action_visible = QtGui.QAction("Hide", self) + self._action_visible.triggered.connect(self._toggle_visible) + + self._center_window() + self._wizard = None + if self._first_run(): + self._wizard = Wizard() + # Give this window time to finish init and then show the wizard + QtCore.QTimer.singleShot(1, self._launch_wizard) + self._wizard.finished.connect(self._finish_init) + else: + self._finish_init() + + def _launch_wizard(self): + if self._wizard is None: + self._wizard = Wizard() + self._wizard.exec_() + + def _finish_init(self): + self.ui.cmbProviders.addItems(self._configured_providers()) + self._show_systray() + self.show() + if self._wizard: + possible_username = self._wizard.get_username() + if possible_username is not None: + self.ui.lnUser.setText(possible_username) + self._focus_password() + self._wizard = None + + def _show_systray(self): + """ + Sets up the systray icon + """ + systrayMenu = QtGui.QMenu(self) + systrayMenu.addAction(self._action_visible) + systrayMenu.addAction(self.ui.action_sign_out) + systrayMenu.addSeparator() + systrayMenu.addAction(self.ui.action_quit) + self._systray = QtGui.QSystemTrayIcon(self) + self._systray.setContextMenu(systrayMenu) + self._systray.setIcon(QtGui.QIcon(self.ERROR_ICON)) + self._systray.setVisible(True) + self._systray.activated.connect(self._toggle_visible) + + def _toggle_visible(self): + """ + SLOT + TRIGGER: self._systray.activated + + Toggles the window visibility + """ + self.setVisible(not self.isVisible()) + action_visible_text = "Hide" + if not self.isVisible(): + action_visible_text = "Show" + self._action_visible.setText(action_visible_text) + + def _center_window(self): + """ + Centers the mainwindow based on the desktop geometry + """ + app = QtGui.QApplication.instance() + width = app.desktop().width() + height = app.desktop().height() + window_width = self.size().width() + window_height = self.size().height() + x = (width / 2.0) - (window_width / 2.0) + y = (height / 2.0) - (window_height / 2.0) + self.move(x, y) + + def _about(self): + """ + Display the About LEAP dialog + """ + QtGui.QMessageBox.about(self, "About LEAP", + "LEAP is a non-profit dedicated to giving " + "all internet users access to secure " + "communication. Our focus is on adapting " + "encryption technology to make it easy to use " + "and widely available. " + "More about LEAP" + "") + + def quit(self): + self._really_quit = True + if self._wizard: + self._wizard.accept() + self.close() + + def changeEvent(self, e): + """ + Reimplements the changeEvent method to minimize to tray + """ + if QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \ + e.type() == QtCore.QEvent.WindowStateChange and \ + self.isMinimized(): + self._toggle_visible() + e.accept() + return + QtGui.QMainWindow.changeEvent(self, e) + + def closeEvent(self, e): + """ + Reimplementation of closeEvent to close to tray + """ + if QtGui.QSystemTrayIcon.isSystemTrayAvailable() and \ + not self._really_quit: + self._toggle_visible() + e.ignore() + return + QtGui.QMainWindow.closeEvent(self, e) + + def _configured_providers(self): + """ + Returns the available providers based on the file structure + + @rtype: list + """ + providers = os.listdir( + os.path.join(self._provider_config.get_path_prefix(), + "leap", + "providers")) + return providers + + def _first_run(self): + """ + Returns True if there are no configured providers. False otherwise + + @rtype: bool + """ + return len(self._configured_providers()) == 0 + + def _focus_password(self): + """ + Focuses in the password lineedit + """ + self.ui.lnPassword.setFocus() + + def _set_status(self, status): + """ + Sets the status label at the login stage to status + + @param status: status message + @type status: str + """ + self.ui.lblStatus.setText(status) + + def _set_eip_status(self, status): + """ + Sets the status label at the VPN stage to status + + @param status: status message + @type status: str + """ + self.ui.lblEIPStatus.setText(status) + + def _login_set_enabled(self, enabled=False): + """ + Enables or disables all the login widgets + + @param enabled: wether they should be enabled or not + @type enabled: bool + """ + self.ui.lnUser.setEnabled(enabled) + self.ui.lnPassword.setEnabled(enabled) + self.ui.btnLogin.setEnabled(enabled) + self.ui.chkRemember.setEnabled(enabled) + self.ui.cmbProviders.setEnabled(enabled) + + def _download_provider_config(self): + """ + Starts the bootstrapping sequence. It will download the + provider configuration if it's not present, otherwise will + emit the corresponding signals inmediately + """ + provider = self.ui.cmbProviders.currentText() + + self._provider_bootstrapper.start() + self._provider_bootstrapper.run_provider_select_checks( + provider, + download_if_needed=True) + + def _load_provider_config(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.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 + @type data: dict + """ + if data[self._provider_bootstrapper.PASSED_KEY]: + provider = self.ui.cmbProviders.currentText() + if self._provider_config.loaded() or \ + self._provider_config.load(os.path.join("leap", + "providers", + provider, + "provider.json")): + self._provider_bootstrapper.run_provider_setup_checks( + self._provider_config, + download_if_needed=True) + else: + self._set_status("Could not load provider configuration") + self._login_set_enabled(True) + else: + self._set_status(data[self._provider_bootstrapper.ERROR_KEY]) + self._login_set_enabled(True) + + def _login(self): + """ + SLOT + TRIGGERS: + self.ui.btnLogin.clicked + self.ui.lnPassword.returnPressed + + Starts the login sequence. Which involves bootstrapping the + selected provider if the selection is valid (not empty), then + start the SRP authentication, and as the last step + bootstrapping the EIP service + """ + assert self._provider_config, "We need a provider config" + + username = self.ui.lnUser.text() + password = self.ui.lnPassword.text() + provider = self.ui.cmbProviders.currentText() + + if len(provider) == 0: + self._set_status("Please select a valid provider") + return + + if len(username) == 0: + self._set_status("Please provide a valid username") + return + + if len(password) == 0: + self._set_status("Please provide a valid Password") + return + + self._set_status("Logging in...") + self._login_set_enabled(False) + + self._download_provider_config() + + def _provider_config_loaded(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.check_api_certificate + + Once the provider configuration is loaded, this starts the SRP + authentication + """ + assert self._provider_config, "We need a provider config!" + + self._provider_bootstrapper.set_should_quit() + + if data[self._provider_bootstrapper.PASSED_KEY]: + username = self.ui.lnUser.text() + password = self.ui.lnPassword.text() + + if self._srp_auth is None: + 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.authenticate(username, password) + else: + self._set_status(data[self._provider_bootstrapper.ERROR_KEY]) + self._login_set_enabled(True) + + def _authentication_finished(self, ok, message): + """ + SLOT + TRIGGER: self._srp_auth.authentication_finished + + Once the user is properly authenticated, try starting the EIP + service + """ + self._set_status(message) + if ok: + self.ui.action_sign_out.setEnabled(True) + # We leave a bit of room for the user to see the + # "Succeeded" message and then we switch to the EIP status + # panel + QtCore.QTimer.singleShot(1000, self._switch_to_status) + else: + self._login_set_enabled(True) + + def _switch_to_status(self): + """ + Changes the stackedWidget index to the EIP status one and + triggers the eip bootstrapping + """ + self.ui.stackedWidget.setCurrentIndex(self.EIP_STATUS_INDEX) + self._download_eip_config() + + def _download_eip_config(self): + """ + Starts the EIP bootstrapping sequence + """ + assert self._eip_bootstrapper, "We need an eip bootstrapper!" + assert self._provider_config, "We need a provider config" + + self._set_eip_status("Checking configuration, please wait...") + + if self._provider_config.provides_eip(): + self._eip_bootstrapper.start() + self._eip_bootstrapper.run_eip_setup_checks( + self._provider_config, + download_if_needed=True) + else: + self._set_eip_status("%s does not support EIP" % + (self._provider_config.get_domain(),)) + + def _set_eip_status_icon(self, status): + """ + Given a status step from the VPN thread, set the icon properly + + @param status: status step + @type status: str + """ + selected_pixmap = self.ERROR_ICON + if status in ("AUTH", "GET_CONFIG"): + selected_pixmap = self.CONNECTING_ICON + elif status in ("CONNECTED"): + selected_pixmap = self.CONNECTED_ICON + + self.ui.lblVPNStatusIcon.setPixmap(selected_pixmap) + self._systray.setIcon(QtGui.QIcon(selected_pixmap)) + + def _update_vpn_state(self, data): + """ + SLOT + TRIGGER: self._vpn.state_changed + + Updates the displayed VPN state based on the data provided by + the VPN thread + """ + status = data[self._vpn.STATUS_STEP_KEY] + self._set_eip_status_icon(status) + if status == "AUTH": + self._set_eip_status("VPN: Authenticating...") + elif status == "GET_CONFIG": + self._set_eip_status("VPN: Retrieving configuration...") + elif status == "CONNECTED": + self._set_eip_status("VPN: Connected!") + else: + self._set_eip_status(status) + + def _update_vpn_status(self, data): + """ + SLOT + TRIGGER: self._vpn.status_changed + + Updates the download/upload labels based on the data provided + by the VPN thread + """ + upload = float(data[self._vpn.TUNTAP_WRITE_KEY]) + upload = upload / 1000.0 + self.ui.lblUpload.setText("%s Kb" % (upload,)) + download = float(data[self._vpn.TUNTAP_READ_KEY]) + download = download / 1000.0 + self.ui.lblDownload.setText("%s Kb" % (download,)) + + def _start_eip(self, data): + """ + SLOT + TRIGGER: self._eip_bootstrapper.download_client_certificate + + Starts the VPN thread if the eip configuration is properly + loaded + """ + assert self._eip_config, "We need an eip config!" + assert self._provider_config, "We need a provider config!" + + self._eip_bootstrapper.set_should_quit() + if self._eip_config.loaded() or \ + self._eip_config.load(os.path.join("leap", + "providers", + self._provider_config + .get_domain(), + "eip-service.json")): + self._vpn.start(eipconfig=self._eip_config, + providerconfig=self._provider_config, + socket_host="/home/chiiph/vpnsock", + socket_port="unix") + # TODO: display a message if the EIP configuration cannot be + # loaded + + def _logout(self): + """ + SLOT + TRIGGER: self.ui.action_sign_out.triggered + + Starts the logout sequence + """ + self._set_eip_status_icon("error") + self._set_eip_status("Signing out...") + self._srp_auth.logout() + + def _done_logging_out(self, ok, message): + """ + SLOT + TRIGGER: self._srp_auth.logout_finished + + Switches the stackedWidget back to the login stage after + logging out + """ + self._set_status(message) + self.ui.action_sign_out.setEnabled(False) + self.ui.stackedWidget.setCurrentIndex(self.LOGIN_INDEX) + self.ui.lnPassword.setText("") + self._login_set_enabled(True) + self._set_status("") + self._vpn.set_should_quit() + + def _intermediate_stage(self, data): + """ + SLOT + TRIGGERS: + self._provider_bootstrapper.name_resolution + self._provider_bootstrapper.https_connection + self._provider_bootstrapper.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] + if not passed: + self._login_set_enabled(True) + self._set_status(data[self._provider_bootstrapper.ERROR_KEY]) + +if __name__ == "__main__": + import signal + from functools import partial + + def sigint_handler(*args, **kwargs): + logger.debug('SIGINT catched. shutting down...') + mainwindow = args[0] + mainwindow.quit() + + import sys + + logger = logging.getLogger(name='leap') + logger.setLevel(logging.DEBUG) + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + formatter = logging.Formatter( + '%(asctime)s ' + '- %(name)s - %(levelname)s - %(message)s') + console.setFormatter(formatter) + logger.addHandler(console) + + app = QtGui.QApplication(sys.argv) + mainwindow = MainWindow() + mainwindow.show() + + timer = QtCore.QTimer() + timer.start(500) + timer.timeout.connect(lambda: None) + + sigint = partial(sigint_handler, mainwindow) + signal.signal(signal.SIGINT, sigint) + + sys.exit(app.exec_()) diff --git a/src/leap/gui/ui/mainwindow.ui b/src/leap/gui/ui/mainwindow.ui new file mode 100644 index 00000000..a527eaf6 --- /dev/null +++ b/src/leap/gui/ui/mainwindow.ui @@ -0,0 +1,377 @@ + + + MainWindow + + + + 0 + 0 + 405 + 579 + + + + LEAP + + + + :/images/leap-color-big.png:/images/leap-color-big.png + + + Qt::ImhHiddenText + + + + 128 + 128 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 1 + + + + + + + + + + Remember + + + + + + + Login + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <b>Provider:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <b>Password:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + + <b>User:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Qt::AlignCenter + + + + + + + + + + + Disconnected + + + Qt::AlignCenter + + + + + + + + + + :/images/conn_error.png + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 70 + 0 + + + + 0.0 Kb + + + + + + + + 70 + 0 + + + + 0.0 Kb + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + :/images/Arrow-Up-32.png + + + + + + + + + + :/images/Arrow-Down-32.png + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + + + :/images/leap-color-big.png + + + Qt::AlignCenter + + + + + + + + + 0 + 0 + 405 + 25 + + + + + &Session + + + + + + + + Help + + + + + + + + S&ettings + + + + + + + + + + + &Sign out + + + + + &Quit + + + + + About &LEAP + + + + + &Help + + + + + &Wizard + + + + + lnUser + lnPassword + chkRemember + btnLogin + cmbProviders + + + + + + diff --git a/src/leap/gui/ui/wizard.ui b/src/leap/gui/ui/wizard.ui new file mode 100644 index 00000000..86f8d458 --- /dev/null +++ b/src/leap/gui/ui/wizard.ui @@ -0,0 +1,800 @@ + + + Wizard + + + + 0 + 0 + 536 + 452 + + + + LEAP First run + + + + :/images/leap-color-big.png:/images/leap-color-big.png + + + true + + + QWizard::ModernStyle + + + QWizard::IndependentPages + + + + Welcome + + + This is the LEAP Client first run wizard + + + 0 + + + + + + Log In with my credentials + + + + + + + <html><head/><body><p>New we will guide you through some configuration that is needed before you can connect for the first time.</p><p>If you ever need to modify these options again, you can find the wizard in the <span style=" font-style:italic;">'Settings'</span> menu from the main window.</p><p>Do you want to <span style=" font-weight:600;">sign up</span> for a new account, or <span style=" font-weight:600;">log in</span> with an already existing username?</p></body></html> + + + Qt::RichText + + + true + + + + + + + Sign up for a new account + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Provider selection + + + Please enter the domain of the provider you want to user for your connection + + + 1 + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 60 + + + + + + + + https:// + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + Checking provider + + + + + + Download provider information + + + + + + + HTTPS Connection + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + Name resolution + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + + Check + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Provider Information + + + Services offered by this provider + + + 2 + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + <b>Enrollment policy:</b> + + + + + + + URL + + + + + + + Name + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + policy + + + + + + + Desc + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Provider setup + + + Gathering configuration options for this provider + + + 3 + + + + + + Checking provider + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + Download CA Certificate + + + + + + + Check CA Certificate Fingerprint + + + + + + + Check API Certificate + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + + Qt::Vertical + + + + 20 + 60 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Register new user + + + Register a new user with provider + + + 4 + + + + + + + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <b>User:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + <b>Password:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + + + <b>Re-enter password:</b> + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Register + + + + + + + Qt::Vertical + + + + 20 + 60 + + + + + + + + + EIP Setup + + + Setting up Encrypted Internet + + + 5 + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Checking EIP + + + + + + Download EIP configuration + + + + + + + Download client certificate + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + + 0 + 0 + + + + + + + :/images/Emblem-question.png + + + + + + + Qt::Horizontal + + + + 40 + 0 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Congratulations! + + + You have successfully configured the LEAP client. + + + 6 + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + :/images/leap-color-big.png + + + + + + + + 0 + 0 + + + + + + + :/images/Globe.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + WizardPage + QWizardPage +
wizardpage.h
+ 1 +
+
+ + lblUser + lblPassword + lblPassword2 + btnRegister + rdoRegister + rdoLogin + lnProvider + btnCheck + + + + + +
diff --git a/src/leap/gui/wizard.py b/src/leap/gui/wizard.py new file mode 100644 index 00000000..7dcc8dd6 --- /dev/null +++ b/src/leap/gui/wizard.py @@ -0,0 +1,403 @@ +# -*- coding: utf-8 -*- +# wizard.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +First run wizard +""" +import os +import logging + +from PySide import QtCore, QtGui + +from ui_wizard import Ui_Wizard +from leap.config.providerconfig import ProviderConfig +from leap.crypto.srpregister import SRPRegister +from leap.services.eip.providerbootstrapper import ProviderBootstrapper +from leap.services.eip.eipbootstrapper import EIPBootstrapper + +logger = logging.getLogger(__name__) + + +class Wizard(QtGui.QWizard): + """ + First run wizard to register a user and setup a provider + """ + + INTRO_PAGE = 0 + SELECT_PROVIDER_PAGE = 1 + PRESENT_PROVIDER_PAGE = 2 + SETUP_PROVIDER_PAGE = 3 + REGISTER_USER_PAGE = 4 + SETUP_EIP_PAGE = 5 + FINISH_PATH = 6 + + WEAK_PASSWORDS = ("1234", "12345", "123456", + "password") + + def __init__(self): + QtGui.QWizard.__init__(self) + + self.ui = Ui_Wizard() + self.ui.setupUi(self) + + self.QUESTION_ICON = QtGui.QPixmap(":/images/Emblem-question.png") + self.ERROR_ICON = QtGui.QPixmap(":/images/Dialog-error.png") + self.OK_ICON = QtGui.QPixmap(":/images/Dialog-accept.png") + + self._show_register = False + + self.ui.grpCheckProvider.setVisible(False) + self.ui.btnCheck.clicked.connect(self._check_provider) + self.ui.lnProvider.returnPressed.connect(self._check_provider) + + self._provider_bootstrapper = ProviderBootstrapper() + 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._eip_bootstrapper = EIPBootstrapper() + + self._eip_bootstrapper.download_config.connect( + self._download_eip_config) + self._eip_bootstrapper.download_client_certificate.connect( + self._download_client_certificate) + + self._domain = None + self._provider_config = ProviderConfig() + + self.currentIdChanged.connect(self._current_id_changed) + + self.ui.lblPassword.setEchoMode(QtGui.QLineEdit.Password) + self.ui.lblPassword2.setEchoMode(QtGui.QLineEdit.Password) + + self.ui.lblUser.returnPressed.connect( + self._focus_password) + self.ui.lblPassword.returnPressed.connect( + self._focus_second_password) + self.ui.lblPassword2.returnPressed.connect( + self._register) + self.ui.btnRegister.clicked.connect( + self._register) + + self._username = None + + def __del__(self): + self._provider_bootstrapper.set_should_quit() + self._eip_bootstrapper.set_should_quit() + self._provider_bootstrapper.wait() + self._eip_bootstrapper.wait() + + def get_username(self): + return self._username + + def _focus_password(self): + """ + Focuses at the password lineedit for the registration page + """ + self.ui.lblPassword.setFocus() + + def _focus_second_password(self): + """ + Focuses at the second password lineedit for the registration page + """ + self.ui.lblPassword2.setFocus() + + def _basic_password_checks(self, username, password, password2): + """ + Performs basic password checks to avoid really easy passwords. + + @param username: username provided at the registrarion form + @type username: str + @param password: password from the registration form + @type password: str + @param password2: second password from the registration form + @type password: str + + @return: returns True if all the checks pass, False otherwise + @rtype: bool + """ + message = None + + try: + username.encode("ascii") + password.encode("ascii") + except: + message = u"Refrain from using non ASCII áñ characters" + + if message is not None and password != password2: + message = "Passwords don't match" + + if message is not None and len(password) < 4: + message = "Password too short" + + if message is not None and password in self.WEAK_PASSWORDS: + message = "Password too easy" + + if message is not None and username == password: + message = "Password equal to username" + + if message is not None: + self._set_register_status(message) + self._focus_password() + return False + + return True + + def _register(self): + """ + Performs the registration based on the values provided in the form + """ + self.ui.btnRegister.setEnabled(False) + # See the disabled button + while QtGui.QApplication.instance().hasPendingEvents(): + QtGui.QApplication.instance().processEvents() + self.button(QtGui.QWizard.NextButton).setFocus() + + username = self.ui.lblUser.text() + password = self.ui.lblPassword.text() + password2 = self.ui.lblPassword2.text() + + if self._basic_password_checks(username, password, password2): + register = SRPRegister(provider_config=self._provider_config) + ok, req = register.register_user(username, password) + if ok: + self._set_register_status("User registration OK") + self._username = username + self.ui.lblPassword2.clearFocus() + # Detach this call to allow UI updates briefly + QtCore.QTimer.singleShot(1, + self.page(self.REGISTER_USER_PAGE) + .set_completed) + else: + print req.content + error_msg = "Unknown error" + try: + error_msg = req.json().get("errors").get("login")[0] + except: + logger.error("Unknown error: %r" % (req.content,)) + self._set_register_status(error_msg) + self.ui.btnRegister.setEnabled(True) + else: + self.ui.btnRegister.setEnabled(True) + + def _set_register_status(self, status): + """ + Sets the status label in the registration page to status + + @param status: status message to display, can be HTML + @type status: str + """ + self.ui.lblRegisterStatus.setText(status) + + def _check_provider(self): + """ + SLOT + TRIGGERS: + self.ui.btnCheck.clicked + self.ui.lnProvider.returnPressed + + Starts the checks for a given provider + """ + self.ui.grpCheckProvider.setVisible(True) + self.ui.btnCheck.setEnabled(False) + self._domain = self.ui.lnProvider.text() + + self._provider_bootstrapper.start() + self._provider_bootstrapper.run_provider_select_checks(self._domain) + + def _complete_task(self, data, label, complete=False, complete_page=-1): + """ + Checks a task and completes a page if specified + + @param data: data as it comes from the bootstrapper thread for + a specific check + @type data: dict + @param label: label that displays the status icon for a + specific check that corresponds to the data + @type label: QtGui.QLabel + @param complete: if True, it completes the page specified, + which must be of type WizardPage + @type complete: bool + @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] + if passed: + label.setPixmap(self.OK_ICON) + if complete: + self.page(complete_page).set_completed() + self.button(QtGui.QWizard.NextButton).setFocus() + else: + label.setPixmap(self.ERROR_ICON) + logger.error(error) + + def _name_resolution(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.name_resolution + + Sets the status for the name resolution check + """ + self._complete_task(data, self.ui.lblNameResolution) + + def _https_connection(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.https_connection + + Sets the status for the https connection check + """ + self._complete_task(data, self.ui.lblHTTPS) + + def _download_provider_info(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.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")): + self._complete_task(data, self.ui.lblProviderInfo, + True, self.SELECT_PROVIDER_PAGE) + else: + new_data = { + self._provider_bootstrapper.PASSED_KEY: False, + self._provider_bootstrapper.ERROR_KEY: + "Unable to load provider configuration" + } + self._complete_task(new_data, self.ui.lblProviderInfo) + + self.ui.btnCheck.setEnabled(True) + + def _download_ca_cert(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.download_ca_cert + + Sets the status for the download of the CA certificate check + """ + self._complete_task(data, self.ui.lblDownloadCaCert) + + def _check_ca_fingerprint(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.check_ca_fingerprint + + Sets the status for the CA fingerprint check + """ + self._complete_task(data, self.ui.lblCheckCaFpr) + + def _check_api_certificate(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.check_api_certificate + + Sets the status for the API certificate check. Also finishes + the provider bootstrapper thread since it's not needed anymore + from this point on, unless the whole check chain is restarted + """ + self._complete_task(data, self.ui.lblCheckApiCert, + True, self.SETUP_PROVIDER_PAGE) + self._provider_bootstrapper.set_should_quit() + + def _download_eip_config(self, data): + """ + SLOT + TRIGGER: self._eip_bootstrapper.download_config + + Sets the status for the EIP config downloading check + """ + self._complete_task(data, self.ui.lblDownloadEIPConfig) + + def _download_client_certificate(self, data): + """ + SLOT + TRIGGER: self._provider_bootstrapper.download_client_certificate + + Sets the status for the download client certificate check and + completes the page if passed. Also stops the eip bootstrapper + thread since it's not needed from this point on unless the + check chain is restarted + """ + self._complete_task(data, self.ui.lblDownloadClientCert, + True, self.SETUP_EIP_PAGE) + self._eip_bootstrapper.set_should_quit() + + def _current_id_changed(self, pageId): + """ + SLOT + TRIGGER: self.currentIdChanged + + Prepares the pages when they appear + """ + if pageId == self.SELECT_PROVIDER_PAGE: + self.ui.grpCheckProvider.setVisible(False) + self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON) + self.ui.lblHTTPS.setPixmap(self.QUESTION_ICON) + self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON) + + if pageId == self.SETUP_PROVIDER_PAGE: + self._provider_bootstrapper.\ + run_provider_setup_checks(self._provider_config) + + if pageId == self.SETUP_EIP_PAGE: + self._eip_bootstrapper.start() + self._eip_bootstrapper.run_eip_setup_checks(self._provider_config) + + if pageId == self.PRESENT_PROVIDER_PAGE: + # TODO: get the right lang for these + self.ui.lblProviderName.setText( + "%s" % + (self._provider_config.get_name(),)) + self.ui.lblProviderURL.setText(self._provider_config.get_domain()) + self.ui.lblProviderDesc.setText( + "%s" % + (self._provider_config.get_description(),)) + self.ui.lblProviderPolicy.setText(self._provider_config + .get_enrollment_policy()) + + def nextId(self): + """ + Sets the next page id for the wizard based on wether the user + wants to register a new identity or uses an existing one + """ + if self.currentPage() == self.page(self.INTRO_PAGE): + self._show_register = self.ui.rdoRegister.isChecked() + + if self.currentPage() == self.page(self.SETUP_PROVIDER_PAGE): + if self._show_register: + return self.REGISTER_USER_PAGE + else: + return self.SETUP_EIP_PAGE + + return QtGui.QWizard.nextId(self) diff --git a/src/leap/gui/wizardpage.py b/src/leap/gui/wizardpage.py new file mode 100644 index 00000000..2138ac7b --- /dev/null +++ b/src/leap/gui/wizardpage.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# wizardpage.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from PySide import QtGui + + +class WizardPage(QtGui.QWizardPage): + """ + Simple wizard page helper + """ + + def __init__(self): + QtGui.QWizardPage.__init__(self) + self._completed = False + + def set_completed(self): + self._completed = True + self.completeChanged.emit() + + def isComplete(self): + return self._completed + + def cleanupPage(self): + self._completed = False + QtGui.QWizardPage.cleanupPage(self) -- cgit v1.2.3