""" Select Provider Page, used in First Run Wizard """ import logging 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.gui.constants import APP_LOGO from leap.gui.progress import InlineValidationPage from leap.gui.styles import ErrorLabelStyleSheet from leap.util.web import get_https_domain_and_port logger = logging.getLogger(__name__) class SelectProviderPage(InlineValidationPage): def __init__(self, parent=None, providers=None): super(SelectProviderPage, self).__init__(parent) self.setTitle("Enter Provider") self.setSubTitle( "Please enter the domain of the provider you want " "to use for your connection." ) self.setPixmap( QtGui.QWizard.LogoPixmap, QtGui.QPixmap(APP_LOGO)) self.did_cert_check = False self.current_page = 'providerselection' self.is_done = False self.setupSteps() self.setupUI() def setupUI(self): """ initializes the UI """ providerNameLabel = QtGui.QLabel("h&ttps://") # note that we expect the bare domain name # we will add the scheme later providerNameEdit = QtGui.QLineEdit() providerNameEdit.cursorPositionChanged.connect( self.reset_validation_status) providerNameLabel.setBuddy(providerNameEdit) # add regex validator providerDomainRe = QtCore.QRegExp(r"^[a-z\d_-.]+$") providerNameEdit.setValidator( QtGui.QRegExpValidator(providerDomainRe, self)) self.providerNameEdit = providerNameEdit # Eventually we will seed a list of # well known providers here. #providercombo = QtGui.QComboBox() #if providers: #for provider in providers: #providercombo.addItem(provider) #providerNameSelect = providercombo self.registerField("provider_domain*", self.providerNameEdit) #self.registerField('provider_name_index', providerNameSelect) validationMsg = QtGui.QLabel("") validationMsg.setStyleSheet(ErrorLabelStyleSheet) self.validationMsg = validationMsg providerCheckButton = QtGui.QPushButton("chec&k") self.providerCheckButton = providerCheckButton # cert info # this is used in the callback # for the checkbox changes. # tricky, since the first time came # from the exception message. # should get string from exception too! self.bad_cert_status = "Server certificate could not be verified." self.certInfo = QtGui.QLabel("") self.certInfo.setWordWrap(True) self.certWarning = QtGui.QLabel("") self.trustProviderCertCheckBox = QtGui.QCheckBox( "&Trust this provider certificate.") self.trustProviderCertCheckBox.stateChanged.connect( self.onTrustCheckChanged) self.providerNameEdit.textChanged.connect( self.onProviderChanged) self.providerCheckButton.clicked.connect( self.onCheckButtonClicked) layout = QtGui.QGridLayout() layout.addWidget(validationMsg, 0, 2) layout.addWidget(providerNameLabel, 1, 1) layout.addWidget(providerNameEdit, 1, 2) layout.addWidget(providerCheckButton, 1, 3) # add certinfo group # XXX not shown now. should move to validation box. #layout.addWidget(certinfoGroup, 4, 1, 4, 2) #self.certinfoGroup = certinfoGroup #self.certinfoGroup.hide() # add validation frame self.setupValidationFrame() layout.addWidget(self.valFrame, 4, 2, 4, 2) self.valFrame.hide() self.setLayout(layout) # certinfo def setupCertInfoGroup(self): # XXX not used now. certinfoGroup = QtGui.QGroupBox("Certificate validation") certinfoLayout = QtGui.QVBoxLayout() certinfoLayout.addWidget(self.certInfo) certinfoLayout.addWidget(self.certWarning) certinfoLayout.addWidget(self.trustProviderCertCheckBox) certinfoGroup.setLayout(certinfoLayout) self.certinfoGroup = self.certinfoGroup # progress frame def setupValidationFrame(self): qframe = QtGui.QFrame valFrame = qframe() valFrame.setFrameStyle(qframe.NoFrame) # Box | qframe.Plain) # NoFrame, StyledPanel) | qframe.Sunken) #valFrame.setContentsMargins(0, 0, 0, 0) valframeLayout = QtGui.QVBoxLayout() zeros = (0, 0, 0, 0) valframeLayout.setContentsMargins(*zeros) #dummylabel = QtGui.QLabel('test foo') #valframeLayout.addWidget(dummylabel) valframeLayout.addWidget(self.stepsTableWidget) valFrame.setLayout(valframeLayout) self.valFrame = valFrame # check domain def onCheckButtonClicked(self): print 'check button called....' self.providerCheckButton.setDisabled(True) self.valFrame.show() self.do_checks() def _do_checks(self, update_signal=None, failed_signal=None): """ executes actual checks in a separate thread """ finish = lambda: update_signal.emit("end_sentinel", 100) wizard = self.wizard() prevpage = "providerselection" full_domain = self.providerNameEdit.text() # 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) ######################## # 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) failed_signal.emit() return False self.is_done = True finish() def _inline_validation_ready(self): """ called after _do_checks has finished. """ # XXX check if it's really done (catch signal for completed) #self.done = True self.completeChanged.emit() # cert trust verification # (disabled for now) def is_insecure_cert_trusted(self): return self.trustProviderCertCheckBox.isChecked() def onTrustCheckChanged(self, state): checked = False if state == 2: checked = True if checked: self.reset_validation_status() else: self.set_validation_status(self.bad_cert_status) # trigger signal to redraw next button self.completeChanged.emit() def add_cert_info(self, certinfo): self.certWarning.setText( "Do you want to trust this provider certificate?") self.certInfo.setText( 'SHA-256 fingerprint: %s
' % certinfo) self.certInfo.setWordWrap(True) self.certinfoGroup.show() def onProviderChanged(self, text): provider = self.providerNameEdit.text() if provider: self.providerCheckButton.setDisabled(False) else: self.providerCheckButton.setDisabled(True) self.completeChanged.emit() def reset_validation_status(self): """ empty the validation msg """ self.validationMsg.setText('') # pagewizard methods def isComplete(self): provider = self.providerNameEdit.text() if not self.is_done: return False if not provider: return False else: if self.is_insecure_cert_trusted(): return True if not self.did_cert_check: if self.is_done: # XXX sure? return True return False def populateErrors(self): # XXX could move this to ValidationMixin # with some defaults for the validating fields # (now it only allows one field, manually specified) #logger.debug('getting errors') errors = self.wizard().get_validation_error( self.current_page) if errors: bad_str = getattr(self, 'bad_string', None) cur_str = self.providerNameEdit.text() showerr = self.validationMsg.setText if bad_str is None: # first time we fall here. # save the current bad_string value self.bad_string = cur_str showerr(errors) else: # not the first time if cur_str == bad_str: showerr(errors) else: showerr('') def cleanup_errormsg(self): """ we reset bad_string to None should be called before leaving the page """ self.bad_string = None def paintEvent(self, event): """ we hook our populate errors on paintEvent because we need it to catch when user enters the page coming from next, and initializePage does not cover that case. Maybe there's a better event to hook upon. """ super(SelectProviderPage, self).paintEvent(event) self.populateErrors() def initializePage(self): self.validationMsg.setText('') if hasattr(self, 'certinfoGroup'): # XXX remove ? self.certinfoGroup.hide() self.is_done = False self.providerCheckButton.setDisabled(True) self.valFrame.hide() self.steps.removeAllSteps() self.clearTable() def validatePage(self): # some cleanup before we leave the page self.cleanup_errormsg() # go return True def nextId(self): wizard = self.wizard() if not wizard: return return wizard.get_page_index('providerinfo')