From f6f0a0c1ee3b9ef497933554d548471d06cd4e43 Mon Sep 17 00:00:00 2001 From: miruka Date: Tue, 2 Mar 2021 12:11:54 -0400 Subject: [PATCH] Add General.proxy setting, supports HTTP & SOCKS5 --- src/backend/backend.py | 3 ++- src/backend/matrix_client.py | 7 ++++++ src/config/settings.py | 6 ++++++ src/gui/PythonBridge/PythonRootBridge.qml | 3 +++ src/utils.h | 26 +++++++++++++++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/backend/backend.py b/src/backend/backend.py index 5dbca563..e0ec282c 100644 --- a/src/backend/backend.py +++ b/src/backend/backend.py @@ -540,7 +540,8 @@ class Backend: # We just want that client's aiohttp session, that way we don't have # to depend ourselves on aiohttp + aiohttp-socks - client = nio.AsyncClient(homeserver="") + proxy = self.settings.General.proxy + client = nio.AsyncClient(homeserver="", proxy=proxy) await have_session_be_created(client) session = type(client.client_session)( diff --git a/src/backend/matrix_client.py b/src/backend/matrix_client.py index 8311d138..ccecacab 100644 --- a/src/backend/matrix_client.py +++ b/src/backend/matrix_client.py @@ -167,11 +167,18 @@ class MatrixClient(nio.AsyncClient): store = Path(backend.appdirs.user_data_dir) / "encryption" store.mkdir(parents=True, exist_ok=True) + proxy = backend.settings.General.proxy + host = re.sub(r":\d+$", "", urlparse(homeserver).netloc) + + if host in ("127.0.0.1", "localhost", "::1"): + proxy = None + super().__init__( homeserver = homeserver, user = user, device_id = device_id, store_path = store, + proxy = proxy, config = nio.AsyncClientConfig( max_timeout_retry_wait_time = 10, # TODO: pass a custom encryption DB pickle key? diff --git a/src/config/settings.py b/src/config/settings.py index 3bc61b16..dc4b54d5 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -33,6 +33,12 @@ class General: # Interface scale multiplier, e.g. 0.5 makes everything half-size. zoom: float = 1.0 + # Adress of an HTTP or SOCKS5 proxy to pass network traffic through. + # Example adresses: "socks5://localhost:9050" (TOR with default port), + # "http://username:password@123.456.7.8:80" (HTTP with authentication). + # The application must be restarted to apply changes for this setting. + proxy: Optional[str] = None + class Presence: # Automatically set your presence to unavailable after this number of # seconds without any mouse or keyboard activity. diff --git a/src/gui/PythonBridge/PythonRootBridge.qml b/src/gui/PythonBridge/PythonRootBridge.qml index b3ab9c62..e1d4b9ea 100644 --- a/src/gui/PythonBridge/PythonRootBridge.qml +++ b/src/gui/PythonBridge/PythonRootBridge.qml @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later import QtQuick 2.12 +import CppUtils 0.1 import "." PythonBridge { @@ -22,6 +23,8 @@ PythonBridge { importNames("backend.qml_bridge", ["BRIDGE"], () => { callCoro("get_settings", [], ([settings, state, hist, theme, themeRules]) => { + CppUtils.setProxy(settings.General.proxy || "") + window.settings = settings window.uiState = state window.history = hist diff --git a/src/utils.h b/src/utils.h index 05dc9ad5..b6fd3caf 100644 --- a/src/utils.h +++ b/src/utils.h @@ -11,6 +11,9 @@ #include #include #include +#include +#include +#include #ifdef Q_OS_LINUX #ifndef NO_X11 @@ -88,6 +91,29 @@ public slots: #endif } + void setProxy(QUrl url) const { + if (url.isEmpty()) return; + + const QString scheme = url.scheme(); + + if (scheme != "socks5" && scheme != "http") { + qCritical() << "Unsupported proxy type on the Qt side:" << scheme; + return; + } + + QNetworkProxy proxy; + proxy.setType( + scheme == "socks5" ? + QNetworkProxy::Socks5Proxy : + QNetworkProxy::HttpProxy + ); + proxy.setHostName(url.host()); + proxy.setPort(url.port() == -1 ? 0 : url.port()); + proxy.setUser(url.userName()); + proxy.setPassword(url.password()); + QNetworkProxy::setApplicationProxy(proxy); + } + private: QLocale appLocale; QString waylandDisplay = qEnvironmentVariable("WAYLAND_DISPLAY");