aboutsummaryrefslogtreecommitdiff
path: root/lib/jobs
diff options
context:
space:
mode:
authorn-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
committern-peugnet <n.peugnet@free.fr>2022-10-06 19:27:24 +0200
commit08632625e1a04257b5c7d4a9db2246ac07436748 (patch)
tree9ddadf219a7e352ddd3549ad1683282c944adfb6 /lib/jobs
parente9c2e2a26d3711e755aa5eb8a8478917c71d612b (diff)
parentd911b207f49e936b3e992200796110f0749ed150 (diff)
downloadlibquotient-08632625e1a04257b5c7d4a9db2246ac07436748.tar.gz
libquotient-08632625e1a04257b5c7d4a9db2246ac07436748.zip
Update upstream source from tag 'upstream/0.7.0'
Update to upstream version '0.7.0' with Debian dir 30dcb77a77433e4a54eab77c0b82ae925dead2d8
Diffstat (limited to 'lib/jobs')
-rw-r--r--lib/jobs/basejob.cpp226
-rw-r--r--lib/jobs/basejob.h144
-rw-r--r--lib/jobs/downloadfilejob.cpp93
-rw-r--r--lib/jobs/downloadfilejob.h12
-rw-r--r--lib/jobs/mediathumbnailjob.cpp27
-rw-r--r--lib/jobs/mediathumbnailjob.h21
-rw-r--r--lib/jobs/postreadmarkersjob.h38
-rw-r--r--lib/jobs/requestdata.cpp10
-rw-r--r--lib/jobs/requestdata.h44
-rw-r--r--lib/jobs/syncjob.cpp27
-rw-r--r--lib/jobs/syncjob.h21
11 files changed, 310 insertions, 353 deletions
diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp
index 5960203d..da645a2d 100644
--- a/lib/jobs/basejob.cpp
+++ b/lib/jobs/basejob.cpp
@@ -1,20 +1,6 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "basejob.h"
@@ -22,15 +8,12 @@
#include <QtCore/QRegularExpression>
#include <QtCore/QTimer>
-#include <QtCore/QStringBuilder>
#include <QtCore/QMetaEnum>
#include <QtCore/QPointer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
-#include <array>
-
using namespace Quotient;
using std::chrono::seconds, std::chrono::milliseconds;
using namespace std::chrono_literals;
@@ -39,7 +22,7 @@ BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode)
{
// Based on https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
if (httpCode / 10 == 41) // 41x errors
- return httpCode == 410 ? IncorrectRequestError : NotFoundError;
+ return httpCode == 410 ? IncorrectRequest : NotFound;
switch (httpCode) {
case 401:
return Unauthorised;
@@ -47,19 +30,19 @@ BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode)
case 403: case 407: // clang-format on
return ContentAccessError;
case 404:
- return NotFoundError;
+ return NotFound;
// clang-format off
case 400: case 405: case 406: case 426: case 428: case 505: // clang-format on
case 494: // Unofficial nginx "Request header too large"
case 497: // Unofficial nginx "HTTP request sent to HTTPS port"
- return IncorrectRequestError;
+ return IncorrectRequest;
case 429:
- return TooManyRequestsError;
+ return TooManyRequests;
case 501:
case 510:
- return RequestNotImplementedError;
+ return RequestNotImplemented;
case 511:
- return NetworkAuthRequiredError;
+ return NetworkAuthRequired;
default:
return NetworkError;
}
@@ -77,12 +60,6 @@ QDebug BaseJob::Status::dumpToLog(QDebug dbg) const
return dbg << ": " << message;
}
-template <typename... Ts>
-constexpr auto make_array(Ts&&... items)
-{
- return std::array<std::common_type_t<Ts...>, sizeof...(Ts)>({items...});
-}
-
class BaseJob::Private {
public:
struct JobTimeoutConfig {
@@ -92,8 +69,8 @@ public:
// Using an idiom from clang-tidy:
// http://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html
- Private(HttpVerb v, QString endpoint, const QUrlQuery& q, Data&& data,
- bool nt)
+ Private(HttpVerb v, QByteArray endpoint, const QUrlQuery& q,
+ RequestData&& data, bool nt)
: verb(v)
, apiEndpoint(std::move(endpoint))
, requestQuery(q)
@@ -127,10 +104,10 @@ public:
// Contents for the network request
HttpVerb verb;
- QString apiEndpoint;
+ QByteArray apiEndpoint;
QHash<QByteArray, QByteArray> requestHeaders;
QUrlQuery requestQuery;
- Data requestData;
+ RequestData requestData;
bool needsToken;
bool inBackground = false;
@@ -161,9 +138,8 @@ public:
QTimer timer;
QTimer retryTimer;
- static constexpr std::array<const JobTimeoutConfig, 3> errorStrategy {
- { { 90s, 5s }, { 90s, 10s }, { 120s, 30s } }
- };
+ static constexpr auto errorStrategy = std::to_array<const JobTimeoutConfig>(
+ { { 90s, 5s }, { 90s, 10s }, { 120s, 30s } });
int maxRetries = int(errorStrategy.size());
int retriesTaken = 0;
@@ -175,10 +151,8 @@ public:
[[nodiscard]] QString dumpRequest() const
{
- // FIXME: use std::array {} when Apple stdlib gets deduction guides for it
- static const auto verbs =
- make_array(QStringLiteral("GET"), QStringLiteral("PUT"),
- QStringLiteral("POST"), QStringLiteral("DELETE"));
+ static const std::array verbs { "GET"_ls, "PUT"_ls, "POST"_ls,
+ "DELETE"_ls };
const auto verbWord = verbs.at(size_t(verb));
return verbWord % ' '
% (reply ? reply->url().toString(QUrl::RemoveQuery)
@@ -187,14 +161,36 @@ public:
}
};
-BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
+inline bool isHex(QChar c)
+{
+ return c.isDigit() || (c >= u'A' && c <= u'F') || (c >= u'a' && c <= u'f');
+}
+
+QByteArray BaseJob::encodeIfParam(const QString& paramPart)
+{
+ const auto percentIndex = paramPart.indexOf('%');
+ if (percentIndex != -1 && paramPart.size() > percentIndex + 2
+ && isHex(paramPart[percentIndex + 1])
+ && isHex(paramPart[percentIndex + 2])) {
+ qCWarning(JOBS)
+ << "Developers, upfront percent-encoding of job parameters is "
+ "deprecated since libQuotient 0.7; the string involved is"
+ << paramPart;
+ return QUrl(paramPart, QUrl::TolerantMode).toEncoded();
+ }
+ return QUrl::toPercentEncoding(paramPart);
+}
+
+BaseJob::BaseJob(HttpVerb verb, const QString& name, QByteArray endpoint,
bool needsToken)
- : BaseJob(verb, name, endpoint, Query {}, Data {}, needsToken)
+ : BaseJob(verb, name, std::move(endpoint), QUrlQuery {}, RequestData {},
+ needsToken)
{}
-BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
- const Query& query, Data&& data, bool needsToken)
- : d(new Private(verb, endpoint, query, std::move(data), needsToken))
+BaseJob::BaseJob(HttpVerb verb, const QString& name, QByteArray endpoint,
+ const QUrlQuery& query, RequestData&& data, bool needsToken)
+ : d(makeImpl<Private>(verb, std::move(endpoint), query, std::move(data),
+ needsToken))
{
setObjectName(name);
connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout);
@@ -215,13 +211,6 @@ QUrl BaseJob::requestUrl() const { return d->reply ? d->reply->url() : QUrl(); }
bool BaseJob::isBackground() const { return d->inBackground; }
-const QString& BaseJob::apiEndpoint() const { return d->apiEndpoint; }
-
-void BaseJob::setApiEndpoint(const QString& apiEndpoint)
-{
- d->apiEndpoint = apiEndpoint;
-}
-
const BaseJob::headers_t& BaseJob::requestHeaders() const
{
return d->requestHeaders;
@@ -238,16 +227,19 @@ void BaseJob::setRequestHeaders(const BaseJob::headers_t& headers)
d->requestHeaders = headers;
}
-const QUrlQuery& BaseJob::query() const { return d->requestQuery; }
+QUrlQuery BaseJob::query() const { return d->requestQuery; }
void BaseJob::setRequestQuery(const QUrlQuery& query)
{
d->requestQuery = query;
}
-const BaseJob::Data& BaseJob::requestData() const { return d->requestData; }
+const RequestData& BaseJob::requestData() const { return d->requestData; }
-void BaseJob::setRequestData(Data&& data) { std::swap(d->requestData, data); }
+void BaseJob::setRequestData(RequestData&& data)
+{
+ std::swap(d->requestData, data);
+}
const QByteArrayList& BaseJob::expectedContentTypes() const
{
@@ -264,7 +256,7 @@ void BaseJob::setExpectedContentTypes(const QByteArrayList& contentTypes)
d->expectedContentTypes = contentTypes;
}
-const QByteArrayList BaseJob::expectedKeys() const { return d->expectedKeys; }
+QByteArrayList BaseJob::expectedKeys() const { return d->expectedKeys; }
void BaseJob::addExpectedKey(const QByteArray& key) { d->expectedKeys << key; }
@@ -277,17 +269,17 @@ const QNetworkReply* BaseJob::reply() const { return d->reply.data(); }
QNetworkReply* BaseJob::reply() { return d->reply.data(); }
-QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path,
+QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QByteArray& encodedPath,
const QUrlQuery& query)
{
- auto pathBase = baseUrl.path();
- // QUrl::adjusted(QUrl::StripTrailingSlashes) doesn't help with root '/'
- while (pathBase.endsWith('/'))
- pathBase.chop(1);
- if (!path.startsWith('/')) // Normally API files do start with '/'
- pathBase.push_back('/'); // so this shouldn't be needed these days
-
- baseUrl.setPath(pathBase + path, QUrl::TolerantMode);
+ // Make sure the added path is relative even if it's not (the official
+ // API definitions have the leading slash though it's not really correct).
+ const auto pathUrl =
+ QUrl::fromEncoded(encodedPath.mid(encodedPath.startsWith('/')),
+ QUrl::StrictMode);
+ Q_ASSERT_X(pathUrl.isValid(), __FUNCTION__,
+ qPrintable(pathUrl.errorString()));
+ baseUrl = baseUrl.resolved(pathUrl);
baseUrl.setQuery(query);
return baseUrl;
}
@@ -302,19 +294,14 @@ void BaseJob::Private::sendRequest()
req.setRawHeader("Authorization",
QByteArray("Bearer ") + connection->accessToken());
req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground);
- req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
+ req.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
+ QNetworkRequest::NoLessSafeRedirectPolicy);
req.setMaximumRedirectsAllowed(10);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
- req.setAttribute(
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
- QNetworkRequest::Http2AllowedAttribute
-#else
- QNetworkRequest::HTTP2AllowedAttribute
-#endif
// Qt doesn't combine HTTP2 with SSL quite right, occasionally crashing at
// what seems like an attempt to write to a closed channel. If/when that
// changes, false should be turned to true below.
- , false);
+ req.setAttribute(QNetworkRequest::Http2AllowedAttribute, false);
Q_ASSERT(req.url().isValid());
for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it)
req.setRawHeader(it.key(), it.value());
@@ -367,7 +354,7 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground)
qCCritical(d->logCat)
<< "Developers, ensure the Connection is valid before using it";
Q_ASSERT(false);
- setStatus(IncorrectRequestError, tr("Invalid server connection"));
+ setStatus(IncorrectRequest, tr("Invalid server connection"));
}
// The status is no good, finalise
QTimer::singleShot(0, this, &BaseJob::finishJob);
@@ -417,42 +404,42 @@ BaseJob::Status BaseJob::Private::parseJson()
void BaseJob::gotReply()
{
- setStatus(checkReply(reply()));
-
- if (status().good()
- && d->expectedContentTypes == QByteArrayList { "application/json" }) {
+ // Defer actually updating the status until it's finalised
+ auto statusSoFar = checkReply(reply());
+ if (statusSoFar.good()
+ && d->expectedContentTypes == QByteArrayList { "application/json" }) //
+ {
d->rawResponse = reply()->readAll();
- setStatus(d->parseJson());
- if (status().good() && !expectedKeys().empty()) {
+ statusSoFar = d->parseJson();
+ if (statusSoFar.good() && !expectedKeys().empty()) {
const auto& responseObject = jsonData();
QByteArrayList missingKeys;
for (const auto& k: expectedKeys())
if (!responseObject.contains(k))
missingKeys.push_back(k);
if (!missingKeys.empty())
- setStatus(IncorrectResponse, tr("Required JSON keys missing: ")
- + missingKeys.join());
+ statusSoFar = { IncorrectResponse,
+ tr("Required JSON keys missing: ")
+ + missingKeys.join() };
}
+ setStatus(statusSoFar);
if (!status().good()) // Bad JSON in a "good" reply: bail out
return;
- } // else {
+ }
// If the endpoint expects anything else than just (API-related) JSON
// reply()->readAll() is not performed and the whole reply processing
// is left to derived job classes: they may read it piecemeal or customise
// per content type in prepareResult(), or even have read it already
// (see, e.g., DownloadFileJob).
- // }
-
- if (status().good())
+ if (statusSoFar.good()) {
setStatus(prepareResult());
- else {
- d->rawResponse = reply()->readAll();
- qCDebug(d->logCat).noquote()
- << "Error body (truncated if long):" << rawDataSample(500);
- // Parse the error payload and update the status if needed
- if (const auto newStatus = prepareError(); !newStatus.good())
- setStatus(newStatus);
+ return;
}
+
+ d->rawResponse = reply()->readAll();
+ qCDebug(d->logCat).noquote()
+ << "Error body (truncated if long):" << rawDataSample(500);
+ setStatus(prepareError(statusSoFar));
}
bool checkContentType(const QByteArray& type, const QByteArrayList& patterns)
@@ -517,7 +504,7 @@ BaseJob::Status BaseJob::checkReply(const QNetworkReply* reply) const
BaseJob::Status BaseJob::prepareResult() { return Success; }
-BaseJob::Status BaseJob::prepareError()
+BaseJob::Status BaseJob::prepareError(Status currentStatus)
{
// Try to make sense of the error payload but be prepared for all kinds
// of unexpected stuff (raw HTML, plain text, foreign JSON among those)
@@ -527,10 +514,10 @@ BaseJob::Status BaseJob::prepareError()
// By now, if d->parseJson() above succeeded then jsonData() will return
// a valid JSON object - or an empty object otherwise (in which case most
- // of if's below will fall through to `return NoError` at the end
+ // of if's below will fall through retaining the current status)
const auto& errorJson = jsonData();
const auto errCode = errorJson.value("errcode"_ls).toString();
- if (error() == TooManyRequestsError || errCode == "M_LIMIT_EXCEEDED") {
+ if (error() == TooManyRequests || errCode == "M_LIMIT_EXCEEDED") {
QString msg = tr("Too many requests");
int64_t retryAfterMs = errorJson.value("retry_after_ms"_ls).toInt(-1);
if (retryAfterMs >= 0)
@@ -540,16 +527,16 @@ BaseJob::Status BaseJob::prepareError()
d->connection->limitRate(milliseconds(retryAfterMs));
- return { TooManyRequestsError, msg };
+ return { TooManyRequests, msg };
}
if (errCode == "M_CONSENT_NOT_GIVEN") {
- d->errorUrl = errorJson.value("consent_uri"_ls).toString();
- return { UserConsentRequiredError };
+ d->errorUrl = QUrl(errorJson.value("consent_uri"_ls).toString());
+ return { UserConsentRequired };
}
if (errCode == "M_UNSUPPORTED_ROOM_VERSION"
|| errCode == "M_INCOMPATIBLE_ROOM_VERSION")
- return { UnsupportedRoomVersionError,
+ return { UnsupportedRoomVersion,
errorJson.contains("room_version"_ls)
? tr("Requested room version: %1")
.arg(errorJson.value("room_version"_ls).toString())
@@ -562,9 +549,9 @@ BaseJob::Status BaseJob::prepareError()
// Not localisable on the client side
if (errorJson.contains("error"_ls)) // Keep the code, update the message
- return { d->status.code, errorJson.value("error"_ls).toString() };
+ return { currentStatus.code, errorJson.value("error"_ls).toString() };
- return NoError; // Retain the status if the error payload is not recognised
+ return currentStatus; // The error payload is not recognised
}
QJsonValue BaseJob::takeValueFromJson(const QString& key)
@@ -731,38 +718,41 @@ QString BaseJob::statusCaption() const
return tr("Request was abandoned");
case NetworkError:
return tr("Network problems");
- case TimeoutError:
+ case Timeout:
return tr("Request timed out");
case Unauthorised:
return tr("Unauthorised request");
case ContentAccessError:
return tr("Access error");
- case NotFoundError:
+ case NotFound:
return tr("Not found");
- case IncorrectRequestError:
+ case IncorrectRequest:
return tr("Invalid request");
- case IncorrectResponseError:
+ case IncorrectResponse:
return tr("Response could not be parsed");
- case TooManyRequestsError:
+ case TooManyRequests:
return tr("Too many requests");
- case RequestNotImplementedError:
+ case RequestNotImplemented:
return tr("Function not implemented by the server");
- case NetworkAuthRequiredError:
+ case NetworkAuthRequired:
return tr("Network authentication required");
- case UserConsentRequiredError:
+ case UserConsentRequired:
return tr("User consent required");
- case UnsupportedRoomVersionError:
+ case UnsupportedRoomVersion:
return tr("The server does not support the needed room version");
default:
return tr("Request failed");
}
}
-int BaseJob::error() const { return d->status.code; }
+int BaseJob::error() const {
+ return d->status.code; }
-QString BaseJob::errorString() const { return d->status.message; }
+QString BaseJob::errorString() const {
+ return d->status.message; }
-QUrl BaseJob::errorUrl() const { return d->errorUrl; }
+QUrl BaseJob::errorUrl() const {
+ return d->errorUrl; }
void BaseJob::setStatus(Status s)
{
@@ -813,7 +803,7 @@ void BaseJob::abandon()
void BaseJob::timeout()
{
- setStatus(TimeoutError, "The job has timed out");
+ setStatus(Timeout, "The job has timed out");
finishJob();
}
diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h
index be2926be..555c602b 100644
--- a/lib/jobs/basejob.h
+++ b/lib/jobs/basejob.h
@@ -1,28 +1,16 @@
-/******************************************************************************
- * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de>
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
#include "requestdata.h"
-#include "../logging.h"
-#include "../converters.h"
+#include "logging.h"
+#include "converters.h" // Common for csapi/ headers even though not used here
+#include "quotient_common.h" // For DECL_DEPRECATED_ENUMERATOR
#include <QtCore/QObject>
+#include <QtCore/QStringBuilder>
class QNetworkReply;
class QSslError;
@@ -32,12 +20,23 @@ class ConnectionData;
enum class HttpVerb { Get, Put, Post, Delete };
-class BaseJob : public QObject {
+class QUOTIENT_API BaseJob : public QObject {
Q_OBJECT
Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT)
Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries)
Q_PROPERTY(int statusCode READ error NOTIFY statusChanged)
+
+ static QByteArray encodeIfParam(const QString& paramPart);
+ template <int N>
+ static auto encodeIfParam(const char (&constPart)[N])
+ {
+ return constPart;
+ }
+
public:
+#define WITH_DEPRECATED_ERROR_VERSION(Recommended) \
+ Recommended, DECL_DEPRECATED_ENUMERATOR(Recommended##Error, Recommended)
+
/*! The status code of a job
*
* Every job is created in Unprepared status; upon calling prepare()
@@ -48,7 +47,7 @@ public:
*/
enum StatusCode {
Success = 0,
- NoError = Success, // To be compatible with Qt conventions
+ NoError = Success,
Pending = 1,
WarningLevel = 20, //< Warnings have codes starting from this
UnexpectedResponseType = 21,
@@ -57,28 +56,18 @@ public:
Abandoned = 50, //< A tiny period between abandoning and object deletion
ErrorLevel = 100, //< Errors have codes starting from this
NetworkError = 101,
- Timeout,
- TimeoutError = Timeout,
+ WITH_DEPRECATED_ERROR_VERSION(Timeout),
Unauthorised,
ContentAccessError,
- NotFoundError,
- IncorrectRequest,
- IncorrectRequestError = IncorrectRequest,
- IncorrectResponse,
- IncorrectResponseError = IncorrectResponse,
- JsonParseError //< \deprecated Use IncorrectResponse instead
- = IncorrectResponse,
- TooManyRequests,
- TooManyRequestsError = TooManyRequests,
+ WITH_DEPRECATED_ERROR_VERSION(NotFound),
+ WITH_DEPRECATED_ERROR_VERSION(IncorrectRequest),
+ WITH_DEPRECATED_ERROR_VERSION(IncorrectResponse),
+ WITH_DEPRECATED_ERROR_VERSION(TooManyRequests),
RateLimited = TooManyRequests,
- RequestNotImplemented,
- RequestNotImplementedError = RequestNotImplemented,
- UnsupportedRoomVersion,
- UnsupportedRoomVersionError = UnsupportedRoomVersion,
- NetworkAuthRequired,
- NetworkAuthRequiredError = NetworkAuthRequired,
- UserConsentRequired,
- UserConsentRequiredError = UserConsentRequired,
+ WITH_DEPRECATED_ERROR_VERSION(RequestNotImplemented),
+ WITH_DEPRECATED_ERROR_VERSION(UnsupportedRoomVersion),
+ WITH_DEPRECATED_ERROR_VERSION(NetworkAuthRequired),
+ WITH_DEPRECATED_ERROR_VERSION(UserConsentRequired),
CannotLeaveRoom,
UserDeactivated,
FileError,
@@ -86,21 +75,19 @@ public:
};
Q_ENUM(StatusCode)
- /**
- * A simple wrapper around QUrlQuery that allows its creation from
- * a list of string pairs
- */
- class Query : public QUrlQuery {
- public:
- using QUrlQuery::QUrlQuery;
- Query() = default;
- Query(const std::initializer_list<QPair<QString, QString>>& l)
- {
- setQueryItems(l);
- }
- };
+#undef WITH_DEPRECATED_ERROR_VERSION
- using Data = RequestData;
+ template <typename... StrTs>
+ static QByteArray makePath(StrTs&&... parts)
+ {
+ return (QByteArray() % ... % encodeIfParam(parts));
+ }
+
+ using Data
+#ifndef Q_CC_MSVC
+ Q_DECL_DEPRECATED_X("Use Quotient::RequestData instead")
+#endif
+ = RequestData;
/*!
* This structure stores the status of a server call job. The status
@@ -136,16 +123,25 @@ public:
{
return !operator==(other);
}
+ bool operator==(int otherCode) const
+ {
+ return code == otherCode;
+ }
+ bool operator!=(int otherCode) const
+ {
+ return !operator==(otherCode);
+ }
int code;
QString message;
};
public:
- BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
+ BaseJob(HttpVerb verb, const QString& name, QByteArray endpoint,
+ bool needsToken = true);
+ BaseJob(HttpVerb verb, const QString& name, QByteArray endpoint,
+ const QUrlQuery& query, RequestData&& data = {},
bool needsToken = true);
- BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
- const Query& query, Data&& data = {}, bool needsToken = true);
QUrl requestUrl() const;
bool isBackground() const;
@@ -200,7 +196,7 @@ public:
* If there's no top-level JSON object in the response or if there's
* no node with the key \p keyName, \p defaultValue is returned.
*/
- template <typename T, typename StrT> // Waiting for QStringViews...
+ template <typename T, typename StrT>
T loadFromJson(const StrT& keyName, T&& defaultValue = {}) const
{
const auto& jv = jsonData().value(keyName);
@@ -251,8 +247,8 @@ public:
return dbg << j->objectName();
}
-public slots:
- void initiate(ConnectionData* connData, bool inBackground);
+public Q_SLOTS:
+ void initiate(Quotient::ConnectionData* connData, bool inBackground);
/**
* Abandons the result of this job, arrived or unarrived.
@@ -263,7 +259,7 @@ public slots:
*/
void abandon();
-signals:
+Q_SIGNALS:
/** The job is about to send a network request */
void aboutToSendRequest();
@@ -342,20 +338,22 @@ signals:
protected:
using headers_t = QHash<QByteArray, QByteArray>;
+ Q_DECL_DEPRECATED_X("Deprecated due to being unused")
const QString& apiEndpoint() const;
+ Q_DECL_DEPRECATED_X("Deprecated due to being unused")
void setApiEndpoint(const QString& apiEndpoint);
const headers_t& requestHeaders() const;
void setRequestHeader(const headers_t::key_type& headerName,
const headers_t::mapped_type& headerValue);
void setRequestHeaders(const headers_t& headers);
- const QUrlQuery& query() const;
+ QUrlQuery query() const;
void setRequestQuery(const QUrlQuery& query);
- const Data& requestData() const;
- void setRequestData(Data&& data);
+ const RequestData& requestData() const;
+ void setRequestData(RequestData&& data);
const QByteArrayList& expectedContentTypes() const;
void addExpectedContentType(const QByteArray& contentType);
void setExpectedContentTypes(const QByteArrayList& contentTypes);
- const QByteArrayList expectedKeys() const;
+ QByteArrayList expectedKeys() const;
void addExpectedKey(const QByteArray &key);
void setExpectedKeys(const QByteArrayList &keys);
@@ -367,7 +365,7 @@ protected:
* The function ensures exactly one '/' between the path component of
* \p baseUrl and \p path. The query component of \p baseUrl is ignored.
*/
- static QUrl makeRequestUrl(QUrl baseUrl, const QString& path,
+ static QUrl makeRequestUrl(QUrl baseUrl, const QByteArray &encodedPath,
const QUrlQuery& query = {});
/*! Prepares the job for execution
@@ -401,10 +399,12 @@ protected:
* was not good (usually because of an unsuccessful HTTP code).
* The base implementation assumes Matrix JSON error object in the body;
* overrides are strongly recommended to call it for all stock Matrix
- * responses as early as possible but in addition can process custom errors,
+ * responses as early as possible and only then process custom errors,
* with JSON or non-JSON payload.
+ *
+ * \return updated (if necessary) job status
*/
- virtual Status prepareError();
+ virtual Status prepareError(Status currentStatus);
/*! \brief Get direct access to the JSON response object in the job
*
@@ -433,7 +433,7 @@ protected:
// Job objects should only be deleted via QObject::deleteLater
~BaseJob() override;
-protected slots:
+protected Q_SLOTS:
void timeout();
/*! \brief Check the pending or received reply for upfront issues
@@ -456,7 +456,7 @@ protected slots:
*/
virtual Status checkReply(const QNetworkReply *reply) const;
-private slots:
+private Q_SLOTS:
void sendRequest();
void gotReply();
@@ -467,10 +467,10 @@ private:
void finishJob();
class Private;
- QScopedPointer<Private> d;
+ ImplPtr<Private> d;
};
-inline bool isJobRunning(BaseJob* job)
+inline bool QUOTIENT_API isJobPending(BaseJob* job)
{
return job && job->error() == BaseJob::Pending;
}
diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp
index 0011a97c..759d52c9 100644
--- a/lib/jobs/downloadfilejob.cpp
+++ b/lib/jobs/downloadfilejob.cpp
@@ -1,11 +1,19 @@
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#include "downloadfilejob.h"
#include <QtCore/QFile>
#include <QtCore/QTemporaryFile>
#include <QtNetwork/QNetworkReply>
-using namespace Quotient;
+#ifdef Quotient_E2EE_ENABLED
+# include "events/filesourceinfo.h"
+# include <QtCore/QCryptographicHash>
+#endif
+
+using namespace Quotient;
class DownloadFileJob::Private {
public:
Private() : tempFile(new QTemporaryFile()) {}
@@ -17,6 +25,10 @@ public:
QScopedPointer<QFile> targetFile;
QScopedPointer<QFile> tempFile;
+
+#ifdef Quotient_E2EE_ENABLED
+ Omittable<EncryptedFileMetadata> encryptedFileMetadata;
+#endif
};
QUrl DownloadFileJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri)
@@ -29,11 +41,25 @@ DownloadFileJob::DownloadFileJob(const QString& serverName,
const QString& mediaId,
const QString& localFilename)
: GetContentJob(serverName, mediaId)
- , d(localFilename.isEmpty() ? new Private : new Private(localFilename))
+ , d(localFilename.isEmpty() ? makeImpl<Private>()
+ : makeImpl<Private>(localFilename))
{
setObjectName(QStringLiteral("DownloadFileJob"));
}
+#ifdef Quotient_E2EE_ENABLED
+DownloadFileJob::DownloadFileJob(const QString& serverName,
+ const QString& mediaId,
+ const EncryptedFileMetadata& file,
+ const QString& localFilename)
+ : GetContentJob(serverName, mediaId)
+ , d(localFilename.isEmpty() ? makeImpl<Private>()
+ : makeImpl<Private>(localFilename))
+{
+ setObjectName(QStringLiteral("DownloadFileJob"));
+ d->encryptedFileMetadata = file;
+}
+#endif
QString DownloadFileJob::targetFileName() const
{
return (d->targetFile ? d->targetFile : d->tempFile)->fileName();
@@ -48,7 +74,7 @@ void DownloadFileJob::doPrepare()
setStatus(FileError, "Could not open the target file for writing");
return;
}
- if (!d->tempFile->isReadable() && !d->tempFile->open(QIODevice::WriteOnly)) {
+ if (!d->tempFile->isReadable() && !d->tempFile->open(QIODevice::ReadWrite)) {
qCWarning(JOBS) << "Couldn't open the temporary file"
<< d->tempFile->fileName() << "for writing";
setStatus(FileError, "Could not open the temporary download file");
@@ -93,21 +119,60 @@ void DownloadFileJob::beforeAbandon()
d->tempFile->remove();
}
+void decryptFile(QFile& sourceFile, const EncryptedFileMetadata& metadata,
+ QFile& targetFile)
+{
+ sourceFile.seek(0);
+ const auto encrypted = sourceFile.readAll(); // TODO: stream decryption
+ const auto decrypted = decryptFile(encrypted, metadata);
+ targetFile.write(decrypted);
+}
+
BaseJob::Status DownloadFileJob::prepareResult()
{
if (d->targetFile) {
- d->targetFile->close();
- if (!d->targetFile->remove()) {
- qCWarning(JOBS) << "Failed to remove the target file placeholder";
- return { FileError, "Couldn't finalise the download" };
+#ifdef Quotient_E2EE_ENABLED
+ if (d->encryptedFileMetadata.has_value()) {
+ decryptFile(*d->tempFile, *d->encryptedFileMetadata, *d->targetFile);
+ d->tempFile->remove();
+ } else {
+#endif
+ d->targetFile->close();
+ if (!d->targetFile->remove()) {
+ qWarning(JOBS) << "Failed to remove the target file placeholder";
+ return { FileError, "Couldn't finalise the download" };
+ }
+ if (!d->tempFile->rename(d->targetFile->fileName())) {
+ qWarning(JOBS) << "Failed to rename" << d->tempFile->fileName()
+ << "to" << d->targetFile->fileName();
+ return { FileError, "Couldn't finalise the download" };
+ }
+#ifdef Quotient_E2EE_ENABLED
}
- if (!d->tempFile->rename(d->targetFile->fileName())) {
- qCWarning(JOBS) << "Failed to rename" << d->tempFile->fileName()
- << "to" << d->targetFile->fileName();
- return { FileError, "Couldn't finalise the download" };
+#endif
+ } else {
+#ifdef Quotient_E2EE_ENABLED
+ if (d->encryptedFileMetadata.has_value()) {
+ QTemporaryFile tempTempFile; // Assuming it to be next to tempFile
+ decryptFile(*d->tempFile, *d->encryptedFileMetadata, tempTempFile);
+ d->tempFile->close();
+ if (!d->tempFile->remove()) {
+ qWarning(JOBS)
+ << "Failed to remove the decrypted file placeholder";
+ return { FileError, "Couldn't finalise the download" };
+ }
+ if (!tempTempFile.rename(d->tempFile->fileName())) {
+ qWarning(JOBS) << "Failed to rename" << tempTempFile.fileName()
+ << "to" << d->tempFile->fileName();
+ return { FileError, "Couldn't finalise the download" };
+ }
+ } else {
+#endif
+ d->tempFile->close();
+#ifdef Quotient_E2EE_ENABLED
}
- } else
- d->tempFile->close();
- qCDebug(JOBS) << "Saved a file as" << targetFileName();
+#endif
+ }
+ qDebug(JOBS) << "Saved a file as" << targetFileName();
return Success;
}
diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h
index e00fd9e4..cbbfd244 100644
--- a/lib/jobs/downloadfilejob.h
+++ b/lib/jobs/downloadfilejob.h
@@ -1,9 +1,14 @@
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#pragma once
#include "csapi/content-repo.h"
+#include "events/filesourceinfo.h"
+
namespace Quotient {
-class DownloadFileJob : public GetContentJob {
+class QUOTIENT_API DownloadFileJob : public GetContentJob {
public:
using GetContentJob::makeRequestUrl;
static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri);
@@ -11,11 +16,14 @@ public:
DownloadFileJob(const QString& serverName, const QString& mediaId,
const QString& localFilename = {});
+#ifdef Quotient_E2EE_ENABLED
+ DownloadFileJob(const QString& serverName, const QString& mediaId, const EncryptedFileMetadata& file, const QString& localFilename = {});
+#endif
QString targetFileName() const;
private:
class Private;
- QScopedPointer<Private> d;
+ ImplPtr<Private> d;
void doPrepare() override;
void onSentRequest(QNetworkReply* reply) override;
diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp
index a69f00e9..6fe8ef26 100644
--- a/lib/jobs/mediathumbnailjob.cpp
+++ b/lib/jobs/mediathumbnailjob.cpp
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "mediathumbnailjob.h"
@@ -32,13 +17,17 @@ MediaThumbnailJob::MediaThumbnailJob(const QString& serverName,
const QString& mediaId, QSize requestedSize)
: GetContentThumbnailJob(serverName, mediaId, requestedSize.width(),
requestedSize.height(), "scale")
-{}
+{
+ setLoggingCategory(THUMBNAILJOB);
+}
MediaThumbnailJob::MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize)
: MediaThumbnailJob(mxcUri.authority(),
mxcUri.path().mid(1), // sans leading '/'
requestedSize)
-{}
+{
+ setLoggingCategory(THUMBNAILJOB);
+}
QImage MediaThumbnailJob::thumbnail() const { return _thumbnail; }
diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h
index e6d39085..c9f6da35 100644
--- a/lib/jobs/mediathumbnailjob.h
+++ b/lib/jobs/mediathumbnailjob.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -23,7 +8,7 @@
#include <QtGui/QPixmap>
namespace Quotient {
-class MediaThumbnailJob : public GetContentThumbnailJob {
+class QUOTIENT_API MediaThumbnailJob : public GetContentThumbnailJob {
public:
using GetContentThumbnailJob::makeRequestUrl;
static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri,
diff --git a/lib/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h
deleted file mode 100644
index 5a4d942c..00000000
--- a/lib/jobs/postreadmarkersjob.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/******************************************************************************
- * Copyright (C) 2017 Kitsune Ral <kitsune-ral@users.sf.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#pragma once
-
-#include "basejob.h"
-
-#include <QtCore/QJsonObject>
-
-using namespace Quotient;
-
-class PostReadMarkersJob : public BaseJob {
-public:
- explicit PostReadMarkersJob(const QString& roomId,
- const QString& readUpToEventId)
- : BaseJob(
- HttpVerb::Post, "PostReadMarkersJob",
- QStringLiteral("_matrix/client/r0/rooms/%1/read_markers").arg(roomId))
- {
- setRequestData(
- QJsonObject { { QStringLiteral("m.fully_read"), readUpToEventId } });
- }
-};
diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp
index cec15954..ab249f6d 100644
--- a/lib/jobs/requestdata.cpp
+++ b/lib/jobs/requestdata.cpp
@@ -1,5 +1,9 @@
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
#include "requestdata.h"
+#include <QtCore/QIODevice>
#include <QtCore/QBuffer>
#include <QtCore/QByteArray>
#include <QtCore/QJsonArray>
@@ -10,7 +14,7 @@ using namespace Quotient;
auto fromData(const QByteArray& data)
{
- auto source = std::make_unique<QBuffer>();
+ auto source = makeImpl<QBuffer, QIODevice>();
source->setData(data);
source->open(QIODevice::ReadOnly);
return source;
@@ -28,4 +32,6 @@ RequestData::RequestData(const QJsonObject& jo) : _source(fromJson(jo)) {}
RequestData::RequestData(const QJsonArray& ja) : _source(fromJson(ja)) {}
-RequestData::~RequestData() = default;
+RequestData::RequestData(QIODevice* source)
+ : _source(acquireImpl(source))
+{}
diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h
index 9cb5ecaf..accc8f71 100644
--- a/lib/jobs/requestdata.h
+++ b/lib/jobs/requestdata.h
@@ -1,26 +1,9 @@
-/******************************************************************************
- * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
-#include <QtCore/QByteArray>
-
-#include <memory>
+#include "util.h"
class QJsonObject;
class QJsonArray;
@@ -34,22 +17,19 @@ namespace Quotient {
* as well as JSON (and possibly other structures in the future) to
* a QByteArray consumed by QNetworkAccessManager request methods.
*/
-class RequestData {
+class QUOTIENT_API RequestData {
public:
- RequestData(const QByteArray& a = {});
- RequestData(const QJsonObject& jo);
- RequestData(const QJsonArray& ja);
- RequestData(QIODevice* source) : _source(std::unique_ptr<QIODevice>(source))
- {}
- RequestData(RequestData&&) = default;
- RequestData& operator=(RequestData&&) = default;
- ~RequestData();
+ // NOLINTBEGIN(google-explicit-constructor): that check should learn about
+ // explicit(false)
+ QUO_IMPLICIT RequestData(const QByteArray& a = {});
+ QUO_IMPLICIT RequestData(const QJsonObject& jo);
+ QUO_IMPLICIT RequestData(const QJsonArray& ja);
+ QUO_IMPLICIT RequestData(QIODevice* source);
+ // NOLINTEND(google-explicit-constructor)
QIODevice* source() const { return _source.get(); }
private:
- std::unique_ptr<QIODevice> _source;
+ ImplPtr<QIODevice> _source;
};
} // namespace Quotient
-/// \deprecated Use namespace Quotient instead
-namespace QMatrixClient = Quotient;
diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp
index 9087fe50..f5c632bf 100644
--- a/lib/jobs/syncjob.cpp
+++ b/lib/jobs/syncjob.cpp
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#include "syncjob.h"
@@ -25,7 +10,7 @@ static size_t jobId = 0;
SyncJob::SyncJob(const QString& since, const QString& filter, int timeout,
const QString& presence)
: BaseJob(HttpVerb::Get, QStringLiteral("SyncJob-%1").arg(++jobId),
- QStringLiteral("_matrix/client/r0/sync"))
+ "_matrix/client/r0/sync")
{
setLoggingCategory(SYNCJOB);
QUrlQuery query;
@@ -52,10 +37,12 @@ SyncJob::SyncJob(const QString& since, const Filter& filter, int timeout,
BaseJob::Status SyncJob::prepareResult()
{
d.parseJson(jsonData());
- if (d.unresolvedRooms().isEmpty())
+ if (Q_LIKELY(d.unresolvedRooms().isEmpty()))
return Success;
- qCCritical(MAIN).noquote() << "Incomplete sync response, missing rooms:"
+ Q_ASSERT(d.unresolvedRooms().isEmpty());
+ qCCritical(MAIN).noquote() << "Rooms missing after processing sync "
+ "response, possibly a bug in SyncData: "
<< d.unresolvedRooms().join(',');
return IncorrectResponse;
}
diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h
index bf139a7b..b7bfbbb3 100644
--- a/lib/jobs/syncjob.h
+++ b/lib/jobs/syncjob.h
@@ -1,20 +1,5 @@
-/******************************************************************************
- * Copyright (C) 2016 Felix Rohrbach <kde@fxrh.de>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- */
+// SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net>
+// SPDX-License-Identifier: LGPL-2.1-or-later
#pragma once
@@ -30,7 +15,7 @@ public:
explicit SyncJob(const QString& since, const Filter& filter,
int timeout = -1, const QString& presence = {});
- SyncData&& takeData() { return std::move(d); }
+ SyncData takeData() { return std::move(d); }
protected:
Status prepareResult() override;