diff options
20 files changed, 280 insertions, 199 deletions
| diff --git a/changes/bug-5424_handle-provider-setup-errors b/changes/bug-5424_handle-provider-setup-errors new file mode 100644 index 00000000..e55bcdcc --- /dev/null +++ b/changes/bug-5424_handle-provider-setup-errors @@ -0,0 +1 @@ +- Handle provider setup problems and show an error to the user. Closes #5424. diff --git a/changes/bug-5559_avoid-errors-when-ctrl-c-wizard b/changes/bug-5559_avoid-errors-when-ctrl-c-wizard new file mode 100644 index 00000000..36f352b8 --- /dev/null +++ b/changes/bug-5559_avoid-errors-when-ctrl-c-wizard @@ -0,0 +1 @@ +- Avoid user getting errors if he does a 'ctrl-c' on the wizard during the first run. Closes #5559. diff --git a/changes/feature-5506_use-pyside-slot-decorator b/changes/feature-5506_use-pyside-slot-decorator new file mode 100644 index 00000000..7a9ea20e --- /dev/null +++ b/changes/feature-5506_use-pyside-slot-decorator @@ -0,0 +1 @@ +- Use PySide @Slot decorator instead of 'SLOT' docstring. Closes #5506. diff --git a/changes/feature-5513_update-note-in-akm b/changes/feature-5513_update-note-in-akm new file mode 100644 index 00000000..e0ae0632 --- /dev/null +++ b/changes/feature-5513_update-note-in-akm @@ -0,0 +1 @@ +- Advanced key management: show a note to the user if the provider does not support Encrypted Email. Closes #5513. diff --git a/changes/feature_skip-wizard-checks b/changes/feature_skip-wizard-checks new file mode 100644 index 00000000..c1eb07a4 --- /dev/null +++ b/changes/feature_skip-wizard-checks @@ -0,0 +1 @@ +- Add flag to skip provider checks in wizard (only for testing). diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 0a315be7..146743b5 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -214,6 +214,7 @@ def main():      flags.APP_VERSION_CHECK = opts.app_version_check      flags.API_VERSION_CHECK = opts.api_version_check      flags.OPENVPN_VERBOSITY = opts.openvpn_verb +    flags.SKIP_WIZARD_CHECKS = opts.skip_wizard_checks      flags.CA_CERT_FILE = opts.ca_cert_file diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index d208a6cd..d5a8a4e9 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -340,11 +340,11 @@ class EIP(object):          try:              self._start_eip()          except vpnprocess.OpenVPNAlreadyRunning: -            signaler.signal(signaler.EIP_OPEN_VPN_ALREADY_RUNNING) +            signaler.signal(signaler.EIP_OPENVPN_ALREADY_RUNNING)          except vpnprocess.AlienOpenVPNAlreadyRunning: -            signaler.signal(signaler.EIP_ALIEN_OPEN_VPN_ALREADY_RUNNING) +            signaler.signal(signaler.EIP_ALIEN_OPENVPN_ALREADY_RUNNING)          except vpnlauncher.OpenVPNNotFoundException: -            signaler.signal(signaler.EIP_OPEN_VPN_NOT_FOUND_ERROR) +            signaler.signal(signaler.EIP_OPENVPN_NOT_FOUND_ERROR)          except vpnlauncher.VPNLauncherException:              # TODO: this seems to be used for 'gateway not found' only.              #       see vpnlauncher.py @@ -1027,15 +1027,15 @@ class Backend(object):          Signals:              backend_bad_call -            eip_alien_open_vpn_already_running +            eip_alien_openvpn_already_running              eip_connected              eip_connection_aborted              eip_network_unreachable              eip_no_pkexec_error              eip_no_polkit_agent_error              eip_no_tun_kext_error -            eip_open_vpn_already_running -            eip_open_vpn_not_found_error +            eip_openvpn_already_running +            eip_openvpn_not_found_error              eip_process_finished              eip_process_restart_ping              eip_process_restart_tls diff --git a/src/leap/bitmask/config/flags.py b/src/leap/bitmask/config/flags.py index 7cc8711c..6b70659d 100644 --- a/src/leap/bitmask/config/flags.py +++ b/src/leap/bitmask/config/flags.py @@ -52,3 +52,6 @@ CA_CERT_FILE = None  # OpenVPN verbosity level  OPENVPN_VERBOSITY = 1 + +# Skip the checks in the wizard, use for testing purposes only! +SKIP_WIZARD_CHECKS = False diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py index cbc8c3e3..be6b4410 100644 --- a/src/leap/bitmask/gui/advanced_key_management.py +++ b/src/leap/bitmask/gui/advanced_key_management.py @@ -30,12 +30,17 @@ from ui_advanced_key_management import Ui_AdvancedKeyManagement  logger = logging.getLogger(__name__) -class AdvancedKeyManagement(QtGui.QWidget): +class AdvancedKeyManagement(QtGui.QDialog):      """      Advanced Key Management      """ -    def __init__(self, user, keymanager, soledad): +    def __init__(self, parent, has_mx, user, keymanager, soledad):          """ +        :param parent: parent object of AdvancedKeyManagement. +        :parent type: QWidget +        :param has_mx: defines whether the current provider provides email or +                       not. +        :type has_mx: bool          :param user: the current logged in user.          :type user: unicode          :param keymanager: the existing keymanager instance @@ -43,7 +48,7 @@ class AdvancedKeyManagement(QtGui.QWidget):          :param soledad: a loaded instance of Soledad          :type soledad: Soledad          """ -        QtGui.QWidget.__init__(self) +        QtGui.QDialog.__init__(self, parent)          self.ui = Ui_AdvancedKeyManagement()          self.ui.setupUi(self) @@ -52,13 +57,18 @@ class AdvancedKeyManagement(QtGui.QWidget):          self.ui.pbImportKeys.setVisible(False)          # if Soledad is not started yet +        if not has_mx: +            msg = self.tr("The provider that you are using " +                          "does not support {0}.") +            msg = msg.format(get_service_display_name(MX_SERVICE)) +            self._disable_ui(msg) +            return + +        # if Soledad is not started yet          if sameProxiedObjects(soledad, None): -            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 = self.tr("To use this, you need to enable/start {0}.")              msg = msg.format(get_service_display_name(MX_SERVICE)) -            self.ui.lblStatus.setText(msg) +            self._disable_ui(msg)              return          # XXX: since import is disabled this is no longer a dangerous feature.          # else: @@ -90,6 +100,18 @@ class AdvancedKeyManagement(QtGui.QWidget):          self._list_keys() +    def _disable_ui(self, msg): +        """ +        Disable the UI and set a note in the status bar. + +        :param msg: note to display in the status bar. +        :type msg: unicode +        """ +        self.ui.gbMyKeyPair.setEnabled(False) +        self.ui.gbStoredPublicKeys.setEnabled(False) +        msg = self.tr("<span style='color:#0000FF;'>NOTE</span>: ") + msg +        self.ui.lblStatus.setText(msg) +      def _import_keys(self):          """          Imports the user's key pair. diff --git a/src/leap/bitmask/gui/eip_preferenceswindow.py b/src/leap/bitmask/gui/eip_preferenceswindow.py index baf17395..530cd38d 100644 --- a/src/leap/bitmask/gui/eip_preferenceswindow.py +++ b/src/leap/bitmask/gui/eip_preferenceswindow.py @@ -98,9 +98,9 @@ class EIPPreferencesWindow(QtGui.QDialog):          self._backend.eip_get_initialized_providers(providers) +    @QtCore.Slot(list)      def _load_providers_in_combo(self, providers):          """ -        SLOT          TRIGGERS:              Signaler.eip_get_initialized_providers @@ -128,9 +128,9 @@ class EIPPreferencesWindow(QtGui.QDialog):                  domain, QtCore.Qt.MatchStartsWith)              self.ui.cbProvidersGateway.setCurrentIndex(provider_index) +    @QtCore.Slot(str)      def _save_selected_gateway(self, provider):          """ -        SLOT          TRIGGERS:              self.ui.pbSaveGateway.clicked @@ -153,9 +153,9 @@ class EIPPreferencesWindow(QtGui.QDialog):              "Gateway settings for provider '{0}' saved.").format(provider)          self._set_providers_gateway_status(msg, success=True) +    @QtCore.Slot(int)      def _populate_gateways(self, domain_idx):          """ -        SLOT          TRIGGERS:              self.ui.cbProvidersGateway.currentIndexChanged[unicode] @@ -176,12 +176,15 @@ class EIPPreferencesWindow(QtGui.QDialog):          self._backend.eip_get_gateways_list(domain) +    @QtCore.Slot(list)      def _update_gateways_list(self, gateways):          """ -        SLOT          TRIGGERS:              Signaler.eip_get_gateways_list +        :param gateways: a list of gateways +        :type gateways: list of unicode +          Add the available gateways and select the one stored in configuration          file.          """ @@ -214,9 +217,9 @@ class EIPPreferencesWindow(QtGui.QDialog):          self.ui.cbGateways.setCurrentIndex(index) +    @QtCore.Slot()      def _gateways_list_error(self):          """ -        SLOT          TRIGGERS:              Signaler.eip_get_gateways_list_error @@ -229,9 +232,9 @@ class EIPPreferencesWindow(QtGui.QDialog):          self.ui.pbSaveGateway.setEnabled(False)          self.ui.cbGateways.setEnabled(False) +    @QtCore.Slot()      def _gateways_list_uninitialized(self):          """ -        SLOT          TRIGGERS:              Signaler.eip_uninitialized_provider diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index f24d87c7..0d75b8e5 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -88,11 +88,12 @@ class EIPStatusWidget(QtGui.QWidget):          self.ui.btnUpload.clicked.connect(onclicked)          self.ui.btnDownload.clicked.connect(onclicked) +    @QtCore.Slot()      def _on_VPN_status_clicked(self):          """ -        SLOT -        TRIGGER: self.ui.btnUpload.clicked -                 self.ui.btnDownload.clicked +        TRIGGERS: +            self.ui.btnUpload.clicked +            self.ui.btnDownload.clicked          Toggles between rate and total throughput display for vpn          status figures. @@ -347,10 +348,11 @@ class EIPStatusWidget(QtGui.QWidget):              self.tr("Traffic is being routed in the clear"))          self.ui.lblEIPStatus.show() +    @QtCore.Slot(dict)      def update_vpn_status(self, data=None):          """ -        SLOT -            TRIGGER: Signaler.eip_status_changed +        TRIGGERS: +            Signaler.eip_status_changed          Updates the download/upload labels based on the data provided by the          VPN thread. @@ -381,14 +383,18 @@ class EIPStatusWidget(QtGui.QWidget):          self.ui.btnUpload.setText(upload_str)          self.ui.btnDownload.setText(download_str) +    @QtCore.Slot(dict)      def update_vpn_state(self, vpn_state):          """ -        SLOT -            TRIGGER: Signaler.eip_state_changed +        TRIGGERS: +            Signaler.eip_state_changed          Updates the displayed VPN state based on the data provided by          the VPN thread. +        :param vpn_state: the state of the VPN +        :type vpn_state: dict +          Emits:              If the vpn_state is connected, we emit EIPConnection.qtsigs.              connected_signal diff --git a/src/leap/bitmask/gui/login.py b/src/leap/bitmask/gui/login.py index 4a483c32..ac7ad878 100644 --- a/src/leap/bitmask/gui/login.py +++ b/src/leap/bitmask/gui/login.py @@ -259,12 +259,16 @@ class LoginWidget(QtGui.QWidget):          """          self.ui.lnPassword.setFocus() -    def _current_provider_changed(self, param): +    @QtCore.Slot(int) +    def _current_provider_changed(self, idx):          """ -        SLOT -        TRIGGERS: self.ui.cmbProviders.currentIndexChanged +        TRIGGERS: +            self.ui.cmbProviders.currentIndexChanged + +        :param idx: the index of the new selected item +        :type idx: int          """ -        if param == (self.ui.cmbProviders.count() - 1): +        if idx == (self.ui.cmbProviders.count() - 1):              self.show_wizard.emit()              # Leave the previously selected provider in the combobox              prev_provider = 0 @@ -274,7 +278,7 @@ class LoginWidget(QtGui.QWidget):              self.ui.cmbProviders.setCurrentIndex(prev_provider)              self.ui.cmbProviders.blockSignals(False)          else: -            self._selected_provider_index = param +            self._selected_provider_index = idx      def start_login(self):          """ diff --git a/src/leap/bitmask/gui/mail_status.py b/src/leap/bitmask/gui/mail_status.py index 44a138e2..d3346780 100644 --- a/src/leap/bitmask/gui/mail_status.py +++ b/src/leap/bitmask/gui/mail_status.py @@ -184,10 +184,10 @@ class MailStatusWidget(QtGui.QWidget):          leap_assert_type(action_mail_status, QtGui.QAction)          self._action_mail_status = action_mail_status +    @QtCore.Slot()      def set_soledad_failed(self):          """ -        SLOT -        TRIGGER: +        TRIGGERS:              SoledadBootstrapper.soledad_failed          This method is called whenever soledad has a failure. @@ -195,10 +195,10 @@ class MailStatusWidget(QtGui.QWidget):          msg = self.tr("There was an unexpected problem with Soledad.")          self._set_mail_status(msg, ready=-1) +    @QtCore.Slot()      def set_soledad_invalid_auth_token(self):          """ -        SLOT -        TRIGGER: +        TRIGGERS:              SoledadBootstrapper.soledad_invalid_token          This method is called when the auth token is invalid @@ -250,10 +250,11 @@ class MailStatusWidget(QtGui.QWidget):          """          self._soledad_event.emit(req) +    @QtCore.Slot(object)      def _mail_handle_soledad_events_slot(self, req):          """ -        SLOT -        TRIGGER: _mail_handle_soledad_events +        TRIGGERS: +            _mail_handle_soledad_events          Reacts to an Soledad event @@ -284,10 +285,11 @@ class MailStatusWidget(QtGui.QWidget):          """          self._keymanager_event.emit(req) +    @QtCore.Slot(object)      def _mail_handle_keymanager_events_slot(self, req):          """ -        SLOT -        TRIGGER: _mail_handle_keymanager_events +        TRIGGERS: +            _mail_handle_keymanager_events          Reacts to an KeyManager event @@ -330,10 +332,11 @@ class MailStatusWidget(QtGui.QWidget):          """          self._smtp_event.emit(req) +    @QtCore.Slot(object)      def _mail_handle_smtp_events_slot(self, req):          """ -        SLOT -        TRIGGER: _mail_handle_smtp_events +        TRIGGERS: +            _mail_handle_smtp_events          Reacts to an SMTP event @@ -364,10 +367,11 @@ class MailStatusWidget(QtGui.QWidget):          """          self._imap_event.emit(req) +    @QtCore.Slot(object)      def _mail_handle_imap_events_slot(self, req):          """ -        SLOT -        TRIGGER: _mail_handle_imap_events +        TRIGGERS: +            _mail_handle_imap_events          Reacts to an IMAP event diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index e5c11eb7..e4443434 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -457,10 +457,11 @@ class MainWindow(QtGui.QMainWindow):          self._backend_connected_signals = {} +    @QtCore.Slot()      def _rejected_wizard(self):          """ -        SLOT -        TRIGGERS: self._wizard.rejected +        TRIGGERS: +            self._wizard.rejected          Called if the wizard has been cancelled or closed before          finishing. @@ -481,12 +482,12 @@ class MainWindow(QtGui.QMainWindow):              if self._wizard_firstrun:                  self._finish_init() +    @QtCore.Slot()      def _launch_wizard(self):          """ -        SLOT          TRIGGERS: -          self._login_widget.show_wizard -          self.ui.action_wizard.triggered +            self._login_widget.show_wizard +            self.ui.action_wizard.triggered          Also called in first run. @@ -509,11 +510,11 @@ class MainWindow(QtGui.QMainWindow):          self._wizard.finished.connect(self._wizard_finished)          self._settings.set_skip_first_run(True) +    @QtCore.Slot()      def _wizard_finished(self):          """ -        SLOT -        TRIGGERS -          self._wizard.finished +        TRIGGERS: +            self._wizard.finished          Called when the wizard has finished.          """ @@ -534,11 +535,11 @@ class MainWindow(QtGui.QMainWindow):                  return h          return None +    @QtCore.Slot()      def _show_logger_window(self):          """ -        SLOT          TRIGGERS: -          self.ui.action_show_logs.triggered +            self.ui.action_show_logs.triggered          Displays the window with the history of messages logged until now          and displays the new ones on arrival. @@ -555,9 +556,9 @@ class MainWindow(QtGui.QMainWindow):          else:              self._logger_window.setVisible(not self._logger_window.isVisible()) +    @QtCore.Slot()      def _show_AKM(self):          """ -        SLOT          TRIGGERS:              self.ui.action_advanced_key_management.triggered @@ -565,16 +566,22 @@ class MainWindow(QtGui.QMainWindow):          """          domain = self._login_widget.get_selected_provider()          logged_user = "{0}@{1}".format(self._logged_user, domain) -        self._akm = AdvancedKeyManagement( -            logged_user, self._keymanager, self._soledad) -        self._akm.show() +        has_mx = True +        if self._logged_user is not None: +            provider_config = self._get_best_provider_config() +            has_mx = provider_config.provides_mx() + +        akm = AdvancedKeyManagement( +            self, has_mx, logged_user, self._keymanager, self._soledad) +        akm.show() + +    @QtCore.Slot()      def _show_preferences(self):          """ -        SLOT          TRIGGERS: -          self.ui.btnPreferences.clicked (disabled for now) -          self.ui.action_preferences +            self.ui.btnPreferences.clicked (disabled for now) +            self.ui.action_preferences          Displays the preferences window.          """ @@ -586,9 +593,9 @@ class MainWindow(QtGui.QMainWindow):          preferences.show()          preferences.preferences_saved.connect(self._update_eip_enabled_status) +    @QtCore.Slot()      def _update_eip_enabled_status(self):          """ -        SLOT          TRIGGER:              PreferencesWindow.preferences_saved @@ -622,12 +629,12 @@ class MainWindow(QtGui.QMainWindow):          return eip_enabled +    @QtCore.Slot()      def _show_eip_preferences(self):          """ -        SLOT          TRIGGERS: -          self.ui.btnEIPPreferences.clicked -          self.ui.action_eip_preferences (disabled for now) +            self.ui.btnEIPPreferences.clicked +            self.ui.action_eip_preferences (disabled for now)          Displays the EIP preferences window.          """ @@ -647,22 +654,27 @@ class MainWindow(QtGui.QMainWindow):          """          self.new_updates.emit(req) +    @QtCore.Slot(object)      def _react_to_new_updates(self, req):          """ -        SLOT -        TRIGGER: self._new_updates_available +        TRIGGERS: +            self.new_updates          Displays the new updates label and sets the updates_content + +        :param req: Request type +        :type req: leap.common.events.events_pb2.SignalRequest          """          self.moveToThread(QtCore.QCoreApplication.instance().thread())          self.ui.lblNewUpdates.setVisible(True)          self.ui.btnMore.setVisible(True)          self._updates_content = req.content +    @QtCore.Slot()      def _updates_details(self):          """ -        SLOT -        TRIGGER: self.ui.btnMore.clicked +        TRIGGERS: +            self.ui.btnMore.clicked          Parses and displays the updates details          """ @@ -686,11 +698,11 @@ class MainWindow(QtGui.QMainWindow):                                        self.tr("Updates available"),                                        msg) +    @QtCore.Slot()      def _finish_init(self):          """ -        SLOT          TRIGGERS: -          self._wizard.accepted +            self._wizard.accepted          Also called at the end of the constructor if not first run. @@ -826,16 +838,21 @@ class MainWindow(QtGui.QMainWindow):          self._mail_status.set_systray(self._systray)          self._eip_status.set_systray(self._systray) -        hello = lambda: self._systray.showMessage( -            self.tr('Hello!'), -            self.tr('Bitmask has started in the tray.')) -        # we wait for the systray to be ready -        reactor.callLater(1, hello) +        if self._start_hidden: +            hello = lambda: self._systray.showMessage( +                self.tr('Hello!'), +                self.tr('Bitmask has started in the tray.')) +            # we wait for the systray to be ready +            reactor.callLater(1, hello) +    @QtCore.Slot(int)      def _tray_activated(self, reason=None):          """ -        SLOT -        TRIGGER: self._systray.activated +        TRIGGERS: +            self._systray.activated + +        :param reason: the reason why the tray got activated. +        :type reason: int          Displays the context menu from the tray icon          """ @@ -862,10 +879,11 @@ class MainWindow(QtGui.QMainWindow):          visible = self.isVisible() and self.isActiveWindow()          self._action_visible.setText(get_action(visible)) +    @QtCore.Slot()      def _toggle_visible(self):          """ -        SLOT -        TRIGGER: self._action_visible.triggered +        TRIGGERS: +            self._action_visible.triggered          Toggles the window visibility          """ @@ -909,10 +927,11 @@ class MainWindow(QtGui.QMainWindow):          if state is not None:              self.restoreState(state) +    @QtCore.Slot()      def _about(self):          """ -        SLOT -        TRIGGERS: self.ui.action_about_leap.triggered +        TRIGGERS: +            self.ui.action_about_leap.triggered          Display the About Bitmask dialog          """ @@ -936,10 +955,11 @@ class MainWindow(QtGui.QMainWindow):                      "<a href='https://leap.se'>More about LEAP"                      "</a>") % (VERSION, VERSION_HASH[:10], greet)) +    @QtCore.Slot()      def _help(self):          """ -        SLOT -        TRIGGERS: self.ui.action_help.triggered +        TRIGGERS: +            self.ui.action_help.triggered          Display the Bitmask help dialog.          """ @@ -1036,10 +1056,11 @@ class MainWindow(QtGui.QMainWindow):          provider = self._login_widget.get_selected_provider()          self._backend.setup_provider(provider) +    @QtCore.Slot(dict)      def _load_provider_config(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_download_provider_info +        TRIGGERS: +            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 @@ -1054,8 +1075,9 @@ class MainWindow(QtGui.QMainWindow):              self._backend.provider_bootstrap(selected_provider)          else:              logger.error(data[self._backend.ERROR_KEY]) -            self._login_widget.set_enabled(True) +            self._login_problem_provider() +    @QtCore.Slot()      def _login_problem_provider(self):          """          Warns the user about a problem with the provider during login. @@ -1064,11 +1086,11 @@ class MainWindow(QtGui.QMainWindow):              self.tr("Unable to login: Problem with provider"))          self._login_widget.set_enabled(True) +    @QtCore.Slot()      def _login(self):          """ -        SLOT          TRIGGERS: -          self._login_widget.login +            self._login_widget.login          Starts the login sequence. Which involves bootstrapping the          selected provider if the selection is valid (not empty), then @@ -1092,9 +1114,9 @@ class MainWindow(QtGui.QMainWindow):              if self._login_widget.start_login():                  self._download_provider_config() +    @QtCore.Slot(unicode)      def _authentication_error(self, msg):          """ -        SLOT          TRIGGERS:              Signaler.srp_auth_error              Signaler.srp_auth_server_error @@ -1110,11 +1132,11 @@ class MainWindow(QtGui.QMainWindow):          self._login_widget.set_enabled(True)          self.ui.action_create_new_account.setEnabled(True) +    @QtCore.Slot()      def _cancel_login(self):          """ -        SLOT          TRIGGERS: -          self._login_widget.cancel_login +            self._login_widget.cancel_login          Stops the login sequence.          """ @@ -1134,9 +1156,9 @@ class MainWindow(QtGui.QMainWindow):              self._soledad_defer.cancel()              self._soledad_defer = None +    @QtCore.Slot()      def _set_login_cancelled(self):          """ -        SLOT          TRIGGERS:              Signaler.prov_cancelled_setup fired by              self._backend.cancel_setup_provider() @@ -1147,10 +1169,11 @@ class MainWindow(QtGui.QMainWindow):          self._login_widget.set_status(self.tr("Log in cancelled by the user."))          self._login_widget.set_enabled(True) +    @QtCore.Slot(dict)      def _provider_config_loaded(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_check_api_certificate +        TRIGGERS: +            self._backend.signaler.prov_check_api_certificate          Once the provider configuration is loaded, this starts the SRP          authentication @@ -1166,15 +1189,14 @@ class MainWindow(QtGui.QMainWindow):              domain = self._provider_config.get_domain()              self._backend.login(domain, username, password)          else: -            self._login_widget.set_status( -                "Unable to login: Problem with provider")              logger.error(data[self._backend.ERROR_KEY]) -            self._login_widget.set_enabled(True) +            self._login_problem_provider() +    @QtCore.Slot()      def _authentication_finished(self):          """ -        SLOT -        TRIGGER: self._srp_auth.authentication_finished +        TRIGGERS: +            self._srp_auth.authentication_finished          Once the user is properly authenticated, try starting the EIP          service @@ -1285,12 +1307,12 @@ class MainWindow(QtGui.QMainWindow):      ###################################################################      # Service control methods: soledad +    @QtCore.Slot(dict)      def _soledad_intermediate_stage(self, data):          # TODO missing param docstring          """ -        SLOT          TRIGGERS: -          self._soledad_bootstrapper.download_config +            self._soledad_bootstrapper.download_config          If there was a problem, displays it, otherwise it does nothing.          This is used for intermediate bootstrapping stages, in case @@ -1320,12 +1342,12 @@ class MainWindow(QtGui.QMainWindow):              logger.warning("Max number of soledad initialization "                             "retries reached.") +    @QtCore.Slot(dict)      def _soledad_bootstrapped_stage(self, data):          """ -        SLOT          TRIGGERS: -          self._soledad_bootstrapper.gen_key -          self._soledad_bootstrapper.local_only_ready +            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 @@ -1364,7 +1386,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _start_smtp_bootstrapping(self):          """ -        SLOT          TRIGGERS:              self.soledad_ready          """ @@ -1385,7 +1406,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _stop_smtp_service(self):          """ -        SLOT          TRIGGERS:              self.logout          """ @@ -1398,7 +1418,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _start_imap_service(self):          """ -        SLOT          TRIGGERS:              self.soledad_ready          """ @@ -1428,7 +1447,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _fetch_incoming_mail(self):          """ -        SLOT          TRIGGERS:              self.mail_client_logged_in          """ @@ -1438,7 +1456,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _stop_imap_service(self):          """ -        SLOT          TRIGGERS:              self.logout          """ @@ -1487,9 +1504,9 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _on_eip_connection_connected(self):          """ -        SLOT          TRIGGERS:              self._eip_status.eip_connection_connected +          Emits the EIPConnection.qtsigs.connected_signal          This is a little workaround for connecting the vpn-connected @@ -1589,9 +1606,9 @@ class MainWindow(QtGui.QMainWindow):          self._backend.start_eip() +    @QtCore.Slot()      def _on_eip_connection_aborted(self):          """ -        SLOT          TRIGGERS:              Signaler.eip_connection_aborted          """ @@ -1667,7 +1684,6 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _stop_eip(self):          """ -        SLOT          TRIGGERS:            self._eip_connection.qtsigs.do_disconnect_signal (via state machine) @@ -1697,7 +1713,6 @@ class MainWindow(QtGui.QMainWindow):      def _on_eip_network_unreachable(self):          # XXX Should move to EIP Conductor          """ -        SLOT          TRIGGERS:              self._eip_connection.qtsigs.network_unreachable @@ -1711,7 +1726,7 @@ class MainWindow(QtGui.QMainWindow):      def _do_eip_restart(self):          # XXX Should move to EIP Conductor          """ -        SLOT +        TRIGGERS:              self._eip_connection.qtsigs.process_restart          Restart the connection. @@ -1729,9 +1744,9 @@ class MainWindow(QtGui.QMainWindow):          self._eip_status.set_eip_status("", error=error)          self._eip_status.set_eip_status_icon("error") +    @QtCore.Slot(int)      def _eip_finished(self, exitCode):          """ -        SLOT          TRIGGERS:              Signaler.eip_process_finished @@ -1794,8 +1809,6 @@ class MainWindow(QtGui.QMainWindow):          Start the EIP bootstrapping sequence if the client is configured to          do so.          """ -        provider_config = self._get_best_provider_config() -          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( @@ -1822,10 +1835,11 @@ class MainWindow(QtGui.QMainWindow):              # eip will not start, so we start soledad anyway              self._maybe_run_soledad_setup_checks() +    @QtCore.Slot(dict)      def _finish_eip_bootstrap(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.eip_client_certificate_ready +        TRIGGERS: +            self._backend.signaler.eip_client_certificate_ready          Starts the VPN thread if the eip configuration is properly          loaded @@ -1842,12 +1856,12 @@ class MainWindow(QtGui.QMainWindow):          # DO START EIP Connection!          self._eip_connection.qtsigs.do_connect_signal.emit() +    @QtCore.Slot(dict)      def _eip_intermediate_stage(self, data):          # TODO missing param          """ -        SLOT          TRIGGERS: -          self._backend.signaler.eip_config_ready +            self._backend.signaler.eip_config_ready          If there was a problem, displays it, otherwise it does nothing.          This is used for intermediate bootstrapping stages, in case @@ -1889,8 +1903,8 @@ class MainWindow(QtGui.QMainWindow):      @QtCore.Slot()      def _logout(self):          """ -        SLOT -        TRIGGER: self._login_widget.logout +        TRIGGERS: +            self._login_widget.logout          Starts the logout sequence          """ @@ -1907,10 +1921,11 @@ class MainWindow(QtGui.QMainWindow):          self._backend.logout()          self.logout.emit() +    @QtCore.Slot()      def _logout_error(self):          """ -        SLOT -        TRIGGER: self._srp_auth.logout_error +        TRIGGER: +            self._srp_auth.logout_error          Inform the user about a logout error.          """ @@ -1919,10 +1934,11 @@ class MainWindow(QtGui.QMainWindow):          self._login_widget.set_status(              self.tr("Something went wrong with the logout.")) +    @QtCore.Slot()      def _logout_ok(self):          """ -        SLOT -        TRIGGER: self._srp_auth.logout_ok +        TRIGGER: +            self._srp_auth.logout_ok          Switches the stackedWidget back to the login stage after          logging out @@ -1934,15 +1950,14 @@ class MainWindow(QtGui.QMainWindow):          self._login_widget.logged_out()          self._mail_status.mail_state_disabled() +    @QtCore.Slot(dict)      def _intermediate_stage(self, data):          # TODO this method name is confusing as hell.          """ -        SLOT          TRIGGERS: -          self._backend.signaler.prov_name_resolution -          self._backend.signaler.prov_https_connection -          self._backend.signaler.prov_download_ca_cert -          self._backend.signaler.eip_config_ready +            self._backend.signaler.prov_name_resolution +            self._backend.signaler.prov_https_connection +            self._backend.signaler.prov_download_ca_cert          If there was a problem, displays it, otherwise it does nothing.          This is used for intermediate bootstrapping stages, in case @@ -1950,10 +1965,8 @@ class MainWindow(QtGui.QMainWindow):          """          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)              logger.error(data[self._backend.ERROR_KEY]) +            self._login_problem_provider()      #      # window handling methods @@ -1967,9 +1980,9 @@ class MainWindow(QtGui.QMainWindow):              raise_window_ack()          self.raise_window.emit() +    @QtCore.Slot()      def _do_raise_mainwindow(self):          """ -        SLOT          TRIGGERS:              self._on_raise_window_event @@ -2033,9 +2046,10 @@ class MainWindow(QtGui.QMainWindow):          # first thing to do quitting, hide the mainwindow and show tooltip.          self.hide() -        self._systray.showMessage( -            self.tr('Quitting...'), -            self.tr('The app is quitting, please wait.')) +        if self._systray is not None: +            self._systray.showMessage( +                self.tr('Quitting...'), +                self.tr('The app is quitting, please wait.'))          # explicitly process events to display tooltip immediately          QtCore.QCoreApplication.processEvents() diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index f6bd1ed3..014a0a4f 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -89,9 +89,9 @@ class PreferencesWindow(QtGui.QDialog):          self._select_provider_by_name(domain) +    @QtCore.Slot()      def _is_logged_in(self):          """ -        SLOT          TRIGGERS:              Signaler.srp_status_logged_in @@ -123,9 +123,9 @@ class PreferencesWindow(QtGui.QDialog):          self.ui.gbPasswordChange.setEnabled(pw_enabled) +    @QtCore.Slot()      def _not_logged_in(self):          """ -        SLOT          TRIGGERS:              Signaler.srp_status_not_logged_in @@ -136,9 +136,9 @@ class PreferencesWindow(QtGui.QDialog):          self._set_password_change_status(msg)          self.ui.gbPasswordChange.setEnabled(False) +    @QtCore.Slot()      def set_soledad_ready(self):          """ -        SLOT          TRIGGERS:              parent.soledad_ready @@ -183,9 +183,9 @@ class PreferencesWindow(QtGui.QDialog):          self.ui.leNewPassword2.setEnabled(not disable)          self.ui.pbChangePassword.setEnabled(not disable) +    @QtCore.Slot()      def _change_password(self):          """ -        SLOT          TRIGGERS:              self.ui.pbChangePassword.clicked @@ -207,11 +207,11 @@ class PreferencesWindow(QtGui.QDialog):          self._set_changing_password(True)          self._backend.change_password(current_password, new_password) +    @QtCore.Slot()      def _change_password_ok(self):          """ -        SLOT          TRIGGERS: -          self._backend.signaler.srp_password_change_ok +            self._backend.signaler.srp_password_change_ok          Callback used to display a successfully changed password.          """ @@ -229,12 +229,12 @@ class PreferencesWindow(QtGui.QDialog):          self._clear_password_inputs()          self._set_changing_password(False) +    @QtCore.Slot(unicode)      def _change_password_problem(self, msg):          """ -        SLOT          TRIGGERS: -          self._backend.signaler.srp_password_change_error -          self._backend.signaler.srp_password_change_badpw +            self._backend.signaler.srp_password_change_error +            self._backend.signaler.srp_password_change_badpw          Callback used to display an error on changing password. @@ -287,10 +287,12 @@ class PreferencesWindow(QtGui.QDialog):          provider_index = self.ui.cbProvidersServices.findText(name)          self.ui.cbProvidersServices.setCurrentIndex(provider_index) +    @QtCore.Slot(str, int)      def _service_selection_changed(self, service, state):          """ -        SLOT -        TRIGGER: service_checkbox.stateChanged +        TRIGGERS: +            service_checkbox.stateChanged +          Adds the service to the state if the state is checked, removes          it otherwise @@ -309,9 +311,9 @@ class PreferencesWindow(QtGui.QDialog):          # We hide the maybe-visible status label after a change          self.ui.lblProvidersServicesStatus.setVisible(False) +    @QtCore.Slot(str)      def _populate_services(self, domain):          """ -        SLOT          TRIGGERS:              self.ui.cbProvidersServices.currentIndexChanged[unicode] @@ -368,9 +370,9 @@ class PreferencesWindow(QtGui.QDialog):                  logger.error("Something went wrong while trying to "                               "load service %s" % (service,)) +    @QtCore.Slot(str)      def _save_enabled_services(self, provider):          """ -        SLOT          TRIGGERS:              self.ui.pbSaveServices.clicked diff --git a/src/leap/bitmask/gui/twisted_main.py b/src/leap/bitmask/gui/twisted_main.py index ece32ca2..f39d0bbe 100644 --- a/src/leap/bitmask/gui/twisted_main.py +++ b/src/leap/bitmask/gui/twisted_main.py @@ -22,9 +22,6 @@ import logging  from twisted.internet import error, reactor  from PySide import QtCore -# Resist the temptation of putting the import reactor here, -# it will raise an "reactor already imported" error. -  logger = logging.getLogger(__name__) @@ -32,7 +29,11 @@ def stop():      logger.debug("Really stoping all the things...")      QtCore.QCoreApplication.sendPostedEvents()      QtCore.QCoreApplication.flush() -    reactor.stop() +    try: +        reactor.stop() +        logger.debug('Twisted reactor stopped') +    except error.ReactorNotRunning: +        logger.debug('Twisted reactor not running')      logger.debug("Done stopping all the things.") @@ -43,8 +44,4 @@ def quit(app):      :param app: the main qt QApplication instance.      :type app: QtCore.QApplication      """ -    logger.debug('Stopping twisted reactor') -    try: -        reactor.callLater(0, stop) -    except error.ReactorNotRunning: -        logger.debug('Reactor not running') +    reactor.callLater(0, stop) diff --git a/src/leap/bitmask/gui/ui/advanced_key_management.ui b/src/leap/bitmask/gui/ui/advanced_key_management.ui index 1112670f..3b567347 100644 --- a/src/leap/bitmask/gui/ui/advanced_key_management.ui +++ b/src/leap/bitmask/gui/ui/advanced_key_management.ui @@ -1,7 +1,7 @@  <?xml version="1.0" encoding="UTF-8"?>  <ui version="4.0">   <class>AdvancedKeyManagement</class> - <widget class="QWidget" name="AdvancedKeyManagement"> + <widget class="QDialog" name="AdvancedKeyManagement">    <property name="geometry">     <rect>      <x>0</x> diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index ebcee400..020a58e2 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -24,6 +24,7 @@ from functools import partial  from PySide import QtCore, QtGui +from leap.bitmask.config import flags  from leap.bitmask.config.leapsettings import LeapSettings  from leap.bitmask.config.providerconfig import ProviderConfig  from leap.bitmask.provider import get_provider_path @@ -145,8 +146,7 @@ class Wizard(QtGui.QWizard):      @QtCore.Slot()      def _wizard_finished(self):          """ -        SLOT -        TRIGGER: +        TRIGGERS:              self.finished          This method is called when the wizard is accepted or rejected. @@ -210,15 +210,19 @@ class Wizard(QtGui.QWizard):      def get_services(self):          return self._selected_services -    @QtCore.Slot() +    @QtCore.Slot(unicode)      def _enable_check(self, reset=True):          """ -        SLOT -        TRIGGER: +        TRIGGERS:              self.ui.lnProvider.textChanged          Enables/disables the 'check' button in the SELECT_PROVIDER_PAGE          depending on the lnProvider content. + +        :param reset: this contains the text of the line edit, and when is +                      called directly defines whether we want to reset the +                      checks. +        :type reset: unicode or bool          """          enabled = len(self.ui.lnProvider.text()) != 0          enabled = enabled or self.ui.rbExistingProvider.isChecked() @@ -282,11 +286,11 @@ class Wizard(QtGui.QWizard):          # register button          self.ui.btnRegister.setVisible(visible) +    @QtCore.Slot()      def _registration_finished(self):          """ -        SLOT          TRIGGERS: -          self._backend.signaler.srp_registration_finished +            self._backend.signaler.srp_registration_finished          The registration has finished successfully, so we do some final steps.          """ @@ -308,11 +312,11 @@ class Wizard(QtGui.QWizard):          self.page(self.REGISTER_USER_PAGE).set_completed()          self.button(QtGui.QWizard.BackButton).setEnabled(False) +    @QtCore.Slot()      def _registration_failed(self):          """ -        SLOT          TRIGGERS: -          self._backend.signaler.srp_registration_failed +            self._backend.signaler.srp_registration_failed          The registration has failed, so we report the problem.          """ @@ -322,11 +326,11 @@ class Wizard(QtGui.QWizard):          self._set_register_status(error_msg, error=True)          self.ui.btnRegister.setEnabled(True) +    @QtCore.Slot()      def _registration_taken(self):          """ -        SLOT          TRIGGERS: -          self._backend.signaler.srp_registration_taken +            self._backend.signaler.srp_registration_taken          The requested username is taken, warn the user about that.          """ @@ -358,7 +362,8 @@ class Wizard(QtGui.QWizard):          self.ui.lblProviderSelectStatus.setText("")          self._domain = None          self.button(QtGui.QWizard.NextButton).setEnabled(False) -        self.page(self.SELECT_PROVIDER_PAGE).set_completed(False) +        self.page(self.SELECT_PROVIDER_PAGE).set_completed( +            flags.SKIP_WIZARD_CHECKS)      def _reset_provider_setup(self):          """ @@ -368,12 +373,12 @@ class Wizard(QtGui.QWizard):          self.ui.lblCheckCaFpr.setPixmap(None)          self.ui.lblCheckApiCert.setPixmap(None) +    @QtCore.Slot()      def _check_provider(self):          """ -        SLOT          TRIGGERS: -          self.ui.btnCheck.clicked -          self.ui.lnProvider.returnPressed +            self.ui.btnCheck.clicked +            self.ui.lnProvider.returnPressed          Starts the checks for a given provider          """ @@ -403,10 +408,10 @@ class Wizard(QtGui.QWizard):          self._provider_select_defer = self._backend.\              setup_provider(self._domain) +    @QtCore.Slot(bool)      def _skip_provider_checks(self, skip):          """ -        SLOT -        Triggered: +        TRIGGERS:              self.ui.rbExistingProvider.toggled          Allows the user to move to the next page without make any checks, @@ -447,10 +452,11 @@ class Wizard(QtGui.QWizard):              label.setPixmap(self.ERROR_ICON)              logger.error(error) +    @QtCore.Slot(dict)      def _name_resolution(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_name_resolution +        TRIGGERS: +            self._backend.signaler.prov_name_resolution          Sets the status for the name resolution check          """ @@ -466,10 +472,11 @@ class Wizard(QtGui.QWizard):          self.ui.btnCheck.setEnabled(not passed)          self.ui.lnProvider.setEnabled(not passed) +    @QtCore.Slot(dict)      def _https_connection(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_https_connection +        TRIGGERS: +            self._backend.signaler.prov_https_connection          Sets the status for the https connection check          """ @@ -485,10 +492,11 @@ class Wizard(QtGui.QWizard):          self.ui.btnCheck.setEnabled(not passed)          self.ui.lnProvider.setEnabled(not passed) +    @QtCore.Slot(dict)      def _download_provider_info(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_download_provider_info +        TRIGGERS: +            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 @@ -519,10 +527,11 @@ class Wizard(QtGui.QWizard):          else:              self.ui.cbProviders.setEnabled(True) +    @QtCore.Slot(dict)      def _download_ca_cert(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_download_ca_cert +        TRIGGERS: +            self._backend.signaler.prov_download_ca_cert          Sets the status for the download of the CA certificate check          """ @@ -531,10 +540,11 @@ class Wizard(QtGui.QWizard):          if passed:              self.ui.lblCheckCaFpr.setPixmap(self.QUESTION_ICON) +    @QtCore.Slot(dict)      def _check_ca_fingerprint(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_check_ca_fingerprint +        TRIGGERS: +            self._backend.signaler.prov_check_ca_fingerprint          Sets the status for the CA fingerprint check          """ @@ -543,10 +553,11 @@ class Wizard(QtGui.QWizard):          if passed:              self.ui.lblCheckApiCert.setPixmap(self.QUESTION_ICON) +    @QtCore.Slot(dict)      def _check_api_certificate(self, data):          """ -        SLOT -        TRIGGER: self._backend.signaler.prov_check_api_certificate +        TRIGGERS: +            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 @@ -556,10 +567,12 @@ class Wizard(QtGui.QWizard):                              True, self.SETUP_PROVIDER_PAGE)          self._provider_setup_ok = True +    @QtCore.Slot(str, int)      def _service_selection_changed(self, service, state):          """ -        SLOT -        TRIGGER: service_checkbox.stateChanged +        TRIGGERS: +            service_checkbox.stateChanged +          Adds the service to the state if the state is checked, removes          it otherwise @@ -604,12 +617,16 @@ class Wizard(QtGui.QWizard):                      self.tr("Something went wrong while trying to "                              "load service %s" % (service,))) +    @QtCore.Slot(int)      def _current_id_changed(self, pageId):          """ -        SLOT -        TRIGGER: self.currentIdChanged +        TRIGGERS: +            self.currentIdChanged          Prepares the pages when they appear + +        :param pageId: the new current page id. +        :type pageId: int          """          if pageId == self.SELECT_PROVIDER_PAGE:              self._clear_register_widgets() diff --git a/src/leap/bitmask/services/mail/conductor.py b/src/leap/bitmask/services/mail/conductor.py index 79f324dc..c1761afa 100644 --- a/src/leap/bitmask/services/mail/conductor.py +++ b/src/leap/bitmask/services/mail/conductor.py @@ -238,12 +238,11 @@ class SMTPControl(object):              self._smtp_port.stopListening()              self._smtp_service.doStop() -    @QtCore.Slot() +    @QtCore.Slot(dict)      def smtp_bootstrapped_stage(self, data):          """ -        SLOT          TRIGGERS: -          self.smtp_bootstrapper.download_config +            self.smtp_bootstrapper.download_config          If there was a problem, displays it, otherwise it does nothing.          This is used for intermediate bootstrapping stages, in case diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index 8aacc85d..84af4e8d 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -62,6 +62,10 @@ def build_parser():      parser.add_argument('-H', '--start-hidden', default=False,                          action="store_true", dest="start_hidden",                          help='Starts the application just in the taskbar.') +    parser.add_argument('-S', '--skip-wizard-checks', default=False, +                        action="store_true", dest="skip_wizard_checks", +                        help='Skips the provider checks in the wizard (use ' +                             'for testing purposes only).')      # openvpn options      parser.add_argument('--openvpn-verbosity', nargs='?', | 
