Exemple #1
0
 def on_imagesTree_currentItemChanged(self, current, previous):
     """
     Private slot to show a preview of the selected image.
     
     @param current current image entry (QTreeWidgetItem)
     @param previous old current entry (QTreeWidgetItem)
     """
     if current is None:
         return
     
     imageUrl = QUrl(current.text(1))
     if not imageUrl.host():
         imageUrl.setHost(QUrl(self.siteAddressLabel.text()).host())
         imageUrl.setScheme(QUrl(self.siteAddressLabel.text()).scheme())
     
     import Helpviewer.HelpWindow
     cache = Helpviewer.HelpWindow.HelpWindow.networkAccessManager().cache()
     if cache:
         cacheData = cache.data(imageUrl)
     else:
         cacheData = None
     pixmap = QPixmap()
     invalidPixmap = False
     scene = QGraphicsScene(self.imagePreview)
     if not cacheData:
         invalidPixmap = True
     else:
         pixmap.loadFromData(cacheData.readAll())
         if pixmap.isNull():
             invalidPixmap = True
     if invalidPixmap:
         scene.addText(self.tr("Preview not available."))
     else:
         scene.addPixmap(pixmap)
     self.imagePreview.setScene(scene)
def encode_uri(ds_uri, schema_name, project_name=None):
    u = QUrl()
    urlQuery = QUrlQuery()

    u.setScheme("postgresql")
    u.setHost(ds_uri.host())
    if ds_uri.port() != '':
        u.setPort(int(ds_uri.port()))
    if ds_uri.username() != '':
        u.setUserName(ds_uri.username())
    if ds_uri.password() != '':
        u.setPassword(ds_uri.password())

    if ds_uri.service() != '':
        urlQuery.addQueryItem("service", ds_uri.service())
    if ds_uri.authConfigId() != '':
        urlQuery.addQueryItem("authcfg", ds_uri.authConfigId())
    if ds_uri.sslMode() != QgsDataSourceUri.SslPrefer:
        urlQuery.addQueryItem("sslmode", QgsDataSourceUri.encodeSslMode(ds_uri.sslMode()))

    urlQuery.addQueryItem("dbname", ds_uri.database())

    urlQuery.addQueryItem("schema", schema_name)
    if project_name:
        urlQuery.addQueryItem("project", project_name)

    u.setQuery(urlQuery)
    return str(u.toEncoded(), 'utf-8')
Exemple #3
0
    def keyPressEvent(self, evt):
        """
        Protected method to handle key presses.
        
        @param evt reference to the key press event (QKeyEvent)
        """
        if evt.key() == Qt.Key_Escape and self.__browser is not None:
            self.setText(
                str(self.__browser.url().toEncoded(), encoding="utf-8"))
            self.selectAll()
            return

        currentText = self.text().strip()
        if evt.key() in [Qt.Key_Enter, Qt.Key_Return] and \
           not currentText.lower().startswith("http://"):
            append = ""
            if evt.modifiers() == Qt.KeyboardModifiers(Qt.ControlModifier):
                append = ".com"
            elif evt.modifiers() == Qt.KeyboardModifiers(Qt.ControlModifier
                                                         | Qt.ShiftModifier):
                append = ".org"
            elif evt.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier):
                append = ".net"

            if append != "":
                url = QUrl("http://www." + currentText)
                host = url.host()
                if not host.lower().endswith(append):
                    host += append
                    url.setHost(host)
                    self.setText(url.toString())

        E5LineEdit.keyPressEvent(self, evt)
    def open_help(self):

        url = QUrl()
        url.setScheme(DefaultConfig.Help.url_scheme)
        url.setHost(DefaultConfig.Help.host)
        url.setPath(DefaultConfig.Help.url_path_pattern) # % str(Version.babel))
        QDesktopServices.openUrl(url)
Exemple #5
0
 def keyPressEvent(self, evt):
     """
     Protected method to handle key presses.
     
     @param evt reference to the key press event (QKeyEvent)
     """
     if evt.key() == Qt.Key_Escape and self.__browser is not None:
         self.setText(
             str(self.__browser.url().toEncoded(), encoding="utf-8"))
         self.selectAll()
         return
     
     currentText = self.text().strip()
     if evt.key() in [Qt.Key_Enter, Qt.Key_Return] and \
        not currentText.lower().startswith("http://"):
         append = ""
         if evt.modifiers() == Qt.KeyboardModifiers(Qt.ControlModifier):
             append = ".com"
         elif evt.modifiers() == Qt.KeyboardModifiers(
                 Qt.ControlModifier | Qt.ShiftModifier):
             append = ".org"
         elif evt.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier):
             append = ".net"
         
         if append != "":
             url = QUrl("http://www." + currentText)
             host = url.host()
             if not host.lower().endswith(append):
                 host += append
                 url.setHost(host)
                 self.setText(url.toString())
     
     E5LineEdit.keyPressEvent(self, evt)
Exemple #6
0
def encode_uri(ds_uri, schema_name, project_name=None):
    u = QUrl()
    urlQuery = QUrlQuery()

    u.setScheme("postgresql")
    u.setHost(ds_uri.host())
    if ds_uri.port() != '':
        u.setPort(int(ds_uri.port()))
    if ds_uri.username() != '':
        u.setUserName(ds_uri.username())
    if ds_uri.password() != '':
        u.setPassword(ds_uri.password())

    if ds_uri.service() != '':
        urlQuery.addQueryItem("service", ds_uri.service())
    if ds_uri.authConfigId() != '':
        urlQuery.addQueryItem("authcfg", ds_uri.authConfigId())
    if ds_uri.sslMode() != QgsDataSourceUri.SslPrefer:
        urlQuery.addQueryItem("sslmode", QgsDataSourceUri.encodeSslMode(ds_uri.sslMode()))

    urlQuery.addQueryItem("dbname", ds_uri.database())

    urlQuery.addQueryItem("schema", schema_name)
    if project_name:
        urlQuery.addQueryItem("project", project_name)

    u.setQuery(urlQuery)
    return str(u.toEncoded(), 'utf-8')
Exemple #7
0
    def on_imagesTree_currentItemChanged(self, current, previous):
        """
        Private slot to show a preview of the selected image.
        
        @param current current image entry (QTreeWidgetItem)
        @param previous old current entry (QTreeWidgetItem)
        """
        if current is None:
            return

        imageUrl = QUrl(current.text(1))
        if not imageUrl.host():
            imageUrl.setHost(QUrl(self.siteAddressLabel.text()).host())
            imageUrl.setScheme(QUrl(self.siteAddressLabel.text()).scheme())

        import Helpviewer.HelpWindow
        cache = Helpviewer.HelpWindow.HelpWindow.networkAccessManager().cache()
        if cache:
            cacheData = cache.data(imageUrl)
        else:
            cacheData = None
        pixmap = QPixmap()
        invalidPixmap = False
        scene = QGraphicsScene(self.imagePreview)
        if not cacheData:
            invalidPixmap = True
        else:
            pixmap.loadFromData(cacheData.readAll())
            if pixmap.isNull():
                invalidPixmap = True
        if invalidPixmap:
            scene.addText(self.tr("Preview not available."))
        else:
            scene.addPixmap(pixmap)
        self.imagePreview.setScene(scene)
Exemple #8
0
    def open_help(self):

        url = QUrl()
        url.setScheme(DefaultConfig.Help.url_scheme)
        url.setHost(DefaultConfig.Help.host)
        url.setPath(
            DefaultConfig.Help.url_path_pattern)  # % str(Version.babel))
        QDesktopServices.openUrl(url)
Exemple #9
0
def intercept(info: interceptor.Request) -> None:
    if not info.request_url.scheme().startswith('http'):
        return

    new_url = QUrl(clear_url(info.request_url.url()))

    if new_host := redirects.get(new_url.host()):
        new_url.setHost(new_host)
def data_for_url(url):
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    norm_url = url.adjusted(QUrl.NormalizePathSegments
                            | QUrl.StripTrailingSlash)
    if norm_url != url:
        raise Redirect(norm_url)

    path = url.path()
    host = url.host()
    query = urlutils.query_string(url)
    # A url like "qute:foo" is split as "scheme:path", not "scheme:host".
    log.misc.debug("url: {}, path: {}, host {}".format(url.toDisplayString(),
                                                       path, host))
    if not path or not host:
        new_url = QUrl()
        new_url.setScheme('qute')
        # When path is absent, e.g. qute://help (with no trailing slash)
        if host:
            new_url.setHost(host)
        # When host is absent, e.g. qute:help
        else:
            new_url.setHost(path)

        new_url.setPath('/')
        if query:
            new_url.setQuery(query)
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NoHandlerFound(url)

    try:
        mimetype, data = handler(url)
    except OSError as e:
        # FIXME:qtwebengine how to handle this?
        raise QuteSchemeOSError(e)
    except QuteSchemeError as e:
        raise

    assert mimetype is not None, url
    if mimetype == 'text/html' and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode('utf-8', errors='xmlcharrefreplace')

    return mimetype, data
Exemple #11
0
def data_for_url(url):
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    norm_url = url.adjusted(QUrl.NormalizePathSegments |
                            QUrl.StripTrailingSlash)
    if norm_url != url:
        raise Redirect(norm_url)

    path = url.path()
    host = url.host()
    query = urlutils.query_string(url)
    # A url like "qute:foo" is split as "scheme:path", not "scheme:host".
    log.misc.debug("url: {}, path: {}, host {}".format(
        url.toDisplayString(), path, host))
    if not path or not host:
        new_url = QUrl()
        new_url.setScheme('qute')
        # When path is absent, e.g. qute://help (with no trailing slash)
        if host:
            new_url.setHost(host)
        # When host is absent, e.g. qute:help
        else:
            new_url.setHost(path)

        new_url.setPath('/')
        if query:
            new_url.setQuery(query)
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NoHandlerFound(url)

    try:
        mimetype, data = handler(url)
    except OSError as e:
        # FIXME:qtwebengine how to handle this?
        raise QuteSchemeOSError(e)
    except QuteSchemeError as e:
        raise

    assert mimetype is not None, url
    if mimetype == 'text/html' and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode('utf-8', errors='xmlcharrefreplace')

    return mimetype, data
 def __saveImage(self):
     """
     Private slot to save the selected image to disk.
     """
     act = self.sender()
     index = act.data()
     itm = self.imagesTree.topLevelItem(index)
     if itm is None:
         return
     
     imageUrl = QUrl(itm.text(1))
     if not imageUrl.host():
         imageUrl.setHost(QUrl(self.siteAddressLabel.text()).host())
         imageUrl.setScheme(QUrl(self.siteAddressLabel.text()).scheme())
     
     import Helpviewer.HelpWindow
     cache = Helpviewer.HelpWindow.HelpWindow.networkAccessManager().cache()
     if cache:
         cacheData = cache.data(imageUrl)
     else:
         cacheData = None
     if not cacheData:
         E5MessageBox.critical(
             self,
             self.tr("Save Image"),
             self.tr("""This image is not available."""))
         return
     
     downloadDirectory = Helpviewer.HelpWindow.HelpWindow\
         .downloadManager().downloadDirectory()
     fn = os.path.join(downloadDirectory, os.path.basename(itm.text(1)))
     filename = E5FileDialog.getSaveFileName(
         self,
         self.tr("Save Image"),
         fn,
         self.tr("All Files (*)"),
         E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
     
     if not filename:
         return
     
     f = QFile(filename)
     if not f.open(QFile.WriteOnly):
         E5MessageBox.critical(
             self,
             self.tr("Save Image"),
             self.tr(
                 """<p>Cannot write to file <b>{0}</b>.</p>""")
             .format(filename))
         return
     f.write(cacheData.readAll())
     f.close()
Exemple #13
0
def data_for_url(url: QUrl):
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    norm_url = url.adjusted(QUrl.NormalizePathSegments
                            | QUrl.StripTrailingSlash)
    if norm_url != url:
        raise Redirect(norm_url)

    path = url.path()
    host = url.host()
    query = urlutils.query_string(url)
    # A url like "luminos:foo" is split as "scheme:path", not "scheme:host".

    if not path or not host:
        new_url = QUrl()
        new_url.setScheme("luminos")
        # When path is absent, e.g. luminos://help (with no trailing slash)
        if host:
            new_url.setHost(host)
        # When host is absent, e.g. luminos:help
        else:
            new_url.setHost(path)

        new_url.setPath("/")
        if query:
            new_url.setQuery(query)
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NotFoundError("No handler found for {}".format(
            url.toDisplayString()))

    try:
        mimetype, data = handler(url)
    except OSError as e:
        raise SchemeOSError(e)

    assert mimetype is not None, url
    if mimetype == "text/html" and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode("utf-8", errors="xmlcharrefreplace")

    return mimetype, data
Exemple #14
0
 def __saveImage(self):
     """
     Private slot to save the selected image to disk.
     """
     act = self.sender()
     index = act.data()
     itm = self.imagesTree.topLevelItem(index)
     if itm is None:
         return
     
     imageUrl = QUrl(itm.text(1))
     if not imageUrl.host():
         imageUrl.setHost(QUrl(self.siteAddressLabel.text()).host())
         imageUrl.setScheme(QUrl(self.siteAddressLabel.text()).scheme())
     
     import Helpviewer.HelpWindow
     cache = Helpviewer.HelpWindow.HelpWindow.networkAccessManager().cache()
     if cache:
         cacheData = cache.data(imageUrl)
     else:
         cacheData = None
     if not cacheData:
         E5MessageBox.critical(
             self,
             self.tr("Save Image"),
             self.tr("""This image is not available."""))
         return
     
     downloadDirectory = Helpviewer.HelpWindow.HelpWindow\
         .downloadManager().downloadDirectory()
     fn = os.path.join(downloadDirectory, os.path.basename(itm.text(1)))
     filename = E5FileDialog.getSaveFileName(
         self,
         self.tr("Save Image"),
         fn,
         self.tr("All Files (*)"),
         E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite))
     
     if not filename:
         return
     
     f = QFile(filename)
     if not f.open(QFile.WriteOnly):
         E5MessageBox.critical(
             self,
             self.tr("Save Image"),
             self.tr(
                 """<p>Cannot write to file <b>{0}</b>.</p>""")
             .format(filename))
         return
     f.write(cacheData.readAll())
     f.close()
Exemple #15
0
 def addHistoryEntry(self, url):
     """
     Public method to add a history entry.
     
     @param url URL to be added (string)
     """
     cleanurl = QUrl(url)
     if cleanurl.scheme() not in ["eric", "about"]:
         if cleanurl.password():
             # don't save the password in the history
             cleanurl.setPassword("")
         if cleanurl.host():
             cleanurl.setHost(cleanurl.host().lower())
         itm = HistoryEntry(cleanurl.toString(), QDateTime.currentDateTime())
         self._addHistoryEntry(itm)
Exemple #16
0
 def addHistoryEntry(self, url):
     """
     Public method to add a history entry.
     
     @param url URL to be added (string)
     """
     cleanurl = QUrl(url)
     if cleanurl.scheme() not in ["eric", "about"]:
         if cleanurl.password():
             # don't save the password in the history
             cleanurl.setPassword("")
         if cleanurl.host():
             cleanurl.setHost(cleanurl.host().lower())
         itm = HistoryEntry(cleanurl.toString(),
                            QDateTime.currentDateTime())
         self._addHistoryEntry(itm)
Exemple #17
0
    def _init_host(self, parsed: urllib.parse.ParseResult) -> None:
        """Parse the host from the given URL.

        Deviation from Chromium:
        - http://:1234/ is not a valid URL because it has no host.
        - We don't allow patterns for dot/space hosts which QUrl considers
          invalid.
        """
        if parsed.hostname is None or not parsed.hostname.strip():
            if self._scheme not in self._SCHEMES_WITHOUT_HOST:
                raise ParseError("Pattern without host")
            assert self.host is None
            return

        if parsed.netloc.startswith('['):
            # Using QUrl parsing to minimize ipv6 addresses
            url = QUrl()
            url.setHost(parsed.hostname)
            if not url.isValid():
                raise ParseError(url.errorString())
            self.host = url.host()
            return

        if parsed.hostname == '*':
            self._match_subdomains = True
            hostname = None
        elif parsed.hostname.startswith('*.'):
            if len(parsed.hostname) == 2:
                # We don't allow just '*.' as a host.
                raise ParseError("Pattern without host")
            self._match_subdomains = True
            hostname = parsed.hostname[2:]
        elif set(parsed.hostname) in {frozenset('.'), frozenset('. ')}:
            raise ParseError("Invalid host")
        else:
            hostname = parsed.hostname

        if hostname is None:
            self.host = None
        elif '*' in hostname:
            # Only * or *.foo is allowed as host.
            raise ParseError("Invalid host wildcard")
        else:
            self.host = hostname.rstrip('.')
Exemple #18
0
def resource_url(path, qutescheme=False):
    """Load images from a relative path (to qutebrowser).

    Arguments:
        path: The relative path to the image
        qutescheme: If the logo needs to be served via a qute:// scheme.
                    This is the case when we want to show an error page from
                    there.
    """
    if qutescheme:
        url = QUrl()
        url.setScheme('qute')
        url.setHost('resource')
        url.setPath('/' + path)
        qtutils.ensure_valid(url)
        return url.toString(QUrl.FullyEncoded)
    else:
        full_path = utils.resource_filename(path)
        return QUrl.fromLocalFile(full_path).toString(QUrl.FullyEncoded)
Exemple #19
0
    def _init_host(self, parsed: urllib.parse.ParseResult) -> None:
        """Parse the host from the given URL.

        Deviation from Chromium:
        - http://:1234/ is not a valid URL because it has no host.
        """
        # https://github.com/python/typeshed/commit/f0ccb325aa787ca0a539ef9914276b2c3148327a
        if (parsed.hostname is None or  # type: ignore
                not parsed.hostname.strip()):
            if self._scheme not in self._SCHEMES_WITHOUT_HOST:
                raise ParseError("Pattern without host")
            assert self._host is None
            return

        if parsed.netloc.startswith('['):
            # Using QUrl parsing to minimize ipv6 addresses
            url = QUrl()
            url.setHost(parsed.hostname)
            if not url.isValid():
                raise ParseError(url.errorString())
            self._host = url.host()
            return

        # FIXME what about multiple dots?
        host_parts = parsed.hostname.rstrip('.').split('.')
        if host_parts[0] == '*':
            host_parts = host_parts[1:]
            self._match_subdomains = True

        if not host_parts:
            self._host = None
            return

        self._host = '.'.join(host_parts)

        if self._host.endswith('.*'):
            # Special case to have a nicer error
            raise ParseError("TLD wildcards are not implemented yet")
        if '*' in self._host:
            # Only * or *.foo is allowed as host.
            raise ParseError("Invalid host wildcard")
Exemple #20
0
def data_for_url(url):
    """Get the data to show for the given URL.

    Args:
        url: The QUrl to show.

    Return:
        A (mimetype, data) tuple.
    """
    path = url.path()
    host = url.host()
    # A url like "qute:foo" is split as "scheme:path", not "scheme:host".
    log.misc.debug("url: {}, path: {}, host {}".format(
        url.toDisplayString(), path, host))
    if path and not host:
        new_url = QUrl()
        new_url.setScheme('qute')
        new_url.setHost(path)
        new_url.setPath('/')
        if new_url.host():  # path was a valid host
            raise Redirect(new_url)

    try:
        handler = _HANDLERS[host]
    except KeyError:
        raise NoHandlerFound(url)

    try:
        mimetype, data = handler(url)
    except OSError as e:
        # FIXME:qtwebengine how to handle this?
        raise QuteSchemeOSError(e)
    except QuteSchemeError as e:
        raise

    assert mimetype is not None, url
    if mimetype == 'text/html' and isinstance(data, str):
        # We let handlers return HTML as text
        data = data.encode('utf-8', errors='xmlcharrefreplace')

    return mimetype, data
Exemple #21
0
    def url(self, player_id):
        if self.state == GameState.CLOSED:
            return None

        url = QUrl()
        url.setHost("lobby.faforever.com")
        query = QUrlQuery()
        query.addQueryItem("map", self.mapname)
        query.addQueryItem("mod", self.featured_mod)

        if self.state == GameState.OPEN:
            url.setScheme("fafgame")
            url.setPath("/" + str(player_id))
            query.addQueryItem("uid", str(self.uid))
        else:
            url.setScheme("faflive")
            url.setPath("/" + str(self.uid) + "/" + str(player_id) +
                        ".SCFAreplay")

        url.setQuery(query)
        return url
Exemple #22
0
    def _init_host(self, parsed):
        """Parse the host from the given URL.

        Deviation from Chromium:
        - http://:1234/ is not a valid URL because it has no host.
        """
        if parsed.hostname is None or not parsed.hostname.strip():
            if self._scheme not in self._SCHEMES_WITHOUT_HOST:
                raise ParseError("Pattern without host")
            assert self._host is None
            return

        if parsed.netloc.startswith('['):
            # Using QUrl parsing to minimize ipv6 addresses
            url = QUrl()
            url.setHost(parsed.hostname)
            if not url.isValid():
                raise ParseError(url.errorString())
            self._host = url.host()
            return

        # FIXME what about multiple dots?
        host_parts = parsed.hostname.rstrip('.').split('.')
        if host_parts[0] == '*':
            host_parts = host_parts[1:]
            self._match_subdomains = True

        if not host_parts:
            self._host = None
            return

        self._host = '.'.join(host_parts)

        if self._host.endswith('.*'):
            # Special case to have a nicer error
            raise ParseError("TLD wildcards are not implemented yet")
        if '*' in self._host:
            # Only * or *.foo is allowed as host.
            raise ParseError("Invalid host wildcard")
class AuthWidget(QWebEngineView):

    def __init__(self, parent, config=None, credential_file=None, cookie_persistence=False, log_level=logging.INFO):
        super(AuthWidget, self).__init__(parent)

        self.parent = parent
        self.config = None
        self.config_file = DEFAULT_CONFIG_FILE
        self.credential = DEFAULT_CREDENTIAL
        self.credential_file = None
        self.cookie_file = None
        self.cookie_jar = None
        self.auth_url = None
        self.authn_session = None
        self.authn_session_page = None
        self.authn_cookie_name = None
        self.authn_expires = time.time()
        self._success_callback = None
        self._failure_callback = None
        self._session = requests.session()
        self.token = None
        self.default_profile = QWebEngineProfile("deriva-auth", self)
        self.private_profile = QWebEngineProfile(self)

        logging.getLogger().setLevel(log_level)
        info = "%s v%s [Python: %s (PyQt: %s), %s]" % (
            self.__class__.__name__, get_installed_version(VERSION),
            platform.python_version(), PYQT_VERSION_STR, platform.platform(aliased=True))
        logging.info("Initializing authorization provider: %s" % info)
        self.cookie_persistence = cookie_persistence
        self._timer = QTimer(self)
        self._timer.timeout.connect(self._onTimerFired)
        self.configure(config, credential_file)

    def configure(self, config, credential_file):
        self.config = config if config else read_config(self.config_file, create_default=True, default=DEFAULT_CONFIG)
        self.credential_file = credential_file
        host = self.config.get("host")
        if not host:
            self.set_current_html(ERROR_HTML % "Could not locate hostname parameter in configuration.")
            return
        self.auth_url = QUrl()
        self.auth_url.setScheme(config.get("protocol", "https"))
        self.auth_url.setHost(host)
        if config.get("port") is not None:
            self.auth_url.setPort(config["port"])
        self.authn_cookie_name = self.config.get("cookie_name", "webauthn")

        self.cookie_file = DEFAULT_SESSION_CONFIG.get("cookie_jar")
        self.cookie_jar = load_cookies_from_file(self.cookie_file)

        retries = Retry(connect=DEFAULT_SESSION_CONFIG['retry_connect'],
                        read=DEFAULT_SESSION_CONFIG['retry_read'],
                        backoff_factor=DEFAULT_SESSION_CONFIG['retry_backoff_factor'],
                        status_forcelist=DEFAULT_SESSION_CONFIG['retry_status_forcelist'])

        self._session.mount(self.auth_url.toString() + '/',
                            HTTPAdapter(max_retries=retries))

    def set_current_html(self, html):
        page = QWebEnginePage(self.parent)
        page.setHtml(html)
        self.setPage(page)
        self.update()
        qApp.processEvents()

    def authenticated(self):
        if self.authn_session is None:
            return False

        now = time.time()
        if now >= self.authn_expires:
            return False

        return True

    def login(self):
        if not (self.auth_url and (self.auth_url.host() and self.auth_url.scheme())):
            logging.error("Missing or invalid hostname parameter in configuration.")
            return
        logging.info("Authenticating with host: %s" % self.auth_url.toString())
        qApp.setOverrideCursor(Qt.WaitCursor)
        self._cleanup()
        self.authn_session_page = QWebEnginePage(self.private_profile, self.parent) \
            if not self.cookie_persistence else QWebEnginePage(self.default_profile, self.parent)
        self.authn_session_page.profile().setPersistentCookiesPolicy(
            QWebEngineProfile.ForcePersistentCookies if self.cookie_persistence else
            QWebEngineProfile.NoPersistentCookies)
        if self.cookie_persistence:
            logging.debug("QTWebEngine persistent storage located at: %s" %
                          self.authn_session_page.profile().persistentStoragePath())
        self.authn_session_page.profile().cookieStore().cookieAdded.connect(self._onCookieAdded)
        self.authn_session_page.profile().cookieStore().cookieRemoved.connect(self._onCookieRemoved)
        self.authn_session_page.loadProgress.connect(self._onLoadProgress)
        self.authn_session_page.loadFinished.connect(self._onLoadFinished)

        self.authn_session_page.setUrl(QUrl(self.auth_url.toString() + "/authn/preauth"))
        self.setPage(self.authn_session_page)

    def logout(self, delete_cookies=False):
        if not (self.auth_url and (self.auth_url.host() and self.auth_url.scheme())):
            return
        if self.authenticated():
            try:
                logging.info("Logging out of host: %s" % self.auth_url.toString())
                if delete_cookies and self.cookie_persistence:
                    self.authn_session_page.profile().cookieStore().deleteAllCookies()
                self._session.delete(self.auth_url.toString() + "/authn/session")
                if self.credential_file:
                    creds = read_credential(self.credential_file, create_default=True)
                    host = self.auth_url.host()
                    if creds.get(host):
                        del creds[host]
                    write_credential(self.credential_file, creds)
            except Exception as e:
                logging.warning("Logout error: %s" % format_exception(e))
        self._cleanup()

    def setSuccessCallback(self, callback=None):
        self._success_callback = callback

    def setFailureCallback(self, callback=None):
        self._failure_callback = callback

    def setStatus(self, message):
        if self.window().statusBar is not None:
            self.window().statusBar().showMessage(message)

    def _execSuccessCallback(self):
        if self._success_callback:
            self._success_callback(host=self.auth_url.host(), credential=self.credential)

    def _execFailureCallback(self, message):
        if self._failure_callback:
            self._failure_callback(host=self.auth_url.host(), message=message)

    def _onTimerFired(self):
        if not self.authenticated():
            self.authn_session = None
            return
        resp = self._session.put(self.auth_url.toString() + "/authn/session")
        seconds_remaining = self.authn_session['seconds_remaining']
        self.authn_expires = time.time() + seconds_remaining + 1
        if resp.ok:
            logging.trace("webauthn session:\n%s\n", resp.json())
            logging.info("Session refreshed for: %s" % self.auth_url.host())
        else:
            logging.warning(
                "Unable to refresh session for: %s. Server responded: %s" %
                (self.auth_url.host(),
                 str.format("%s %s: %s" % (resp.status_code, resp.reason, resp.content.decode()))))

    def _onSessionContent(self, content):
        try:
            qApp.restoreOverrideCursor()
            self.set_current_html(SUCCESS_HTML)
            try:
                self.authn_session = json.loads(content)
            except json.JSONDecodeError:
                raise RuntimeError("Unable to parse response from server: %s" % content)
            seconds_remaining = self.authn_session['seconds_remaining']
            if not self._timer.isActive():
                interval = seconds_remaining // 2
                logging.info("Authentication successful for [%s]: credential refresh in %d seconds." %
                             (self.auth_url.toString(), interval))
                self._timer.start(interval * 1000)
            self.authn_expires = time.time() + seconds_remaining + 1
            logging.trace("webauthn session:\n%s\n", json.dumps(self.authn_session, indent=2))
            QTimer.singleShot(100, self._execSuccessCallback)
        except (ValueError, Exception) as e:
            error = format_exception(e)
            logging.error(error)
            self.set_current_html(ERROR_HTML % content)
            self._execFailureCallback(error)

    def _onPreAuthContent(self, content):
        try:
            if not content:
                logging.debug("no preauth content")
                return
            preauth = json.loads(content)
            logging.trace("webauthn preauth:\n%s\n", json.dumps(preauth, indent=2))
            qApp.setOverrideCursor(Qt.WaitCursor)
            self.authn_session_page.setUrl(QUrl(preauth["redirect_url"]))
        except (ValueError, Exception) as e:
            logging.error(format_exception(e))
            self.set_current_html(ERROR_HTML % content)

    def _onLoadFinished(self, result):
        qApp.restoreOverrideCursor()
        qApp.processEvents()
        if not result:
            self.setPage(self.authn_session_page)
            logging.debug("Page load error: %s" % self.authn_session_page.url().toDisplayString())
            return
        self.set_current_html(DEFAULT_HTML % self.auth_url.host())
        path = self.authn_session_page.url().path()
        if path == "/authn/preauth":
            self.authn_session_page.toPlainText(self._onPreAuthContent)
        elif path == "/authn/session":
            self.authn_session_page.toPlainText(self._onSessionContent)
        else:
            if self.page() != self.authn_session_page:
                self.page().deleteLater()
                self.setPage(self.authn_session_page)

    def _onLoadProgress(self, progress):
        self.setStatus("Loading page: %s [%d%%]" % (self.page().url().host(), progress))

    def _onCookieAdded(self, cookie):
        cookie_str = str(cookie.toRawForm(QNetworkCookie.NameAndValueOnly), encoding='utf-8')
        cookie_name = str(cookie.name(), encoding='utf-8')
        cookie_val = str(cookie.value(), encoding='utf-8')
        if (cookie_name == self.authn_cookie_name) and (cookie.domain() == self.config.get("host")):
            logging.trace("%s cookie added:\n\n%s\n\n" % (self.authn_cookie_name, cookie_str))
            self.credential["cookie"] = "%s=%s" % (self.authn_cookie_name, cookie_val)
            host = self.auth_url.host()
            cred_entry = dict()
            cred_entry[host] = self.credential
            if self.credential_file:
                creds = read_credential(self.credential_file, create_default=True)
                creds.update(cred_entry)
                write_credential(self.credential_file, creds)
            self.token = cookie_val
            self._session.cookies.set(self.authn_cookie_name, cookie_val, domain=host, path='/')
            if self.cookie_jar is not None:
                self.cookie_jar.set_cookie(
                    create_cookie(self.authn_cookie_name,
                                  cookie_val,
                                  domain=host,
                                  path='/',
                                  expires=0,
                                  discard=False,
                                  secure=True))
                for path in self.config.get("cookie_jars", DEFAULT_CONFIG["cookie_jars"]):
                    path_dir = os.path.dirname(path)
                    if os.path.isdir(path_dir):
                        logging.debug("Saving cookie jar to: %s" % path)
                        self.cookie_jar.save(path, ignore_discard=True, ignore_expires=True)
                    else:
                        logging.debug("Cookie jar save path [%s] does not exist." % path_dir)

    def _onCookieRemoved(self, cookie):
        cookie_str = str(cookie.toRawForm(QNetworkCookie.NameAndValueOnly), encoding='utf-8')
        cookie_name = str(cookie.name(), encoding='utf-8')
        if cookie_name == self.authn_cookie_name and cookie.domain() == self.url().host():
            logging.trace("%s cookie removed:\n\n%s\n\n" % (self.authn_cookie_name, cookie_str))
            if self.cookie_jar:
                self.cookie_jar.clear(cookie_name, path=cookie.path(), domain=cookie.domain())

    def _cleanup(self):
        self._timer.stop()
        self.token = None
        self.authn_session = None
        self.authn_expires = time.time()
        if self.authn_session_page:
            self.authn_session_page.loadProgress.disconnect(self._onLoadProgress)
            self.authn_session_page.loadFinished.disconnect(self._onLoadFinished)
            self.authn_session_page.profile().cookieStore().cookieAdded.disconnect(self._onCookieAdded)
            self.authn_session_page.profile().cookieStore().cookieRemoved.disconnect(self._onCookieRemoved)
            self.authn_session_page.deleteLater()
            self.authn_session_page = None