diff options
-rw-r--r-- | src/leap/bitmask/app.py | 2 | ||||
-rw-r--r-- | src/leap/bitmask/backend.py | 62 | ||||
-rw-r--r-- | src/leap/bitmask/gui/mainwindow.py | 98 | ||||
-rw-r--r-- | src/leap/bitmask/util/leap_argparse.py | 10 |
4 files changed, 103 insertions, 69 deletions
diff --git a/src/leap/bitmask/app.py b/src/leap/bitmask/app.py index 374c91d2..6a7d6ff1 100644 --- a/src/leap/bitmask/app.py +++ b/src/leap/bitmask/app.py @@ -121,7 +121,7 @@ def main(): Starts the main event loop and launches the main window. """ # Parse arguments and store them - _, opts = leap_argparse.init_leapc_args() + opts = leap_argparse.get_options() do_display_version(opts) bypass_checks = opts.danger diff --git a/src/leap/bitmask/backend.py b/src/leap/bitmask/backend.py index f7200dd7..5748c4c6 100644 --- a/src/leap/bitmask/backend.py +++ b/src/leap/bitmask/backend.py @@ -19,6 +19,7 @@ Backend for everything """ import logging import os +import socket import time from functools import partial @@ -653,6 +654,45 @@ class EIP(object): if self._signaler is not None: self._signaler.signal(self._signaler.EIP_CANNOT_START) + def check_dns(self, domain): + """ + Check if we can resolve the given domain name. + + :param domain: the domain to check. + :type domain: str + """ + def do_check(): + """ + Try to resolve the domain name. + """ + socket.gethostbyname(domain.encode('idna')) + + def check_ok(_): + """ + Callback handler for `do_check`. + """ + self._signaler.signal(self._signaler.EIP_DNS_OK) + logger.debug("DNS check OK") + + def check_err(failure): + """ + Errback handler for `do_check`. + + :param failure: the failure that triggered the errback. + :type failure: twisted.python.failure.Failure + """ + logger.debug("Can't resolve hostname. {0!r}".format(failure)) + + self._signaler.signal(self._signaler.EIP_DNS_ERROR) + + # python 2.7.4 raises socket.error + # python 2.7.5 raises socket.gaierror + failure.trap(socket.gaierror, socket.error) + + d = threads.deferToThread(do_check) + d.addCallback(check_ok) + d.addErrback(check_err) + class Soledad(object): """ @@ -1177,6 +1217,9 @@ class Signaler(QtCore.QObject): eip_connection_aborted = QtCore.Signal(object) eip_stopped = QtCore.Signal(object) + eip_dns_ok = QtCore.Signal(object) + eip_dns_error = QtCore.Signal(object) + # EIP problems eip_no_polkit_agent_error = QtCore.Signal(object) eip_no_tun_kext_error = QtCore.Signal(object) @@ -1308,6 +1351,9 @@ class Signaler(QtCore.QObject): EIP_CAN_START = "eip_can_start" EIP_CANNOT_START = "eip_cannot_start" + EIP_DNS_OK = "eip_dns_ok" + EIP_DNS_ERROR = "eip_dns_error" + SOLEDAD_BOOTSTRAP_FAILED = "soledad_bootstrap_failed" SOLEDAD_BOOTSTRAP_FINISHED = "soledad_bootstrap_finished" SOLEDAD_OFFLINE_FAILED = "soledad_offline_failed" @@ -1395,6 +1441,9 @@ class Signaler(QtCore.QObject): self.EIP_CAN_START, self.EIP_CANNOT_START, + self.EIP_DNS_OK, + self.EIP_DNS_ERROR, + self.SRP_AUTH_OK, self.SRP_AUTH_ERROR, self.SRP_AUTH_SERVER_ERROR, @@ -1840,6 +1889,19 @@ class Backend(object): self._call_queue.put(("eip", "can_start", None, domain)) + def eip_check_dns(self, domain): + """ + Check if we can resolve the given domain name. + + :param domain: the domain for the provider to check + :type domain: str + + Signals: + eip_dns_ok + eip_dns_error + """ + self._call_queue.put(("eip", "check_dns", None, domain)) + def tear_fw_down(self): """ Signal the need to tear the fw down. diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py index 885cb792..03c29d0e 100644 --- a/src/leap/bitmask/gui/mainwindow.py +++ b/src/leap/bitmask/gui/mainwindow.py @@ -18,12 +18,10 @@ Main window for Bitmask. """ import logging -import socket from datetime import datetime from PySide import QtCore, QtGui -from twisted.internet import reactor, threads from leap.bitmask import __version__ as VERSION from leap.bitmask import __version_hash__ as VERSION_HASH @@ -177,8 +175,8 @@ class MainWindow(QtGui.QMainWindow): # Set used to track the services being stopped and need wait. self._services_being_stopped = {} - # timeout object used to trigger quit - self._quit_timeout_callater = None + # used to know if we are in the final steps of quitting + self._finally_quitting = False self._backend_connected_signals = [] self._backend_connect() @@ -403,6 +401,8 @@ class MainWindow(QtGui.QMainWindow): sig.eip_can_start.connect(self._backend_can_start_eip) sig.eip_cannot_start.connect(self._backend_cannot_start_eip) + sig.eip_dns_error.connect(self._eip_dns_error) + # ================================================================== # Soledad signals @@ -897,7 +897,7 @@ class MainWindow(QtGui.QMainWindow): self.tr('Hello!'), self.tr('Bitmask has started in the tray.')) # we wait for the systray to be ready - reactor.callLater(1, hello) + QtDelayedCall(1, hello) @QtCore.Slot(int) def _tray_activated(self, reason=None): @@ -1461,55 +1461,25 @@ class MainWindow(QtGui.QMainWindow): self._already_started_eip = True # check for connectivity - # we might want to leave a little time here... - self._check_name_resolution(domain) - - def _check_name_resolution(self, domain): - # FIXME this has to be moved to backend !!! - # Should move to netchecks module. - # and separate qt from reactor... - """ - Check if we can resolve the given domain name. - - :param domain: the domain to check. - :type domain: str - """ - def do_check(): - """ - Try to resolve the domain name. - """ - socket.gethostbyname(domain.encode('idna')) - - def check_err(failure): - """ - Errback handler for `do_check`. - - :param failure: the failure that triggered the errback. - :type failure: twisted.python.failure.Failure - """ - logger.error(repr(failure)) - logger.error("Can't resolve hostname.") - - msg = self.tr( - "The server at {0} can't be found, because the DNS lookup " - "failed. DNS is the network service that translates a " - "website's name to its Internet address. Either your computer " - "is having trouble connecting to the network, or you are " - "missing some helper files that are needed to securely use " - "DNS while {1} is active. To install these helper files, quit " - "this application and start it again." - ).format(domain, self._eip_conductor.eip_name) - - show_err = lambda: QtGui.QMessageBox.critical( - self, self.tr("Connection Error"), msg) - reactor.callLater(0, show_err) - - # python 2.7.4 raises socket.error - # python 2.7.5 raises socket.gaierror - failure.trap(socket.gaierror, socket.error) - - d = threads.deferToThread(do_check) - d.addErrback(check_err) + self._backend.eip_check_dns(domain) + + @QtCore.Slot() + def _eip_dns_error(self): + """ + Trigger this if we don't have a working DNS resolver. + """ + domain = self._login_widget.get_selected_provider() + msg = self.tr( + "The server at {0} can't be found, because the DNS lookup " + "failed. DNS is the network service that translates a " + "website's name to its Internet address. Either your computer " + "is having trouble connecting to the network, or you are " + "missing some helper files that are needed to securely use " + "DNS while {1} is active. To install these helper files, quit " + "this application and start it again." + ).format(domain, self._eip_conductor.eip_name) + + QtGui.QMessageBox.critical(self, self.tr("Connection Error"), msg) def _try_autostart_eip(self): """ @@ -1768,8 +1738,7 @@ class MainWindow(QtGui.QMainWindow): # call final quit when all the services are stopped self.all_services_stopped.connect(self.final_quit) # or if we reach the timeout - self._quit_timeout_callater = reactor.callLater( - self.SERVICES_STOP_TIMEOUT, self.final_quit) + QtDelayedCall(self.SERVICES_STOP_TIMEOUT, self.final_quit) @QtCore.Slot() def _remove_service(self, service): @@ -1795,16 +1764,13 @@ class MainWindow(QtGui.QMainWindow): """ logger.debug('Final quit...') - try: - # disconnect signal if we get here due a timeout. - self.all_services_stopped.disconnect(self.final_quit) - except RuntimeError: - pass # Signal was not connected + # We can reach here because all the services are stopped or because a + # timeout was triggered. Since we want to run this only once, we exit + # if this is called twice. + if self._finally_quitting: + return - # Cancel timeout to avoid being called if we reached here through the - # signal - if self._quit_timeout_callater.active(): - self._quit_timeout_callater.cancel() + self._finally_quitting = True # Remove lockfiles on a clean shutdown. logger.debug('Cleaning pidfiles') @@ -1814,4 +1780,4 @@ class MainWindow(QtGui.QMainWindow): self._backend.stop() self.close() - reactor.callLater(1, twisted_main.quit) + QtDelayedCall(1, twisted_main.quit) diff --git a/src/leap/bitmask/util/leap_argparse.py b/src/leap/bitmask/util/leap_argparse.py index 84af4e8d..0717aea5 100644 --- a/src/leap/bitmask/util/leap_argparse.py +++ b/src/leap/bitmask/util/leap_argparse.py @@ -123,7 +123,13 @@ def build_parser(): return parser -def init_leapc_args(): +def get_options(): + """ + Get the command line options used when the app was started. + + :return: the command options + :rtype: argparse.Namespace + """ parser = build_parser() opts, unknown = parser.parse_known_args() - return parser, opts + return opts |