Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/owncloud/client.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFabian Müller <fmueller@owncloud.com>2022-05-13 09:52:07 +0300
committerHannah von Reth <vonreth@kde.org>2022-05-13 14:08:30 +0300
commitb2daa0004d32dd4691dc54a81cd9b9f56c50d230 (patch)
treec027acaf56f94f6921afc0c44d6b93849322b97c /src/libsync
parentc7fa763aa85fccc4c38856e86d690349a069add1 (diff)
Make the CheckServer job independent of the account
The job needs to run on an independent QNAM
Diffstat (limited to 'src/libsync')
-rw-r--r--src/libsync/CMakeLists.txt1
-rw-r--r--src/libsync/creds/oauth.cpp23
-rw-r--r--src/libsync/networkjobs.cpp154
-rw-r--r--src/libsync/networkjobs.h75
-rw-r--r--src/libsync/networkjobs/checkserverjobfactory.cpp139
-rw-r--r--src/libsync/networkjobs/checkserverjobfactory.h54
6 files changed, 204 insertions, 242 deletions
diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt
index 8e73a2c1f..c837ee7a7 100644
--- a/src/libsync/CMakeLists.txt
+++ b/src/libsync/CMakeLists.txt
@@ -52,6 +52,7 @@ set(libsync_SRCS
creds/credentialscommon.cpp
creds/oauth.cpp
+ networkjobs/checkserverjobfactory.cpp
networkjobs/jsonjob.cpp
abstractcorejob.cpp
diff --git a/src/libsync/creds/oauth.cpp b/src/libsync/creds/oauth.cpp
index 4d97df5a6..5a6330913 100644
--- a/src/libsync/creds/oauth.cpp
+++ b/src/libsync/creds/oauth.cpp
@@ -20,6 +20,7 @@
#include "creds/httpcredentials.h"
#include "creds/jobs/determineuserjobfactory.h"
#include "networkjobs.h"
+#include "networkjobs/checkserverjobfactory.h"
#include "theme.h"
#include <QApplication>
@@ -597,23 +598,19 @@ void AccountBasedOAuth::startAuthentication()
void AccountBasedOAuth::fetchWellKnown()
{
- auto *checkServerJob = new CheckServerJob(_account->sharedFromThis(), this);
- checkServerJob->setClearCookies(true);
- checkServerJob->setTimeout(defaultTimeout());
+ auto *checkServerJob = CheckServerJobFactory(_networkAccessManager, this).startJob(_serverUrl);
- connect(checkServerJob, &CheckServerJob::instanceNotFound, this, [this](QNetworkReply *reply) {
- if (_isRefreshingToken) {
- Q_EMIT refreshError(reply->error(), reply->errorString());
+ connect(checkServerJob, &CoreJob::finished, this, [checkServerJob, this]() {
+ if (checkServerJob->success()) {
+ OAuth::fetchWellKnown();
} else {
- Q_EMIT result(Error);
+ if (_isRefreshingToken) {
+ Q_EMIT refreshError(checkServerJob->reply()->error(), checkServerJob->errorMessage());
+ } else {
+ Q_EMIT result(Error);
+ }
}
});
-
- connect(checkServerJob, &CheckServerJob::instanceFound, this, [this](const QUrl &url, const QJsonObject &info) {
- OAuth::fetchWellKnown();
- });
-
- checkServerJob->start();
}
void AccountBasedOAuth::dynamicRegistrationDataReceived(const QVariantMap &dynamicRegistrationData)
diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp
index 86d8d4e36..a689f7635 100644
--- a/src/libsync/networkjobs.cpp
+++ b/src/libsync/networkjobs.cpp
@@ -47,7 +47,6 @@ namespace OCC {
Q_LOGGING_CATEGORY(lcEtagJob, "sync.networkjob.etag", QtInfoMsg)
Q_LOGGING_CATEGORY(lcLsColJob, "sync.networkjob.lscol", QtInfoMsg)
-Q_LOGGING_CATEGORY(lcCheckServerJob, "sync.networkjob.checkserver", QtInfoMsg)
Q_LOGGING_CATEGORY(lcPropfindJob, "sync.networkjob.propfind", QtInfoMsg)
Q_LOGGING_CATEGORY(lcAvatarJob, "sync.networkjob.avatar", QtInfoMsg)
Q_LOGGING_CATEGORY(lcMkColJob, "sync.networkjob.mkcol", QtInfoMsg)
@@ -396,159 +395,6 @@ const QHash<QString, qint64> &LsColJob::sizes() const
/*********************************************************************************************/
-CheckServerJob::CheckServerJob(AccountPtr account, QObject *parent)
- : AbstractNetworkJob(account, account->url(), QStringLiteral("status.php"), parent)
-{
- setIgnoreCredentialFailure(true);
- setAuthenticationJob(true);
-
- connect(this, &CheckServerJob::networkError, this, [this] {
- if (timedOut()) {
- Q_EMIT timeout(url());
- }
- });
-}
-
-void CheckServerJob::start()
-{
- _serverUrl = baseUrl();
- QNetworkRequest req;
- // don't authenticate the request to a possibly external service
- req.setAttribute(HttpCredentials::DontAddCredentialsAttribute, true);
- req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
- req.setRawHeader(QByteArrayLiteral("OC-Connection-Validator"), QByteArrayLiteral("desktop"));
- req.setMaximumRedirectsAllowed(_maxRedirectsAllowed);
- if (_clearCookies && Theme::instance()->connectionValidatorClearCookies()) {
- _account->clearCookieJar();
- }
- sendRequest("GET", req);
- AbstractNetworkJob::start();
-}
-
-void CheckServerJob::setClearCookies(bool clearCookies)
-{
- _clearCookies = clearCookies;
-}
-
-QString CheckServerJob::version(const QJsonObject &info)
-{
- return info.value(QLatin1String("version")).toString() + QLatin1Char('-') + info.value(QLatin1String("productname")).toString();
-}
-
-QString CheckServerJob::versionString(const QJsonObject &info)
-{
- return info.value(QLatin1String("versionstring")).toString();
-}
-
-bool CheckServerJob::installed(const QJsonObject &info)
-{
- return info.value(QLatin1String("installed")).toBool();
-}
-
-static void mergeSslConfigurationForSslButton(const QSslConfiguration &config, AccountPtr account)
-{
- if (config.peerCertificateChain().length() > 0) {
- const auto certs = config.peerCertificateChain();
- account->_peerCertificateChain = { certs.cbegin(), certs.cend() };
- }
- if (!config.sessionCipher().isNull()) {
- account->_sessionCipher = config.sessionCipher();
- }
- if (config.sessionTicket().length() > 0) {
- account->_sessionTicket = config.sessionTicket();
- }
-}
-
-void CheckServerJob::encryptedSlot()
-{
- mergeSslConfigurationForSslButton(reply()->sslConfiguration(), account());
-}
-
-void CheckServerJob::newReplyHook(QNetworkReply *reply)
-{
- connect(reply, &QNetworkReply::metaDataChanged, this, &CheckServerJob::metaDataChangedSlot);
- connect(reply, &QNetworkReply::encrypted, this, &CheckServerJob::encryptedSlot);
- connect(reply, &QNetworkReply::sslErrors, this, &CheckServerJob::sslErrors);
- connect(reply, &QNetworkReply::redirected, this, [reply, this] {
- const auto code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (code == 302 || code == 307) {
- _redirectDistinct = false;
- }
- });
-}
-
-int CheckServerJob::maxRedirectsAllowed() const
-{
- return _maxRedirectsAllowed;
-}
-
-void CheckServerJob::setMaxRedirectsAllowed(int maxRedirectsAllowed)
-{
- _maxRedirectsAllowed = maxRedirectsAllowed;
-}
-
-void CheckServerJob::metaDataChangedSlot()
-{
- mergeSslConfigurationForSslButton(reply()->sslConfiguration(), account());
-}
-
-bool CheckServerJob::finished()
-{
- const QUrl targetUrl = reply()->url().adjusted(QUrl::RemoveFilename);
- if (targetUrl.scheme() == QLatin1String("https")
- && reply()->sslConfiguration().sessionTicket().isEmpty()
- && reply()->error() == QNetworkReply::NoError) {
- qCWarning(lcCheckServerJob) << "No SSL session identifier / session ticket is used, this might impact sync performance negatively.";
- }
- if (_serverUrl != targetUrl) {
- if (_redirectDistinct) {
- _serverUrl = targetUrl;
- } else {
- if (_firstTry) {
- qCWarning(lcCheckServerJob) << "Server might have moved, retry";
- _firstTry = false;
- _redirectDistinct = true;
- start();
- return false;
- } else {
- qCWarning(lcCheckServerJob) << "We got a temporary moved server aborting";
- emit instanceNotFound(reply());
- return true;
- }
- }
- }
-
- mergeSslConfigurationForSslButton(reply()->sslConfiguration(), account());
-
- const int httpStatus = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- if (reply()->error() == QNetworkReply::TooManyRedirectsError) {
- qCWarning(lcCheckServerJob) << "error:" << reply()->errorString();
- emit instanceNotFound(reply());
- } else if (httpStatus != 200 || reply()->bytesAvailable() == 0) {
- qCWarning(lcCheckServerJob) << "error: status.php replied " << httpStatus;
- emit instanceNotFound(reply());
- } else {
- const QByteArray body = reply()->peek(4 * 1024);
- QJsonParseError error;
- auto status = QJsonDocument::fromJson(body, &error);
- // empty or invalid response
- if (error.error != QJsonParseError::NoError || status.isNull()) {
- qCWarning(lcCheckServerJob) << "status.php from server is not valid JSON!" << body << reply()->request().url() << error.errorString();
- }
-
- qCInfo(lcCheckServerJob) << "status.php returns: " << status << " " << reply()->error() << " Reply: " << reply();
- if (status.object().contains(QStringLiteral("installed"))) {
- emit instanceFound(_serverUrl, status.object());
- } else {
- qCWarning(lcCheckServerJob) << "No proper answer on " << reply()->url();
- emit instanceNotFound(reply());
- }
- }
- return true;
-}
-
-/*********************************************************************************************/
-
void PropfindJob::start()
{
diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h
index 24f770940..4ecfbc07d 100644
--- a/src/libsync/networkjobs.h
+++ b/src/libsync/networkjobs.h
@@ -194,81 +194,6 @@ private:
};
/**
- * @brief The CheckServerJob class
- * @ingroup libsync
- */
-class OWNCLOUDSYNC_EXPORT CheckServerJob : public AbstractNetworkJob
-{
- Q_OBJECT
-public:
- explicit CheckServerJob(AccountPtr account, QObject *parent = nullptr);
- void start() override;
-
- static QString version(const QJsonObject &info);
- static QString versionString(const QJsonObject &info);
- static bool installed(const QJsonObject &info);
-
- int maxRedirectsAllowed() const;
- void setMaxRedirectsAllowed(int maxRedirectsAllowed);
-
- /** Whether to clear the cookies before we start the job
- * This option also depends on Theme::instance()->connectionValidatorClearCookies()
- */
- void setClearCookies(bool clearCookies);
-
-signals:
- /** Emitted when a status.php was successfully read.
- *
- * \a url see _serverStatusUrl (does not include "/status.php")
- * \a info The status.php reply information
- */
- void instanceFound(const QUrl &url, const QJsonObject &info);
-
- /** Emitted on invalid status.php reply.
- *
- * \a reply is never null
- */
- void instanceNotFound(QNetworkReply *reply);
-
- /** A timeout occurred.
- *
- * \a url The specific url where the timeout happened.
- */
- void timeout(const QUrl &url);
-
- void sslErrors(const QList<QSslError> &errors);
-
-private:
- bool finished() override;
-private slots:
- virtual void metaDataChangedSlot();
- virtual void encryptedSlot();
-
-protected:
- void newReplyHook(QNetworkReply *) override;
-
-private:
- bool _clearCookies = false;
-
- /** The permanent-redirect adjusted account url.
- *
- * Note that temporary redirects or a permanent redirect behind a temporary
- * one do not affect this url.
- */
- QUrl _serverUrl;
-
- int _maxRedirectsAllowed = 5;
-
- /** we only got permanent redirects */
- bool _redirectDistinct = true;
- /** only retry once, the first try might give us needed cookies
- the second is supposed to succeed
- */
- bool _firstTry = true;
-};
-
-
-/**
* @brief The RequestEtagJob class
*/
class OWNCLOUDSYNC_EXPORT RequestEtagJob : public AbstractNetworkJob
diff --git a/src/libsync/networkjobs/checkserverjobfactory.cpp b/src/libsync/networkjobs/checkserverjobfactory.cpp
new file mode 100644
index 000000000..a83692d46
--- /dev/null
+++ b/src/libsync/networkjobs/checkserverjobfactory.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) Fabian Müller <fmueller@owncloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "checkserverjobfactory.h"
+#include "common/utility.h"
+#include "creds/httpcredentials.h"
+#include <QJsonParseError>
+
+namespace {
+
+// FIXME: this is not a permanent solution, eventually we want to replace the job factories with job classes so we can store such information there
+class CheckServerCoreJob : OCC::CoreJob
+{
+ friend OCC::CheckServerJobFactory;
+
+private:
+ // doesn't concern users of the job factory
+ // we just need a place to maintain these variables, but the factory is likely deleted before the job has finished
+ bool _redirectDistinct;
+ bool _firstTry;
+};
+
+}
+
+namespace OCC {
+
+Q_LOGGING_CATEGORY(lcCheckServerJob, "sync.checkserverjob", QtInfoMsg)
+
+CheckServerJobResult::CheckServerJobResult(const QJsonObject &statusObject, const QUrl &serverUrl)
+ : _statusObject(statusObject)
+ , _serverUrl(serverUrl)
+{
+}
+
+QJsonObject CheckServerJobResult::statusObject() const
+{
+ return _statusObject;
+}
+
+QUrl CheckServerJobResult::serverUrl() const
+{
+ return _serverUrl;
+}
+
+CoreJob *CheckServerJobFactory::startJob(const QUrl &url)
+{
+ // the custom job class is used to store some state we need to maintain until the job has finished
+ auto job = new CheckServerCoreJob;
+
+ auto req = makeRequest(Utility::concatUrlPath(url, QStringLiteral("status.php")));
+
+ req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
+ req.setRawHeader(QByteArrayLiteral("OC-Connection-Validator"), QByteArrayLiteral("desktop"));
+ req.setMaximumRedirectsAllowed(_maxRedirectsAllowed);
+
+ auto *reply = nam()->get(req);
+
+ connect(reply, &QNetworkReply::redirected, job, [reply, job] {
+ const auto code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (code == 302 || code == 307) {
+ job->_redirectDistinct = false;
+ }
+ });
+
+ connect(reply, &QNetworkReply::finished, job, [url, reply, job] {
+ reply->deleteLater();
+
+ // need a mutable copy
+ auto serverUrl = url;
+
+ const QUrl targetUrl = reply->url().adjusted(QUrl::RemoveFilename);
+
+ // TODO: still needed?
+ if (targetUrl.scheme() == QLatin1String("https")
+ && reply->sslConfiguration().sessionTicket().isEmpty()
+ && reply->error() == QNetworkReply::NoError) {
+ qCWarning(lcCheckServerJob) << "No SSL session identifier / session ticket is used, this might impact sync performance negatively.";
+ }
+
+ if (serverUrl != targetUrl) {
+ if (job->_redirectDistinct) {
+ serverUrl = targetUrl;
+ } else {
+ if (job->_firstTry) {
+ qCWarning(lcCheckServerJob) << "Server might have moved, retry";
+ job->_firstTry = false;
+ job->_redirectDistinct = true;
+
+ // FIXME
+ } else {
+ qCWarning(lcCheckServerJob) << "We got a temporary moved server aborting";
+ setJobError(job, QStringLiteral("Illegal redirect by server"), reply);
+ }
+ }
+ }
+
+ const int httpStatus = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (reply->error() == QNetworkReply::TooManyRedirectsError) {
+ qCWarning(lcCheckServerJob) << "error:" << reply->errorString();
+ setJobError(job, reply->errorString(), reply);
+ } else if (httpStatus != 200 || reply->bytesAvailable() == 0) {
+ qCWarning(lcCheckServerJob) << "error: status.php replied " << httpStatus;
+ setJobError(job, QStringLiteral("Invalid HTTP status code received for status.php: %1").arg(httpStatus), reply);
+ } else {
+ const QByteArray body = reply->peek(4 * 1024);
+ QJsonParseError error;
+ auto status = QJsonDocument::fromJson(body, &error);
+ // empty or invalid response
+ if (error.error != QJsonParseError::NoError || status.isNull()) {
+ qCWarning(lcCheckServerJob) << "status.php from server is not valid JSON!" << body << reply->request().url() << error.errorString();
+ }
+
+ qCInfo(lcCheckServerJob) << "status.php returns: " << status << " " << reply->error() << " Reply: " << reply;
+
+ if (status.object().contains(QStringLiteral("installed"))) {
+ CheckServerJobResult result(status.object(), serverUrl);
+ setJobResult(job, QVariant::fromValue(result));
+ } else {
+ qCWarning(lcCheckServerJob) << "No proper answer on " << reply->url();
+ setJobError(job, QStringLiteral("Did not receive expected reply from server"), reply);
+ }
+ }
+ });
+
+ return job;
+}
+
+} // OCC
diff --git a/src/libsync/networkjobs/checkserverjobfactory.h b/src/libsync/networkjobs/checkserverjobfactory.h
new file mode 100644
index 000000000..e3ffff2a2
--- /dev/null
+++ b/src/libsync/networkjobs/checkserverjobfactory.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) Fabian Müller <fmueller@owncloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include "abstractcorejob.h"
+
+#include <QJsonObject>
+
+namespace OCC {
+
+class OWNCLOUDSYNC_EXPORT CheckServerJobResult
+{
+
+public:
+ CheckServerJobResult() = default;
+ CheckServerJobResult(const QJsonObject &statusObject, const QUrl &serverUrl);
+
+ QJsonObject statusObject() const;
+ QUrl serverUrl() const;
+
+private:
+ const QJsonObject _statusObject;
+ const QUrl _serverUrl;
+};
+
+
+class OWNCLOUDSYNC_EXPORT CheckServerJobFactory : public AbstractCoreJobFactory
+{
+ Q_OBJECT
+
+public:
+ using AbstractCoreJobFactory::AbstractCoreJobFactory;
+
+ CoreJob *startJob(const QUrl &url) override;
+
+private:
+ int _maxRedirectsAllowed = 5;
+};
+
+} // OCC
+
+Q_DECLARE_METATYPE(OCC::CheckServerJobResult)