From 68190ead759847c8a3e890579f7fb6b8553cbaf0 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 10 Dec 2013 18:22:53 -0400 Subject: update relnotes --- relnotes.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/relnotes.txt b/relnotes.txt index 98af7a30..44b3b488 100644 --- a/relnotes.txt +++ b/relnotes.txt @@ -1,4 +1,4 @@ -ANNOUNCING Bitmask, the internet encryption toolkit, release 0.3.7 +ANNOUNCING Bitmask, the Internet Encryption Toolkit, release 0.3.8 The LEAP team is pleased to announce the immediate availability of version 0.3.8 of Bitmask, the Internet Encryption Toolkit, codename @@ -65,14 +65,16 @@ the two. INSTALLATION We distribute the current version of Bitmask as standalone bundles for -GNU/Linux and OSX, but it is likely that you are able to run it under -other systems, specially if you are skillful and patience is one of -your virtues. +GNU/Linux, OSX and Windows, but it is likely that you are able to run +it under other systems, specially if you are skillful and patience is +one of your virtues. Have a look at "docs/user/install.rst". -Packages will be soon provided for debian and ubuntu, and the release -of windows bundles will be resumed shortly. +Packages are also provided for debian and ubuntu, add the leap +repository to your apt sources: + +deb http://deb.leap.se/debian wheezy main We will love to hear if you are interested in help making packages available for any other system. -- cgit v1.2.3 From 58e4317945ac857bb57d2a20e38fe9a632b19df0 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 12 Dec 2013 10:14:28 -0300 Subject: Disable and stop EIP on setting save. --- changes/bug-4670_disable-eip-and-disable-actions | 2 + src/leap/bitmask/gui/mainwindow.py | 56 +++++++++++++++++------- src/leap/bitmask/gui/preferenceswindow.py | 3 ++ 3 files changed, 46 insertions(+), 15 deletions(-) create mode 100644 changes/bug-4670_disable-eip-and-disable-actions diff --git a/changes/bug-4670_disable-eip-and-disable-actions b/changes/bug-4670_disable-eip-and-disable-actions new file mode 100644 index 00000000..7f11b3bb --- /dev/null +++ b/changes/bug-4670_disable-eip-and-disable-actions @@ -0,0 +1,2 @@ +- Disable and stop EIP when you set EIP as disabled in the preferences dialog. + Closes #4670. diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 929919ac..44fee6b5 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -471,12 +471,49 @@ class MainWindow(QtGui.QMainWindow): Displays the preferences window. """ - preferences_window = PreferencesWindow( + preferences = PreferencesWindow( self, self._srp_auth, self._provider_config, self._soledad, self._login_widget.get_selected_provider()) - self.soledad_ready.connect(preferences_window.set_soledad_ready) - preferences_window.show() + self.soledad_ready.connect(preferences.set_soledad_ready) + preferences.show() + preferences.preferences_saved.connect(self._update_eip_enabled_status) + + def _update_eip_enabled_status(self): + """ + SLOT + TRIGGER: + PreferencesWindow.preferences_saved + + Enable or disable the EIP start/stop actions and stop EIP if the user + disabled that service. + + :returns: if the eip actions were enabled or disabled + :rtype: bool + """ + settings = self._settings + default_provider = settings.get_defaultprovider() + enabled_services = [] + if default_provider is not None: + enabled_services = settings.get_enabled_services(default_provider) + + eip_enabled = False + if EIP_SERVICE in enabled_services: + should_autostart = settings.get_autostart_eip() + if should_autostart and default_provider is not None: + self._eip_status.enable_eip_start() + self._eip_status.set_eip_status("") + eip_enabled = True + else: + # we don't have an usable provider + # so the user needs to log in first + self._eip_status.disable_eip_start() + else: + self._stop_eip() + self._eip_status.disable_eip_start() + self._eip_status.set_eip_status(self.tr("Disabled")) + + return eip_enabled def _show_eip_preferences(self): """ @@ -1177,21 +1214,10 @@ class MainWindow(QtGui.QMainWindow): """ settings = self._settings - should_autostart = settings.get_autostart_eip() - if not should_autostart: - logger.debug('Will not autostart EIP since it is setup ' - 'to not to do it') - self.eip_needs_login.emit() + if not self._update_eip_enabled_status(): return default_provider = settings.get_defaultprovider() - - if default_provider is None: - logger.info("Cannot autostart Encrypted Internet because there is " - "no default provider configured") - self.eip_needs_login.emit() - return - self._enabled_services = settings.get_enabled_services( default_provider) diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index b4bddef2..517a90c4 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -42,6 +42,8 @@ class PreferencesWindow(QtGui.QDialog): """ Window that displays the preferences. """ + preferences_saved = QtCore.Signal() + def __init__(self, parent, srp_auth, provider_config, soledad, domain): """ :param parent: parent object of the PreferencesWindow. @@ -369,6 +371,7 @@ class PreferencesWindow(QtGui.QDialog): "Services settings for provider '{0}' saved.".format(provider)) logger.debug(msg) self._set_providers_services_status(msg, success=True) + self.preferences_saved.emit() def _get_provider_config(self, domain): """ -- cgit v1.2.3 From 722cf92e10cf73e79e54f463aa19b2d01bd3cf75 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 11 Dec 2013 22:58:59 -0800 Subject: move login widget to top of main window, move preference buttons to menu. --- ...re-4753-move-login-widget-to-top-of-main-window | 2 + src/leap/bitmask/gui/mainwindow.py | 11 +- src/leap/bitmask/gui/ui/mainwindow.ui | 198 +++++++-------------- 3 files changed, 78 insertions(+), 133 deletions(-) create mode 100644 changes/feature-4753-move-login-widget-to-top-of-main-window diff --git a/changes/feature-4753-move-login-widget-to-top-of-main-window b/changes/feature-4753-move-login-widget-to-top-of-main-window new file mode 100644 index 00000000..0ab23b24 --- /dev/null +++ b/changes/feature-4753-move-login-widget-to-top-of-main-window @@ -0,0 +1,2 @@ +-- Minor UI changes: re-arrange main window so that the login widget is at the top and preferences are available under the menu. + diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 44fee6b5..1c80cce8 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -248,6 +248,8 @@ class MainWindow(QtGui.QMainWindow): self._soledad_bootstrapper.soledad_failed.connect( self._mail_status.set_soledad_failed) + self.ui.action_preferences.triggered.connect(self._show_preferences) + self.ui.action_eip_preferences.triggered.connect(self._show_eip_preferences) self.ui.action_about_leap.triggered.connect(self._about) self.ui.action_quit.triggered.connect(self.quit) self.ui.action_wizard.triggered.connect(self._launch_wizard) @@ -279,8 +281,9 @@ class MainWindow(QtGui.QMainWindow): self._action_visible = QtGui.QAction(self.tr("Hide Main Window"), self) self._action_visible.triggered.connect(self._toggle_visible) - self.ui.btnPreferences.clicked.connect(self._show_preferences) - self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences) + # disable buttons for now, may come back later. + # self.ui.btnPreferences.clicked.connect(self._show_preferences) + # self.ui.btnEIPPreferences.clicked.connect(self._show_eip_preferences) self._enabled_services = [] @@ -467,7 +470,8 @@ class MainWindow(QtGui.QMainWindow): """ SLOT TRIGGERS: - self.ui.btnPreferences.clicked + self.ui.btnPreferences.clicked (disabled for now) + self.ui.action_preferences Displays the preferences window. """ @@ -520,6 +524,7 @@ class MainWindow(QtGui.QMainWindow): SLOT TRIGGERS: self.ui.btnEIPPreferences.clicked + self.ui.action_eip_preferences (disabled for now) Displays the EIP preferences window. """ diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index 3b83788e..4dc39d03 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -85,108 +85,8 @@ 0 - - - - - 0 - - - 0 - - - - - - 0 - 0 - - - - QFrame{background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0));} - - - - 24 - - - 24 - - - - - - 16 - 75 - true - - - - background-color: rgba(255, 255, 255, 0); - - - Encrypted Internet - - - - - - - - 48 - 20 - - - - - - - - - - - :/images/black/32/gear.png:/images/black/32/gear.png - - - false - - - false - - - false - - - - - - - - - - 12 - - - 0 - - - 12 - - - 0 - - - - - - - - - - Qt::Horizontal - - - + + @@ -199,9 +99,7 @@ false - QFrame{ -background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(160, 160, 160, 128), stop:1 rgba(255, 255, 255, 0)); -} + background-color: rgba(0,0,0,20); border-bottom: 1px solid rgba(0,0,0,30); @@ -214,36 +112,15 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb - 16 75 true - background-color: rgba(255, 255, 255, 0); - - - Login - - - - - - - - 48 - 20 - - - - + background-color: rgba(255, 255, 255, 0); border: none; - - - - - :/images/black/32/gear.png:/images/black/32/gear.png + Please Log In @@ -257,13 +134,54 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb + - + Qt::Horizontal + + + + + + + 0 + + + 0 + + + + + 12 + + + 0 + + + 12 + + + 0 + + + + + + + + + + + Qt::Horizontal + + + + + @@ -286,6 +204,15 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb + + + + + Qt::Horizontal + + + + @@ -390,6 +317,9 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb + + + @@ -406,8 +336,16 @@ background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgb + + true + + + Account Preferences... + + + - Preferences... + Internet Preferences... -- cgit v1.2.3 From 551e02141d5ffa72b76d2d1541bda2f877c8d379 Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 11 Dec 2013 23:01:43 -0800 Subject: modified eip widget: change font to look better when under login widget, tightened spacing, moved upload and download icons to the buttons themselves, get rid of extra left whitespace padding on bandwidth status. --- src/leap/bitmask/gui/eip_status.py | 4 +-- src/leap/bitmask/gui/ui/eip_status.ui | 66 ++++++----------------------------- 2 files changed, 13 insertions(+), 57 deletions(-) diff --git a/src/leap/bitmask/gui/eip_status.py b/src/leap/bitmask/gui/eip_status.py index 4b4d360f..92bb623e 100644 --- a/src/leap/bitmask/gui/eip_status.py +++ b/src/leap/bitmask/gui/eip_status.py @@ -41,8 +41,8 @@ class EIPStatusWidget(QtGui.QWidget): EIP Status widget that displays the current state of the EIP service """ DISPLAY_TRAFFIC_RATES = True - RATE_STR = "%14.2f KB/s" - TOTAL_STR = "%14.2f Kb" + RATE_STR = "%1.2f KB/s" + TOTAL_STR = "%1.2f Kb" eip_connection_connected = QtCore.Signal() diff --git a/src/leap/bitmask/gui/ui/eip_status.ui b/src/leap/bitmask/gui/ui/eip_status.ui index d078ca0c..64821ad6 100644 --- a/src/leap/bitmask/gui/ui/eip_status.ui +++ b/src/leap/bitmask/gui/ui/eip_status.ui @@ -25,6 +25,9 @@ + + 0 + @@ -75,13 +78,6 @@ 0 - - - 14 - 75 - true - - Traffic is being routed in the clear @@ -124,12 +120,6 @@ - - - 16777215 - 32 - - 0 @@ -145,16 +135,6 @@ QLayout::SetDefaultConstraint - - - - - - - :/images/light/16/down-arrow.png - - - @@ -175,25 +155,18 @@ 16777215 - - - 11 - 75 - true - - PointingHandCursor - - text-align: left; - 0.0 KB/s true + + :/images/light/16/down-arrow.png + @@ -206,22 +179,12 @@ - 10 + 20 20 - - - - - - - :/images/light/16/up-arrow.png - - - @@ -242,25 +205,18 @@ 16777215 - - - 11 - 75 - true - - PointingHandCursor - - text-align: left; - 0.0 KB/s true + + :/images/light/16/up-arrow.png + @@ -271,7 +227,7 @@ 0 - 20 + 0 -- cgit v1.2.3 From 714c9afedfaee3c0288536cb1c3b138b20bab03c Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 11 Dec 2013 23:02:18 -0800 Subject: login widget: tighten spacing, get rid of non-standard font family. --- src/leap/bitmask/gui/ui/login.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/leap/bitmask/gui/ui/login.ui b/src/leap/bitmask/gui/ui/login.ui index e7ca1652..7e8f9daf 100644 --- a/src/leap/bitmask/gui/ui/login.ui +++ b/src/leap/bitmask/gui/ui/login.ui @@ -215,7 +215,7 @@ 0 - 24 + 0 @@ -255,7 +255,7 @@ color: rgb(132, 132, 132); -font: 75 12pt "Lucida Grande"; +font: 75 12pt; -- cgit v1.2.3 From 2a8632e60a538828e8c5b417c5b02fbdaaccc88d Mon Sep 17 00:00:00 2001 From: elijah Date: Wed, 11 Dec 2013 23:06:03 -0800 Subject: main window ui: remove unused statusbar --- src/leap/bitmask/gui/ui/mainwindow.ui | 1 - 1 file changed, 1 deletion(-) diff --git a/src/leap/bitmask/gui/ui/mainwindow.ui b/src/leap/bitmask/gui/ui/mainwindow.ui index 4dc39d03..ce05f8f3 100644 --- a/src/leap/bitmask/gui/ui/mainwindow.ui +++ b/src/leap/bitmask/gui/ui/mainwindow.ui @@ -334,7 +334,6 @@ - true -- cgit v1.2.3 From 97700c4278fdd67123317584dd66c946ee9340a8 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 12 Dec 2013 16:53:35 -0300 Subject: Add view for stored public keys. Closes #4734. --- changes/bug-4734_akm-stored-public-keys-view | 1 + src/leap/bitmask/gui/advanced_key_management.py | 22 +++++- src/leap/bitmask/gui/ui/advanced_key_management.ui | 79 ++++++++++++++++------ 3 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 changes/bug-4734_akm-stored-public-keys-view diff --git a/changes/bug-4734_akm-stored-public-keys-view b/changes/bug-4734_akm-stored-public-keys-view new file mode 100644 index 00000000..bdba2cb2 --- /dev/null +++ b/changes/bug-4734_akm-stored-public-keys-view @@ -0,0 +1 @@ +- Advanced Key Management: add view for stored public keys. Closes #4734. diff --git a/src/leap/bitmask/gui/advanced_key_management.py b/src/leap/bitmask/gui/advanced_key_management.py index 2c0fa034..8f15719d 100644 --- a/src/leap/bitmask/gui/advanced_key_management.py +++ b/src/leap/bitmask/gui/advanced_key_management.py @@ -50,7 +50,8 @@ class AdvancedKeyManagement(QtGui.QWidget): # if Soledad is not started yet if sameProxiedObjects(soledad, None): - self.ui.container.setEnabled(False) + self.ui.gbMyKeyPair.setEnabled(False) + self.ui.gbStoredPublicKeys.setEnabled(False) msg = self.tr("NOTE: " "To use this, you need to enable/start {0}.") msg = msg.format(get_service_display_name(MX_SERVICE)) @@ -79,6 +80,12 @@ class AdvancedKeyManagement(QtGui.QWidget): self.ui.pbImportKeys.clicked.connect(self._import_keys) self.ui.pbExportKeys.clicked.connect(self._export_keys) + # Stretch columns to content + self.ui.twPublicKeys.horizontalHeader().setResizeMode( + 0, QtGui.QHeaderView.Stretch) + + self._list_keys() + def _import_keys(self): """ Imports the user's key pair. @@ -183,3 +190,16 @@ class AdvancedKeyManagement(QtGui.QWidget): return else: logger.debug('Export canceled by the user.') + + def _list_keys(self): + """ + Loads all the public keys stored in the local db to the keys table. + """ + keys = self._keymanager.get_all_keys_in_local_db() + + keys_table = self.ui.twPublicKeys + for key in keys: + row = keys_table.rowCount() + keys_table.insertRow(row) + keys_table.setItem(row, 0, QtGui.QTableWidgetItem(key.address)) + keys_table.setItem(row, 1, QtGui.QTableWidgetItem(key.key_id)) diff --git a/src/leap/bitmask/gui/ui/advanced_key_management.ui b/src/leap/bitmask/gui/ui/advanced_key_management.ui index d61aa87e..1112670f 100644 --- a/src/leap/bitmask/gui/ui/advanced_key_management.ui +++ b/src/leap/bitmask/gui/ui/advanced_key_management.ui @@ -6,8 +6,8 @@ 0 0 - 431 - 188 + 504 + 546 @@ -17,10 +17,13 @@ :/images/mask-icon.png:/images/mask-icon.png - - - - + + + + + My key pair + + @@ -90,20 +93,7 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - + @@ -135,9 +125,56 @@ + leKeyID + leUser + leFingerprint + label_3 + label_5 + label + + + + + + Stored public keys + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideRight + + + true + + + + Email + + + + + Key ID + + + + + - + -- cgit v1.2.3 From 8536638d0f57aed211c73c178b41757e28ee8a43 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 17 Dec 2013 15:56:54 -0400 Subject: display mail version too --- src/leap/bitmask/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 3bb9c8c3..719ec8ad 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -55,6 +55,7 @@ from leap.bitmask.util.leap_log_handler import LeapLogHandler from leap.bitmask.util.streamtologger import StreamToLogger from leap.bitmask.platform_init import IS_WIN from leap.common.events import server as event_server +from leap.mail import __version__ as MAIL_VERSION import codecs codecs.register(lambda name: codecs.lookup('utf-8') @@ -170,6 +171,7 @@ def main(): if opts.version: print "Bitmask version: %s" % (VERSION,) + print "leap.mail version: %s" % (MAIL_VERSION,) sys.exit(0) standalone = opts.standalone @@ -217,6 +219,7 @@ def main(): logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') logger.info('Bitmask version %s', VERSION) + logger.info('leap.mail version %s', MAIL_VERSION) logger.info('~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~') logger.info('Starting app') -- cgit v1.2.3 From ac55fee7ef10b5e760d72b0a98ef5dd6925fe72e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 17 Dec 2013 15:57:30 -0400 Subject: take get_db_paths function out of class --- .../services/soledad/soledadbootstrapper.py | 54 +++++++++++----------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index d078ae96..a92c24a0 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -59,6 +59,33 @@ class SoledadInitError(Exception): message = "Error while initializing Soledad" +def get_db_paths(uuid): + """ + Returns the secrets and local db paths needed for soledad + initialization + + :param uuid: uuid for user + :type uuid: str + + :return: a tuple with secrets, local_db paths + :rtype: tuple + """ + prefix = os.path.join(get_path_prefix(), "leap", "soledad") + secrets = "%s/%s.secret" % (prefix, uuid) + local_db = "%s/%s.db" % (prefix, uuid) + + # We remove an empty file if found to avoid complains + # about the db not being properly initialized + if is_file(local_db) and is_empty_file(local_db): + try: + os.remove(local_db) + except OSError: + logger.warning( + "Could not remove empty file %s" + % local_db) + return secrets, local_db + + class SoledadBootstrapper(AbstractBootstrapper): """ Soledad init procedure @@ -127,31 +154,6 @@ class SoledadBootstrapper(AbstractBootstrapper): """ self._soledad_retries += 1 - def _get_db_paths(self, uuid): - """ - Returns the secrets and local db paths needed for soledad - initialization - - :param uuid: uuid for user - :type uuid: str - - :return: a tuple with secrets, local_db paths - :rtype: tuple - """ - prefix = os.path.join(get_path_prefix(), "leap", "soledad") - secrets = "%s/%s.secret" % (prefix, uuid) - local_db = "%s/%s.db" % (prefix, uuid) - - # We remove an empty file if found to avoid complains - # about the db not being properly initialized - if is_file(local_db) and is_empty_file(local_db): - try: - os.remove(local_db) - except OSError: - logger.warning("Could not remove empty file %s" - % local_db) - return secrets, local_db - # initialization def load_and_sync_soledad(self): @@ -163,7 +165,7 @@ class SoledadBootstrapper(AbstractBootstrapper): uuid = self.srpauth.get_uid() token = self.srpauth.get_token() - secrets_path, local_db_path = self._get_db_paths(uuid) + secrets_path, local_db_path = get_db_paths(uuid) # TODO: Select server based on timezone (issue #3308) server_dict = self._soledad_config.get_hosts() -- cgit v1.2.3 From 768a460a0f524e32528063e36a0203a3d83e8de1 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Tue, 17 Dec 2013 17:38:18 -0300 Subject: Reset registration error and input widgets. The registration widgets are cleared if the user goes back to the provider selection page. [Closes #4742] --- changes/bug-4742_reset-registration-page-error | 2 ++ src/leap/bitmask/gui/wizard.py | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 changes/bug-4742_reset-registration-page-error diff --git a/changes/bug-4742_reset-registration-page-error b/changes/bug-4742_reset-registration-page-error new file mode 100644 index 00000000..2b031796 --- /dev/null +++ b/changes/bug-4742_reset-registration-page-error @@ -0,0 +1,2 @@ +- Reset registration error and input widgets if the user goes back to provider + selection in wizard. Closes #4742. diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index 5f5224ae..ad0565e4 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -597,6 +597,7 @@ class Wizard(QtGui.QWizard): Prepares the pages when they appear """ if pageId == self.SELECT_PROVIDER_PAGE: + self._clear_register_widgets() skip = self.ui.rbExistingProvider.isChecked() if not self._provider_checks_ok: self._enable_check() @@ -670,3 +671,12 @@ class Wizard(QtGui.QWizard): return self.SERVICES_PAGE return QtGui.QWizard.nextId(self) + + def _clear_register_widgets(self): + """ + Clears the widgets that my be filled and a possible error message. + """ + self._set_register_status("") + self.ui.lblUser.setText("") + self.ui.lblPassword.setText("") + self.ui.lblPassword2.setText("") -- cgit v1.2.3 From 6b7a1fc2d567a0adb05e1976f809ff78f550f98e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 17 Dec 2013 17:38:40 -0400 Subject: pep8 --- src/leap/bitmask/gui/mainwindow.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 1c80cce8..75a16eb9 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -249,7 +249,8 @@ class MainWindow(QtGui.QMainWindow): self._mail_status.set_soledad_failed) self.ui.action_preferences.triggered.connect(self._show_preferences) - self.ui.action_eip_preferences.triggered.connect(self._show_eip_preferences) + self.ui.action_eip_preferences.triggered.connect( + self._show_eip_preferences) self.ui.action_about_leap.triggered.connect(self._about) self.ui.action_quit.triggered.connect(self.quit) self.ui.action_wizard.triggered.connect(self._launch_wizard) -- cgit v1.2.3 From edf47444232c9e3637e2ad71bdd5ffa8cdcc8480 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 17 Dec 2013 15:57:48 -0400 Subject: add repair mailbox utility --- src/leap/bitmask/app.py | 5 + src/leap/bitmask/services/mail/repair.py | 234 +++++++++++++++++++++++++++++++ src/leap/bitmask/util/leap_argparse.py | 6 + 3 files changed, 245 insertions(+) create mode 100644 src/leap/bitmask/services/mail/repair.py diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 719ec8ad..3bf4575e 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -54,6 +54,7 @@ from leap.bitmask.util import log_silencer from leap.bitmask.util.leap_log_handler import LeapLogHandler from leap.bitmask.util.streamtologger import StreamToLogger from leap.bitmask.platform_init import IS_WIN +from leap.bitmask.services.mail.repair import repair_account from leap.common.events import server as event_server from leap.mail import __version__ as MAIL_VERSION @@ -174,6 +175,10 @@ def main(): print "leap.mail version: %s" % (MAIL_VERSION,) sys.exit(0) + if opts.acct_to_repair: + repair_account(opts.acct_to_repair) + sys.exit(0) + standalone = opts.standalone bypass_checks = getattr(opts, 'danger', False) debug = opts.debug diff --git a/src/leap/bitmask/services/mail/repair.py b/src/leap/bitmask/services/mail/repair.py new file mode 100644 index 00000000..767df1ef --- /dev/null +++ b/src/leap/bitmask/services/mail/repair.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +# repair.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 . +""" +Utils for repairing mailbox indexes. +""" +import logging +import getpass +import os + +from collections import defaultdict + +from leap.bitmask.config.providerconfig import ProviderConfig +from leap.bitmask.crypto.srpauth import SRPAuth +from leap.bitmask.util import get_path_prefix +from leap.bitmask.services.soledad.soledadbootstrapper import get_db_paths + +from leap.mail.imap.server import SoledadBackedAccount +from leap.soledad.client import Soledad + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +def initialize_soledad(uuid, email, passwd, + secrets, localdb, + gnupg_home, tempdir): + """ + Initializes soledad by hand + + :param email: ID for the user + :param gnupg_home: path to home used by gnupg + :param tempdir: path to temporal dir + :rtype: Soledad instance + """ + # XXX TODO unify with an authoritative source of mocks + # for soledad (or partial initializations). + # This is copied from the imap tests. + + server_url = "http://provider" + cert_file = "" + + class Mock(object): + def __init__(self, return_value=None): + self._return = return_value + + def __call__(self, *args, **kwargs): + return self._return + + class MockSharedDB(object): + + get_doc = Mock() + put_doc = Mock() + lock = Mock(return_value=('atoken', 300)) + unlock = Mock(return_value=True) + + def __call__(self): + return self + + Soledad._shared_db = MockSharedDB() + soledad = Soledad( + uuid, + passwd, + secrets, + localdb, + server_url, + cert_file) + + return soledad + + +class MBOXPlumber(object): + """ + An class that can fix things inside a soledadbacked account. + The idea is to gather in this helper different fixes for mailboxes + that can be invoked when data migration in the client is needed. + """ + + def __init__(self, userid, passwd): + """ + Initializes the plumber with all that's needed to authenticate + against the provider. + + :param userid: user identifier, foo@bar + :type userid: basestring + :param passwd: the soledad passphrase + :type passwd: basestring + """ + self.userid = userid + self.passwd = passwd + user, provider = userid.split('@') + self.user = user + self.sol = None + provider_config_path = os.path.join( + get_path_prefix(), + "leap", "providers", + provider, "provider.json") + provider_config = ProviderConfig() + loaded = provider_config.load(provider_config_path) + if not loaded: + print "could not load provider config!" + return self.exit() + + self.srp = SRPAuth(provider_config) + self.srp.authentication_finished.connect(self.repair_account) + + def start_auth(self): + """ + returns the user identifier for a given provider. + + :param provider: the provider to which we authenticate against. + """ + print "Authenticating with provider..." + self.d = self.srp.authenticate(self.user, self.passwd) + + def repair_account(self, *args): + """ + Gets the user id for this account. + """ + print "Got authenticated." + self.uid = self.srp.get_uid() + if not self.uid: + print "Got BAD UID from provider!" + return self.exit() + print "UID: %s" % (self.uid) + + secrets, localdb = get_db_paths(self.uid) + + self.sol = initialize_soledad( + self.uid, self.userid, self.passwd, + secrets, localdb, "/tmp", "/tmp") + + self.acct = SoledadBackedAccount(self.userid, self.sol) + for mbox_name in self.acct.mailboxes: + self.repair_mbox(mbox_name) + print "done." + self.exit() + + def repair_mbox(self, mbox_name): + """ + Repairs indexes for a given mbox + + :param mbox_name: mailbox to repair + :type mbox_name: basestring + """ + print + print "REPAIRING INDEXES FOR MAILBOX %s" % (mbox_name,) + print "----------------------------------------------" + mbox = self.acct.getMailbox(mbox_name) + len_mbox = mbox.getMessageCount() + print "There are %s messages" % (len_mbox,) + + last_ok = True if mbox.last_uid == len_mbox else False + uids_iter = (doc.content['uid'] for doc in mbox.messages.get_all()) + dupes = self._has_dupes(uids_iter) + if last_ok and not dupes: + print "Mbox does not need repair." + return + + msgs = mbox.messages.get_all() + for zindex, doc in enumerate(msgs): + mindex = zindex + 1 + old_uid = doc.content['uid'] + doc.content['uid'] = mindex + self.sol.put_doc(doc) + print "%s -> %s (%s)" % (mindex, doc.content['uid'], old_uid) + + old_last_uid = mbox.last_uid + mbox.last_uid = len_mbox + print "LAST UID: %s (%s)" % (mbox.last_uid, old_last_uid) + + def _has_dupes(self, sequence): + """ + Returns True if the given sequence of ints has duplicates. + + :param sequence: a sequence of ints + :type sequence: sequence + :rtype: bool + """ + d = defaultdict(lambda: 0) + for uid in sequence: + d[uid] += 1 + if d[uid] != 1: + return True + return False + + def exit(self): + from twisted.internet import reactor + self.d.cancel() + if self.sol: + self.sol.close() + try: + reactor.stop() + except Exception: + pass + return + + +def repair_account(userid): + """ + Starts repair process for a given account. + :param userid: the user id (email-like) + """ + from twisted.internet import reactor + passwd = unicode(getpass.getpass("Passphrase: ")) + + # go mario! + plumber = MBOXPlumber(userid, passwd) + reactor.callLater(1, plumber.start_auth) + reactor.run() + + +if __name__ == "__main__": + import sys + + logging.basicConfig() + + if len(sys.argv) != 2: + print "Usage: repair " + sys.exit(1) + repair_account(sys.argv[1]) diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index e8a9fda9..00192247 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -51,6 +51,12 @@ Launches Bitmask""", epilog=epilog) 'searching') parser.add_argument('-V', '--version', action="store_true", help='Displays Bitmask version and exits') + parser.add_argument('-r', '--repair-mailboxes', metavar="user@provider", + nargs='?', + action="store", dest="acct_to_repair", + help='Repair mailboxes for a given account. ' + 'Use when upgrading versions after a schema ' + 'change.') # Not in use, we might want to reintroduce them. #parser.add_argument('-i', '--no-provider-checks', -- cgit v1.2.3 From a8fc15ee9fdd92189f53bf7ee700ee4db5beb30e Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Tue, 17 Dec 2013 16:22:29 -0400 Subject: add changes file --- changes/feature_4792_repair-mailboxes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changes/feature_4792_repair-mailboxes diff --git a/changes/feature_4792_repair-mailboxes b/changes/feature_4792_repair-mailboxes new file mode 100644 index 00000000..cb570e8b --- /dev/null +++ b/changes/feature_4792_repair-mailboxes @@ -0,0 +1,3 @@ +- Add --repair-mailboxes command line option. It will be needed to migrate + existing account after a data schema changes, like it will be happening for + 0.5.0. Closes: #4792 -- cgit v1.2.3 From d9d558d44660777795d3e85611b0cfe848a92a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 11 Dec 2013 14:48:49 -0300 Subject: Refactor provider_bootstrapper out of mainwindow --- src/leap/bitmask/backend.py | 381 +++++++++++++++++++++ src/leap/bitmask/gui/mainwindow.py | 139 ++++---- src/leap/bitmask/gui/wizard.py | 68 ++-- src/leap/bitmask/provider/providerbootstrapper.py | 38 +- .../provider/tests/test_providerbootstrapper.py | 3 +- src/leap/bitmask/services/abstractbootstrapper.py | 33 +- 6 files changed, 543 insertions(+), 119 deletions(-) create mode 100644 src/leap/bitmask/backend.py diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py new file mode 100644 index 00000000..8a289a79 --- /dev/null +++ b/src/leap/bitmask/backend.py @@ -0,0 +1,381 @@ +# -*- coding: utf-8 -*- +# backend.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 . +""" +Backend for everything +""" +import logging +import os + +from Queue import Queue, Empty + +from twisted.internet import threads, defer +from twisted.internet.task import LoopingCall +from twisted.python import log + +import zope.interface + +from leap.bitmask.config.providerconfig import ProviderConfig +from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper + +# Frontend side +from PySide import QtCore + +logger = logging.getLogger(__name__) + + +class ILEAPComponent(zope.interface.Interface): + """ + Interface that every component for the backend should comply to + """ + + key = zope.interface.Attribute("Key id for this component") + + +class ILEAPService(ILEAPComponent): + """ + Interface that every Service needs to implement + """ + + def start(self): + """ + Starts the service. + """ + pass + + def stop(self): + """ + Stops the service. + """ + pass + + def terminate(self): + """ + Terminates the service, not necessarily in a nice way. + """ + pass + + def status(self): + """ + Returns a json object with the current status for the service. + + :rtype: object (list, str, dict) + """ + # XXX: Use a namedtuple or a specific object instead of a json + # object, since parsing it will be problematic otherwise. + # It has to be something easily serializable though. + pass + + def set_configs(self, keyval): + """ + Sets the config parameters for this Service. + + :param keyval: values to configure + :type keyval: dict, {str: str} + """ + pass + + def get_configs(self, keys): + """ + Returns the configuration values for the list of keys. + + :param keys: keys to retrieve + :type keys: list of str + + :rtype: dict, {str: str} + """ + pass + + +class Provider(object): + """ + Interfaces with setup and bootstrapping operations for a provider + """ + + zope.interface.implements(ILEAPComponent) + + PROBLEM_SIGNAL = "prov_problem_with_provider" + + def __init__(self, signaler=None, bypass_checks=False): + """ + Constructor for the Provider component + + :param signaler: Object in charge of handling communication + back to the frontend + :type signaler: Signaler + :param bypass_checks: Set to true if the app should bypass + first round of checks for CA + certificates at bootstrap + :type bypass_checks: bool + """ + object.__init__(self) + self.key = "provider" + self._provider_bootstrapper = ProviderBootstrapper(signaler, + bypass_checks) + self._download_provider_defer = None + self._provider_config = ProviderConfig() + + def setup_provider(self, provider): + """ + Initiates the setup for a provider + + :param provider: URL for the provider + :type provider: unicode + """ + log.msg("Setting up provider %s..." % (provider.encode("idna"),)) + pb = self._provider_bootstrapper + d = pb.run_provider_select_checks(provider, download_if_needed=True) + self._download_provider_defer = d + return d + + def bootstrap(self, provider): + """ + Second stage of bootstrapping for a provider. + + :param provider: URL for the provider + :type provider: unicode + """ + + d = None + + # If there's no loaded provider or + # we want to connect to other provider... + if (not self._provider_config.loaded() or + self._provider_config.get_domain() != provider): + self._provider_config.load( + os.path.join("leap", "providers", + provider, "provider.json")) + + if self._provider_config.loaded(): + d = self._provider_bootstrapper.run_provider_setup_checks( + self._provider_config, + download_if_needed=True) + else: + if self._signaler is not None: + self._signaler.signal(self.PROBLEM_SIGNAL) + logger.error("Could not load provider configuration.") + self._login_widget.set_enabled(True) + + if d is None: + d = defer.Deferred() + return d + + +class Signaler(QtCore.QObject): + """ + Signaler object, handles converting string commands to Qt signals. + + This is intended for the separation in frontend/backend, this will + live in the frontend. + """ + + # Signals for the ProviderBootstrapper + # These will only exist in the frontend + prov_name_resolution = QtCore.Signal(object) + prov_https_connection = QtCore.Signal(object) + prov_download_provider_info = QtCore.Signal(object) + + prov_download_ca_cert = QtCore.Signal(object) + prov_check_ca_fingerprint = QtCore.Signal(object) + prov_check_api_certificate = QtCore.Signal(object) + + prov_problem_with_provider = QtCore.Signal(object) + + # These will exist both in the backend and the front end. + # The frontend might choose to not "interpret" all the signals + # from the backend, but the backend needs to have all the signals + # it's going to emit defined here + PROV_NAME_RESOLUTION_KEY = "prov_name_resolution" + PROV_HTTPS_CONNECTION_KEY = "prov_https_connection" + PROV_DOWNLOAD_PROVIDER_INFO_KEY = "prov_download_provider_info" + PROV_DOWNLOAD_CA_CERT_KEY = "prov_download_ca_cert" + PROV_CHECK_CA_FINGERPRINT_KEY = "prov_check_ca_fingerprint" + PROV_CHECK_API_CERTIFICATE_KEY = "prov_check_api_certificate" + PROV_PROV_PROBLEM_WITH_PROVIER_KEY = "prov_problem_with_provider" + + def __init__(self): + """ + Constructor for the Signaler + """ + QtCore.QObject.__init__(self) + self._signals = {} + + signals = [ + self.PROV_NAME_RESOLUTION_KEY, + self.PROV_HTTPS_CONNECTION_KEY, + self.PROV_DOWNLOAD_PROVIDER_INFO_KEY, + self.PROV_DOWNLOAD_CA_CERT_KEY, + self.PROV_CHECK_CA_FINGERPRINT_KEY, + self.PROV_CHECK_API_CERTIFICATE_KEY, + self.PROV_PROV_PROBLEM_WITH_PROVIER_KEY + ] + + for sig in signals: + self._signals[sig] = getattr(self, sig) + + def signal(self, key, data=None): + """ + Emits a Qt signal based on the key provided, with the data if provided. + + :param key: string identifying the signal to emit + :type key: str + :param data: object to send with the data + :type data: object + + NOTE: The data object will be a serialized str in the backend, + and an unserialized object in the frontend, but for now we + just care about objects. + """ + # Right now it emits Qt signals. The backend version of this + # will do zmq.send_multipart, and the frontend version will be + # similar to this + log.msg("Signaling %s :: %s" % (key, data)) + try: + self._signals[key].emit(data) + except KeyError: + log.msg("Unknown key for signal %s!" % (key,)) + + +class Backend(object): + """ + Backend for everything, the UI should only use this class. + """ + + PASSED_KEY = "passed" + ERROR_KEY = "error" + + def __init__(self, bypass_checks=False): + """ + Constructor for the backend. + """ + object.__init__(self) + + # Components map for the commands received + self._components = {} + + # Ongoing defers that will be cancelled at stop time + self._ongoing_defers = [] + + # Signaler object to translate commands into Qt signals + self._signaler = Signaler() + + # Component registration + self._register(Provider(self._signaler, bypass_checks)) + + # We have a looping call on a thread executing all the + # commands in queue. Right now this queue is an actual Queue + # object, but it'll become the zmq recv_multipart queue + self._lc = LoopingCall(threads.deferToThread, self._worker) + + # Temporal call_queue for worker, will be replaced with + # recv_multipart os something equivalent in the loopingcall + self._call_queue = Queue() + + @property + def signaler(self): + """ + Public signaler access to let the UI connect to its signals. + """ + return self._signaler + + def start(self): + """ + Starts the looping call + """ + log.msg("Starting worker...") + self._lc.start(0.01) + + def stop(self): + """ + Stops the looping call and tries to cancel all the defers. + """ + log.msg("Stopping worker...") + self._lc.stop() + while len(self._ongoing_defers) > 0: + d = self._ongoing_defers.pop() + d.cancel() + + def _register(self, component): + """ + Registers a component in this backend + + :param component: Component to register + :type component: any object that implements ILEAPComponent + """ + # TODO: assert that the component implements the interfaces + # expected + try: + self._components[component.key] = component + except Exception: + log.msg("There was a problem registering %s" % (component,)) + log.err() + + def _signal_back(self, _, signal): + """ + Helper method to signal back (callback like behavior) to the + UI that an operation finished. + + :param signal: signal name + :type signal: str + """ + self._signaler.signal(signal) + + def _worker(self): + """ + Worker method, called from a different thread and as a part of + a looping call + """ + try: + # this'll become recv_multipart + cmd = self._call_queue.get(block=False) + + # cmd is: component, method, signalback, *args + func = getattr(self._components[cmd[0]], cmd[1]) + d = func(*cmd[3:]) + # A call might not have a callback signal, but if it does, + # we add it to the chain + if cmd[2] is not None: + d.addCallbacks(self._signal_back, log.err, cmd[2]) + d.addCallbacks(self._done_action, log.err, + callbackKeywords={"d": d}) + d.addErrback(log.err) + self._ongoing_defers.append(d) + except Empty: + # If it's just empty we don't have anything to do. + pass + except Exception: + # But we log the rest + log.err() + + def _done_action(self, _, d): + """ + Remover of the defer once it's done + + :param d: defer to remove + :type d: twisted.internet.defer.Deferred + """ + self._ongoing_defers.remove(d) + + # XXX: Temporal interface until we migrate to zmq + # We simulate the calls to zmq.send_multipart. Once we separate + # this in two processes, the methods bellow can be changed to + # send_multipart and this backend class will be really simple. + + def setup_provider(self, provider): + self._call_queue.put(("provider", "setup_provider", None, provider)) + + def provider_bootstrap(self, provider): + self._call_queue.put(("provider", "bootstrap", None, provider)) diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 75a16eb9..7dcb9908 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -18,9 +18,9 @@ Main window for Bitmask. """ import logging -import os from PySide import QtCore, QtGui +from functools import partial from twisted.internet import threads from zope.proxy import ProxyBase, setProxiedObject @@ -42,9 +42,10 @@ from leap.bitmask.gui.systray import SysTray from leap.bitmask import provider from leap.bitmask.platform_init import IS_WIN, IS_MAC from leap.bitmask.platform_init.initializers import init_platform -from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper -from leap.bitmask.services import get_service_display_name, EIP_SERVICE +from leap.bitmask import backend + +from leap.bitmask.services import get_service_display_name from leap.bitmask.services.mail import conductor as mail_conductor @@ -138,6 +139,9 @@ class MainWindow(QtGui.QMainWindow): self.ui = Ui_MainWindow() self.ui.setupUi(self) + self._backend = backend.Backend(bypass_checks) + self._backend.start() + self._settings = LeapSettings() self._login_widget = LoginWidget( @@ -180,7 +184,10 @@ class MainWindow(QtGui.QMainWindow): # This is loaded only once, there's a bug when doing that more # than once - self._provider_config = ProviderConfig() + # XXX HACK!! But we need it as long as we are using + # provider_config in here + self._provider_config = ( + self._backend._components["provider"]._provider_config) # Used for automatic start of EIP self._provisional_provider_config = ProviderConfig() self._eip_config = eipconfig.EIPConfig() @@ -191,25 +198,7 @@ class MainWindow(QtGui.QMainWindow): self._srp_auth = None self._logged_user = 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(bypass_checks) - - # Intermediate stages, only do something if there was an error - self._provider_bootstrapper.name_resolution.connect( - self._intermediate_stage) - self._provider_bootstrapper.https_connection.connect( - self._intermediate_stage) - self._provider_bootstrapper.download_ca_cert.connect( - self._intermediate_stage) - - # Important stages, loads the provider config and checks - # certificates - self._provider_bootstrapper.download_provider_info.connect( - self._load_provider_config) - self._provider_bootstrapper.check_api_certificate.connect( - self._provider_config_loaded) + self._backend_connect() # This thread is similar to the provider bootstrapper self._eip_bootstrapper = EIPBootstrapper() @@ -349,7 +338,9 @@ class MainWindow(QtGui.QMainWindow): if self._first_run(): self._wizard_firstrun = True - self._wizard = Wizard(bypass_checks=bypass_checks) + self._backend_disconnect() + self._wizard = Wizard(backend=self._backend, + bypass_checks=bypass_checks) # Give this window time to finish init and then show the wizard QtCore.QTimer.singleShot(1, self._launch_wizard) self._wizard.accepted.connect(self._finish_init) @@ -359,6 +350,47 @@ class MainWindow(QtGui.QMainWindow): # so this has to be done after eip_machine is started self._finish_init() + def _backend_connect(self): + """ + Helper to connect to backend signals + """ + self._backend.signaler.prov_name_resolution.connect( + self._intermediate_stage) + self._backend.signaler.prov_https_connection.connect( + self._intermediate_stage) + self._backend.signaler.prov_download_ca_cert.connect( + self._intermediate_stage) + + self._backend.signaler.prov_download_provider_info.connect( + self._load_provider_config) + self._backend.signaler.prov_check_api_certificate.connect( + self._provider_config_loaded) + + # Only used at login, no need to disconnect this like we do + # with the other + self._backend.signaler.prov_problem_with_provider.connect( + partial(self._login_widget.set_status, + self.tr("Unable to login: Problem with provider"))) + + def _backend_disconnect(self): + """ + Helper to disconnect from backend signals. + + Some signals are emitted from the wizard, and we want to + ignore those. + """ + self._backend.signaler.prov_name_resolution.disconnect( + self._intermediate_stage) + self._backend.signaler.prov_https_connection.disconnect( + self._intermediate_stage) + self._backend.signaler.prov_download_ca_cert.disconnect( + self._intermediate_stage) + + self._backend.signaler.prov_download_provider_info.disconnect( + self._load_provider_config) + self._backend.signaler.prov_check_api_certificate.disconnect( + self._provider_config_loaded) + def _rejected_wizard(self): """ SLOT @@ -379,6 +411,7 @@ class MainWindow(QtGui.QMainWindow): # This happens if the user finishes the provider # setup but does not register self._wizard = None + self._backend_connect() self._finish_init() def _launch_wizard(self): @@ -394,7 +427,9 @@ class MainWindow(QtGui.QMainWindow): there. """ if self._wizard is None: - self._wizard = Wizard(bypass_checks=self._bypass_checks) + self._backend_disconnect() + self._wizard = Wizard(backend=self._backend, + bypass_checks=self._bypass_checks) self._wizard.accepted.connect(self._finish_init) self._wizard.rejected.connect(self._wizard.close) @@ -628,6 +663,7 @@ class MainWindow(QtGui.QMainWindow): self.eip_needs_login.emit() self._wizard = None + self._backend_connect() else: self._try_autostart_eip() @@ -856,14 +892,12 @@ class MainWindow(QtGui.QMainWindow): # XXX should rename this provider, name clash. provider = self._login_widget.get_selected_provider() - pb = self._provider_bootstrapper - d = pb.run_provider_select_checks(provider, download_if_needed=True) - self._download_provider_defer = d + self._backend.setup_provider(provider) def _load_provider_config(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.download_provider_info + TRIGGER: self._backend.signaler.prov_download_provider_info Once the provider config has been downloaded, this loads the self._provider_config instance with it and starts the second @@ -873,31 +907,13 @@ class MainWindow(QtGui.QMainWindow): run_provider_select_checks :type data: dict """ - if data[self._provider_bootstrapper.PASSED_KEY]: - # XXX should rename this provider, name clash. - provider = self._login_widget.get_selected_provider() - - # If there's no loaded provider or - # we want to connect to other provider... - if (not self._provider_config.loaded() or - self._provider_config.get_domain() != provider): - self._provider_config.load( - os.path.join("leap", "providers", - provider, "provider.json")) - - if self._provider_config.loaded(): - self._provider_bootstrapper.run_provider_setup_checks( - self._provider_config, - download_if_needed=True) - else: - self._login_widget.set_status( - self.tr("Unable to login: Problem with provider")) - logger.error("Could not load provider configuration.") - self._login_widget.set_enabled(True) + if data[self._backend.PASSED_KEY]: + selected_provider = self._login_widget.get_selected_provider() + self._backend.provider_bootstrap(selected_provider) else: self._login_widget.set_status( self.tr("Unable to login: Problem with provider")) - logger.error(data[self._provider_bootstrapper.ERROR_KEY]) + logger.error(data[self._backend.ERROR_KEY]) self._login_widget.set_enabled(True) def _login(self): @@ -939,14 +955,14 @@ class MainWindow(QtGui.QMainWindow): def _provider_config_loaded(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.check_api_certificate + TRIGGER: self._backend.signaler.prov_check_api_certificate Once the provider configuration is loaded, this starts the SRP authentication """ leap_assert(self._provider_config, "We need a provider config!") - if data[self._provider_bootstrapper.PASSED_KEY]: + if data[self._backend.PASSED_KEY]: username = self._login_widget.get_user() password = self._login_widget.get_password() @@ -964,7 +980,7 @@ class MainWindow(QtGui.QMainWindow): else: self._login_widget.set_status( "Unable to login: Problem with provider") - logger.error(data[self._provider_bootstrapper.ERROR_KEY]) + logger.error(data[self._backend.ERROR_KEY]) self._login_widget.set_enabled(True) def _authentication_finished(self, ok, message): @@ -1538,11 +1554,11 @@ class MainWindow(QtGui.QMainWindow): This is used for intermediate bootstrapping stages, in case they fail. """ - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if not passed: self._login_widget.set_status( self.tr("Unable to connect: Problem with provider")) - logger.error(data[self._provider_bootstrapper.ERROR_KEY]) + logger.error(data[self._backend.ERROR_KEY]) self._already_started_eip = False # end of EIP methods --------------------------------------------- @@ -1615,21 +1631,21 @@ class MainWindow(QtGui.QMainWindow): """ SLOT TRIGGERS: - self._provider_bootstrapper.name_resolution - self._provider_bootstrapper.https_connection - self._provider_bootstrapper.download_ca_cert + self._backend.signaler.prov_name_resolution + self._backend.signaler.prov_https_connection + self._backend.signaler.prov_download_ca_cert self._eip_bootstrapper.download_config If there was a problem, displays it, otherwise it does nothing. This is used for intermediate bootstrapping stages, in case they fail. """ - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if not passed: self._login_widget.set_enabled(True) self._login_widget.set_status( self.tr("Unable to connect: Problem with provider")) - logger.error(data[self._provider_bootstrapper.ERROR_KEY]) + logger.error(data[self._backend.ERROR_KEY]) # # window handling methods @@ -1719,6 +1735,7 @@ class MainWindow(QtGui.QMainWindow): # Set this in case that the app is hidden QtGui.QApplication.setQuitOnLastWindowClosed(True) + self._backend.stop() self._cleanup_and_quit() self._really_quit = True diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py index ad0565e4..ec007110 100644 --- a/src/leap/bitmask/gui/wizard.py +++ b/src/leap/bitmask/gui/wizard.py @@ -30,7 +30,6 @@ from twisted.internet import threads from leap.bitmask.config.leapsettings import LeapSettings from leap.bitmask.config.providerconfig import ProviderConfig from leap.bitmask.crypto.srpregister import SRPRegister -from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper from leap.bitmask.services import get_service_display_name, get_supported from leap.bitmask.util.request_helpers import get_content from leap.bitmask.util.keyring_helpers import has_keyring @@ -55,12 +54,15 @@ class Wizard(QtGui.QWizard): BARE_USERNAME_REGEX = r"^[A-Za-z\d_]+$" - def __init__(self, bypass_checks=False): + def __init__(self, backend, bypass_checks=False): """ Constructor for the main Wizard. + :param backend: Backend being used + :type backend: Backend :param bypass_checks: Set to true if the app should bypass - first round of checks for CA certificates at bootstrap + first round of checks for CA + certificates at bootstrap :type bypass_checks: bool """ QtGui.QWizard.__init__(self) @@ -68,6 +70,8 @@ class Wizard(QtGui.QWizard): self.ui = Ui_Wizard() self.ui.setupUi(self) + self._backend = backend + self.setPixmap(QtGui.QWizard.LogoPixmap, QtGui.QPixmap(":/images/mask-icon.png")) @@ -86,23 +90,25 @@ class Wizard(QtGui.QWizard): self.ui.btnCheck.clicked.connect(self._check_provider) self.ui.lnProvider.returnPressed.connect(self._check_provider) - self._provider_bootstrapper = ProviderBootstrapper(bypass_checks) - self._provider_bootstrapper.name_resolution.connect( + self._backend.signaler.prov_name_resolution.connect( self._name_resolution) - self._provider_bootstrapper.https_connection.connect( + self._backend.signaler.prov_https_connection.connect( self._https_connection) - self._provider_bootstrapper.download_provider_info.connect( + self._backend.signaler.prov_download_provider_info.connect( self._download_provider_info) - self._provider_bootstrapper.download_ca_cert.connect( + self._backend.signaler.prov_download_ca_cert.connect( self._download_ca_cert) - self._provider_bootstrapper.check_ca_fingerprint.connect( + self._backend.signaler.prov_check_ca_fingerprint.connect( self._check_ca_fingerprint) - self._provider_bootstrapper.check_api_certificate.connect( + self._backend.signaler.prov_check_api_certificate.connect( self._check_api_certificate) self._domain = None - self._provider_config = ProviderConfig() + # HACK!! We need provider_config for the time being, it'll be + # removed + self._provider_config = ( + self._backend._components["provider"]._provider_config) # We will store a reference to the defers for eventual use # (eg, to cancel them) but not doing anything with them right now. @@ -385,8 +391,8 @@ class Wizard(QtGui.QWizard): self._domain = self.ui.lnProvider.text() self.ui.lblNameResolution.setPixmap(self.QUESTION_ICON) - self._provider_select_defer = self._provider_bootstrapper.\ - run_provider_select_checks(self._domain) + self._provider_select_defer = self._backend.\ + setup_provider(self._domain) def _skip_provider_checks(self, skip): """ @@ -423,8 +429,8 @@ class Wizard(QtGui.QWizard): :param complete_page: page id to complete :type complete_page: int """ - passed = data[self._provider_bootstrapper.PASSED_KEY] - error = data[self._provider_bootstrapper.ERROR_KEY] + passed = data[self._backend.PASSED_KEY] + error = data[self._backend.ERROR_KEY] if passed: label.setPixmap(self.OK_ICON) if complete: @@ -437,13 +443,13 @@ class Wizard(QtGui.QWizard): def _name_resolution(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.name_resolution + TRIGGER: self._backend.signaler.prov_name_resolution Sets the status for the name resolution check """ self._complete_task(data, self.ui.lblNameResolution) status = "" - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if not passed: status = self.tr("Non-existent " "provider") @@ -456,16 +462,16 @@ class Wizard(QtGui.QWizard): def _https_connection(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.https_connection + TRIGGER: self._backend.signaler.prov_https_connection Sets the status for the https connection check """ self._complete_task(data, self.ui.lblHTTPS) status = "" - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if not passed: status = self.tr("%s") \ - % (data[self._provider_bootstrapper.ERROR_KEY]) + % (data[self._backend.ERROR_KEY]) self.ui.lblProviderSelectStatus.setText(status) else: self.ui.lblProviderInfo.setPixmap(self.QUESTION_ICON) @@ -475,7 +481,7 @@ class Wizard(QtGui.QWizard): def _download_provider_info(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.download_provider_info + TRIGGER: self._backend.signaler.prov_download_provider_info Sets the status for the provider information download check. Since this check is the last of this set, it also @@ -490,14 +496,14 @@ class Wizard(QtGui.QWizard): self._provider_checks_ok = True else: new_data = { - self._provider_bootstrapper.PASSED_KEY: False, - self._provider_bootstrapper.ERROR_KEY: + self._backend.PASSED_KEY: False, + self._backend.ERROR_KEY: self.tr("Unable to load provider configuration") } self._complete_task(new_data, self.ui.lblProviderInfo) status = "" - if not data[self._provider_bootstrapper.PASSED_KEY]: + if not data[self._backend.PASSED_KEY]: status = self.tr("Not a valid provider" "") self.ui.lblProviderSelectStatus.setText(status) @@ -507,31 +513,31 @@ class Wizard(QtGui.QWizard): def _download_ca_cert(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.download_ca_cert + TRIGGER: self._backend.signaler.prov_download_ca_cert Sets the status for the download of the CA certificate check """ self._complete_task(data, self.ui.lblDownloadCaCert) - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if passed: self.ui.lblCheckCaFpr.setPixmap(self.QUESTION_ICON) def _check_ca_fingerprint(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.check_ca_fingerprint + TRIGGER: self._backend.signaler.prov_check_ca_fingerprint Sets the status for the CA fingerprint check """ self._complete_task(data, self.ui.lblCheckCaFpr) - passed = data[self._provider_bootstrapper.PASSED_KEY] + passed = data[self._backend.PASSED_KEY] if passed: self.ui.lblCheckApiCert.setPixmap(self.QUESTION_ICON) def _check_api_certificate(self, data): """ SLOT - TRIGGER: self._provider_bootstrapper.check_api_certificate + TRIGGER: self._backend.signaler.prov_check_api_certificate Sets the status for the API certificate check. Also finishes the provider bootstrapper thread since it's not needed anymore @@ -612,8 +618,8 @@ class Wizard(QtGui.QWizard): sub_title = sub_title.format(self._provider_config.get_name()) self.page(pageId).setSubTitle(sub_title) self.ui.lblDownloadCaCert.setPixmap(self.QUESTION_ICON) - self._provider_setup_defer = self._provider_bootstrapper.\ - run_provider_setup_checks(self._provider_config) + self._provider_setup_defer = self._backend.\ + provider_bootstrap(self._domain) if pageId == self.PRESENT_PROVIDER_PAGE: self.page(pageId).setSubTitle(self.tr("Description of services " diff --git a/src/leap/bitmask/provider/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py index f5a2003f..947ba0c9 100644 --- a/src/leap/bitmask/provider/providerbootstrapper.py +++ b/src/leap/bitmask/provider/providerbootstrapper.py @@ -24,8 +24,6 @@ import sys import requests -from PySide import QtCore - from leap.bitmask.config.providerconfig import ProviderConfig, MissingCACert from leap.bitmask.util.request_helpers import get_content from leap.bitmask import util @@ -61,25 +59,19 @@ class ProviderBootstrapper(AbstractBootstrapper): If a check fails, the subsequent checks are not executed """ - # All dicts returned are of the form - # {"passed": bool, "error": str} - name_resolution = QtCore.Signal(dict) - https_connection = QtCore.Signal(dict) - download_provider_info = QtCore.Signal(dict) - - download_ca_cert = QtCore.Signal(dict) - check_ca_fingerprint = QtCore.Signal(dict) - check_api_certificate = QtCore.Signal(dict) - - def __init__(self, bypass_checks=False): + def __init__(self, signaler=None, bypass_checks=False): """ Constructor for provider bootstrapper object + :param signaler: Signaler object used to receive notifications + from the backend + :type signaler: Signaler :param bypass_checks: Set to true if the app should bypass - first round of checks for CA certificates at bootstrap + first round of checks for CA + certificates at bootstrap :type bypass_checks: bool """ - AbstractBootstrapper.__init__(self, bypass_checks) + AbstractBootstrapper.__init__(self, signaler, bypass_checks) self._domain = None self._provider_config = None @@ -238,9 +230,11 @@ class ProviderBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._check_name_resolution, self.name_resolution), - (self._check_https, self.https_connection), - (self._download_provider_info, self.download_provider_info) + (self._check_name_resolution, + self._signaler.PROV_NAME_RESOLUTION_KEY), + (self._check_https, self._signaler.PROV_HTTPS_CONNECTION_KEY), + (self._download_provider_info, + self._signaler.PROV_DOWNLOAD_PROVIDER_INFO_KEY) ] return self.addCallbackChain(cb_chain) @@ -367,9 +361,11 @@ class ProviderBootstrapper(AbstractBootstrapper): self._download_if_needed = download_if_needed cb_chain = [ - (self._download_ca_cert, self.download_ca_cert), - (self._check_ca_fingerprint, self.check_ca_fingerprint), - (self._check_api_certificate, self.check_api_certificate) + (self._download_ca_cert, self._signaler.PROV_DOWNLOAD_CA_CERT_KEY), + (self._check_ca_fingerprint, + self._signaler.PROV_CHECK_CA_FINGERPRINT_KEY), + (self._check_api_certificate, + self._signaler.PROV_CHECK_API_CERTIFICATE_KEY) ] return self.addCallbackChain(cb_chain) diff --git a/src/leap/bitmask/provider/tests/test_providerbootstrapper.py b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py index 88a4ff0b..d8336fec 100644 --- a/src/leap/bitmask/provider/tests/test_providerbootstrapper.py +++ b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py @@ -42,6 +42,7 @@ from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper from leap.bitmask.provider.providerbootstrapper import UnsupportedProviderAPI from leap.bitmask.provider.providerbootstrapper import WrongFingerprint from leap.bitmask.provider.supportedapis import SupportedAPIs +from leap.bitmask.backend import Signaler from leap.bitmask import util from leap.common.files import mkdir_p from leap.common.testing.https_server import where @@ -50,7 +51,7 @@ from leap.common.testing.basetest import BaseLeapTest class ProviderBootstrapperTest(BaseLeapTest): def setUp(self): - self.pb = ProviderBootstrapper() + self.pb = ProviderBootstrapper(Signaler()) def tearDown(self): pass diff --git a/src/leap/bitmask/services/abstractbootstrapper.py b/src/leap/bitmask/services/abstractbootstrapper.py index 6d4d319b..3bee8e01 100644 --- a/src/leap/bitmask/services/abstractbootstrapper.py +++ b/src/leap/bitmask/services/abstractbootstrapper.py @@ -25,6 +25,8 @@ import requests from functools import partial from PySide import QtCore + +from twisted.python import log from twisted.internet import threads from leap.common.check import leap_assert, leap_assert_type @@ -40,10 +42,13 @@ class AbstractBootstrapper(QtCore.QObject): PASSED_KEY = "passed" ERROR_KEY = "error" - def __init__(self, bypass_checks=False): + def __init__(self, signaler=None, bypass_checks=False): """ Constructor for the abstract bootstrapper + :param signaler: Signaler object used to receive notifications + from the backend + :type signaler: Signaler :param bypass_checks: Set to true if the app should bypass first round of checks for CA certificates at bootstrap @@ -71,6 +76,7 @@ class AbstractBootstrapper(QtCore.QObject): self._bypass_checks = bypass_checks self._signal_to_emit = None self._err_msg = None + self._signaler = signaler def _gui_errback(self, failure): """ @@ -89,10 +95,20 @@ class AbstractBootstrapper(QtCore.QObject): err_msg = self._err_msg \ if self._err_msg is not None \ else str(failure.value) - self._signal_to_emit.emit({ + data = { self.PASSED_KEY: False, self.ERROR_KEY: err_msg - }) + } + # TODO: Remove this check when all the bootstrappers are + # in the backend form + if isinstance(self._signal_to_emit, basestring): + if self._signaler is not None: + self._signaler.signal(self._signal_to_emit, data) + else: + logger.warning("Tried to notify but no signaler found") + else: + self._signal_to_emit.emit(data) + log.err(failure) failure.trap(Exception) def _errback(self, failure, signal=None): @@ -127,8 +143,15 @@ class AbstractBootstrapper(QtCore.QObject): :param signal: Signal to emit if it fails here first :type signal: QtCore.SignalInstance """ - if signal: - signal.emit({self.PASSED_KEY: True, self.ERROR_KEY: ""}) + if signal is not None: + data = {self.PASSED_KEY: True, self.ERROR_KEY: ""} + if isinstance(signal, basestring): + if self._signaler is not None: + self._signaler.signal(signal, data) + else: + logger.warning("Tried to notify but no signaler found") + else: + signal.emit(data) def _callback_threader(self, cb, res, *args, **kwargs): return threads.deferToThread(cb, res, *args, **kwargs) -- cgit v1.2.3 From 49d66da0e0bd4f52491e6dbf490010647681f759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 12 Dec 2013 15:50:14 -0300 Subject: Add changes file --- changes/feature_refactor_provider_bootstrapper | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changes/feature_refactor_provider_bootstrapper diff --git a/changes/feature_refactor_provider_bootstrapper b/changes/feature_refactor_provider_bootstrapper new file mode 100644 index 00000000..969b259f --- /dev/null +++ b/changes/feature_refactor_provider_bootstrapper @@ -0,0 +1,2 @@ +- Refactor ProviderBootstrapper out of the UI modules to a Backend + module, obscuring all the details. \ No newline at end of file -- cgit v1.2.3 From 2bb48366937bf0d5191be954ea21c671c3722d04 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Wed, 18 Dec 2013 18:08:34 -0300 Subject: Avoids permanent break of resolv.conf. The resolv.conf file gets updated by resolvconf, maintaining it as a symlink allows the system to reset it during boot time. That is useful when the app has a hard crash that gives no chance to roll back our changes. [Closes #4633] --- changes/bug-4633_fix-resolvconf-usage | 1 + pkg/linux/resolv-update | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) create mode 100644 changes/bug-4633_fix-resolvconf-usage diff --git a/changes/bug-4633_fix-resolvconf-usage b/changes/bug-4633_fix-resolvconf-usage new file mode 100644 index 00000000..de40d03a --- /dev/null +++ b/changes/bug-4633_fix-resolvconf-usage @@ -0,0 +1 @@ +- Correct resolvconf usage. Avoids permanent break of resolv.conf. Closes #4633. diff --git a/pkg/linux/resolv-update b/pkg/linux/resolv-update index 601d3bd2..c308b788 100755 --- a/pkg/linux/resolv-update +++ b/pkg/linux/resolv-update @@ -70,7 +70,7 @@ SETVAR R="${R}nameserver $NS " done - mv /etc/resolv.conf /etc/resolv.conf.bak + cp /etc/resolv.conf /etc/resolv.conf.bak echo "$comment $custom_head $R @@ -79,8 +79,8 @@ $custom_tail" > /etc/resolv.conf function down() { if [ -f /etc/resolv.conf.bak ] ; then - unlink /etc/resolv.conf - mv /etc/resolv.conf.bak /etc/resolv.conf + cat /etc/resolv.conf.bak > /etc/resolv.conf + rm /etc/resolv.conf.bak fi } -- cgit v1.2.3 From d054554649ccc4c25599046dafe6427a19797891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 19 Dec 2013 16:58:01 -0300 Subject: Use Bitmask icon for admin permission dialog in OSX --- changes/bug_use_bitmask_icon_mac | 2 ++ src/leap/bitmask/services/eip/darwinvpnlauncher.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changes/bug_use_bitmask_icon_mac diff --git a/changes/bug_use_bitmask_icon_mac b/changes/bug_use_bitmask_icon_mac new file mode 100644 index 00000000..d9015466 --- /dev/null +++ b/changes/bug_use_bitmask_icon_mac @@ -0,0 +1,2 @@ +- Use Bitmask icon instead of LEAP's for the super user dialog in + OSX. Fixes #4273. \ No newline at end of file diff --git a/src/leap/bitmask/services/eip/darwinvpnlauncher.py b/src/leap/bitmask/services/eip/darwinvpnlauncher.py index fe3fe4c1..a03bfc44 100644 --- a/src/leap/bitmask/services/eip/darwinvpnlauncher.py +++ b/src/leap/bitmask/services/eip/darwinvpnlauncher.py @@ -95,7 +95,7 @@ class DarwinVPNLauncher(VPNLauncher): resources_path = os.path.abspath( os.path.join(os.getcwd(), "../../Contents/Resources")) - return os.path.join(resources_path, "leap-client.tiff") + return os.path.join(resources_path, "bitmask.tiff") @classmethod def get_cocoasudo_ovpn_cmd(kls): -- cgit v1.2.3 From 2ef6913425a4cae950c01c9d00c1016afbb3206d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 19 Dec 2013 17:05:38 -0300 Subject: Display domain instead of provider name in the login info --- changes/feature_display_domain_instead_of_provider_name | 2 ++ src/leap/bitmask/gui/mainwindow.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 changes/feature_display_domain_instead_of_provider_name diff --git a/changes/feature_display_domain_instead_of_provider_name b/changes/feature_display_domain_instead_of_provider_name new file mode 100644 index 00000000..0b2c42a6 --- /dev/null +++ b/changes/feature_display_domain_instead_of_provider_name @@ -0,0 +1,2 @@ +- Display domain for provider the user has just logged in. Fixes + #4631. \ No newline at end of file diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 75a16eb9..979becc6 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -999,7 +999,7 @@ class MainWindow(QtGui.QMainWindow): """ self._login_widget.logged_in() - self.ui.lblLoginProvider.setText(self._provider_config.get_name()) + self.ui.lblLoginProvider.setText(self._provider_config.get_domain()) self._enabled_services = self._settings.get_enabled_services( self._provider_config.get_domain()) -- cgit v1.2.3 From 8ba650488ea1f1a50b4c22758f647c9f2ee7839d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 20 Dec 2013 13:44:53 -0400 Subject: mail logs --- changes/feature_mail-debug | 1 + src/leap/bitmask/app.py | 2 ++ src/leap/bitmask/config/flags.py | 2 ++ src/leap/bitmask/services/mail/imap.py | 12 ++++++------ src/leap/bitmask/services/soledad/soledadbootstrapper.py | 4 ++++ src/leap/bitmask/util/leap_argparse.py | 5 +++++ 6 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 changes/feature_mail-debug diff --git a/changes/feature_mail-debug b/changes/feature_mail-debug new file mode 100644 index 00000000..5535a49a --- /dev/null +++ b/changes/feature_mail-debug @@ -0,0 +1 @@ +- Add ability to write mail logs to a separate file. diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 3bf4575e..b16a51aa 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -183,6 +183,7 @@ def main(): bypass_checks = getattr(opts, 'danger', False) debug = opts.debug logfile = opts.log_file + mail_logfile = opts.mail_log_file openvpn_verb = opts.openvpn_verb try: @@ -198,6 +199,7 @@ def main(): from leap.bitmask.config import flags from leap.common.config.baseconfig import BaseConfig flags.STANDALONE = standalone + flags.MAIL_LOGFILE = mail_logfile BaseConfig.standalone = standalone logger = add_logger_handlers(debug, logfile) diff --git a/src/leap/bitmask/config/flags.py b/src/leap/bitmask/config/flags.py index 98395def..ba1b65b9 100644 --- a/src/leap/bitmask/config/flags.py +++ b/src/leap/bitmask/config/flags.py @@ -30,3 +30,5 @@ WARNING: You should NOT use this kind of flags unless you're sure of what # - search for binaries inside the bundled app instead of the system ones. # e.g.: openvpn, gpg STANDALONE = False + +MAIL_LOGFILE = None diff --git a/src/leap/bitmask/services/mail/imap.py b/src/leap/bitmask/services/mail/imap.py index 2667f156..5db18cb9 100644 --- a/src/leap/bitmask/services/mail/imap.py +++ b/src/leap/bitmask/services/mail/imap.py @@ -19,10 +19,10 @@ Initialization of imap service """ import logging import os -#import sys +import sys from leap.mail.imap.service import imap -#from twisted.python import log +from twisted.python import log logger = logging.getLogger(__name__) @@ -58,15 +58,15 @@ def start_imap_service(*args, **kwargs): :returns: twisted.internet.task.LoopingCall instance """ + from leap.bitmask.config import flags logger.debug('Launching imap service') override_period = get_mail_check_period() if override_period: kwargs['check_period'] = override_period - # Uncomment the next two lines to get a separate debugging log - # TODO handle this by a separate flag. - #log.startLogging(open('/tmp/leap-imap.log', 'w')) - #log.startLogging(sys.stdout) + if flags.MAIL_LOGFILE: + log.startLogging(open(flags.MAIL_LOGFILE, 'w')) + log.startLogging(sys.stdout) return imap.run_service(*args, **kwargs) diff --git a/src/leap/bitmask/services/soledad/soledadbootstrapper.py b/src/leap/bitmask/services/soledad/soledadbootstrapper.py index a92c24a0..3ab62b2e 100644 --- a/src/leap/bitmask/services/soledad/soledadbootstrapper.py +++ b/src/leap/bitmask/services/soledad/soledadbootstrapper.py @@ -302,6 +302,10 @@ class SoledadBootstrapper(AbstractBootstrapper): except SSLError as exc: logger.error("%r" % (exc,)) raise SoledadSyncError("Failed to sync soledad") + except u1db_errors.InvalidGeneration as exc: + logger.error("%r" % (exc,)) + raise SoledadSyncError("u1db: InvalidGeneration") + except Exception as exc: logger.exception("Unhandled error while syncing " "soledad: %r" % (exc,)) diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index 00192247..6703b600 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -41,6 +41,11 @@ Launches Bitmask""", epilog=epilog) action="store", dest="log_file", #type=argparse.FileType('w'), help='optional log file') + parser.add_argument('-m', '--mail-logfile', + metavar="MAIL LOG FILE", nargs='?', + action="store", dest="mail_log_file", + #type=argparse.FileType('w'), + help='optional log file for email') parser.add_argument('--openvpn-verbosity', nargs='?', type=int, action="store", dest="openvpn_verb", -- cgit v1.2.3