mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-20 21:33:27 -07:00
Add SOCKS4/SOCKS4a proxy support to search engine
Pass 'Perform hostname lookup via proxy' setting along the way. Also add underline to variables and functions that are private to the python module. PR #22510.
This commit is contained in:
parent
abafbc0685
commit
3d73026ff2
2 changed files with 59 additions and 25 deletions
|
@ -409,11 +409,11 @@ Path SearchPluginManager::engineLocation()
|
|||
|
||||
void SearchPluginManager::applyProxySettings()
|
||||
{
|
||||
// Define environment variables for urllib in search engine plugins
|
||||
|
||||
// for python `urllib`: https://docs.python.org/3/library/urllib.request.html#urllib.request.ProxyHandler
|
||||
const QString HTTP_PROXY = u"http_proxy"_s;
|
||||
const QString HTTPS_PROXY = u"https_proxy"_s;
|
||||
const QString SOCKS_PROXY = u"sock_proxy"_s;
|
||||
// for `helpers.setupSOCKSProxy()`: https://everything.curl.dev/usingcurl/proxies/socks.html
|
||||
const QString SOCKS_PROXY = u"qbt_socks_proxy"_s;
|
||||
|
||||
if (!Preferences::instance()->useProxyForGeneralPurposes())
|
||||
{
|
||||
|
@ -427,7 +427,6 @@ void SearchPluginManager::applyProxySettings()
|
|||
switch (proxyConfig.type)
|
||||
{
|
||||
case Net::ProxyType::None:
|
||||
case Net::ProxyType::SOCKS4: // TODO: implement python code
|
||||
m_proxyEnv.remove(HTTP_PROXY);
|
||||
m_proxyEnv.remove(HTTPS_PROXY);
|
||||
m_proxyEnv.remove(SOCKS_PROXY);
|
||||
|
@ -435,9 +434,12 @@ void SearchPluginManager::applyProxySettings()
|
|||
|
||||
case Net::ProxyType::HTTP:
|
||||
{
|
||||
const QString proxyURL = proxyConfig.authEnabled
|
||||
? u"http://%1:%2@%3:%4"_s.arg(proxyConfig.username, proxyConfig.password, proxyConfig.ip, QString::number(proxyConfig.port))
|
||||
: u"http://%1:%2"_s.arg(proxyConfig.ip, QString::number(proxyConfig.port));
|
||||
const QString credential = proxyConfig.authEnabled
|
||||
? (proxyConfig.username + u':' + proxyConfig.password + u'@')
|
||||
: QString();
|
||||
const QString proxyURL = u"http://%1%2:%3"_s
|
||||
.arg(credential, proxyConfig.ip, QString::number(proxyConfig.port));
|
||||
|
||||
m_proxyEnv.insert(HTTP_PROXY, proxyURL);
|
||||
m_proxyEnv.insert(HTTPS_PROXY, proxyURL);
|
||||
m_proxyEnv.remove(SOCKS_PROXY);
|
||||
|
@ -446,9 +448,25 @@ void SearchPluginManager::applyProxySettings()
|
|||
|
||||
case Net::ProxyType::SOCKS5:
|
||||
{
|
||||
const QString proxyURL = proxyConfig.authEnabled
|
||||
? u"%1:%2@%3:%4"_s.arg(proxyConfig.username, proxyConfig.password, proxyConfig.ip, QString::number(proxyConfig.port))
|
||||
: u"%1:%2"_s.arg(proxyConfig.ip, QString::number(proxyConfig.port));
|
||||
const QString scheme = proxyConfig.hostnameLookupEnabled ? u"socks5h"_s : u"socks5"_s;
|
||||
const QString credential = proxyConfig.authEnabled
|
||||
? (proxyConfig.username + u':' + proxyConfig.password + u'@')
|
||||
: QString();
|
||||
const QString proxyURL = u"%1://%2%3:%4"_s
|
||||
.arg(scheme, credential, proxyConfig.ip, QString::number(proxyConfig.port));
|
||||
|
||||
m_proxyEnv.remove(HTTP_PROXY);
|
||||
m_proxyEnv.remove(HTTPS_PROXY);
|
||||
m_proxyEnv.insert(SOCKS_PROXY, proxyURL);
|
||||
}
|
||||
break;
|
||||
|
||||
case Net::ProxyType::SOCKS4:
|
||||
{
|
||||
const QString scheme = proxyConfig.hostnameLookupEnabled ? u"socks4a"_s : u"socks4"_s;
|
||||
const QString proxyURL = u"%1://%2:%3"_s
|
||||
.arg(scheme, proxyConfig.ip, QString::number(proxyConfig.port));
|
||||
|
||||
m_proxyEnv.remove(HTTP_PROXY);
|
||||
m_proxyEnv.remove(HTTPS_PROXY);
|
||||
m_proxyEnv.insert(SOCKS_PROXY, proxyURL);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#VERSION: 1.51
|
||||
#VERSION: 1.52
|
||||
|
||||
# Author:
|
||||
# Christophe DUMEZ (chris@qbittorrent.org)
|
||||
|
@ -32,19 +32,19 @@ import gzip
|
|||
import html
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import socks
|
||||
import ssl
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
def getBrowserUserAgent() -> str:
|
||||
def _getBrowserUserAgent() -> str:
|
||||
""" Disguise as browser to circumvent website blocking """
|
||||
|
||||
# Firefox release calendar
|
||||
|
@ -60,17 +60,33 @@ def getBrowserUserAgent() -> str:
|
|||
return f"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:{nowVersion}.0) Gecko/20100101 Firefox/{nowVersion}.0"
|
||||
|
||||
|
||||
headers: dict[str, Any] = {'User-Agent': getBrowserUserAgent()}
|
||||
_headers: dict[str, Any] = {'User-Agent': _getBrowserUserAgent()}
|
||||
|
||||
# SOCKS5 Proxy support
|
||||
if "sock_proxy" in os.environ and len(os.environ["sock_proxy"].strip()) > 0:
|
||||
proxy_str = os.environ["sock_proxy"].strip()
|
||||
m = re.match(r"^(?:(?P<username>[^:]+):(?P<password>[^@]+)@)?(?P<host>[^:]+):(?P<port>\w+)$",
|
||||
proxy_str)
|
||||
if m is not None:
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, m.group('host'),
|
||||
int(m.group('port')), True, m.group('username'), m.group('password'))
|
||||
socket.socket = socks.socksocket # type: ignore[misc]
|
||||
|
||||
def injectSOCKSProxySocket() -> None:
|
||||
socksURL = os.environ.get("qbt_socks_proxy")
|
||||
if socksURL is not None:
|
||||
parts = urllib.parse.urlsplit(socksURL)
|
||||
resolveHostname = (parts.scheme == "socks4a") or (parts.scheme == "socks5h")
|
||||
if (parts.scheme == "socks4") or (parts.scheme == "socks4a"):
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS4, parts.hostname, parts.port, resolveHostname)
|
||||
socket.socket = socks.socksocket # type: ignore[misc]
|
||||
elif (parts.scheme == "socks5") or (parts.scheme == "socks5h"):
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, parts.hostname, parts.port, resolveHostname, parts.username, parts.password)
|
||||
socket.socket = socks.socksocket # type: ignore[misc]
|
||||
else:
|
||||
# the following code provide backward compatibility for older qbt versions
|
||||
# TODO: scheduled be removed with qbt >= 5.3
|
||||
legacySocksURL = os.environ.get("sock_proxy")
|
||||
if legacySocksURL is not None:
|
||||
legacySocksURL = f"socks5h://{legacySocksURL.strip()}"
|
||||
parts = urllib.parse.urlsplit(legacySocksURL)
|
||||
socks.setdefaultproxy(socks.PROXY_TYPE_SOCKS5, parts.hostname, parts.port, True, parts.username, parts.password)
|
||||
socket.socket = socks.socksocket # type: ignore[misc]
|
||||
|
||||
|
||||
# monkey patching, run it now
|
||||
injectSOCKSProxySocket()
|
||||
|
||||
|
||||
# This is only provided for backward compatibility, new code should not use it
|
||||
|
@ -80,7 +96,7 @@ htmlentitydecode = html.unescape
|
|||
def retrieve_url(url: str, custom_headers: Mapping[str, Any] = {}, request_data: Optional[Any] = None, ssl_context: Optional[ssl.SSLContext] = None, unescape_html_entities: bool = True) -> str:
|
||||
""" Return the content of the url page as a string """
|
||||
|
||||
request = urllib.request.Request(url, request_data, {**headers, **custom_headers})
|
||||
request = urllib.request.Request(url, request_data, {**_headers, **custom_headers})
|
||||
try:
|
||||
response = urllib.request.urlopen(request, context=ssl_context)
|
||||
except urllib.error.URLError as errno:
|
||||
|
@ -112,7 +128,7 @@ def download_file(url: str, referer: Optional[str] = None, ssl_context: Optional
|
|||
""" Download file at url and write it to a file, return the path to the file and the url """
|
||||
|
||||
# Download url
|
||||
request = urllib.request.Request(url, headers=headers)
|
||||
request = urllib.request.Request(url, headers=_headers)
|
||||
if referer is not None:
|
||||
request.add_header('referer', referer)
|
||||
response = urllib.request.urlopen(request, context=ssl_context)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue