diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/gui/firstrun/providerinfo.py | 119 | ||||
| -rw-r--r-- | src/leap/gui/firstrun/providerselect.py | 200 | ||||
| -rw-r--r-- | src/leap/gui/progress.py | 97 | ||||
| -rw-r--r-- | src/leap/gui/threads.py | 8 | 
4 files changed, 265 insertions, 159 deletions
| diff --git a/src/leap/gui/firstrun/providerinfo.py b/src/leap/gui/firstrun/providerinfo.py index c5f39938..48763357 100644 --- a/src/leap/gui/firstrun/providerinfo.py +++ b/src/leap/gui/firstrun/providerinfo.py @@ -98,120 +98,8 @@ class ProviderInfoPage(ValidationPage):          """          executes actual checks in a separate thread          """ -        finish = lambda: update_signal.emit("end_sentinel", 100) - -        def pause_and_finish(): -            # only for local debug -            finish() -            pause_for_user() - -        wizard = self.wizard() -        prevpage = "providerselection" - -        full_domain = self.field('provider_domain') - -        # we check if we have a port in the domain string. -        domain, port = get_https_domain_and_port(full_domain) -        _domain = u"%s:%s" % (domain, port) if port != 443 else unicode(domain) - -        netchecker = wizard.netchecker() -        providercertchecker = wizard.providercertchecker() -        eipconfigchecker = wizard.eipconfigchecker(domain=_domain) - -        update_signal.emit("head_sentinel", 0) -        pause_for_user() - -        ######################## -        # 1) try name resolution -        ######################## -        update_signal.emit("Checking that server is reachable", 20) -        logger.debug('checking name resolution') -        try: -            netchecker.check_name_resolution( -                domain) - -        except baseexceptions.LeapException as exc: -            logger.error(exc.message) -            wizard.set_validation_error( -                prevpage, exc.usermessage) -            pause_and_finish() -            return False - -        ######################### -        # 2) try https connection -        ######################### -        update_signal.emit("Checking secure connection to provider", 40) -        logger.debug('checking https connection') -        try: -            providercertchecker.is_https_working( -                "https://%s" % _domain, -                verify=True) - -        except eipexceptions.HttpsBadCertError as exc: -            logger.debug('exception') -            # XXX skipping for now... -            ############################################## -            # We had this validation logic -            # in the provider selection page before -            ############################################## -            #if self.trustProviderCertCheckBox.isChecked(): -                #pass -            #else: -            wizard.set_validation_error( -                prevpage, exc.usermessage) -            #fingerprint = certs.get_cert_fingerprint( -                #domain=domain, sep=" ") - -            # it's ok if we've trusted this fgprt before -            #trustedcrts = wizard.trusted_certs -            #if trustedcrts and fingerprint.replace(' ', '') in trustedcrts: -                #pass -            #else: -                # let your user face panick :P -                #self.add_cert_info(fingerprint) -                #self.did_cert_check = True -                #self.completeChanged.emit() -                #return False -            finish() -            return False - -        except baseexceptions.LeapException as exc: -            wizard.set_validation_error( -                prevpage, exc.usermessage) -            finish() -            return False - -        ################################## -        # 3) try download provider info... -        ################################## - -        update_signal.emit("Downloading provider info", 70) -        try: -            # XXX we already set _domain in the initialization -            # so it should not be needed here. -            eipconfigchecker.fetch_definition(domain=_domain) -            wizard.set_providerconfig( -                eipconfigchecker.defaultprovider.config) -        except requests.exceptions.SSLError: -            # XXX we should have catched this before. -            # but cert checking is broken. -            wizard.set_validation_error( -                prevpage, -                "Could not get info from provider.") -            finish() -            return False -        except requests.exceptions.ConnectionError: -            wizard.set_validation_error( -                prevpage, -                "Could not download provider info " -                "(refused conn.).") -            finish() -            return False -        # XXX catch more errors... -          # We're done!          self.set_done() -        finish()      def _do_validation(self):          """ @@ -247,7 +135,12 @@ class ProviderInfoPage(ValidationPage):          self.completeChanged.emit()      def cleanupPage(self): -        del self.wizard().providerconfig +        wizard = self.wizard() + +        # XXX makes sense now? +        # this was created on previous... +        if hasattr(wizard, 'providerconfig'): +            del self.wizard().providerconfig          if self.infoWidget:              QtCore.QObjectCleanupHandler().add( diff --git a/src/leap/gui/firstrun/providerselect.py b/src/leap/gui/firstrun/providerselect.py index 2786c494..08e09ee3 100644 --- a/src/leap/gui/firstrun/providerselect.py +++ b/src/leap/gui/firstrun/providerselect.py @@ -1,14 +1,17 @@  """  Select Provider Page, used in First Run Wizard  """ +from functools import partial  import logging +import requests +  from PyQt4 import QtCore  from PyQt4 import QtGui  from leap.base import exceptions as baseexceptions  #from leap.crypto import certs -#from leap.eip import exceptions as eipexceptions +from leap.eip import exceptions as eipexceptions  from leap.gui.constants import APP_LOGO  from leap.gui.progress import InlineValidationPage @@ -17,8 +20,24 @@ from leap.util.web import get_https_domain_and_port  logger = logging.getLogger(__name__) +# XXX check newer version in progress... + + +def delay(obj, method_str): +    """ +    this is a hack to get responsiveness in the ui +    """ +    QtCore.QTimer().singleShot( +        10, +        lambda: QtCore.QMetaObject.invokeMethod( +            obj, method_str)) +  class SelectProviderPage(InlineValidationPage): + +    #disableCheckButton = QtCore.pyqtSignal() +    launchChecks = QtCore.pyqtSignal() +      def __init__(self, parent=None, providers=None):          super(SelectProviderPage, self).__init__(parent) @@ -39,6 +58,11 @@ class SelectProviderPage(InlineValidationPage):          self.setupSteps()          self.setupUI() +        #self.disableCheckButton.connect( +            #self.onDisableCheckButton) +        self.launchChecks.connect( +            self.launch_checks) +      def setupUI(self):          """          initializes the UI @@ -149,21 +173,41 @@ class SelectProviderPage(InlineValidationPage):          valFrame.setLayout(valframeLayout)          self.valFrame = valFrame -    # check domain - -    def onCheckButtonClicked(self): +    @QtCore.pyqtSlot() +    def onDisableCheckButton(self): +        print 'CHECK BUTTON DISABLED!!!'          self.providerCheckButton.setDisabled(True) -        self.valFrame.show() + +    @QtCore.pyqtSlot() +    def launch_checks(self): +        # trying to delay this... +        #timer = QtCore.QTimer() +        #timer.singleShot(0, self.do_checks)          self.do_checks() -    def _do_checks(self, update_signal=None, failed_signal=None): +    def onCheckButtonClicked(self): +        #self.disableCheckButton.emit() +        # XXX trying to get responsiveness. +        # UI here is blocking, although I'm using +        # threads and signals :( +        QtCore.QMetaObject.invokeMethod( +            self, "onDisableCheckButton") + +        QtCore.QMetaObject.invokeMethod( +            self, "showStepsFrame") + +        delay(self, "launch_checks") + +        print 'ON CHECK BUTTON --- DONE!' +        print 'timer.....' + +    def _do_checks(self):          """          executes actual checks in a separate thread          """ -        finish = lambda: update_signal.emit("end_sentinel", 100)          wizard = self.wizard() -        prevpage = "providerselection" +        curpage = "providerselection"          full_domain = self.providerNameEdit.text() @@ -173,35 +217,143 @@ class SelectProviderPage(InlineValidationPage):          netchecker = wizard.netchecker() -        #providercertchecker = wizard.providercertchecker() -        #eipconfigchecker = wizard.eipconfigchecker(domain=_domain) +        providercertchecker = wizard.providercertchecker() +        eipconfigchecker = wizard.eipconfigchecker(domain=_domain) + +        def fail(): +            self.is_done = False +            return False -        update_signal.emit("head_sentinel", 0) +        yield(("head_sentinel", 0), lambda: None)          ########################          # 1) try name resolution          ######################## -        update_signal.emit(self.tr("Can reach provider"), 20)          logger.debug('checking name resolution') -        try: -            netchecker.check_name_resolution( -                domain) - -        except baseexceptions.LeapException as exc: -            logger.error(exc.message) -            wizard.set_validation_error( -                prevpage, exc.usermessage) -            failed_signal.emit() -            self.is_done = False -            return False +        def namecheck(): +            try: +                netchecker.check_name_resolution( +                    domain) + +            except baseexceptions.LeapException as exc: +                logger.error(exc.message) +                wizard.set_validation_error( +                    curpage, exc.usermessage) +                return fail() + +            except Exception as exc: +                wizard.set_validation_error( +                    curpage, exc.message) +                return fail() + +            else: +                return True + +            # XXX catch more exceptions + +        yield(("check name", 20), namecheck) + +        ######################### +        # 2) try https connection +        ######################### + +        logger.debug('checking https connection') + +        def httpscheck(): +            try: +                providercertchecker.is_https_working( +                    "https://%s" % _domain, +                    verify=True) + +            except eipexceptions.HttpsBadCertError as exc: +                logger.debug('exception') +                # XXX skipping for now... +                ############################################## +                # We had this validation logic +                # in the provider selection page before +                ############################################## +                #if self.trustProviderCertCheckBox.isChecked(): +                    #pass +                #else: +                wizard.set_validation_error( +                    curpage, exc.usermessage) +                #fingerprint = certs.get_cert_fingerprint( +                    #domain=domain, sep=" ") + +                # it's ok if we've trusted this fgprt before +                #trustedcrts = wizard.trusted_certs +                #if trustedcrts and \ +                # fingerprint.replace(' ', '') in trustedcrts: +                    #pass +                #else: +                    # let your user face panick :P +                    #self.add_cert_info(fingerprint) +                    #self.did_cert_check = True +                    #self.completeChanged.emit() +                    #return False +                return fail() + +            except baseexceptions.LeapException as exc: +                wizard.set_validation_error( +                    curpage, exc.usermessage) +                return fail() + +            except Exception as exc: +                wizard.set_validation_error( +                    curpage, exc.message) +                return fail() + +            else: +                return True + +        yield(("https check", 40), httpscheck) + +        ################################## +        # 3) try download provider info... +        ################################## + +        def fetchinfo(): +            try: +                # XXX we already set _domain in the initialization +                # so it should not be needed here. +                eipconfigchecker.fetch_definition(domain=_domain) +                wizard.set_providerconfig( +                    eipconfigchecker.defaultprovider.config) +            except requests.exceptions.SSLError: +                # XXX we should have catched this before. +                # but cert checking is broken. +                wizard.set_validation_error( +                    curpage, +                    self.tr( +                        "Could not get info from provider.")) +                return fail() +            except requests.exceptions.ConnectionError: +                wizard.set_validation_error( +                    curpage, +                    self.tr( +                        "Could not download provider info " +                        "(refused conn.).")) +                return fail() + +            except Exception as exc: +                wizard.set_validation_error( +                    curpage, exc.message) +                return fail() + +            else: +                return True +        yield(("fetch info", 80), fetchinfo) + +        # done!          self.is_done = True -        finish() +        yield(("end_sentinel", 100), lambda: None)      def _inline_validation_ready(self):          """          called after _do_checks has finished.          """ +        print 'VALIDATION READY ---------------'          self.domain_checked = True          if self.is_done:              self.wizard().clean_validation_error(self.current_page) diff --git a/src/leap/gui/progress.py b/src/leap/gui/progress.py index 33b0cb8c..9a5b352c 100644 --- a/src/leap/gui/progress.py +++ b/src/leap/gui/progress.py @@ -24,6 +24,24 @@ ICON_WAITING = ":/images/Emblem-question.png"  logger = logging.getLogger(__name__) +# XXX import this from threads +def delay(obj, method_str=None, call_args=None): +    """ +    this is a hack to get responsiveness in the ui +    """ +    if callable(obj) and not method_str: +        QtCore.QTimer().singleShot( +            50, +            lambda: obj()) +        return + +    if method_str: +        QtCore.QTimer().singleShot( +            50, +            lambda: QtCore.QMetaObject.invokeMethod( +                obj, method_str)) + +  class ImgWidget(QtGui.QWidget):      # XXX move to widgets @@ -168,6 +186,22 @@ class WithStepsMixIn(object):              self.progress.setValue(progress)              self.progress.update() +    def processStepsQueue(self): +        """ +        consume steps queue +        and pass messages +        to the ui updater functions +        """ +        while self.queue.qsize(): +            try: +                status = self.queue.get(0) +                if status == "failed": +                    self.set_failed_icon() +                else: +                    self.onStepStatusChanged(*status) +            except Queue.Empty: +                pass +      def setupSteps(self):          self.steps = ProgressStepContainer()          # steps table widget @@ -266,38 +300,59 @@ class WithStepsMixIn(object):  """ -Resist the temptation to refactor the declaration of the signal -to the mixin. -PyQt and multiple inheritance do not mix well together. -You can only have one QObject base. -Therefore, we will use one base class for the intermediate pages +We will use one base class for the intermediate pages  and another one for the in-page validations, both sharing the creation  of the tablewidgets. +The logic of this split comes from where I was trying to solve +the ui update using signals, but now that it's working well with +queues I could join them again.  """ +import Queue +from functools import partial -class InlineValidationPage(QtGui.QWizardPage, WithStepsMixIn): -    # signals -    stepChanged = QtCore.pyqtSignal([str, int]) -    stepFailed = QtCore.pyqtSignal() +class InlineValidationPage(QtGui.QWizardPage, WithStepsMixIn):      def __init__(self, parent=None):          super(InlineValidationPage, self).__init__(parent) -        self.connect_step_status() -        self.connect_failstep_status() + +        self.queue = Queue.Queue() +        self.timer = QtCore.QTimer() +        self.timer.timeout.connect(self.processStepsQueue) +        self.timer.start(100) +        self.threads = []      def do_checks(self): -        """ -        launches a thread to do the checks -        """ -        beupdate = self.stepChanged -        befailed = self.stepFailed -        self.checks = FunThread( -            self._do_checks(update_signal=beupdate, failed_signal=befailed)) -        self.checks.finished.connect(self._inline_validation_ready) -        self.checks.begin() -        #self.checks.wait() + +        # yo dawg, I heard you like checks +        # so I put a __do_checks in your do_checks +        # for calling others' _do_checks + +        def __do_checks(fun=None, queue=None): + +            for checkcase in fun(): +                checkmsg, checkfun = checkcase + +                queue.put(checkmsg) +                if checkfun() is False: +                    queue.put("failed") +                    break + +        t = FunThread(fun=partial( +            __do_checks, +            fun=self._do_checks, +            queue=self.queue)) +        t.finished.connect(self._inline_validation_ready) +        t.begin() +        self.threads.append(t) + +    # slot + +    @QtCore.pyqtSlot() +    def showStepsFrame(self): +        self.valFrame.show() +        self.update()  class ValidationPage(QtGui.QWizardPage, WithStepsMixIn): diff --git a/src/leap/gui/threads.py b/src/leap/gui/threads.py index 176c19b1..8aad8866 100644 --- a/src/leap/gui/threads.py +++ b/src/leap/gui/threads.py @@ -3,10 +3,16 @@ from PyQt4 import QtCore  class FunThread(QtCore.QThread): -    def __init__(self, fun, parent=None): +    def __init__(self, fun=None, parent=None): +          QtCore.QThread.__init__(self, parent) +        self.exiting = False          self.fun = fun +    def __del__(self): +        self.exiting = True +        self.wait() +      def run(self):          if self.fun:              self.fun() | 
