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 | 
