summaryrefslogtreecommitdiff
path: root/qutebrowser/browser/network/proxy.py
blob: 80a08f62a4e69565d1343e84634b09ea35c2cb2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:

# Copyright 2014-2021 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser.  If not, see <https://www.gnu.org/licenses/>.

"""Handling of proxies."""

from qutebrowser.config import config, configtypes
from qutebrowser.utils import message, usertypes, urlutils, utils
from qutebrowser.misc import objects
from qutebrowser.browser.network import pac
from qutebrowser.qt import QtNetwork, QtCore


application_factory = None


def init():
    """Set the application wide proxy factory."""
    global application_factory
    application_factory = ProxyFactory()
    QtNetwork.QNetworkProxyFactory.setApplicationProxyFactory(application_factory)

    config.instance.changed.connect(_warn_for_pac)
    _warn_for_pac()


@config.change_filter('content.proxy', function=True)
def _warn_for_pac():
    """Show a warning if PAC is used with QtWebEngine."""
    proxy = config.val.content.proxy
    if (isinstance(proxy, pac.PACFetcher) and
            objects.backend == usertypes.Backend.QtWebEngine):
        message.error("PAC support isn't implemented for QtWebEngine yet!")


@QtCore.pyqtSlot()
def shutdown():
    QtNetwork.QNetworkProxyFactory.setApplicationProxyFactory(
        None)  # type: ignore[arg-type]


class ProxyFactory(QtNetwork.QNetworkProxyFactory):

    """Factory for proxies to be used by qutebrowser."""

    def get_error(self):
        """Check if proxy can't be resolved.

        Return:
           None if proxy is correct, otherwise an error message.
        """
        proxy = config.val.content.proxy
        if isinstance(proxy, pac.PACFetcher):
            return proxy.fetch_error()
        else:
            return None

    def _set_capabilities(self, proxy):
        if proxy.type() == QtNetwork.QNetworkProxy.NoProxy:
            return

        capabilities = proxy.capabilities()
        lookup_cap = QtNetwork.QNetworkProxy.HostNameLookupCapability
        if config.val.content.proxy_dns_requests:
            capabilities |= lookup_cap
        else:
            capabilities &= ~lookup_cap
        proxy.setCapabilities(capabilities)

    def queryProxy(self, query):
        """Get the QNetworkProxies for a query.

        Args:
            query: The QNetworkProxyQuery to get a proxy for.

        Return:
            A list of QNetworkProxy objects in order of preference.
        """
        proxy = config.val.content.proxy
        if proxy is configtypes.SYSTEM_PROXY:
            # On Linux, use "export http_proxy=socks5://host:port" to manually
            # set system proxy.
            # ref. https://doc.qt.io/qt-5/qnetworkproxyfactory.html#systemProxyForQuery
            proxies = QtNetwork.QNetworkProxyFactory.systemProxyForQuery(query)
        elif isinstance(proxy, pac.PACFetcher):
            if objects.backend == usertypes.Backend.QtWebEngine:
                # Looks like query.url() is always invalid on QtWebEngine...
                proxy = urlutils.proxy_from_url(QtCore.QUrl('direct://'))
                assert not isinstance(proxy, pac.PACFetcher)
                proxies = [proxy]
            elif objects.backend == usertypes.Backend.QtWebKit:
                proxies = proxy.resolve(query)
            else:
                raise utils.Unreachable(objects.backend)
        else:
            proxies = [proxy]
        for proxy in proxies:
            self._set_capabilities(proxy)
        return proxies