diff --git a/src/fetcher.cpp b/src/fetcher.cpp index 831f835a..049f5acc 100644 --- a/src/fetcher.cpp +++ b/src/fetcher.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -43,7 +44,14 @@ Fetcher::Fetcher() manager->setStrictTransportSecurityEnabled(true); manager->enableStrictTransportSecurityStore(true); - QNetworkProxyFactory::setUseSystemConfiguration(true); + // First save the original system proxy settings + m_systemHttpProxy = qgetenv("http_proxy"); + m_systemHttpsProxy = qgetenv("https_proxy"); + m_isSystemProxyDefined = (QNetworkProxy::applicationProxy().type() != QNetworkProxy::ProxyType::NoProxy); + qCDebug(kastsFetcher) << "saved system proxy:" << m_systemHttpProxy << m_systemHttpsProxy << m_isSystemProxyDefined; + + // Set network proxy based on saved settings + setNetworkProxy(); // setup update timer if required initializeUpdateTimer(); @@ -286,3 +294,84 @@ void Fetcher::checkUpdateTimer() qCDebug(kastsFetcher) << "new auto feed update trigger set to" << m_updateTriggerTime; } } + +void Fetcher::setNetworkProxy() +{ + SettingsManager *settings = SettingsManager::self(); + QNetworkProxy proxy; + + // define network proxy environment variable + // this is needed for the audio backends which don't obey qt's settings + QByteArray appProxy; + if (!settings->proxyUser().isEmpty()) { + appProxy += QUrl::toPercentEncoding(settings->proxyUser()); + if (!settings->proxyPassword().isEmpty()) { + appProxy += ":" + QUrl::toPercentEncoding(settings->proxyPassword()); + } + appProxy += "@"; + } + appProxy += settings->proxyHost().toLocal8Bit() + ":" + QByteArray::number(settings->proxyPort()); + + // type match to ProxyType from config.ksettings + switch (settings->proxyType()) { + case 1: // No Proxy + proxy.setType(QNetworkProxy::NoProxy); + QNetworkProxy::setApplicationProxy(proxy); + + // also reset environment variables if they have been set + qunsetenv("http_proxy"); + qunsetenv("https_proxy"); + break; + case 2: // HTTP + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName(settings->proxyHost()); + proxy.setPort(settings->proxyPort()); + proxy.setUser(settings->proxyUser()); + proxy.setPassword(settings->proxyPassword()); + QNetworkProxy::setApplicationProxy(proxy); + + // also set it through environment variables for the audio backends + appProxy.prepend("http://"); + qputenv("http_proxy", appProxy); + qputenv("https_proxy", appProxy); + qCDebug(kastsFetcher) << "appProxy environment variable" << appProxy; + break; + case 3: // SOCKS 5 + proxy.setType(QNetworkProxy::Socks5Proxy); + proxy.setHostName(settings->proxyHost()); + proxy.setPort(settings->proxyPort()); + proxy.setUser(settings->proxyUser()); + proxy.setPassword(settings->proxyPassword()); + QNetworkProxy::setApplicationProxy(proxy); + + // also set it through environment variables for the audio backends + appProxy.prepend("socks5://"); + qputenv("http_proxy", appProxy); + qputenv("https_proxy", appProxy); + qCDebug(kastsFetcher) << "appProxy environment variable" << appProxy; + break; + case 0: // System Default + default: + QNetworkProxyFactory::setUseSystemConfiguration(true); + + // also reset env variables that might have been overridden + if (!m_systemHttpProxy.isEmpty()) { + qputenv("http_proxy", m_systemHttpProxy); + } else { + qunsetenv("http_proxy"); + } + if (!m_systemHttpProxy.isEmpty()) { + qputenv("https_proxy", m_systemHttpsProxy); + } else { + qunsetenv("https_proxy"); + } + break; + } + + qCDebug(kastsFetcher) << "Network proxy set to:" << QNetworkProxy::applicationProxy(); +} + +bool Fetcher::isSystemProxyDefined() +{ + return m_isSystemProxyDefined; +} diff --git a/src/fetcher.h b/src/fetcher.h index 222b93ff..8c1a730b 100644 --- a/src/fetcher.h +++ b/src/fetcher.h @@ -55,6 +55,9 @@ public: void initializeUpdateTimer(); void checkUpdateTimer(); + Q_INVOKABLE void setNetworkProxy(); + Q_INVOKABLE bool isSystemProxyDefined(); + Q_SIGNALS: void entryAdded(const QString &feedurl, const QString &id); void entryUpdated(const QString &feedurl, const QString &id); @@ -93,4 +96,8 @@ private: const qint64 m_checkInterval = 10 * 60 * 1000; // trigger timer every 10 minutes QTimer *m_updateTimer; QDateTime m_updateTriggerTime; + + QByteArray m_systemHttpProxy; + QByteArray m_systemHttpsProxy; + bool m_isSystemProxyDefined; }; diff --git a/src/qml/Settings/NetworkSettingsPage.qml b/src/qml/Settings/NetworkSettingsPage.qml index e83364b5..dada517d 100644 --- a/src/qml/Settings/NetworkSettingsPage.qml +++ b/src/qml/Settings/NetworkSettingsPage.qml @@ -1,6 +1,7 @@ /** * SPDX-FileCopyrightText: 2020 Tobias Fella * SPDX-FileCopyrightText: 2021 Bart De Vries + * SPDX-FileCopyrightText: 2022 Gary Wang * * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL */ @@ -11,6 +12,7 @@ import QtQuick.Layouts import org.kde.kirigami as Kirigami import org.kde.kirigamiaddons.formcard as FormCard +import org.kde.kirigamiaddons.labs.components as Addons import org.kde.kasts import org.kde.kasts.settings @@ -18,6 +20,9 @@ import org.kde.kasts.settings FormCard.FormCardPage { id: root + property int currentType: SettingsManager.proxyType + property bool proxyConfigChanged: false + FormCard.FormHeader { Layout.fillWidth: true title: i18nc("@title Form header for settings related to network connections", "Network") @@ -89,4 +94,130 @@ FormCard.FormCardPage { } } } + + FormCard.FormHeader { + title: i18nc("@title Form header for settings related to network proxies", "Network Proxy") + } + + FormCard.FormCard { + FormCard.FormRadioDelegate { + text: i18nc("@option:radio Network proxy selection", "System Default") + checked: currentType === 0 + enabled: !SettingsManager.isProxyTypeImmutable + onToggled: { + currentType = 0; + } + } + + FormCard.FormRadioDelegate { + text: i18nc("@option:radio Network proxy selection", "No Proxy") + checked: currentType === 1 + enabled: !SettingsManager.isProxyTypeImmutable + onToggled: { + currentType = 1; + } + } + + FormCard.FormRadioDelegate { + text: i18nc("@option:radio Network proxy selection", "HTTP") + checked: currentType === 2 + enabled: !SettingsManager.isProxyTypeImmutable + onToggled: { + currentType = 2; + } + } + + FormCard.FormRadioDelegate { + text: i18nc("@option:radio Network proxy selection", "Socks5") + checked: currentType === 3 + enabled: !SettingsManager.isProxyTypeImmutable + onToggled: { + currentType = 3; + } + } + + FormCard.FormDelegateSeparator { + visible: currentType > 1 + } + + FormCard.FormTextFieldDelegate { + id: hostField + visible: currentType > 1 + label: i18nc("@label:textbox Hostname for proxy config", "Host") + text: SettingsManager.proxyHost + inputMethodHints: Qt.ImhUrlCharactersOnly + onEditingFinished: { + proxyConfigChanged = true; + } + } + + FormCard.FormSpinBoxDelegate { + id: portField + visible: currentType > 1 + label: i18nc("@label:spinbox Port for proxy config", "Port") + value: SettingsManager.proxyPort + from: 0 + to: 65536 + textFromValue: (value, locale) => { + return value; // it will add a thousands separator if we don't do this, not sure why + } + onValueChanged: { + proxyConfigChanged = true; + } + } + + FormCard.FormTextFieldDelegate { + id: userField + visible: currentType > 1 + label: i18nc("@label:textbox Username for proxy config", "User") + text: SettingsManager.proxyUser + inputMethodHints: Qt.ImhUrlCharactersOnly + onEditingFinished: { + proxyConfigChanged = true; + } + } + + FormCard.FormTextFieldDelegate { + id: passwordField + visible: currentType > 1 + label: i18nc("@label:textbox Password for proxy config", "Password") + text: SettingsManager.proxyPassword + echoMode: TextInput.Password + inputMethodHints: Qt.ImhUrlCharactersOnly + onEditingFinished: { + proxyConfigChanged = true; + } + } + + FormCard.FormDelegateSeparator {} + + FormCard.FormTextDelegate { + trailing: Controls.Button { + icon.name: "dialog-ok" + text: i18nc("@action:button", "Apply") + enabled: currentType !== SettingsManager.proxyType || proxyConfigChanged + onClicked: { + SettingsManager.proxyType = currentType; + SettingsManager.proxyHost = hostField.text; + SettingsManager.proxyPort = portField.value; + SettingsManager.proxyUser = userField.text; + SettingsManager.proxyPassword = passwordField.text; + SettingsManager.save(); + proxyConfigChanged = false; + Fetcher.setNetworkProxy(); + } + } + } + } + + footer: Addons.Banner { + Layout.fillWidth: true + type: Kirigami.MessageType.Warning + visible: currentType < 2 && Fetcher.isSystemProxyDefined() + text: i18nc("@info:status Warning message related to app proxy settings", "Your system level or app level proxy settings might be ignored by the audio backend when streaming audio. The settings should still be honored by all other network related actions, including downloading episodes.") + } + + Component.onCompleted: { + proxyConfigChanged = false; + } } diff --git a/src/settingsmanager.kcfg b/src/settingsmanager.kcfg index 4a77382c..17b281d0 100644 --- a/src/settingsmanager.kcfg +++ b/src/settingsmanager.kcfg @@ -237,4 +237,37 @@ + + + + + + + + + + + + + + System + + + + + 127.0.0.1 + + + + 1080 + + + + + + + + + +