diff --git a/src/gui/accountmanager.cpp b/src/gui/accountmanager.cpp index 9a11314ce21c0..e89fdc7e72c6e 100644 --- a/src/gui/accountmanager.cpp +++ b/src/gui/accountmanager.cpp @@ -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" @@ -29,6 +29,13 @@ #include #include #include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) +#include +#else +#include +#endif namespace { constexpr auto urlC = "url"; @@ -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"; @@ -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"; @@ -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>(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>(acc->uploadLimitSetting())); + settings.setValue(networkDownloadLimitSettingC, static_cast>(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) { @@ -451,6 +506,41 @@ AccountPtr AccountManager::loadAccountHelper(QSettings &settings) acc->setCredentials(CredentialsFactory::create(authType)); + acc->setNetworkProxySetting(settings.value(networkProxySettingC).value()); + acc->setProxyType(settings.value(networkProxyTypeC).value()); + 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()); + acc->setDownloadLimitSetting( + settings.value( + networkDownloadLimitSettingC, + QVariant::fromValue(Account::AccountNetworkTransferLimitSetting::GlobalLimit) + ).value()); + 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(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()); diff --git a/src/gui/accountsettings.cpp b/src/gui/accountsettings.cpp index 2b581e3606cf5..15498d5629f5b 100644 --- a/src/gui/accountsettings.cpp +++ b/src/gui/accountsettings.cpp @@ -42,6 +42,7 @@ #include "syncresult.h" #include "ignorelisttablewidget.h" #include "wizard/owncloudwizard.h" +#include "networksettings.h" #include "ui_mnemonicdialog.h" #include @@ -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; @@ -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; @@ -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" diff --git a/src/gui/accountsettings.h b/src/gui/accountsettings.h index 117a3aa536a0a..8981f1dffe6f8 100644 --- a/src/gui/accountsettings.h +++ b/src/gui/accountsettings.h @@ -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; diff --git a/src/gui/accountsettings.ui b/src/gui/accountsettings.ui index bc1594e8f3189..3e6323d26eb5c 100644 --- a/src/gui/accountsettings.ui +++ b/src/gui/accountsettings.ui @@ -259,8 +259,11 @@ + + QTabWidget::Rounded + - 1 + 0 @@ -308,6 +311,11 @@ Virtual file sync + + + Connection settings + + diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 0b49a2e93e543..c2d0b55c3729a 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -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()); diff --git a/src/gui/connectionvalidator.cpp b/src/gui/connectionvalidator.cpp index 9cc24de4a7521..6bdeca0f7c3a1 100644 --- a/src/gui/connectionvalidator.cpp +++ b/src/gui/connectionvalidator.cpp @@ -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)); diff --git a/src/gui/folder.cpp b/src/gui/folder.cpp index 37d83430ae117..3aad88a0f8078 100644 --- a/src/gui/folder.cpp +++ b/src/gui/folder.cpp @@ -47,6 +47,7 @@ #include #include #include +#include namespace { #ifndef VERSION_C @@ -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>(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>(account->uploadLimitSetting()); if (useUpLimit >= 1) { - uploadLimit = cfg.uploadLimit() * 1000; + uploadLimit = useGlobalUp ? cfg.uploadLimit() * 1000 : account->uploadLimit() * 1000; } else if (useUpLimit == 0) { uploadLimit = 0; } diff --git a/src/gui/folderman.cpp b/src/gui/folderman.cpp index 46073c24874a0..0dc60e626b8b3 100644 --- a/src/gui/folderman.cpp +++ b/src/gui/folderman.cpp @@ -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); } } } @@ -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; diff --git a/src/gui/folderman.h b/src/gui/folderman.h index af2d5e092e696..0fcffc9fdd16c 100644 --- a/src/gui/folderman.h +++ b/src/gui/folderman.h @@ -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); diff --git a/src/gui/networksettings.cpp b/src/gui/networksettings.cpp index 66ddbbaa5f983..c0f60b7bb1f37 100644 --- a/src/gui/networksettings.cpp +++ b/src/gui/networksettings.cpp @@ -15,22 +15,24 @@ #include "networksettings.h" #include "ui_networksettings.h" -#include "theme.h" -#include "configfile.h" +#include "account.h" +#include "accountmanager.h" #include "application.h" #include "configfile.h" #include "folderman.h" -#include "accountmanager.h" +#include "theme.h" #include #include #include +#include namespace OCC { -NetworkSettings::NetworkSettings(QWidget *parent) +NetworkSettings::NetworkSettings(const AccountPtr &account, QWidget *parent) : QWidget(parent) , _ui(new Ui::NetworkSettings) + , _account(account) { _ui->setupUi(this); @@ -38,6 +40,12 @@ NetworkSettings::NetworkSettings(QWidget *parent) _ui->proxyGroupBox->setVisible(!Theme::instance()->doNotUseProxy()); + if (!account) { + _ui->globalProxySettingsRadioButton->setVisible(false); + _ui->globalDownloadSettingsRadioButton->setVisible(false); + _ui->globalUploadSettingsRadioButton->setVisible(false); + } + if (!Theme::instance()->doNotUseProxy()) { _ui->hostLineEdit->setPlaceholderText(tr("Hostname of proxy server")); _ui->userLineEdit->setPlaceholderText(tr("Username for proxy server")); @@ -60,10 +68,8 @@ NetworkSettings::NetworkSettings(QWidget *parent) loadProxySettings(); - connect(_ui->typeComboBox, static_cast(&QComboBox::currentIndexChanged), this, - &NetworkSettings::saveProxySettings); - connect(_ui->proxyButtonGroup, &QButtonGroup::buttonClicked, this, - &NetworkSettings::saveProxySettings); + connect(_ui->typeComboBox, static_cast(&QComboBox::currentIndexChanged), this, &NetworkSettings::saveProxySettings); + connect(_ui->proxyButtonGroup, &QButtonGroup::buttonClicked, this, &NetworkSettings::saveProxySettings); connect(_ui->hostLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings); connect(_ui->userLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings); connect(_ui->passwordLineEdit, &QLineEdit::editingFinished, this, &NetworkSettings::saveProxySettings); @@ -85,9 +91,11 @@ NetworkSettings::NetworkSettings(QWidget *parent) _ui->uploadSpinBox->setVisible(_ui->uploadLimitRadioButton->isChecked()); _ui->uploadSpinBoxLabel->setVisible(_ui->uploadLimitRadioButton->isChecked()); + connect(_ui->globalUploadSettingsRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->uploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->noUploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->autoUploadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); + connect(_ui->globalDownloadSettingsRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->downloadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->noDownloadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); connect(_ui->autoDownloadLimitRadioButton, &QAbstractButton::clicked, this, &NetworkSettings::saveBWLimitSettings); @@ -115,117 +123,168 @@ void NetworkSettings::loadProxySettings() _ui->proxyGroupBox->setEnabled(false); return; } + + const auto useGlobalProxy = !_account || _account->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy; + const auto cfgFile = ConfigFile(); + const auto proxyType = useGlobalProxy ? cfgFile.proxyType() : _account->proxyType(); + const auto proxyPort = useGlobalProxy ? cfgFile.proxyPort() : _account->proxyPort(); + const auto proxyHostName = useGlobalProxy ? cfgFile.proxyHostName() : _account->proxyHostName(); + const auto proxyNeedsAuth = useGlobalProxy ? cfgFile.proxyNeedsAuth() : _account->proxyNeedsAuth(); + const auto proxyUser = useGlobalProxy ? cfgFile.proxyUser() : _account->proxyUser(); + const auto proxyPassword = useGlobalProxy ? cfgFile.proxyPassword() : _account->proxyPassword(); + // load current proxy settings - OCC::ConfigFile cfgFile; - int type = cfgFile.proxyType(); - switch (type) { - case QNetworkProxy::NoProxy: - _ui->noProxyRadioButton->setChecked(true); - break; - case QNetworkProxy::DefaultProxy: - _ui->systemProxyRadioButton->setChecked(true); - break; - case QNetworkProxy::Socks5Proxy: - case QNetworkProxy::HttpProxy: - _ui->typeComboBox->setCurrentIndex(_ui->typeComboBox->findData(type)); - _ui->manualProxyRadioButton->setChecked(true); - break; - default: - break; + if (_account && _account->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy) { + _ui->globalProxySettingsRadioButton->setChecked(true); + } else { + switch (proxyType) { + case QNetworkProxy::NoProxy: + _ui->noProxyRadioButton->setChecked(true); + break; + case QNetworkProxy::DefaultProxy: + _ui->systemProxyRadioButton->setChecked(true); + break; + case QNetworkProxy::Socks5Proxy: + case QNetworkProxy::HttpProxy: + _ui->typeComboBox->setCurrentIndex(_ui->typeComboBox->findData(proxyType)); + _ui->manualProxyRadioButton->setChecked(true); + break; + default: + break; + } } - _ui->hostLineEdit->setText(cfgFile.proxyHostName()); - int port = cfgFile.proxyPort(); - if (port == 0) - port = 8080; - _ui->portSpinBox->setValue(port); - _ui->authRequiredcheckBox->setChecked(cfgFile.proxyNeedsAuth()); - _ui->userLineEdit->setText(cfgFile.proxyUser()); - _ui->passwordLineEdit->setText(cfgFile.proxyPassword()); + _ui->hostLineEdit->setText(proxyHostName); + _ui->portSpinBox->setValue(proxyPort == 0 ? 8080 : proxyPort); + _ui->authRequiredcheckBox->setChecked(proxyNeedsAuth); + _ui->userLineEdit->setText(proxyUser); + _ui->passwordLineEdit->setText(proxyPassword); } void NetworkSettings::loadBWLimitSettings() { - ConfigFile cfgFile; - - int useDownloadLimit = cfgFile.useDownloadLimit(); - if (useDownloadLimit >= 1) { + const auto useGlobalLimit = !_account || _account->downloadLimitSetting() == Account::AccountNetworkTransferLimitSetting::GlobalLimit; + const auto cfgFile = ConfigFile(); + const auto useDownloadLimit = useGlobalLimit ? cfgFile.useDownloadLimit() : static_cast>(_account->downloadLimitSetting()); + const auto downloadLimit = useGlobalLimit ? cfgFile.downloadLimit() : _account->downloadLimit(); + const auto useUploadLimit = useGlobalLimit ? cfgFile.useUploadLimit() : static_cast>(_account->uploadLimitSetting()); + const auto uploadLimit = useGlobalLimit ? cfgFile.uploadLimit() : _account->uploadLimit(); + + if (_account && _account->downloadLimitSetting() == Account::AccountNetworkTransferLimitSetting::GlobalLimit) { + _ui->globalDownloadSettingsRadioButton->setChecked(true); + } else if (useDownloadLimit >= 1) { _ui->downloadLimitRadioButton->setChecked(true); } else if (useDownloadLimit == 0) { _ui->noDownloadLimitRadioButton->setChecked(true); } else { _ui->autoDownloadLimitRadioButton->setChecked(true); } - _ui->downloadSpinBox->setValue(cfgFile.downloadLimit()); + _ui->downloadSpinBox->setValue(downloadLimit); - int useUploadLimit = cfgFile.useUploadLimit(); - if (useUploadLimit >= 1) { + if (_account && _account->uploadLimitSetting() == Account::AccountNetworkTransferLimitSetting::GlobalLimit) { + _ui->globalUploadSettingsRadioButton->setChecked(true); + } else if (useUploadLimit >= 1) { _ui->uploadLimitRadioButton->setChecked(true); } else if (useUploadLimit == 0) { _ui->noUploadLimitRadioButton->setChecked(true); } else { _ui->autoUploadLimitRadioButton->setChecked(true); } - _ui->uploadSpinBox->setValue(cfgFile.uploadLimit()); + _ui->uploadSpinBox->setValue(uploadLimit); } void NetworkSettings::saveProxySettings() { - ConfigFile cfgFile; - checkEmptyProxyHost(); + + const auto useGlobalProxy = _ui->globalProxySettingsRadioButton->isChecked(); + const auto user = _ui->userLineEdit->text(); + const auto password = _ui->passwordLineEdit->text(); + const auto host = _ui->hostLineEdit->text(); + const auto port = _ui->portSpinBox->value(); + const auto needsAuth = _ui->authRequiredcheckBox->isChecked(); + + auto proxyType = QNetworkProxy::NoProxy; + if (_ui->noProxyRadioButton->isChecked()) { - cfgFile.setProxyType(QNetworkProxy::NoProxy); + proxyType = QNetworkProxy::NoProxy; } else if (_ui->systemProxyRadioButton->isChecked()) { - cfgFile.setProxyType(QNetworkProxy::DefaultProxy); + proxyType = QNetworkProxy::DefaultProxy; } else if (_ui->manualProxyRadioButton->isChecked()) { - int type = _ui->typeComboBox->itemData(_ui->typeComboBox->currentIndex()).toInt(); - QString host = _ui->hostLineEdit->text(); - if (host.isEmpty()) - type = QNetworkProxy::NoProxy; - bool needsAuth = _ui->authRequiredcheckBox->isChecked(); - QString user = _ui->userLineEdit->text(); - QString pass = _ui->passwordLineEdit->text(); - cfgFile.setProxyType(type, _ui->hostLineEdit->text(), - _ui->portSpinBox->value(), needsAuth, user, pass); + proxyType = _ui->typeComboBox->itemData(_ui->typeComboBox->currentIndex()).value(); + if (host.isEmpty()) { + proxyType = QNetworkProxy::NoProxy; + } } - ClientProxy proxy; - proxy.setupQtProxyFromConfig(); // Refresh the Qt proxy settings as the - // quota check can happen all the time. + if (_account) { // We must be setting up network proxy for a specific account + const auto proxySetting = useGlobalProxy ? Account::AccountNetworkProxySetting::GlobalProxy : Account::AccountNetworkProxySetting::AccountSpecificProxy; + _account->setProxySettings(proxySetting, proxyType, host, port, needsAuth, user, password); + const auto accountState = AccountManager::instance()->accountFromUserId(_account->userIdAtHostWithPort()); + accountState->freshConnectionAttempt(); + AccountManager::instance()->saveAccount(_account.data()); + } else { + ConfigFile().setProxyType(proxyType, host, port, needsAuth, user, password); + ClientProxy proxy; + proxy.setupQtProxyFromConfig(); // Refresh the Qt proxy settings as the + // quota check can happen all the time. - // ...and set the folders dirty, they refresh their proxy next time they - // start the sync. - FolderMan::instance()->setDirtyProxy(); + // ...and set the folders dirty, they refresh their proxy next time they + // start the sync. + FolderMan::instance()->setDirtyProxy(); - const auto accounts = AccountManager::instance()->accounts(); - for (auto account : accounts) { - account->freshConnectionAttempt(); + const auto accounts = AccountManager::instance()->accounts(); + for (const auto &accountState : accounts) { + if (accountState->account()->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy) { + accountState->freshConnectionAttempt(); + } + } } } void NetworkSettings::saveBWLimitSettings() { - ConfigFile cfgFile; + const auto downloadLimit = _ui->downloadSpinBox->value(); + const auto uploadLimit = _ui->uploadSpinBox->value(); + + auto useDownloadLimit = 0; + auto useUploadLimit = 0; + if (_ui->downloadLimitRadioButton->isChecked()) { - cfgFile.setUseDownloadLimit(1); + useDownloadLimit = 1; } else if (_ui->noDownloadLimitRadioButton->isChecked()) { - cfgFile.setUseDownloadLimit(0); + useDownloadLimit = 0; } else if (_ui->autoDownloadLimitRadioButton->isChecked()) { - cfgFile.setUseDownloadLimit(-1); + useDownloadLimit = -1; + } else if (_account && _ui->globalDownloadSettingsRadioButton->isChecked()) { + useDownloadLimit = -2; } - cfgFile.setDownloadLimit(_ui->downloadSpinBox->value()); if (_ui->uploadLimitRadioButton->isChecked()) { - cfgFile.setUseUploadLimit(1); + useUploadLimit = 1; } else if (_ui->noUploadLimitRadioButton->isChecked()) { - cfgFile.setUseUploadLimit(0); + useUploadLimit = 0; } else if (_ui->autoUploadLimitRadioButton->isChecked()) { - cfgFile.setUseUploadLimit(-1); + useUploadLimit = -1; + } else if (_account && _ui->globalUploadSettingsRadioButton->isChecked()) { + useUploadLimit = -2; + } + + if (_account) { + _account->setDownloadLimitSetting(static_cast(useDownloadLimit)); + _account->setDownloadLimit(downloadLimit); + _account->setUploadLimitSetting(static_cast(useUploadLimit)); + _account->setUploadLimit(uploadLimit); + AccountManager::instance()->saveAccount(_account.data()); + } else { + ConfigFile cfg; + cfg.setUseDownloadLimit(useDownloadLimit); + cfg.setUseUploadLimit(useUploadLimit); + cfg.setDownloadLimit(downloadLimit); + cfg.setUploadLimit(uploadLimit); } - cfgFile.setUploadLimit(_ui->uploadSpinBox->value()); - FolderMan::instance()->setDirtyNetworkLimits(); + FolderMan::instance()->setDirtyNetworkLimits(_account); } void NetworkSettings::checkEmptyProxyHost() diff --git a/src/gui/networksettings.h b/src/gui/networksettings.h index 8e3a97c446b6d..6fa95bd903381 100644 --- a/src/gui/networksettings.h +++ b/src/gui/networksettings.h @@ -17,8 +17,10 @@ #include +#include "libsync/accountfwd.h" namespace OCC { +class Account; namespace Ui { class NetworkSettings; @@ -33,7 +35,7 @@ class NetworkSettings : public QWidget Q_OBJECT public: - explicit NetworkSettings(QWidget *parent = nullptr); + explicit NetworkSettings(const AccountPtr &account = {}, QWidget *parent = nullptr); ~NetworkSettings() override; [[nodiscard]] QSize sizeHint() const override; @@ -54,6 +56,7 @@ private slots: void loadBWLimitSettings(); Ui::NetworkSettings *_ui; + AccountPtr _account; }; diff --git a/src/gui/networksettings.ui b/src/gui/networksettings.ui index 3974a1f82caa0..db6db42be4802 100644 --- a/src/gui/networksettings.ui +++ b/src/gui/networksettings.ui @@ -42,20 +42,7 @@ Proxy Settings - - - - No proxy - - - true - - - proxyButtonGroup - - - - + Use system proxy @@ -65,30 +52,7 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Manually specify proxy - - - proxyButtonGroup - - - - + @@ -276,6 +240,52 @@ + + + + Manually specify proxy + + + proxyButtonGroup + + + + + + + No proxy + + + true + + + proxyButtonGroup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use global settings + + + proxyButtonGroup + + + @@ -311,7 +321,7 @@ 6 - + No limit @@ -321,24 +331,7 @@ - - - - Limit to 3/4 of estimated bandwidth - - - Limit automatically - - - - - - - Limit to - - - - + @@ -368,7 +361,7 @@ - + Qt::Vertical @@ -381,6 +374,30 @@ + + + + Limit to + + + + + + + Limit to 3/4 of estimated bandwidth + + + Limit automatically + + + + + + + Use global settings + + + @@ -411,7 +428,47 @@ 6 + + + + Limit to 3/4 of estimated bandwidth + + + Limit automatically + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + Limit to + + + + + + + No limit + + + true + + + + @@ -444,51 +501,19 @@ - - - - Limit to 3/4 of estimated bandwidth - - - Limit automatically - - - - - - - Limit to - - - - - + + - No limit - - - true + Use global settings - - - - Qt::Vertical - - - - 20 - 40 - - - - autoUploadLimitRadioButton uploadLimitRadioButton noUploadLimitRadioButton verticalSpacer_3 + globalUploadSettingsRadioButton diff --git a/src/gui/owncloudsetupwizard.cpp b/src/gui/owncloudsetupwizard.cpp index c96f0351d0f7c..5eae50e44aae7 100644 --- a/src/gui/owncloudsetupwizard.cpp +++ b/src/gui/owncloudsetupwizard.cpp @@ -178,10 +178,10 @@ void OwncloudSetupWizard::slotCheckServer(const QString &urlString) account->networkAccessManager()->clearAccessCache(); // 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(lcWizard) << "Trying to look up system proxy"; - ClientProxy::lookupSystemProxyAsync(account->url(), - this, SLOT(slotSystemProxyLookupDone(QNetworkProxy))); + ClientProxy::lookupSystemProxyAsync(account->url(), this, SLOT(slotSystemProxyLookupDone(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)); diff --git a/src/gui/proxyauthhandler.cpp b/src/gui/proxyauthhandler.cpp index 2f48f4489c133..64c922037f920 100644 --- a/src/gui/proxyauthhandler.cpp +++ b/src/gui/proxyauthhandler.cpp @@ -61,7 +61,8 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired( return; } - QString key = proxy.hostName() + QLatin1Char(':') + QString::number(proxy.port()); + const auto account = qobject_cast(sender()); + const auto key = proxy.hostName() + QLatin1Char(':') + QString::number(proxy.port()); // If the proxy server has changed, forget what we know. if (key != _proxy) { @@ -73,7 +74,10 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired( // If the user explicitly configured the proxy in the // network settings, don't ask about it. - if (_configFile->proxyType() == QNetworkProxy::HttpProxy + if ((account && (account->networkProxySetting() == Account::AccountNetworkProxySetting::GlobalProxy + || account->proxyType() == QNetworkProxy::HttpProxy + || account->proxyType() == QNetworkProxy::Socks5Proxy)) + || _configFile->proxyType() == QNetworkProxy::HttpProxy || _configFile->proxyType() == QNetworkProxy::Socks5Proxy) { _blocked = true; } @@ -85,7 +89,7 @@ void ProxyAuthHandler::handleProxyAuthenticationRequired( // Find the responsible QNAM if possible. QPointer sending_qnam = nullptr; - if (auto account = qobject_cast(sender())) { + if (account) { // Since we go into an event loop, it's possible for the account's qnam // to be destroyed before we get back. We can use this to check for its // liveness. diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp index 1a05650edad37..632b3bd3f6e87 100644 --- a/src/libsync/account.cpp +++ b/src/libsync/account.cpp @@ -1088,5 +1088,243 @@ void Account::updateDesktopEnterpriseChannel() } } +Account::AccountNetworkProxySetting Account::networkProxySetting() const +{ + return _networkProxySetting; +} + +void Account::setNetworkProxySetting(const AccountNetworkProxySetting setting) +{ + if (setting == _networkProxySetting) { + return; + } + + _networkProxySetting = setting; + if (setting == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setType(proxyType()); + proxy.setHostName(proxyHostName()); + proxy.setPort(proxyPort()); + proxy.setUser(proxyUser()); + proxy.setPassword(proxyPassword()); + _am->setProxy(proxy); + } else { + const auto proxy = QNetworkProxy::applicationProxy(); + _am->setProxy(proxy); + setProxyType(proxy.type()); + setProxyHostName(proxy.hostName()); + setProxyPort(proxy.port()); + setProxyUser(proxy.user()); + setProxyPassword(proxy.password()); + } + emit networkProxySettingChanged(); +} + +QNetworkProxy::ProxyType Account::proxyType() const +{ + return _proxyType; +} + +void Account::setProxyType(QNetworkProxy::ProxyType proxyType) +{ + if (_proxyType == proxyType) { + return; + } + + _proxyType = proxyType; + + if (networkProxySetting() == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setType(proxyType); + _am->setProxy(proxy); + } + + emit proxyTypeChanged(); +} + +QString Account::proxyHostName() const +{ + return _proxyHostName; +} + +void Account::setProxyHostName(const QString &hostName) +{ + if (_proxyHostName == hostName) { + return; + } + + _proxyHostName = hostName; + + if (networkProxySetting() == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setHostName(hostName); + _am->setProxy(proxy); + } + + emit proxyHostNameChanged(); +} + +int Account::proxyPort() const +{ + return _proxyPort; +} + +void Account::setProxyPort(const int port) +{ + if (_proxyPort == port) { + return; + } + + _proxyPort = port; + + if (networkProxySetting() == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setPort(port); + _am->setProxy(proxy); + } + + emit proxyPortChanged(); +} + +bool Account::proxyNeedsAuth() const +{ + return _proxyNeedsAuth; +} + +void Account::setProxyNeedsAuth(const bool needsAuth) +{ + if (_proxyNeedsAuth == needsAuth) { + return; + } + + _proxyNeedsAuth = needsAuth; + emit proxyNeedsAuthChanged(); +} + +QString Account::proxyUser() const +{ + return _proxyUser; +} + +void Account::setProxyUser(const QString &user) +{ + if (_proxyUser == user) { + return; + } + + _proxyUser = user; + + if (networkProxySetting() == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setUser(user); + _am->setProxy(proxy); + } + + emit proxyUserChanged(); +} + +QString Account::proxyPassword() const +{ + return _proxyPassword; +} + +void Account::setProxyPassword(const QString &password) +{ + if (_proxyPassword == password) { + return; + } + + _proxyPassword = password; + + if (networkProxySetting() == AccountNetworkProxySetting::AccountSpecificProxy) { + auto proxy = _am->proxy(); + proxy.setPassword(password); + _am->setProxy(proxy); + } + + emit proxyPasswordChanged(); +} + +void Account::setProxySettings(const AccountNetworkProxySetting networkProxySetting, + const QNetworkProxy::ProxyType proxyType, + const QString &hostName, + const int port, + const bool needsAuth, + const QString &user, + const QString &password) +{ + if (networkProxySetting == AccountNetworkProxySetting::GlobalProxy) { + setNetworkProxySetting(networkProxySetting); + return; + } + + setProxyType(proxyType); + setProxyHostName(hostName); + setProxyPort(port); + setProxyNeedsAuth(needsAuth); + setProxyUser(user); + setProxyPassword(password); + setNetworkProxySetting(networkProxySetting); +} + +Account::AccountNetworkTransferLimitSetting Account::uploadLimitSetting() const +{ + return _uploadLimitSetting; +} + +void Account::setUploadLimitSetting(const AccountNetworkTransferLimitSetting setting) +{ + if (setting == _uploadLimitSetting) { + return; + } + + _uploadLimitSetting = setting; + emit uploadLimitSettingChanged(); +} + +Account::AccountNetworkTransferLimitSetting Account::downloadLimitSetting() const +{ + return _downloadLimitSetting; +} + +void Account::setDownloadLimitSetting(const AccountNetworkTransferLimitSetting setting) +{ + if (setting == _downloadLimitSetting) { + return; + } + + _downloadLimitSetting = setting; + emit downloadLimitSettingChanged(); +} + +unsigned int Account::uploadLimit() const +{ + return _uploadLimit; +} + +void Account::setUploadLimit(const unsigned int limit) +{ + if (_uploadLimit == limit) { + return; + } + + _uploadLimit = limit; + emit uploadLimitChanged(); +} + +unsigned int Account::downloadLimit() const +{ + return _downloadLimit; +} + +void Account::setDownloadLimit(const unsigned int limit) +{ + if (_downloadLimit == limit) { + return; + } + + _downloadLimit = limit; + emit downloadLimitChanged(); +} } // namespace OCC diff --git a/src/libsync/account.h b/src/libsync/account.h index ea371c8ede21e..4afd9f654dc37 100644 --- a/src/libsync/account.h +++ b/src/libsync/account.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -90,8 +91,35 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject Q_PROPERTY(QUrl url MEMBER _url) Q_PROPERTY(bool e2eEncryptionKeysGenerationAllowed MEMBER _e2eEncryptionKeysGenerationAllowed) Q_PROPERTY(bool askUserForMnemonic READ askUserForMnemonic WRITE setAskUserForMnemonic NOTIFY askUserForMnemonicChanged) + Q_PROPERTY(AccountNetworkProxySetting networkProxySetting READ networkProxySetting WRITE setNetworkProxySetting NOTIFY networkProxySettingChanged) + Q_PROPERTY(QNetworkProxy::ProxyType proxyType READ proxyType WRITE setProxyType NOTIFY proxyTypeChanged) + Q_PROPERTY(QString proxyHostName READ proxyHostName WRITE setProxyHostName NOTIFY proxyHostNameChanged) + Q_PROPERTY(int proxyPort READ proxyPort WRITE setProxyPort NOTIFY proxyPortChanged) + Q_PROPERTY(bool proxyNeedsAuth READ proxyNeedsAuth WRITE setProxyNeedsAuth NOTIFY proxyNeedsAuthChanged) + Q_PROPERTY(QString proxyUser READ proxyUser WRITE setProxyUser NOTIFY proxyUserChanged) + Q_PROPERTY(QString proxyPassword READ proxyPassword WRITE setProxyPassword NOTIFY proxyPasswordChanged) + Q_PROPERTY(AccountNetworkTransferLimitSetting uploadLimitSetting READ uploadLimitSetting WRITE setUploadLimitSetting NOTIFY uploadLimitSettingChanged) + Q_PROPERTY(AccountNetworkTransferLimitSetting downloadLimitSetting READ downloadLimitSetting WRITE setDownloadLimitSetting NOTIFY downloadLimitSettingChanged) + Q_PROPERTY(unsigned int uploadLimit READ uploadLimit WRITE setUploadLimit NOTIFY uploadLimitChanged) + Q_PROPERTY(unsigned int downloadLimit READ downloadLimit WRITE setDownloadLimit NOTIFY downloadLimitChanged) public: + // We need to decide whether to use the client's global proxy settings or whether to use + // a specific setting for each account. Hence this enum + enum class AccountNetworkProxySetting { + GlobalProxy = 0, + AccountSpecificProxy, + }; + Q_ENUM(AccountNetworkProxySetting) + + enum class AccountNetworkTransferLimitSetting { + GlobalLimit = -2, + AutoLimit, // Value under 0 is interpreted as auto in general + NoLimit, + ManualLimit, + }; + Q_ENUM(AccountNetworkTransferLimitSetting) + static AccountPtr create(); ~Account() override; @@ -338,6 +366,49 @@ class OWNCLOUDSYNC_EXPORT Account : public QObject void updateServerSubcription(); void updateDesktopEnterpriseChannel(); + // Network-related settings + [[nodiscard]] AccountNetworkProxySetting networkProxySetting() const; + void setNetworkProxySetting(AccountNetworkProxySetting networkProxySetting); + + [[nodiscard]] QNetworkProxy::ProxyType proxyType() const; + void setProxyType(QNetworkProxy::ProxyType proxyType); + + [[nodiscard]] QString proxyHostName() const; + void setProxyHostName(const QString &host); + + [[nodiscard]] int proxyPort() const; + void setProxyPort(int port); + + [[nodiscard]] bool proxyNeedsAuth() const; + void setProxyNeedsAuth(bool needsAuth); + + [[nodiscard]] QString proxyUser() const; + void setProxyUser(const QString &user); + + [[nodiscard]] QString proxyPassword() const; + void setProxyPassword(const QString &password); + + void setProxySettings(const AccountNetworkProxySetting networkProxySetting, + const QNetworkProxy::ProxyType proxyType, + const QString &proxyHostName, + const int proxyPort, + const bool proxyNeedsAuth, + const QString &proxyUser, + const QString &proxyPassword); + + [[nodiscard]] AccountNetworkTransferLimitSetting uploadLimitSetting() const; + void setUploadLimitSetting(AccountNetworkTransferLimitSetting setting); + + [[nodiscard]] AccountNetworkTransferLimitSetting downloadLimitSetting() const; + void setDownloadLimitSetting(AccountNetworkTransferLimitSetting setting); + + /** in kbyte/s */ + [[nodiscard]] unsigned int uploadLimit() const; + void setUploadLimit(unsigned int kbytes); + + [[nodiscard]] unsigned int downloadLimit() const; + void setDownloadLimit(unsigned int kbytes); + public slots: /// Used when forgetting credentials void clearQNAMCache(); @@ -382,6 +453,18 @@ public slots: void lockFileSuccess(); void lockFileError(const QString&); + void networkProxySettingChanged(); + void proxyTypeChanged(); + void proxyHostNameChanged(); + void proxyPortChanged(); + void proxyNeedsAuthChanged(); + void proxyUserChanged(); + void proxyPasswordChanged(); + void uploadLimitSettingChanged(); + void downloadLimitSettingChanged(); + void uploadLimitChanged(); + void downloadLimitChanged(); + protected Q_SLOTS: void slotCredentialsFetched(); void slotCredentialsAsked(); @@ -457,6 +540,18 @@ private slots: QHash> _lockStatusChangeInprogress; + AccountNetworkProxySetting _networkProxySetting = AccountNetworkProxySetting::GlobalProxy; + QNetworkProxy::ProxyType _proxyType = QNetworkProxy::NoProxy; + QString _proxyHostName; + int _proxyPort = 0; + bool _proxyNeedsAuth = false; + QString _proxyUser; + QString _proxyPassword; + AccountNetworkTransferLimitSetting _uploadLimitSetting = AccountNetworkTransferLimitSetting::GlobalLimit; + AccountNetworkTransferLimitSetting _downloadLimitSetting = AccountNetworkTransferLimitSetting::GlobalLimit; + unsigned int _uploadLimit = 0; + unsigned int _downloadLimit = 0; + /* IMPORTANT - remove later - FIXME MS@2019-12-07 --> * TODO: For "Log out" & "Remove account": Remove client CA certs and KEY! *