src/eric7/EricNetwork/EricNetworkProxyFactory.py

Tue, 18 Oct 2022 16:06:21 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Tue, 18 Oct 2022 16:06:21 +0200
branch
eric7
changeset 9413
80c06d472826
parent 9221
bf71ee032bb4
child 9473
3f23dbf37dbe
permissions
-rw-r--r--

Changed the eric7 import statements to include the package name (i.e. eric7) in order to not fiddle with sys.path.

# -*- coding: utf-8 -*-

# Copyright (c) 2010 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a network proxy factory.
"""

import os
import re

from PyQt6.QtCore import QUrl, QCoreApplication
from PyQt6.QtWidgets import QDialog
from PyQt6.QtNetwork import QNetworkProxyFactory, QNetworkProxy, QNetworkProxyQuery

from eric7.EricWidgets import EricMessageBox

from eric7 import Globals, Preferences, Utilities


def schemeFromProxyType(proxyType):
    """
    Module function to determine the scheme name from the proxy type.

    @param proxyType type of the proxy (QNetworkProxy.ProxyType)
    @return scheme (string, one of Http, Https, Ftp)
    """
    scheme = ""
    if proxyType == QNetworkProxy.ProxyType.HttpProxy:
        scheme = "Http"
    elif proxyType == QNetworkProxy.ProxyType.HttpCachingProxy:
        scheme = "Https"
    elif proxyType == QNetworkProxy.ProxyType.FtpCachingProxy:
        scheme = "Ftp"
    elif proxyType == QNetworkProxy.ProxyType.NoProxy:
        scheme = "NoProxy"
    return scheme


def proxyAuthenticationRequired(proxy, auth):
    """
    Module slot to handle a proxy authentication request.

    @param proxy reference to the proxy object (QNetworkProxy)
    @param auth reference to the authenticator object (QAuthenticator)
    """
    info = QCoreApplication.translate(
        "EricNetworkProxyFactory", "<b>Connect to proxy '{0}' using:</b>"
    ).format(Utilities.html_encode(proxy.hostName()))

    from eric7.UI.AuthenticationDialog import AuthenticationDialog

    dlg = AuthenticationDialog(info, proxy.user(), True)
    dlg.setData(proxy.user(), proxy.password())
    if dlg.exec() == QDialog.DialogCode.Accepted:
        username, password = dlg.getData()
        auth.setUser(username)
        auth.setPassword(password)
        if dlg.shallSave():
            scheme = schemeFromProxyType(proxy.type())
            if scheme and scheme != "NoProxy":
                Preferences.setUI("ProxyUser/{0}".format(scheme), username)
                Preferences.setUI("ProxyPassword/{0}".format(scheme), password)
            proxy.setUser(username)
            proxy.setPassword(password)


class HostnameMatcher:
    """
    Class implementing a matcher for host names.
    """

    def __init__(self, pattern):
        """
        Constructor

        @param pattern pattern to be matched against
        @type str
        """
        self.__regExp = None
        self.setPattern(pattern)

    def setPattern(self, pattern):
        """
        Public method to set the match pattern.

        @param pattern pattern to be matched against
        """
        self.__pattern = pattern

        if "?" in pattern or "*" in pattern:
            regexp = "^.*{0}.*$".format(
                pattern.replace(".", "\\.").replace("*", ".*").replace("?", ".")
            )
            self.__regExp = re.compile(regexp, re.IGNORECASE)

    def pattern(self):
        """
        Public method to get the match pattern.

        @return match pattern
        @rtype str
        """
        return self.__pattern

    def match(self, host):
        """
        Public method to test the given string.

        @param host host name to be matched
        @type str
        @return flag indicating a successful match
        @rtype bool
        """
        if self.__regExp is None:
            return self.__pattern in host

        return self.__regExp.search(host) is not None


class EricNetworkProxyFactory(QNetworkProxyFactory):
    """
    Class implementing a network proxy factory.
    """

    def __init__(self):
        """
        Constructor
        """
        super().__init__()

        self.__hostnameMatchers = []
        self.__exceptions = ""

    def __setExceptions(self, exceptions):
        """
        Private method to set the host name exceptions.

        @param exceptions list of exceptions separated by ','
        @type str
        """
        self.__hostnameMatchers = []
        self.__exceptions = exceptions
        for exception in self.__exceptions.split(","):
            self.__hostnameMatchers.append(HostnameMatcher(exception.strip()))

    def queryProxy(self, query):
        """
        Public method to determine a proxy for a given query.

        @param query reference to the query object (QNetworkProxyQuery)
        @return list of proxies in order of preference (list of QNetworkProxy)
        """
        if (
            query.queryType() == QNetworkProxyQuery.QueryType.UrlRequest
            and query.protocolTag() in ["http", "https", "ftp"]
        ):
            # use proxy at all ?
            if not Preferences.getUI("UseProxy"):
                return [QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)]

            # test for exceptions
            exceptions = Preferences.getUI("ProxyExceptions")
            if exceptions != self.__exceptions:
                self.__setExceptions(exceptions)
            urlHost = query.url().host()
            for matcher in self.__hostnameMatchers:
                if matcher.match(urlHost):
                    return [QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)]

            # determine proxy
            if Preferences.getUI("UseSystemProxy"):
                proxyList = QNetworkProxyFactory.systemProxyForQuery(query)
                if (
                    not Globals.isWindowsPlatform()
                    and len(proxyList) == 1
                    and proxyList[0].type() == QNetworkProxy.ProxyType.NoProxy
                ):
                    # try it the Python way
                    # scan the environment for variables named <scheme>_proxy
                    # scan over whole environment to make this case insensitive
                    for name, value in os.environ.items():
                        name = name.lower()
                        if (
                            value
                            and name[-6:] == "_proxy"
                            and name[:-6] == query.protocolTag().lower()
                        ):
                            url = QUrl(value)
                            if url.scheme() in ["http", "https"]:
                                proxyType = QNetworkProxy.ProxyType.HttpProxy
                            elif url.scheme() == "ftp":
                                proxyType = QNetworkProxy.ProxyType.FtpCachingProxy
                            else:
                                proxyType = QNetworkProxy.ProxyType.HttpProxy
                            proxy = QNetworkProxy(
                                proxyType,
                                url.host(),
                                url.port(),
                                url.userName(),
                                url.password(),
                            )
                            proxyList = [proxy]
                            break
                if proxyList:
                    scheme = schemeFromProxyType(proxyList[0].type())
                    if scheme == "":
                        scheme = "Http"
                    if scheme != "NoProxy":
                        proxyList[0].setUser(
                            Preferences.getUI("ProxyUser/{0}".format(scheme))
                        )
                        proxyList[0].setPassword(
                            Preferences.getUI("ProxyPassword/{0}".format(scheme))
                        )
                    return proxyList
                else:
                    return [QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)]
            else:
                if Preferences.getUI("UseHttpProxyForAll"):
                    protocolKey = "Http"
                else:
                    protocolKey = query.protocolTag().capitalize()
                host = Preferences.getUI("ProxyHost/{0}".format(protocolKey))
                if not host:
                    EricMessageBox.critical(
                        None,
                        QCoreApplication.translate(
                            "EricNetworkProxyFactory", "Proxy Configuration Error"
                        ),
                        QCoreApplication.translate(
                            "EricNetworkProxyFactory",
                            """Proxy usage was activated"""
                            """ but no proxy host for protocol"""
                            """ '{0}' configured.""",
                        ).format(protocolKey),
                    )
                    return [QNetworkProxy(QNetworkProxy.ProxyType.DefaultProxy)]
                else:
                    if protocolKey in ["Http", "Https", "Ftp"]:
                        if query.protocolTag() == "ftp":
                            proxyType = QNetworkProxy.ProxyType.FtpCachingProxy
                        else:
                            proxyType = QNetworkProxy.ProxyType.HttpProxy
                        proxy = QNetworkProxy(
                            proxyType,
                            host,
                            Preferences.getUI("ProxyPort/" + protocolKey),
                            Preferences.getUI("ProxyUser/" + protocolKey),
                            Preferences.getUI("ProxyPassword/" + protocolKey),
                        )
                    else:
                        proxy = QNetworkProxy(QNetworkProxy.ProxyType.DefaultProxy)
                    return [proxy, QNetworkProxy(QNetworkProxy.ProxyType.DefaultProxy)]
        else:
            return [QNetworkProxy(QNetworkProxy.ProxyType.NoProxy)]

eric ide

mercurial