Skip to content

Commit

Permalink
Merge pull request #6890 from nextcloud/feature/per-account-connectio…
Browse files Browse the repository at this point in the history
…n-settings

Feature/per account connection settings
  • Loading branch information
claucambra authored Jul 22, 2024
2 parents bd140e4 + 7dec7b2 commit 59b4bba
Show file tree
Hide file tree
Showing 16 changed files with 753 additions and 204 deletions.
92 changes: 91 additions & 1 deletion src/gui/accountmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

#include "sslerrordialog.h"
#include "proxyauthhandler.h"
#include "common/asserts.h"
#include "creds/credentialsfactory.h"
#include "creds/abstractcredentials.h"
#include "creds/keychainchunk.h"
#include "libsync/clientsideencryption.h"
#include "libsync/configfile.h"
#include "libsync/cookiejar.h"
Expand All @@ -29,6 +29,13 @@
#include <QNetworkAccessManager>
#include <QMessageBox>
#include <QPushButton>
#include <type_traits>

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#include <qt6keychain/keychain.h>
#else
#include <qt5keychain/keychain.h>
#endif

namespace {
constexpr auto urlC = "url";
Expand All @@ -46,6 +53,16 @@ constexpr auto serverVersionC = "serverVersion";
constexpr auto serverColorC = "serverColor";
constexpr auto serverTextColorC = "serverTextColor";
constexpr auto skipE2eeMetadataChecksumValidationC = "skipE2eeMetadataChecksumValidation";
constexpr auto networkProxySettingC = "networkProxySetting";
constexpr auto networkProxyTypeC = "networkProxyType";
constexpr auto networkProxyHostNameC = "networkProxyHostName";
constexpr auto networkProxyPortC = "networkProxyPort";
constexpr auto networkProxyNeedsAuthC = "networkProxyNeedsAuth";
constexpr auto networkProxyUserC = "networkProxyUser";
constexpr auto networkUploadLimitSettingC = "networkUploadLimitSetting";
constexpr auto networkDownloadLimitSettingC = "networkDownloadLimitSetting";
constexpr auto networkUploadLimitC = "networkUploadLimit";
constexpr auto networkDownloadLimitC = "networkDownloadLimit";
constexpr auto generalC = "General";

constexpr auto dummyAuthTypeC = "dummy";
Expand All @@ -55,6 +72,8 @@ constexpr auto shibbolethAuthTypeC = "shibboleth";
constexpr auto httpAuthPrefix = "http_";
constexpr auto webflowAuthPrefix = "webflow_";

constexpr auto networkProxyPasswordKeychainKeySuffixC = "_proxy_password";

constexpr auto legacyRelativeConfigLocationC = "/ownCloud/owncloud.cfg";
constexpr auto legacyCfgFileNameC = "owncloud.cfg";

Expand Down Expand Up @@ -330,6 +349,42 @@ void AccountManager::saveAccountHelper(Account *acc, QSettings &settings, bool s
} else {
settings.setValue(QLatin1String(skipE2eeMetadataChecksumValidationC), acc->_skipE2eeMetadataChecksumValidation);
}
settings.setValue(networkProxySettingC, static_cast<std::underlying_type_t<Account::AccountNetworkProxySetting>>(acc->networkProxySetting()));
settings.setValue(networkProxyTypeC, acc->proxyType());
settings.setValue(networkProxyHostNameC, acc->proxyHostName());
settings.setValue(networkProxyPortC, acc->proxyPort());
settings.setValue(networkProxyNeedsAuthC, acc->proxyNeedsAuth());
settings.setValue(networkProxyUserC, acc->proxyUser());
settings.setValue(networkUploadLimitSettingC, static_cast<std::underlying_type_t<Account::AccountNetworkTransferLimitSetting>>(acc->uploadLimitSetting()));
settings.setValue(networkDownloadLimitSettingC, static_cast<std::underlying_type_t<Account::AccountNetworkTransferLimitSetting>>(acc->downloadLimitSetting()));
settings.setValue(networkUploadLimitC, acc->uploadLimit());
settings.setValue(networkDownloadLimitC, acc->downloadLimit());

const auto proxyPasswordKey = QString(acc->userIdAtHostWithPort() + networkProxyPasswordKeychainKeySuffixC);
if (const auto proxyPassword = acc->proxyPassword(); proxyPassword.isEmpty()) {
const auto job = new QKeychain::DeletePasswordJob(Theme::instance()->appName(), this);
job->setKey(proxyPasswordKey);
connect(job, &QKeychain::Job::finished, this, [](const QKeychain::Job *const incomingJob) {
if (incomingJob->error() == QKeychain::NoError) {
qCInfo(lcAccountManager) << "Deleted proxy password from keychain";
} else {
qCWarning(lcAccountManager) << "Failed to delete proxy password to keychain" << incomingJob->errorString();
}
});
job->start();
} else {
const auto job = new QKeychain::WritePasswordJob(Theme::instance()->appName(), this);
job->setKey(proxyPasswordKey);
job->setBinaryData(proxyPassword.toUtf8());
connect(job, &QKeychain::Job::finished, this, [](const QKeychain::Job *const incomingJob) {
if (incomingJob->error() == QKeychain::NoError) {
qCInfo(lcAccountManager) << "Saved proxy password to keychain";
} else {
qCWarning(lcAccountManager) << "Failed to save proxy password to keychain" << incomingJob->errorString();
}
});
job->start();
}

if (acc->_credentials) {
if (saveCredentials) {
Expand Down Expand Up @@ -451,6 +506,41 @@ AccountPtr AccountManager::loadAccountHelper(QSettings &settings)

acc->setCredentials(CredentialsFactory::create(authType));

acc->setNetworkProxySetting(settings.value(networkProxySettingC).value<Account::AccountNetworkProxySetting>());
acc->setProxyType(settings.value(networkProxyTypeC).value<QNetworkProxy::ProxyType>());
acc->setProxyHostName(settings.value(networkProxyHostNameC).toString());
acc->setProxyPort(settings.value(networkProxyPortC).toInt());
acc->setProxyNeedsAuth(settings.value(networkProxyNeedsAuthC).toBool());
acc->setProxyUser(settings.value(networkProxyUserC).toString());
acc->setUploadLimitSetting(
settings.value(
networkUploadLimitSettingC,
QVariant::fromValue(Account::AccountNetworkTransferLimitSetting::GlobalLimit)
).value<Account::AccountNetworkTransferLimitSetting>());
acc->setDownloadLimitSetting(
settings.value(
networkDownloadLimitSettingC,
QVariant::fromValue(Account::AccountNetworkTransferLimitSetting::GlobalLimit)
).value<Account::AccountNetworkTransferLimitSetting>());
acc->setUploadLimit(settings.value(networkUploadLimitC).toInt());
acc->setDownloadLimit(settings.value(networkDownloadLimitC).toInt());

const auto proxyPasswordKey = QString(acc->userIdAtHostWithPort() + networkProxyPasswordKeychainKeySuffixC);
const auto job = new QKeychain::ReadPasswordJob(Theme::instance()->appName(), this);
job->setKey(proxyPasswordKey);
connect(job, &QKeychain::Job::finished, this, [acc](const QKeychain::Job *const incomingJob) {
const auto incomingReadJob = qobject_cast<const QKeychain::ReadPasswordJob *>(incomingJob);
if (incomingReadJob->error() == QKeychain::NoError) {
qCInfo(lcAccountManager) << "Read proxy password to keychain for" << acc->userIdAtHostWithPort();
const auto passwordData = incomingReadJob->binaryData();
const auto password = QString::fromUtf8(passwordData);
acc->setProxyPassword(password);
} else {
qCWarning(lcAccountManager) << "Failed to read proxy password to keychain" << incomingJob->errorString();
}
});
job->start();

// now the server cert, it is in the general group
settings.beginGroup(QLatin1String(generalC));
const auto certs = QSslCertificate::fromData(settings.value(caCertsKeyC).toByteArray());
Expand Down
27 changes: 14 additions & 13 deletions src/gui/accountsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "syncresult.h"
#include "ignorelisttablewidget.h"
#include "wizard/owncloudwizard.h"
#include "networksettings.h"
#include "ui_mnemonicdialog.h"

#include <cmath>
Expand Down Expand Up @@ -173,12 +174,12 @@ class MouseCursorChanger : public QObject
AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
: QWidget(parent)
, _ui(new Ui::AccountSettings)
, _model(new FolderStatusModel)
, _accountState(accountState)
, _userInfo(accountState, false, true)
{
_ui->setupUi(this);

_model = new FolderStatusModel;
_model->setAccountState(_accountState);
_model->setParent(this);
const auto delegate = new FolderStatusDelegate;
Expand Down Expand Up @@ -206,14 +207,22 @@ AccountSettings::AccountSettings(AccountState *accountState, QWidget *parent)
const auto fpSettingsWidget = fpSettingsController->settingsViewWidget(fpAccountUserIdAtHost, fileProviderTab);
fpSettingsLayout->addWidget(fpSettingsWidget);
fileProviderTab->setLayout(fpSettingsLayout);
} else {
disguiseTabWidget();
}
#else
disguiseTabWidget();
_ui->tabWidget->setCurrentIndex(0);
const auto tabWidget = _ui->tabWidget;
const auto fileProviderTab = _ui->fileProviderTab;
if (const auto fileProviderWidgetTabIndex = tabWidget->indexOf(fileProviderTab); fileProviderWidgetTabIndex >= 0) {
tabWidget->removeTab(fileProviderWidgetTabIndex);
}
tabWidget->setCurrentIndex(0);
#endif

const auto connectionSettingsTab = _ui->connectionSettingsTab;
const auto connectionSettingsLayout = new QVBoxLayout(connectionSettingsTab);
const auto networkSettings = new NetworkSettings(_accountState->account(), connectionSettingsTab);
connectionSettingsLayout->addWidget(networkSettings);
connectionSettingsTab->setLayout(connectionSettingsLayout);

const auto mouseCursorChanger = new MouseCursorChanger(this);
mouseCursorChanger->folderList = _ui->_folderList;
mouseCursorChanger->model = _model;
Expand Down Expand Up @@ -1709,14 +1718,6 @@ void AccountSettings::initializeE2eEncryptionSettingsMessage()
connect(actionEnableE2e, &QAction::triggered, this, &AccountSettings::slotE2eEncryptionGenerateKeys);
}

void AccountSettings::disguiseTabWidget() const
{
// Ensure all elements of the tab widget are hidden.
// Document mode lets the child view take up the whole view.
_ui->tabWidget->setDocumentMode(true);
_ui->tabWidget->tabBar()->hide();
}

} // namespace OCC

#include "accountsettings.moc"
2 changes: 0 additions & 2 deletions src/gui/accountsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,6 @@ private slots:
/// Returns the alias of the selected folder, empty string if none
[[nodiscard]] QString selectedFolderAlias() const;

void disguiseTabWidget() const;

Ui::AccountSettings *_ui;

FolderStatusModel *_model;
Expand Down
10 changes: 9 additions & 1 deletion src/gui/accountsettings.ui
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,11 @@
</item>
<item row="3" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="tabShape">
<enum>QTabWidget::Rounded</enum>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="standardSyncTab">
<attribute name="title">
Expand Down Expand Up @@ -308,6 +311,11 @@
<string>Virtual file sync</string>
</attribute>
</widget>
<widget class="QWidget" name="connectionSettingsTab">
<attribute name="title">
<string>Connection settings</string>
</attribute>
</widget>
</widget>
</item>
</layout>
Expand Down
5 changes: 5 additions & 0 deletions src/gui/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ Application::Application(int &argc, char **argv)

if (_theme->doNotUseProxy()) {
ConfigFile().setProxyType(QNetworkProxy::NoProxy);
for (const auto &accountState : AccountManager::instance()->accounts()) {
if (accountState && accountState->account()) {
accountState->account()->setNetworkProxySetting(Account::AccountNetworkProxySetting::GlobalProxy);
}
}
}

parseOptions(arguments());
Expand Down
6 changes: 3 additions & 3 deletions src/gui/connectionvalidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ void ConnectionValidator::checkServerAndAuth()
_isCheckingServerAndAuth = true;

// Lookup system proxy in a thread https://github.com/owncloud/client/issues/2993
if (ClientProxy::isUsingSystemDefault()) {
if ((ClientProxy::isUsingSystemDefault() && _account->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy)
|| _account->proxyType() == QNetworkProxy::DefaultProxy) {
qCDebug(lcConnectionValidator) << "Trying to look up system proxy";
ClientProxy::lookupSystemProxyAsync(_account->url(),
this, SLOT(systemProxyLookupDone(QNetworkProxy)));
ClientProxy::lookupSystemProxyAsync(_account->url(), this, SLOT(systemProxyLookupDone(QNetworkProxy)));
} else {
// We want to reset the QNAM proxy so that the global proxy settings are used (via ClientProxy settings)
_account->networkAccessManager()->setProxy(QNetworkProxy(QNetworkProxy::DefaultProxy));
Expand Down
18 changes: 14 additions & 4 deletions src/gui/folder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include <QMessageBox>
#include <QPushButton>
#include <QApplication>
#include <type_traits>

namespace {
#ifndef VERSION_C
Expand Down Expand Up @@ -1134,19 +1135,28 @@ SyncOptions Folder::initializeSyncOptions() const

void Folder::setDirtyNetworkLimits()
{
const auto account = _accountState->account();
const auto useGlobalDown = account->downloadLimitSetting() == Account::AccountNetworkTransferLimitSetting::GlobalLimit;
const auto useGlobalUp = account->uploadLimitSetting() == Account::AccountNetworkTransferLimitSetting::GlobalLimit;

ConfigFile cfg;

int downloadLimit = -75; // 75%
int useDownLimit = cfg.useDownloadLimit();
const auto useDownLimit = useGlobalDown
? cfg.useDownloadLimit()
: static_cast<std::underlying_type_t<Account::AccountNetworkTransferLimitSetting>>(account->downloadLimitSetting());
if (useDownLimit >= 1) {
downloadLimit = cfg.downloadLimit() * 1000;
downloadLimit = useGlobalDown ? cfg.downloadLimit() * 1000 : account->downloadLimit() * 1000;
} else if (useDownLimit == 0) {
downloadLimit = 0;
}

int uploadLimit = -75; // 75%
int useUpLimit = cfg.useUploadLimit();
const auto useUpLimit = useGlobalUp
? cfg.useUploadLimit()
: static_cast<std::underlying_type_t<Account::AccountNetworkTransferLimitSetting>>(account->uploadLimitSetting());
if (useUpLimit >= 1) {
uploadLimit = cfg.uploadLimit() * 1000;
uploadLimit = useGlobalUp ? cfg.uploadLimit() * 1000 : account->uploadLimit() * 1000;
} else if (useUpLimit == 0) {
uploadLimit = 0;
}
Expand Down
26 changes: 19 additions & 7 deletions src/gui/folderman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1511,13 +1511,14 @@ void FolderMan::setDirtyProxy()
{
const auto folderMapValues = _folderMap.values();
for (const auto folder : folderMapValues) {
if (folder) {
if (folder->accountState() && folder->accountState()->account()
&& folder->accountState()->account()->networkAccessManager()) {
// Need to do this so we do not use the old determined system proxy
folder->accountState()->account()->networkAccessManager()->setProxy(
QNetworkProxy(QNetworkProxy::DefaultProxy));
}
if (folder
&& folder->accountState()
&& folder->accountState()->account()
&& folder->accountState()->account()->networkAccessManager()
&& folder->accountState()->account()->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy) {
// Need to do this so we do not use the old determined system proxy
const auto proxy = QNetworkProxy(QNetworkProxy::DefaultProxy);
folder->accountState()->account()->networkAccessManager()->setProxy(proxy);
}
}
}
Expand All @@ -1533,6 +1534,17 @@ void FolderMan::setDirtyNetworkLimits()
}
}

void FolderMan::setDirtyNetworkLimits(const AccountPtr &account) const
{
const auto folderMapValues = _folderMap.values();
for (const auto folder : folderMapValues) {
// set only in busy folders. Otherwise they read the config anyway.
if (folder && folder->isBusy() && folder->accountState()->account() == account) {
folder->setDirtyNetworkLimits();
}
}
}

void FolderMan::leaveShare(const QString &localFile)
{
const auto localFileNoTrailingSlash = localFile.endsWith('/') ? localFile.chopped(1) : localFile;
Expand Down
1 change: 1 addition & 0 deletions src/gui/folderman.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ class FolderMan : public QObject

void setDirtyProxy();
void setDirtyNetworkLimits();
void setDirtyNetworkLimits(const AccountPtr &account) const;

/** removes current user from the share **/
void leaveShare(const QString &localFile);
Expand Down
Loading

0 comments on commit 59b4bba

Please sign in to comment.