aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml15
-rw-r--r--CMakeLists.txt31
-rw-r--r--connection.cpp62
-rw-r--r--connection.h20
-rw-r--r--converters.h4
-rw-r--r--jobs/basejob.cpp148
-rw-r--r--jobs/basejob.h65
-rw-r--r--jobs/downloadfilejob.cpp113
-rw-r--r--jobs/downloadfilejob.h27
-rw-r--r--jobs/generated/administrative_contact.cpp25
-rw-r--r--jobs/generated/administrative_contact.h7
-rw-r--r--jobs/generated/banning.cpp9
-rw-r--r--jobs/generated/banning.h2
-rw-r--r--jobs/generated/content-repo.cpp212
-rw-r--r--jobs/generated/content-repo.h101
-rw-r--r--jobs/generated/create_room.cpp114
-rw-r--r--jobs/generated/create_room.h55
-rw-r--r--jobs/generated/directory.cpp23
-rw-r--r--jobs/generated/directory.h8
-rw-r--r--jobs/generated/inviting.cpp5
-rw-r--r--jobs/generated/inviting.h2
-rw-r--r--jobs/generated/kicking.cpp5
-rw-r--r--jobs/generated/kicking.h2
-rw-r--r--jobs/generated/leaving.cpp15
-rw-r--r--jobs/generated/leaving.h2
-rw-r--r--jobs/generated/list_public_rooms.cpp43
-rw-r--r--jobs/generated/list_public_rooms.h14
-rw-r--r--jobs/generated/login.cpp11
-rw-r--r--jobs/generated/login.h8
-rw-r--r--jobs/generated/logout.cpp8
-rw-r--r--jobs/generated/logout.h1
-rw-r--r--jobs/generated/profile.cpp42
-rw-r--r--jobs/generated/profile.h20
-rw-r--r--jobs/generated/receipts.cpp5
-rw-r--r--jobs/generated/receipts.h2
-rw-r--r--jobs/generated/redaction.cpp11
-rw-r--r--jobs/generated/redaction.h8
-rw-r--r--jobs/generated/third_party_membership.cpp5
-rw-r--r--jobs/generated/third_party_membership.h2
-rw-r--r--jobs/generated/typing.cpp5
-rw-r--r--jobs/generated/typing.h2
-rw-r--r--jobs/generated/versions.cpp12
-rw-r--r--jobs/generated/versions.h8
-rw-r--r--jobs/generated/whoami.cpp12
-rw-r--r--jobs/generated/whoami.h8
-rw-r--r--jobs/gtad.yaml84
-rw-r--r--jobs/mediathumbnailjob.cpp29
-rw-r--r--jobs/mediathumbnailjob.h13
-rw-r--r--jobs/preamble.mustache3
-rw-r--r--jobs/requestdata.cpp38
-rw-r--r--jobs/requestdata.h59
-rw-r--r--jobs/{{base}}.cpp.mustache103
-rw-r--r--jobs/{{base}}.h.mustache60
-rw-r--r--libqmatrixclient.pri8
54 files changed, 1372 insertions, 324 deletions
diff --git a/.travis.yml b/.travis.yml
index 1b67119d..9c7d8a7d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -23,9 +23,20 @@ before_install:
- eval "${ENV_EVAL}"
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then . /opt/qt56/bin/qt56-env.sh; fi
-script:
+install:
+- git clone https://github.com/QMatrixClient/matrix-doc.git
+- git clone --recursive https://github.com/KitsuneRal/gtad.git
+- pushd gtad
+- cmake -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} .
+- cmake --build .
+- popd
+
+before_script:
- mkdir build && cd build
-- cmake ..
+- cmake -DMATRIX_DOC_PATH="matrix-doc" -DGTAD_PATH="gtad/gtad" -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} ..
+- cmake --build . --target update-api
+
+script:
- cmake --build . --target all
- cd ..
- qmake qmc-example.pro "QMAKE_CC = $CC" "QMAKE_CXX = $CXX" && make all
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 463bfea7..c0ffc0b0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -39,6 +39,11 @@ if (CMAKE_BUILD_TYPE)
endif(CMAKE_BUILD_TYPE)
message( STATUS "Using compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" )
message( STATUS "Using Qt ${Qt5_VERSION} at ${Qt5_Prefix}" )
+if (MATRIX_DOC_PATH AND GTAD_PATH)
+ message( STATUS "Generating API stubs enabled" )
+ message( STATUS " Using GTAD at ${GTAD_PATH}" )
+ message( STATUS " Using CS API files at ${MATRIX_DOC_PATH}/api/client-server" )
+endif ()
message( STATUS "=============================================================================" )
message( STATUS )
@@ -60,6 +65,7 @@ set(libqmatrixclient_SRCS
events/roomavatarevent.cpp
events/typingevent.cpp
events/receiptevent.cpp
+ jobs/requestdata.cpp
jobs/basejob.cpp
jobs/checkauthmethods.cpp
jobs/sendeventjob.cpp
@@ -68,8 +74,33 @@ set(libqmatrixclient_SRCS
jobs/roommessagesjob.cpp
jobs/syncjob.cpp
jobs/mediathumbnailjob.cpp
+ jobs/downloadfilejob.cpp
)
+set(API_DEF_PATH ${MATRIX_DOC_PATH}/api/client-server/)
+file(GLOB_RECURSE API_DEFS RELATIVE ${PROJECT_SOURCE_DIR}
+ ${API_DEF_PATH}/*.yaml
+ ${API_DEF_PATH}/definitions/*.yaml
+ ${MATRIX_DOC_PATH}/event-schemas/schema/*
+)
+if (MATRIX_DOC_PATH AND GTAD_PATH)
+ add_custom_target(update-api
+ ${GTAD_PATH} --config jobs/gtad.yaml --out jobs/generated
+ ${MATRIX_DOC_PATH}/api/client-server
+ cas_login_redirect.yaml- cas_login_ticket.yaml-
+ old_sync.yaml- room_initial_sync.yaml-
+ sync.yaml- room_state.yaml-
+ event_context.yaml- joining.yaml-
+ notifications.yaml- peeking_events.yaml-
+ pushrules.yaml- rooms.yaml- search.yaml-
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
+ SOURCES jobs/gtad.yaml
+ jobs/{{base}}.h.mustache jobs/{{base}}.cpp.mustache
+ ${API_DEFS}
+ VERBATIM
+ )
+endif()
+
aux_source_directory(jobs/generated libqmatrixclient_job_SRCS)
set(example_SRCS examples/qmc-example.cpp)
diff --git a/connection.cpp b/connection.cpp
index 53a38f0d..935546b1 100644
--- a/connection.cpp
+++ b/connection.cpp
@@ -29,6 +29,7 @@
#include "jobs/roommessagesjob.h"
#include "jobs/syncjob.h"
#include "jobs/mediathumbnailjob.h"
+#include "jobs/downloadfilejob.h"
#include <QtNetwork/QDnsLookup>
#include <QtCore/QFile>
@@ -187,8 +188,7 @@ void Connection::Private::connectWithToken(const QString& user,
data->setToken(accessToken.toLatin1());
data->setDeviceId(deviceId);
qCDebug(MAIN) << "Using server" << data->baseUrl() << "by user"
- << userId
- << "from device" << deviceId;
+ << userId << "from device" << deviceId;
emit q->connected();
}
@@ -298,9 +298,24 @@ RoomMessagesJob* Connection::getMessages(Room* room, const QString& from) const
return callApi<RoomMessagesJob>(room->id(), from);
}
+inline auto splitMediaId(const QString& mediaId)
+{
+ auto idParts = mediaId.split('/');
+ Q_ASSERT_X(idParts.size() == 2, __FUNCTION__,
+ "mediaId should have a form 'serverName/localMediaId' (without apostrophes)");
+ return idParts;
+}
+
+MediaThumbnailJob* Connection::getThumbnail(const QString& mediaId, QSize requestedSize) const
+{
+ auto idParts = splitMediaId(mediaId);
+ return callApi<MediaThumbnailJob>(idParts.front(), idParts.back(),
+ requestedSize);
+}
+
MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, QSize requestedSize) const
{
- return callApi<MediaThumbnailJob>(url, requestedSize);
+ return getThumbnail(url.authority() + url.path(), requestedSize);
}
MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, int requestedWidth,
@@ -309,6 +324,47 @@ MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, int requestedWidth,
return getThumbnail(url, QSize(requestedWidth, requestedHeight));
}
+UploadContentJob* Connection::uploadContent(QIODevice* contentSource,
+ const QString& filename, const QString& contentType) const
+{
+ return callApi<UploadContentJob>(contentSource, filename, contentType);
+}
+
+UploadContentJob* Connection::uploadFile(const QString& fileName,
+ const QString& contentType)
+{
+ auto sourceFile = new QFile(fileName);
+ if (sourceFile->open(QIODevice::ReadOnly))
+ {
+ qCWarning(MAIN) << "Couldn't open" << sourceFile->fileName()
+ << "for reading";
+ return nullptr;
+ }
+ return uploadContent(sourceFile, QFileInfo(*sourceFile).fileName(),
+ contentType);
+}
+
+GetContentJob* Connection::getContent(const QString& mediaId) const
+{
+ auto idParts = splitMediaId(mediaId);
+ return callApi<GetContentJob>(idParts.front(), idParts.back());
+}
+
+GetContentJob* Connection::getContent(const QUrl& url) const
+{
+ return getContent(url.authority() + url.path());
+}
+
+DownloadFileJob* Connection::downloadFile(const QUrl& url,
+ const QString& localFilename) const
+{
+ auto mediaId = url.authority() + url.path();
+ auto idParts = splitMediaId(mediaId);
+ auto* job = callApi<DownloadFileJob>(idParts.front(), idParts.back(),
+ localFilename);
+ return job;
+}
+
ForgetRoomJob* Connection::forgetRoom(const QString& id)
{
// To forget is hard :) First we should ensure the local user is not
diff --git a/connection.h b/connection.h
index 8dda2bbf..79d7d658 100644
--- a/connection.h
+++ b/connection.h
@@ -41,6 +41,9 @@ namespace QMatrixClient
class PostReceiptJob;
class MediaThumbnailJob;
class JoinRoomJob;
+ class UploadContentJob;
+ class GetContentJob;
+ class DownloadFileJob;
class Connection: public QObject {
Q_OBJECT
@@ -174,12 +177,26 @@ namespace QMatrixClient
void sync(int timeout = -1);
void stopSync();
- virtual MediaThumbnailJob* getThumbnail(const QUrl& url,
+ virtual MediaThumbnailJob* getThumbnail(const QString& mediaId,
QSize requestedSize) const;
MediaThumbnailJob* getThumbnail(const QUrl& url,
+ QSize requestedSize) const;
+ MediaThumbnailJob* getThumbnail(const QUrl& url,
int requestedWidth,
int requestedHeight) const;
+ // QIODevice* should already be open
+ virtual UploadContentJob* uploadContent(QIODevice* contentSource,
+ const QString& filename = {},
+ const QString& contentType = {}) const;
+ virtual UploadContentJob* uploadFile(const QString& fileName,
+ const QString& contentType = {});
+ virtual GetContentJob* getContent(const QString& mediaId) const;
+ GetContentJob* getContent(const QUrl& url) const;
+ // If localFilename is empty, a temporary file will be created
+ virtual DownloadFileJob* downloadFile(const QUrl& url,
+ const QString& localFilename = {}) const;
+
virtual JoinRoomJob* joinRoom(const QString& roomAlias);
// Old API that will be abolished any time soon. DO NOT USE.
@@ -297,7 +314,6 @@ namespace QMatrixClient
*/
Room* provideRoom(const QString& roomId, JoinState joinState);
-
/**
* Completes loading sync data.
*/
diff --git a/converters.h b/converters.h
index 00d1d339..0d7f734e 100644
--- a/converters.h
+++ b/converters.h
@@ -46,11 +46,7 @@ namespace QMatrixClient
inline QJsonValue toJson(const QByteArray& bytes)
{
-#if QT_VERSION < QT_VERSION_CHECK(5, 3, 0)
- return QJsonValue(QLatin1String(bytes.constData()));
-#else
return QJsonValue(bytes.constData());
-#endif
}
template <typename T>
diff --git a/jobs/basejob.cpp b/jobs/basejob.cpp
index c92c2944..7fc56287 100644
--- a/jobs/basejob.cpp
+++ b/jobs/basejob.cpp
@@ -25,7 +25,6 @@
#include <QtNetwork/QNetworkReply>
#include <QtCore/QTimer>
#include <QtCore/QRegularExpression>
-//#include <QtCore/QStringBuilder>
#include <array>
@@ -46,7 +45,7 @@ class BaseJob::Private
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, QUrlQuery q, Data data, bool nt)
+ Private(HttpVerb v, QString endpoint, QUrlQuery q, Data&& data, bool nt)
: verb(v), apiEndpoint(std::move(endpoint))
, requestQuery(std::move(q)), requestData(std::move(data))
, needsToken(nt)
@@ -60,10 +59,16 @@ class BaseJob::Private
// Contents for the network request
HttpVerb verb;
QString apiEndpoint;
+ QHash<QByteArray, QByteArray> requestHeaders;
QUrlQuery requestQuery;
Data requestData;
bool needsToken;
+ // There's no use of QMimeType here because we don't want to match
+ // content types against the known MIME type hierarchy; and at the same
+ // type QMimeType is of little help with MIME type globs (`text/*` etc.)
+ QByteArrayList expectedContentTypes;
+
QScopedPointer<QNetworkReply, NetworkReplyDeleter> reply;
Status status = Pending;
@@ -90,11 +95,16 @@ QDebug QMatrixClient::operator<<(QDebug dbg, const BaseJob::Status& s)
<< QString(s.message).replace(filter, "\\1 HIDDEN");
}
+BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, bool needsToken)
+ : BaseJob(verb, name, endpoint, Query { }, Data { }, needsToken)
+{ }
+
BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
- const Query& query, const Data& data, bool needsToken)
- : d(new Private(verb, endpoint, query, data, needsToken))
+ const Query& query, Data&& data, bool needsToken)
+ : d(new Private(verb, endpoint, query, std::move(data), needsToken))
{
setObjectName(name);
+ setExpectedContentTypes({ "application/json" });
d->timer.setSingleShot(true);
connect (&d->timer, &QTimer::timeout, this, &BaseJob::timeout);
d->retryTimer.setSingleShot(true);
@@ -117,6 +127,22 @@ void BaseJob::setApiEndpoint(const QString& apiEndpoint)
d->apiEndpoint = apiEndpoint;
}
+const BaseJob::headers_t&BaseJob::requestHeaders() const
+{
+ return d->requestHeaders;
+}
+
+void BaseJob::setRequestHeader(const headers_t::key_type& headerName,
+ const headers_t::mapped_type& headerValue)
+{
+ d->requestHeaders[headerName] = headerValue;
+}
+
+void BaseJob::setRequestHeaders(const BaseJob::headers_t& headers)
+{
+ d->requestHeaders = headers;
+}
+
const QUrlQuery& BaseJob::query() const
{
return d->requestQuery;
@@ -132,9 +158,24 @@ const BaseJob::Data& BaseJob::requestData() const
return d->requestData;
}
-void BaseJob::setRequestData(const BaseJob::Data& data)
+void BaseJob::setRequestData(Data&& data)
+{
+ std::swap(d->requestData, data);
+}
+
+const QByteArrayList& BaseJob::expectedContentTypes() const
+{
+ return d->expectedContentTypes;
+}
+
+void BaseJob::addExpectedContentType(const QByteArray& contentType)
+{
+ d->expectedContentTypes << contentType;
+}
+
+void BaseJob::setExpectedContentTypes(const QByteArrayList& contentTypes)
{
- d->requestData = data;
+ d->expectedContentTypes = contentTypes;
}
void BaseJob::Private::sendRequest()
@@ -148,23 +189,26 @@ void BaseJob::Private::sendRequest()
url.setQuery(requestQuery);
QNetworkRequest req {url};
- req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ if (!requestHeaders.contains("Content-Type"))
+ req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
req.setRawHeader(QByteArray("Authorization"),
QByteArray("Bearer ") + connection->accessToken());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
req.setMaximumRedirectsAllowed(10);
#endif
+ for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it)
+ req.setRawHeader(it.key(), it.value());
switch( verb )
{
case HttpVerb::Get:
reply.reset( connection->nam()->get(req) );
break;
case HttpVerb::Post:
- reply.reset( connection->nam()->post(req, requestData.serialize()) );
+ reply.reset( connection->nam()->post(req, requestData.source()) );
break;
case HttpVerb::Put:
- reply.reset( connection->nam()->put(req, requestData.serialize()) );
+ reply.reset( connection->nam()->put(req, requestData.source()) );
break;
case HttpVerb::Delete:
reply.reset( connection->nam()->deleteResource(req) );
@@ -172,15 +216,21 @@ void BaseJob::Private::sendRequest()
}
}
-void BaseJob::beforeStart(const ConnectionData* connData)
-{
-}
+void BaseJob::beforeStart(const ConnectionData*)
+{ }
+
+void BaseJob::afterStart(const ConnectionData*, QNetworkReply*)
+{ }
+
+void BaseJob::beforeAbandon(QNetworkReply*)
+{ }
void BaseJob::start(const ConnectionData* connData)
{
d->connection = connData;
beforeStart(connData);
sendRequest();
+ afterStart(connData, d->reply.data());
}
void BaseJob::sendRequest()
@@ -194,6 +244,10 @@ void BaseJob::sendRequest()
connect( d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply );
if (d->reply->isRunning())
{
+ connect( d->reply.data(), &QNetworkReply::uploadProgress,
+ this, &BaseJob::uploadProgress);
+ connect( d->reply.data(), &QNetworkReply::downloadProgress,
+ this, &BaseJob::downloadProgress);
d->timer.start(getCurrentTimeout());
qCDebug(d->logCat) << this << "request has been sent";
emit started();
@@ -206,11 +260,34 @@ void BaseJob::gotReply()
{
setStatus(checkReply(d->reply.data()));
if (status().good())
- setStatus(parseReply(d->reply->readAll()));
+ setStatus(parseReply(d->reply.data()));
finishJob();
}
+bool checkContentType(const QByteArray& type, const QByteArrayList& patterns)
+{
+ if (patterns.isEmpty())
+ return true;
+
+ for (const auto& pattern: patterns)
+ {
+ if (pattern.startsWith('*') || type == pattern) // Fast lane
+ return true;
+
+ auto patternParts = pattern.split('/');
+ Q_ASSERT_X(patternParts.size() <= 2, __FUNCTION__,
+ "BaseJob: Expected content type should have up to two"
+ " /-separated parts; violating pattern: " + pattern);
+
+ if (type.split('/').front() == patternParts.front() &&
+ patternParts.back() == "*")
+ return true; // Exact match already went on fast lane
+ }
+
+ return false;
+}
+
BaseJob::Status BaseJob::checkReply(QNetworkReply* reply) const
{
qCDebug(d->logCat) << this << "returned from" << reply->url().toDisplayString();
@@ -218,30 +295,35 @@ BaseJob::Status BaseJob::checkReply(QNetworkReply* reply) const
qCDebug(d->logCat) << this << "returned" << reply->error();
switch( reply->error() )
{
- case QNetworkReply::NoError:
- return NoError;
-
- case QNetworkReply::AuthenticationRequiredError:
- case QNetworkReply::ContentAccessDenied:
- case QNetworkReply::ContentOperationNotPermittedError:
- return { ContentAccessError, reply->errorString() };
-
- case QNetworkReply::ProtocolInvalidOperationError:
- case QNetworkReply::UnknownContentError:
- return { IncorrectRequestError, reply->errorString() };
-
- case QNetworkReply::ContentNotFoundError:
- return { NotFoundError, reply->errorString() };
-
- default:
- return { NetworkError, reply->errorString() };
+ case QNetworkReply::NoError:
+ if (checkContentType(reply->rawHeader("Content-Type"),
+ d->expectedContentTypes))
+ return NoError;
+ else
+ return { IncorrectResponseError,
+ "Incorrect content type of the response" };
+
+ case QNetworkReply::AuthenticationRequiredError:
+ case QNetworkReply::ContentAccessDenied:
+ case QNetworkReply::ContentOperationNotPermittedError:
+ return { ContentAccessError, reply->errorString() };
+
+ case QNetworkReply::ProtocolInvalidOperationError:
+ case QNetworkReply::UnknownContentError:
+ return { IncorrectRequestError, reply->errorString() };
+
+ case QNetworkReply::ContentNotFoundError:
+ return { NotFoundError, reply->errorString() };
+
+ default:
+ return { NetworkError, reply->errorString() };
}
}
-BaseJob::Status BaseJob::parseReply(QByteArray data)
+BaseJob::Status BaseJob::parseReply(QNetworkReply* reply)
{
QJsonParseError error;
- QJsonDocument json = QJsonDocument::fromJson(data, &error);
+ QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error);
if( error.error == QJsonParseError::NoError )
return parseJson(json);
else
@@ -358,6 +440,7 @@ void BaseJob::setStatus(int code, QString message)
void BaseJob::abandon()
{
+ beforeAbandon(d->reply.data());
this->disconnect();
if (d->reply)
d->reply->disconnect(this);
@@ -374,4 +457,3 @@ void BaseJob::setLoggingCategory(LoggingCategory lcf)
{
d->logCat = lcf;
}
-
diff --git a/jobs/basejob.h b/jobs/basejob.h
index 5fcbbc97..e9e108c6 100644
--- a/jobs/basejob.h
+++ b/jobs/basejob.h
@@ -18,14 +18,15 @@
#pragma once
-#include "logging.h"
+#include "../logging.h"
+#include "requestdata.h"
#include <QtCore/QObject>
+#include <QtCore/QUrlQuery>
+
+// Any job that parses the response will need the below two.
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
-#include <QtCore/QJsonArray>
-#include <QtCore/QUrlQuery>
-#include <QtCore/QScopedPointer>
class QNetworkReply;
class QSslError;
@@ -59,6 +60,7 @@ namespace QMatrixClient
, ContentAccessError
, NotFoundError
, IncorrectRequestError
+ , IncorrectResponseError
, UserDefinedError = 200
};
@@ -76,33 +78,8 @@ namespace QMatrixClient
setQueryItems(l);
}
};
- /**
- * A simple wrapper that represents the request body.
- * Provides a unified interface to dump an unstructured byte stream
- * as well as JSON (and possibly other structures in the future) to
- * a QByteArray consumed by QNetworkAccessManager request methods.
- */
- class Data
- {
- public:
- Data() = default;
- Data(const QByteArray& a) : _payload(a) { }
- Data(const QJsonObject& jo)
- : _payload(fromJson(QJsonDocument(jo))) { }
- Data(const QJsonArray& ja)
- : _payload(fromJson(QJsonDocument(ja))) { }
- QByteArray serialize() const
- {
- return _payload;
- }
- private:
- static QByteArray fromJson(const QJsonDocument& jd)
- {
- return jd.toJson(QJsonDocument::Compact);
- }
- QByteArray _payload;
- };
+ using Data = RequestData;
/**
* This structure stores the status of a server call job. The status consists
@@ -130,7 +107,9 @@ namespace QMatrixClient
public:
BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
- const Query& query = {}, const Data& data = {},
+ bool needsToken = true);
+ BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
+ const Query& query, Data&& data = {},
bool needsToken = true);
Status status() const;
@@ -213,15 +192,30 @@ namespace QMatrixClient
*/
void failure(BaseJob*);
+ void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
+ void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
+
protected:
+ using headers_t = QHash<QByteArray, QByteArray>;
+
const QString& apiEndpoint() const;
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;
void setRequestQuery(const QUrlQuery& query);
const Data& requestData() const;
- void setRequestData(const Data& data);
+ void setRequestData(Data&& data);
+ const QByteArrayList& expectedContentTypes() const;
+ void addExpectedContentType(const QByteArray& contentType);
+ void setExpectedContentTypes(const QByteArrayList& contentTypes);
virtual void beforeStart(const ConnectionData* connData);
+ virtual void afterStart(const ConnectionData* connData,
+ QNetworkReply* reply);
+ virtual void beforeAbandon(QNetworkReply*);
/**
* Used by gotReply() to check the received reply for general
@@ -240,11 +234,11 @@ namespace QMatrixClient
* Processes the reply. By default, parses the reply into
* a QJsonDocument and calls parseJson() if it's a valid JSON.
*
- * @param data raw contents of a HTTP reply from the server (without headers)
+ * @param reply raw contents of a HTTP reply from the server (without headers)
*
* @see gotReply, parseJson
*/
- virtual Status parseReply(QByteArray data);
+ virtual Status parseReply(QNetworkReply* reply);
/**
* Processes the JSON document received from the Matrix server.
@@ -265,7 +259,8 @@ namespace QMatrixClient
void setLoggingCategory(LoggingCategory lcf);
// Job objects should only be deleted via QObject::deleteLater
- virtual ~BaseJob();
+ ~BaseJob() override;
+
protected slots:
void timeout();
diff --git a/jobs/downloadfilejob.cpp b/jobs/downloadfilejob.cpp
new file mode 100644
index 00000000..2530e259
--- /dev/null
+++ b/jobs/downloadfilejob.cpp
@@ -0,0 +1,113 @@
+#include "downloadfilejob.h"
+
+#include <QtNetwork/QNetworkReply>
+#include <QtCore/QFile>
+#include <QtCore/QTemporaryFile>
+
+using namespace QMatrixClient;
+
+class DownloadFileJob::Private
+{
+ public:
+ Private() : tempFile(new QTemporaryFile()) { }
+
+ explicit Private(const QString& localFilename)
+ : targetFile(new QFile(localFilename))
+ , tempFile(new QFile(targetFile->fileName() + ".qmcdownload"))
+ { }
+
+ QScopedPointer<QFile> targetFile;
+ QScopedPointer<QFile> tempFile;
+};
+
+DownloadFileJob::DownloadFileJob(const QString& serverName,
+ const QString& mediaId,
+ const QString& localFilename)
+ : GetContentJob(serverName, mediaId)
+ , d(localFilename.isEmpty() ? new Private : new Private(localFilename))
+{
+ setObjectName("DownloadFileJob");
+}
+
+QString DownloadFileJob::targetFileName() const
+{
+ return (d->targetFile ? d->targetFile : d->tempFile)->fileName();
+}
+
+void DownloadFileJob::beforeStart(const ConnectionData*)
+{
+ if (d->targetFile && !d->targetFile->open(QIODevice::WriteOnly))
+ {
+ qCWarning(JOBS) << "Couldn't open the file"
+ << d->targetFile->fileName() << "for writing";
+ setStatus(FileError, "Could not open the target file for writing");
+ return;
+ }
+ if (!d->tempFile->open(QIODevice::WriteOnly))
+ {
+ qCWarning(JOBS) << "Couldn't open the temporary file"
+ << d->tempFile->fileName() << "for writing";
+ setStatus(FileError, "Could not open the temporary download file");
+ }
+ qCDebug(JOBS) << "Downloading to" << d->tempFile->fileName();
+}
+
+void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply)
+{
+ connect(reply, &QNetworkReply::metaDataChanged, this, [this,reply] {
+ auto sizeHeader = reply->header(QNetworkRequest::ContentLengthHeader);
+ if (sizeHeader.isValid())
+ {
+ auto targetSize = sizeHeader.value<qint64>();
+ if (targetSize != -1)
+ if (!d->tempFile->resize(targetSize))
+ {
+ qCWarning(JOBS) << "Failed to allocate" << targetSize
+ << "bytes for" << d->tempFile->fileName();
+ setStatus(FileError,
+ "Could not reserve disk space for download");
+ }
+ }
+ });
+ connect(reply, &QIODevice::readyRead, this, [this,reply] {
+ auto bytes = reply->read(reply->bytesAvailable());
+ if (bytes.isEmpty())
+ {
+ qCWarning(JOBS)
+ << "Unexpected empty chunk when downloading from"
+ << reply->url() << "to" << d->tempFile->fileName();
+ } else {
+ d->tempFile->write(bytes);
+ }
+ });
+}
+
+void DownloadFileJob::beforeAbandon(QNetworkReply*)
+{
+ if (d->targetFile)
+ d->targetFile->remove();
+ d->tempFile->remove();
+}
+
+BaseJob::Status DownloadFileJob::parseReply(QNetworkReply*)
+{
+ 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" };
+ }
+ 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" };
+ }
+ }
+ else
+ d->tempFile->close();
+ qCDebug(JOBS) << "Saved a file as" << targetFileName();
+ return Success;
+}
diff --git a/jobs/downloadfilejob.h b/jobs/downloadfilejob.h
new file mode 100644
index 00000000..d798506c
--- /dev/null
+++ b/jobs/downloadfilejob.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "generated/content-repo.h"
+
+namespace QMatrixClient
+{
+ class DownloadFileJob : public GetContentJob
+ {
+ public:
+ enum { FileError = BaseJob::UserDefinedError + 1 };
+
+ DownloadFileJob(const QString& serverName, const QString& mediaId,
+ const QString& localFilename = {});
+
+ QString targetFileName() const;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+
+ void beforeStart(const ConnectionData*) override;
+ void afterStart(const ConnectionData*,
+ QNetworkReply* reply) override;
+ void beforeAbandon(QNetworkReply*) override;
+ Status parseReply(QNetworkReply*) override;
+ };
+}
diff --git a/jobs/generated/administrative_contact.cpp b/jobs/generated/administrative_contact.cpp
index 705c5d54..479bee52 100644
--- a/jobs/generated/administrative_contact.cpp
+++ b/jobs/generated/administrative_contact.cpp
@@ -2,11 +2,8 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "administrative_contact.h"
-#include "converters.h"
-
#include <QtCore/QStringBuilder>
using namespace QMatrixClient;
@@ -47,16 +44,13 @@ class GetAccount3PIDsJob::Private
GetAccount3PIDsJob::GetAccount3PIDsJob()
: BaseJob(HttpVerb::Get, "GetAccount3PIDsJob",
- basePath % "/account/3pid",
- Query { }
- ), d(new Private)
-{ }
-
-GetAccount3PIDsJob::~GetAccount3PIDsJob()
+ basePath % "/account/3pid")
+ , d(new Private)
{
- delete d;
}
+GetAccount3PIDsJob::~GetAccount3PIDsJob() = default;
+
const QVector<GetAccount3PIDsJob::ThirdPartyIdentifier>& GetAccount3PIDsJob::threepids() const
{
return d->threepids;
@@ -100,9 +94,7 @@ namespace QMatrixClient
Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind)
: BaseJob(HttpVerb::Post, "Post3PIDsJob",
- basePath % "/account/3pid",
- Query { }
- )
+ basePath % "/account/3pid")
{
QJsonObject _data;
_data.insert("three_pid_creds", toJson(threePidCreds));
@@ -112,8 +104,7 @@ Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind)
RequestTokenTo3PIDJob::RequestTokenTo3PIDJob()
: BaseJob(HttpVerb::Post, "RequestTokenTo3PIDJob",
- basePath % "/account/3pid/email/requestToken",
- Query { }, Data { }, false
- )
-{ }
+ basePath % "/account/3pid/email/requestToken", false)
+{
+}
diff --git a/jobs/generated/administrative_contact.h b/jobs/generated/administrative_contact.h
index fa6beba9..67563719 100644
--- a/jobs/generated/administrative_contact.h
+++ b/jobs/generated/administrative_contact.h
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
@@ -34,13 +33,13 @@ namespace QMatrixClient
~GetAccount3PIDsJob() override;
const QVector<ThirdPartyIdentifier>& threepids() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
class Post3PIDsJob : public BaseJob
diff --git a/jobs/generated/banning.cpp b/jobs/generated/banning.cpp
index 96f80ea8..f66b27b6 100644
--- a/jobs/generated/banning.cpp
+++ b/jobs/generated/banning.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "banning.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
BanJob::BanJob(const QString& roomId, const QString& userId, const QString& reason)
: BaseJob(HttpVerb::Post, "BanJob",
- basePath % "/rooms/" % roomId % "/ban",
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/ban")
{
QJsonObject _data;
_data.insert("user_id", toJson(userId));
@@ -28,9 +25,7 @@ BanJob::BanJob(const QString& roomId, const QString& userId, const QString& reas
UnbanJob::UnbanJob(const QString& roomId, const QString& userId)
: BaseJob(HttpVerb::Post, "UnbanJob",
- basePath % "/rooms/" % roomId % "/unban",
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/unban")
{
QJsonObject _data;
_data.insert("user_id", toJson(userId));
diff --git a/jobs/generated/banning.h b/jobs/generated/banning.h
index 6db096ee..2d6fbd9b 100644
--- a/jobs/generated/banning.h
+++ b/jobs/generated/banning.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/content-repo.cpp b/jobs/generated/content-repo.cpp
new file mode 100644
index 00000000..93aa838c
--- /dev/null
+++ b/jobs/generated/content-repo.cpp
@@ -0,0 +1,212 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "content-repo.h"
+
+#include "converters.h"
+
+#include <QtNetwork/QNetworkReply>
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/media/r0");
+
+class UploadContentJob::Private
+{
+ public:
+ QString contentUri;
+};
+
+UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, const QString& contentType)
+ : BaseJob(HttpVerb::Post, "UploadContentJob",
+ basePath % "/upload")
+ , d(new Private)
+{
+ setRequestHeader("Content-Type", contentType.toLatin1());
+
+ QUrlQuery _q;
+ if (!filename.isEmpty())
+ _q.addQueryItem("filename", filename);
+ setRequestQuery(_q);
+ setRequestData(Data(content));
+}
+
+UploadContentJob::~UploadContentJob() = default;
+
+const QString& UploadContentJob::contentUri() const
+{
+ return d->contentUri;
+}
+
+BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ if (!json.contains("content_uri"))
+ return { JsonParseError,
+ "The key 'content_uri' not found in the response" };
+ d->contentUri = fromJson<QString>(json.value("content_uri"));
+ return Success;
+}
+
+class GetContentJob::Private
+{
+ public:
+ QString contentType;
+ QString contentDisposition;
+ QIODevice* content;
+};
+
+GetContentJob::GetContentJob(const QString& serverName, const QString& mediaId)
+ : BaseJob(HttpVerb::Get, "GetContentJob",
+ basePath % "/download/" % serverName % "/" % mediaId, false)
+ , d(new Private)
+{
+ setExpectedContentTypes({ "*/*" });
+}
+
+GetContentJob::~GetContentJob() = default;
+
+const QString& GetContentJob::contentType() const
+{
+ return d->contentType;
+}
+
+const QString& GetContentJob::contentDisposition() const
+{
+ return d->contentDisposition;
+}
+
+QIODevice* GetContentJob::content() const
+{
+ return d->content;
+}
+
+BaseJob::Status GetContentJob::parseReply(QNetworkReply* reply)
+{
+ d->contentType = reply->rawHeader("Content-Type");
+ d->contentDisposition = reply->rawHeader("Content-Disposition");
+ d->content = reply;
+ return Success;
+}
+
+class GetContentOverrideNameJob::Private
+{
+ public:
+ QString contentType;
+ QString contentDisposition;
+ QIODevice* content;
+};
+
+GetContentOverrideNameJob::GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, const QString& fileName)
+ : BaseJob(HttpVerb::Get, "GetContentOverrideNameJob",
+ basePath % "/download/" % serverName % "/" % mediaId % "/" % fileName, false)
+ , d(new Private)
+{
+ setExpectedContentTypes({ "*/*" });
+}
+
+GetContentOverrideNameJob::~GetContentOverrideNameJob() = default;
+
+const QString& GetContentOverrideNameJob::contentType() const
+{
+ return d->contentType;
+}
+
+const QString& GetContentOverrideNameJob::contentDisposition() const
+{
+ return d->contentDisposition;
+}
+
+QIODevice* GetContentOverrideNameJob::content() const
+{
+ return d->content;
+}
+
+BaseJob::Status GetContentOverrideNameJob::parseReply(QNetworkReply* reply)
+{
+ d->contentType = reply->rawHeader("Content-Type");
+ d->contentDisposition = reply->rawHeader("Content-Disposition");
+ d->content = reply;
+ return Success;
+}
+
+class GetContentThumbnailJob::Private
+{
+ public:
+ QString contentType;
+ QIODevice* content;
+};
+
+GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, int height, const QString& method)
+ : BaseJob(HttpVerb::Get, "GetContentThumbnailJob",
+ basePath % "/thumbnail/" % serverName % "/" % mediaId, false)
+ , d(new Private)
+{
+ QUrlQuery _q;
+ _q.addQueryItem("width", QString("%1").arg(width));
+ _q.addQueryItem("height", QString("%1").arg(height));
+ if (!method.isEmpty())
+ _q.addQueryItem("method", method);
+ setRequestQuery(_q);
+ setExpectedContentTypes({ "image/jpeg", "image/png" });
+}
+
+GetContentThumbnailJob::~GetContentThumbnailJob() = default;
+
+const QString& GetContentThumbnailJob::contentType() const
+{
+ return d->contentType;
+}
+
+QIODevice* GetContentThumbnailJob::content() const
+{
+ return d->content;
+}
+
+BaseJob::Status GetContentThumbnailJob::parseReply(QNetworkReply* reply)
+{
+ d->contentType = reply->rawHeader("Content-Type");
+ d->content = reply;
+ return Success;
+}
+
+class GetUrlPreviewJob::Private
+{
+ public:
+ double matrixImageSize;
+ QString ogImage;
+};
+
+GetUrlPreviewJob::GetUrlPreviewJob(const QString& url, double ts)
+ : BaseJob(HttpVerb::Get, "GetUrlPreviewJob",
+ basePath % "/preview_url")
+ , d(new Private)
+{
+ QUrlQuery _q;
+ _q.addQueryItem("url", url);
+ _q.addQueryItem("ts", QString("%1").arg(ts));
+ setRequestQuery(_q);
+}
+
+GetUrlPreviewJob::~GetUrlPreviewJob() = default;
+
+double GetUrlPreviewJob::matrixImageSize() const
+{
+ return d->matrixImageSize;
+}
+
+const QString& GetUrlPreviewJob::ogImage() const
+{
+ return d->ogImage;
+}
+
+BaseJob::Status GetUrlPreviewJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->matrixImageSize = fromJson<double>(json.value("matrix:image:size"));
+ d->ogImage = fromJson<QString>(json.value("og:image"));
+ return Success;
+}
+
diff --git a/jobs/generated/content-repo.h b/jobs/generated/content-repo.h
new file mode 100644
index 00000000..0796322b
--- /dev/null
+++ b/jobs/generated/content-repo.h
@@ -0,0 +1,101 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QIODevice>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class UploadContentJob : public BaseJob
+ {
+ public:
+ explicit UploadContentJob(QIODevice* content, const QString& filename = {}, const QString& contentType = {});
+ ~UploadContentJob() override;
+
+ const QString& contentUri() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class GetContentJob : public BaseJob
+ {
+ public:
+ explicit GetContentJob(const QString& serverName, const QString& mediaId);
+ ~GetContentJob() override;
+
+ const QString& contentType() const;
+ const QString& contentDisposition() const;
+ QIODevice* content() const;
+
+ protected:
+ Status parseReply(QNetworkReply* reply) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class GetContentOverrideNameJob : public BaseJob
+ {
+ public:
+ explicit GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, const QString& fileName);
+ ~GetContentOverrideNameJob() override;
+
+ const QString& contentType() const;
+ const QString& contentDisposition() const;
+ QIODevice* content() const;
+
+ protected:
+ Status parseReply(QNetworkReply* reply) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class GetContentThumbnailJob : public BaseJob
+ {
+ public:
+ explicit GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width = {}, int height = {}, const QString& method = {});
+ ~GetContentThumbnailJob() override;
+
+ const QString& contentType() const;
+ QIODevice* content() const;
+
+ protected:
+ Status parseReply(QNetworkReply* reply) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class GetUrlPreviewJob : public BaseJob
+ {
+ public:
+ explicit GetUrlPreviewJob(const QString& url, double ts = {});
+ ~GetUrlPreviewJob() override;
+
+ double matrixImageSize() const;
+ const QString& ogImage() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/jobs/generated/create_room.cpp b/jobs/generated/create_room.cpp
new file mode 100644
index 00000000..be06873a
--- /dev/null
+++ b/jobs/generated/create_room.cpp
@@ -0,0 +1,114 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "create_room.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+CreateRoomJob::Invite3pid::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("id_server", toJson(idServer));
+ o.insert("medium", toJson(medium));
+ o.insert("address", toJson(address));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<CreateRoomJob::Invite3pid>
+ {
+ CreateRoomJob::Invite3pid operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ CreateRoomJob::Invite3pid result;
+ result.idServer =
+ fromJson<QString>(o.value("id_server"));
+ result.medium =
+ fromJson<QString>(o.value("medium"));
+ result.address =
+ fromJson<QString>(o.value("address"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+CreateRoomJob::StateEvent::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("type", toJson(type));
+ o.insert("state_key", toJson(stateKey));
+ o.insert("content", toJson(content));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<CreateRoomJob::StateEvent>
+ {
+ CreateRoomJob::StateEvent operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ CreateRoomJob::StateEvent result;
+ result.type =
+ fromJson<QString>(o.value("type"));
+ result.stateKey =
+ fromJson<QString>(o.value("state_key"));
+ result.content =
+ fromJson<QJsonObject>(o.value("content"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+class CreateRoomJob::Private
+{
+ public:
+ QString roomId;
+};
+
+CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QVector<QString>& invite, const QVector<Invite3pid>& invite3pid, const QJsonObject& creationContent, const QVector<StateEvent>& initialState, const QString& preset, bool isDirect)
+ : BaseJob(HttpVerb::Post, "CreateRoomJob",
+ basePath % "/createRoom")
+ , d(new Private)
+{
+ QJsonObject _data;
+ if (!visibility.isEmpty())
+ _data.insert("visibility", toJson(visibility));
+ if (!roomAliasName.isEmpty())
+ _data.insert("room_alias_name", toJson(roomAliasName));
+ if (!name.isEmpty())
+ _data.insert("name", toJson(name));
+ if (!topic.isEmpty())
+ _data.insert("topic", toJson(topic));
+ _data.insert("invite", toJson(invite));
+ _data.insert("invite_3pid", toJson(invite3pid));
+ _data.insert("creation_content", toJson(creationContent));
+ _data.insert("initial_state", toJson(initialState));
+ if (!preset.isEmpty())
+ _data.insert("preset", toJson(preset));
+ _data.insert("is_direct", toJson(isDirect));
+ setRequestData(_data);
+}
+
+CreateRoomJob::~CreateRoomJob() = default;
+
+const QString& CreateRoomJob::roomId() const
+{
+ return d->roomId;
+}
+
+BaseJob::Status CreateRoomJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->roomId = fromJson<QString>(json.value("room_id"));
+ return Success;
+}
+
diff --git a/jobs/generated/create_room.h b/jobs/generated/create_room.h
new file mode 100644
index 00000000..13c9d2c0
--- /dev/null
+++ b/jobs/generated/create_room.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QJsonObject>
+#include <QtCore/QVector>
+
+#include "converters.h"
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class CreateRoomJob : public BaseJob
+ {
+ public:
+ // Inner data structures
+
+ struct Invite3pid
+ {
+ QString idServer;
+ QString medium;
+ QString address;
+
+ operator QJsonObject() const;
+ };
+
+ struct StateEvent
+ {
+ QString type;
+ QString stateKey;
+ QJsonObject content;
+
+ operator QJsonObject() const;
+ };
+
+ // End of inner data structures
+
+ explicit CreateRoomJob(const QString& visibility = {}, const QString& roomAliasName = {}, const QString& name = {}, const QString& topic = {}, const QVector<QString>& invite = {}, const QVector<Invite3pid>& invite3pid = {}, const QJsonObject& creationContent = {}, const QVector<StateEvent>& initialState = {}, const QString& preset = {}, bool isDirect = {});
+ ~CreateRoomJob() override;
+
+ const QString& roomId() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/jobs/generated/directory.cpp b/jobs/generated/directory.cpp
index dcec75ac..4e61ed74 100644
--- a/jobs/generated/directory.cpp
+++ b/jobs/generated/directory.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "directory.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0/directory");
SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId)
: BaseJob(HttpVerb::Put, "SetRoomAliasJob",
- basePath % "/room/" % roomAlias,
- Query { }
- )
+ basePath % "/room/" % roomAlias)
{
QJsonObject _data;
if (!roomId.isEmpty())
@@ -34,16 +31,13 @@ class GetRoomIdByAliasJob::Private
GetRoomIdByAliasJob::GetRoomIdByAliasJob(const QString& roomAlias)
: BaseJob(HttpVerb::Get, "GetRoomIdByAliasJob",
- basePath % "/room/" % roomAlias,
- Query { }, Data { }, false
- ), d(new Private)
-{ }
-
-GetRoomIdByAliasJob::~GetRoomIdByAliasJob()
+ basePath % "/room/" % roomAlias, false)
+ , d(new Private)
{
- delete d;
}
+GetRoomIdByAliasJob::~GetRoomIdByAliasJob() = default;
+
const QString& GetRoomIdByAliasJob::roomId() const
{
return d->roomId;
@@ -64,8 +58,7 @@ BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data)
DeleteRoomAliasJob::DeleteRoomAliasJob(const QString& roomAlias)
: BaseJob(HttpVerb::Delete, "DeleteRoomAliasJob",
- basePath % "/room/" % roomAlias,
- Query { }
- )
-{ }
+ basePath % "/room/" % roomAlias)
+{
+}
diff --git a/jobs/generated/directory.h b/jobs/generated/directory.h
index 1dd4e7ed..eeda563b 100644
--- a/jobs/generated/directory.h
+++ b/jobs/generated/directory.h
@@ -2,13 +2,11 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
#include <QtCore/QVector>
-#include <QtCore/QString>
namespace QMatrixClient
@@ -29,13 +27,13 @@ namespace QMatrixClient
const QString& roomId() const;
const QVector<QString>& servers() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
class DeleteRoomAliasJob : public BaseJob
diff --git a/jobs/generated/inviting.cpp b/jobs/generated/inviting.cpp
index 5f89adf7..d2ee2107 100644
--- a/jobs/generated/inviting.cpp
+++ b/jobs/generated/inviting.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "inviting.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
InviteUserJob::InviteUserJob(const QString& roomId, const QString& userId)
: BaseJob(HttpVerb::Post, "InviteUserJob",
- basePath % "/rooms/" % roomId % "/invite",
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/invite")
{
QJsonObject _data;
_data.insert("user_id", toJson(userId));
diff --git a/jobs/generated/inviting.h b/jobs/generated/inviting.h
index 225cb516..eaa884df 100644
--- a/jobs/generated/inviting.h
+++ b/jobs/generated/inviting.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/kicking.cpp b/jobs/generated/kicking.cpp
index 86dde629..bf2490b7 100644
--- a/jobs/generated/kicking.cpp
+++ b/jobs/generated/kicking.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "kicking.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
KickJob::KickJob(const QString& roomId, const QString& userId, const QString& reason)
: BaseJob(HttpVerb::Post, "KickJob",
- basePath % "/rooms/" % roomId % "/kick",
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/kick")
{
QJsonObject _data;
_data.insert("user_id", toJson(userId));
diff --git a/jobs/generated/kicking.h b/jobs/generated/kicking.h
index 7c834e45..3814bef7 100644
--- a/jobs/generated/kicking.h
+++ b/jobs/generated/kicking.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/leaving.cpp b/jobs/generated/leaving.cpp
index 2cf7fda3..89c110dd 100644
--- a/jobs/generated/leaving.cpp
+++ b/jobs/generated/leaving.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "leaving.h"
#include "converters.h"
@@ -15,15 +14,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
LeaveRoomJob::LeaveRoomJob(const QString& roomId)
: BaseJob(HttpVerb::Post, "LeaveRoomJob",
- basePath % "/rooms/" % roomId % "/leave",
- Query { }
- )
-{ }
+ basePath % "/rooms/" % roomId % "/leave")
+{
+}
ForgetRoomJob::ForgetRoomJob(const QString& roomId)
: BaseJob(HttpVerb::Post, "ForgetRoomJob",
- basePath % "/rooms/" % roomId % "/forget",
- Query { }
- )
-{ }
+ basePath % "/rooms/" % roomId % "/forget")
+{
+}
diff --git a/jobs/generated/leaving.h b/jobs/generated/leaving.h
index 28ba3d92..cd39b612 100644
--- a/jobs/generated/leaving.h
+++ b/jobs/generated/leaving.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/list_public_rooms.cpp b/jobs/generated/list_public_rooms.cpp
index 8a96966f..a2c0e406 100644
--- a/jobs/generated/list_public_rooms.cpp
+++ b/jobs/generated/list_public_rooms.cpp
@@ -2,11 +2,8 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "list_public_rooms.h"
-#include "converters.h"
-
#include <QtCore/QStringBuilder>
using namespace QMatrixClient;
@@ -71,20 +68,20 @@ class GetPublicRoomsJob::Private
GetPublicRoomsJob::GetPublicRoomsJob(double limit, const QString& since, const QString& server)
: BaseJob(HttpVerb::Get, "GetPublicRoomsJob",
- basePath % "/publicRooms",
- Query {
- { "limit", toJson(limit).toString() },
- { "since", toJson(since).toString() },
- { "server", toJson(server).toString() }
- }, Data { }, false
- ), d(new Private)
-{ }
-
-GetPublicRoomsJob::~GetPublicRoomsJob()
-{
- delete d;
+ basePath % "/publicRooms", false)
+ , d(new Private)
+{
+ QUrlQuery _q;
+ _q.addQueryItem("limit", QString("%1").arg(limit));
+ if (!since.isEmpty())
+ _q.addQueryItem("since", since);
+ if (!server.isEmpty())
+ _q.addQueryItem("server", server);
+ setRequestQuery(_q);
}
+GetPublicRoomsJob::~GetPublicRoomsJob() = default;
+
const QVector<GetPublicRoomsJob::PublicRoomsChunk>& GetPublicRoomsJob::chunk() const
{
return d->chunk;
@@ -199,12 +196,13 @@ class QueryPublicRoomsJob::Private
QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, double limit, const QString& since, const Filter& filter)
: BaseJob(HttpVerb::Post, "QueryPublicRoomsJob",
- basePath % "/publicRooms",
- Query {
- { "server", toJson(server).toString() }
- }
- ), d(new Private)
+ basePath % "/publicRooms")
+ , d(new Private)
{
+ QUrlQuery _q;
+ if (!server.isEmpty())
+ _q.addQueryItem("server", server);
+ setRequestQuery(_q);
QJsonObject _data;
_data.insert("limit", toJson(limit));
if (!since.isEmpty())
@@ -213,10 +211,7 @@ QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, double limit, co
setRequestData(_data);
}
-QueryPublicRoomsJob::~QueryPublicRoomsJob()
-{
- delete d;
-}
+QueryPublicRoomsJob::~QueryPublicRoomsJob() = default;
const QVector<QueryPublicRoomsJob::PublicRoomsChunk>& QueryPublicRoomsJob::chunk() const
{
diff --git a/jobs/generated/list_public_rooms.h b/jobs/generated/list_public_rooms.h
index 74dd8626..7dcb8cf7 100644
--- a/jobs/generated/list_public_rooms.h
+++ b/jobs/generated/list_public_rooms.h
@@ -2,13 +2,11 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
#include <QtCore/QVector>
-#include <QtCore/QString>
#include "converters.h"
@@ -45,13 +43,13 @@ namespace QMatrixClient
const QString& nextBatch() const;
const QString& prevBatch() const;
double totalRoomCountEstimate() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
class QueryPublicRoomsJob : public BaseJob
@@ -90,12 +88,12 @@ namespace QMatrixClient
const QString& nextBatch() const;
const QString& prevBatch() const;
double totalRoomCountEstimate() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/generated/login.cpp b/jobs/generated/login.cpp
index 4c159517..a4dab428 100644
--- a/jobs/generated/login.cpp
+++ b/jobs/generated/login.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "login.h"
#include "converters.h"
@@ -24,9 +23,8 @@ class LoginJob::Private
LoginJob::LoginJob(const QString& type, const QString& user, const QString& medium, const QString& address, const QString& password, const QString& token, const QString& deviceId, const QString& initialDeviceDisplayName)
: BaseJob(HttpVerb::Post, "LoginJob",
- basePath % "/login",
- Query { }, Data { }, false
- ), d(new Private)
+ basePath % "/login", false)
+ , d(new Private)
{
QJsonObject _data;
_data.insert("type", toJson(type));
@@ -47,10 +45,7 @@ LoginJob::LoginJob(const QString& type, const QString& user, const QString& medi
setRequestData(_data);
}
-LoginJob::~LoginJob()
-{
- delete d;
-}
+LoginJob::~LoginJob() = default;
const QString& LoginJob::userId() const
{
diff --git a/jobs/generated/login.h b/jobs/generated/login.h
index 1c017877..3ac955d4 100644
--- a/jobs/generated/login.h
+++ b/jobs/generated/login.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
@@ -24,12 +22,12 @@ namespace QMatrixClient
const QString& accessToken() const;
const QString& homeServer() const;
const QString& deviceId() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/generated/logout.cpp b/jobs/generated/logout.cpp
index c250bddf..f7f8eff9 100644
--- a/jobs/generated/logout.cpp
+++ b/jobs/generated/logout.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "logout.h"
#include "converters.h"
@@ -15,8 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
LogoutJob::LogoutJob()
: BaseJob(HttpVerb::Post, "LogoutJob",
- basePath % "/logout",
- Query { }
- )
-{ }
+ basePath % "/logout")
+{
+}
diff --git a/jobs/generated/logout.h b/jobs/generated/logout.h
index ae9e54b8..d2b85db5 100644
--- a/jobs/generated/logout.h
+++ b/jobs/generated/logout.h
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
diff --git a/jobs/generated/profile.cpp b/jobs/generated/profile.cpp
index 6ec566f7..9523ca96 100644
--- a/jobs/generated/profile.cpp
+++ b/jobs/generated/profile.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "profile.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displayname)
: BaseJob(HttpVerb::Put, "SetDisplayNameJob",
- basePath % "/profile/" % userId % "/displayname",
- Query { }
- )
+ basePath % "/profile/" % userId % "/displayname")
{
QJsonObject _data;
if (!displayname.isEmpty())
@@ -33,16 +30,13 @@ class GetDisplayNameJob::Private
GetDisplayNameJob::GetDisplayNameJob(const QString& userId)
: BaseJob(HttpVerb::Get, "GetDisplayNameJob",
- basePath % "/profile/" % userId % "/displayname",
- Query { }, Data { }, false
- ), d(new Private)
-{ }
-
-GetDisplayNameJob::~GetDisplayNameJob()
+ basePath % "/profile/" % userId % "/displayname", false)
+ , d(new Private)
{
- delete d;
}
+GetDisplayNameJob::~GetDisplayNameJob() = default;
+
const QString& GetDisplayNameJob::displayname() const
{
return d->displayname;
@@ -57,9 +51,7 @@ BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data)
SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl)
: BaseJob(HttpVerb::Put, "SetAvatarUrlJob",
- basePath % "/profile/" % userId % "/avatar_url",
- Query { }
- )
+ basePath % "/profile/" % userId % "/avatar_url")
{
QJsonObject _data;
if (!avatarUrl.isEmpty())
@@ -75,16 +67,13 @@ class GetAvatarUrlJob::Private
GetAvatarUrlJob::GetAvatarUrlJob(const QString& userId)
: BaseJob(HttpVerb::Get, "GetAvatarUrlJob",
- basePath % "/profile/" % userId % "/avatar_url",
- Query { }, Data { }, false
- ), d(new Private)
-{ }
-
-GetAvatarUrlJob::~GetAvatarUrlJob()
+ basePath % "/profile/" % userId % "/avatar_url", false)
+ , d(new Private)
{
- delete d;
}
+GetAvatarUrlJob::~GetAvatarUrlJob() = default;
+
const QString& GetAvatarUrlJob::avatarUrl() const
{
return d->avatarUrl;
@@ -106,16 +95,13 @@ class GetUserProfileJob::Private
GetUserProfileJob::GetUserProfileJob(const QString& userId)
: BaseJob(HttpVerb::Get, "GetUserProfileJob",
- basePath % "/profile/" % userId,
- Query { }, Data { }, false
- ), d(new Private)
-{ }
-
-GetUserProfileJob::~GetUserProfileJob()
+ basePath % "/profile/" % userId, false)
+ , d(new Private)
{
- delete d;
}
+GetUserProfileJob::~GetUserProfileJob() = default;
+
const QString& GetUserProfileJob::avatarUrl() const
{
return d->avatarUrl;
diff --git a/jobs/generated/profile.h b/jobs/generated/profile.h
index 30e858de..1e09791d 100644
--- a/jobs/generated/profile.h
+++ b/jobs/generated/profile.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
@@ -27,13 +25,13 @@ namespace QMatrixClient
~GetDisplayNameJob() override;
const QString& displayname() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
class SetAvatarUrlJob : public BaseJob
@@ -49,13 +47,13 @@ namespace QMatrixClient
~GetAvatarUrlJob() override;
const QString& avatarUrl() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
class GetUserProfileJob : public BaseJob
@@ -66,12 +64,12 @@ namespace QMatrixClient
const QString& avatarUrl() const;
const QString& displayname() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/generated/receipts.cpp b/jobs/generated/receipts.cpp
index 2820b583..83c38b6f 100644
--- a/jobs/generated/receipts.cpp
+++ b/jobs/generated/receipts.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "receipts.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
PostReceiptJob::PostReceiptJob(const QString& roomId, const QString& receiptType, const QString& eventId, const QJsonObject& receipt)
: BaseJob(HttpVerb::Post, "PostReceiptJob",
- basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" % eventId,
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" % eventId)
{
setRequestData(Data(receipt));
}
diff --git a/jobs/generated/receipts.h b/jobs/generated/receipts.h
index 6f36d7fc..9eb7a489 100644
--- a/jobs/generated/receipts.h
+++ b/jobs/generated/receipts.h
@@ -2,13 +2,11 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
#include <QtCore/QJsonObject>
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/redaction.cpp b/jobs/generated/redaction.cpp
index a9b8ed7e..0da35dfc 100644
--- a/jobs/generated/redaction.cpp
+++ b/jobs/generated/redaction.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "redaction.h"
#include "converters.h"
@@ -21,9 +20,8 @@ class RedactEventJob::Private
RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason)
: BaseJob(HttpVerb::Put, "RedactEventJob",
- basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId,
- Query { }
- ), d(new Private)
+ basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId)
+ , d(new Private)
{
QJsonObject _data;
if (!reason.isEmpty())
@@ -31,10 +29,7 @@ RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, co
setRequestData(_data);
}
-RedactEventJob::~RedactEventJob()
-{
- delete d;
-}
+RedactEventJob::~RedactEventJob() = default;
const QString& RedactEventJob::eventId() const
{
diff --git a/jobs/generated/redaction.h b/jobs/generated/redaction.h
index 600e0daa..e3b3ff4f 100644
--- a/jobs/generated/redaction.h
+++ b/jobs/generated/redaction.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
@@ -21,12 +19,12 @@ namespace QMatrixClient
~RedactEventJob() override;
const QString& eventId() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/generated/third_party_membership.cpp b/jobs/generated/third_party_membership.cpp
index 7a2aa4f4..b637d481 100644
--- a/jobs/generated/third_party_membership.cpp
+++ b/jobs/generated/third_party_membership.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "third_party_membership.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, const QString& medium, const QString& address)
: BaseJob(HttpVerb::Post, "InviteBy3PIDJob",
- basePath % "/rooms/" % roomId % "/invite",
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/invite")
{
QJsonObject _data;
_data.insert("id_server", toJson(idServer));
diff --git a/jobs/generated/third_party_membership.h b/jobs/generated/third_party_membership.h
index 6c1193ed..c7b5214e 100644
--- a/jobs/generated/third_party_membership.h
+++ b/jobs/generated/third_party_membership.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/typing.cpp b/jobs/generated/typing.cpp
index 44bbb131..fa700290 100644
--- a/jobs/generated/typing.cpp
+++ b/jobs/generated/typing.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "typing.h"
#include "converters.h"
@@ -15,9 +14,7 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0");
SetTypingJob::SetTypingJob(const QString& userId, const QString& roomId, bool typing, int timeout)
: BaseJob(HttpVerb::Put, "SetTypingJob",
- basePath % "/rooms/" % roomId % "/typing/" % userId,
- Query { }
- )
+ basePath % "/rooms/" % roomId % "/typing/" % userId)
{
QJsonObject _data;
_data.insert("typing", toJson(typing));
diff --git a/jobs/generated/typing.h b/jobs/generated/typing.h
index e20bca1a..0495ed0a 100644
--- a/jobs/generated/typing.h
+++ b/jobs/generated/typing.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
diff --git a/jobs/generated/versions.cpp b/jobs/generated/versions.cpp
index 66b31290..938c1d34 100644
--- a/jobs/generated/versions.cpp
+++ b/jobs/generated/versions.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "versions.h"
#include "converters.h"
@@ -21,16 +20,13 @@ class GetVersionsJob::Private
GetVersionsJob::GetVersionsJob()
: BaseJob(HttpVerb::Get, "GetVersionsJob",
- basePath % "/versions",
- Query { }, Data { }, false
- ), d(new Private)
-{ }
-
-GetVersionsJob::~GetVersionsJob()
+ basePath % "/versions", false)
+ , d(new Private)
{
- delete d;
}
+GetVersionsJob::~GetVersionsJob() = default;
+
const QVector<QString>& GetVersionsJob::versions() const
{
return d->versions;
diff --git a/jobs/generated/versions.h b/jobs/generated/versions.h
index eab8cf9e..686d7069 100644
--- a/jobs/generated/versions.h
+++ b/jobs/generated/versions.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
#include <QtCore/QVector>
@@ -22,12 +20,12 @@ namespace QMatrixClient
~GetVersionsJob() override;
const QVector<QString>& versions() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/generated/whoami.cpp b/jobs/generated/whoami.cpp
index dce091ec..4f7b052c 100644
--- a/jobs/generated/whoami.cpp
+++ b/jobs/generated/whoami.cpp
@@ -2,7 +2,6 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#include "whoami.h"
#include "converters.h"
@@ -21,16 +20,13 @@ class GetTokenOwnerJob::Private
GetTokenOwnerJob::GetTokenOwnerJob()
: BaseJob(HttpVerb::Get, "GetTokenOwnerJob",
- basePath % "/account/whoami",
- Query { }
- ), d(new Private)
-{ }
-
-GetTokenOwnerJob::~GetTokenOwnerJob()
+ basePath % "/account/whoami")
+ , d(new Private)
{
- delete d;
}
+GetTokenOwnerJob::~GetTokenOwnerJob() = default;
+
const QString& GetTokenOwnerJob::userId() const
{
return d->userId;
diff --git a/jobs/generated/whoami.h b/jobs/generated/whoami.h
index 1b04f337..8e1952da 100644
--- a/jobs/generated/whoami.h
+++ b/jobs/generated/whoami.h
@@ -2,12 +2,10 @@
* THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
*/
-
#pragma once
#include "../basejob.h"
-#include <QtCore/QString>
namespace QMatrixClient
@@ -21,12 +19,12 @@ namespace QMatrixClient
~GetTokenOwnerJob() override;
const QString& userId() const;
-
+
protected:
Status parseJson(const QJsonDocument& data) override;
-
+
private:
class Private;
- Private* d;
+ QScopedPointer<Private> d;
};
} // namespace QMatrixClient
diff --git a/jobs/gtad.yaml b/jobs/gtad.yaml
new file mode 100644
index 00000000..f79ce9b6
--- /dev/null
+++ b/jobs/gtad.yaml
@@ -0,0 +1,84 @@
+preprocess:
+ "%CLIENT_RELEASE_LABEL%": r0
+ "%CLIENT_MAJOR_VERSION%": r0
+ # FIXME: the below only fixes C++ compilation but not actual work - the code
+ # will try to reach out for wrong values in JSON payloads
+ #"signed:": "signedData:"
+ #"unsigned:": "unsignedData:"
+ #"default:": "isDefault:"
+
+# Structure:
+# swaggerType: <targetTypeSpec>
+# OR
+# swaggerType:
+# - swaggerFormat: <targetTypeSpec>
+# - /swaggerFormatRegEx/: <targetTypeSpec>
+# - //: <targetTypeSpec> # default, if the format doesn't mach anything above
+# WHERE
+# targetTypeSpec = targetType OR
+# { type: targetType, imports: <filename OR [ filenames... ]>, <other attributes...> }
+types:
+ integer:
+ - int64: qint64
+ - int32: qint32
+ - //: int
+ number:
+ - float: float
+ - //: double
+ boolean: bool
+ string:
+ - byte: &ByteStream
+ type: QIODevice*
+ #initializer: '"{{defaultValue}}"'
+ #string?: true
+ imports: <QtCore/QIODevice>
+ - binary: *ByteStream
+ - date:
+ type: QDate
+ initializer: QDate::fromString("{{defaultValue}}")
+ avoidCopy?: true
+ imports: <QtCore/QDate>
+ - dateTime:
+ type: QDateTime
+ initializer: QDateTime::fromString("{{defaultValue}}")
+ avoidCopy?: true
+ imports: <QtCore/QDateTime>
+ - //:
+ type: QString
+ initializer: QStringLiteral("{{defaultValue}}")
+ string?: true
+ avoidCopy?: true
+ file: *ByteStream
+ object:
+ type: QJsonObject
+ avoidCopy?: true
+ imports: <QtCore/QJsonObject>
+ array:
+ - /.+/:
+ type: "QVector<{{1}}>"
+ avoidCopy?: true
+ imports: <QtCore/QVector>
+ - //: { type: QJsonArray, "avoidCopy?": true, imports: <QtCore/QJsonArray> }
+ schema:
+ avoidCopy?: true
+
+#operations:
+
+env:
+ _scopeRenderer: "{{scopeCamelCase}}Job::"
+ _literalQuote: '"'
+ maybeCrefType: "{{#avoidCopy?}}const {{/avoidCopy?}}{{dataType.name}}{{#avoidCopy?}}&{{/avoidCopy?}}"
+ qualifiedMaybeCrefType: "{{#avoidCopy?}}const {{/avoidCopy?}}{{dataType.qualifiedName}}{{#avoidCopy?}}&{{/avoidCopy?}}"
+ initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{}{{/defaultValue}}"
+ paramToString: '{{#string?}}{{nameCamelCase}}{{/string?}}{{^string?}}QString("%1").arg({{nameCamelCase}}){{/string?}}'
+# preamble: preamble.mustache
+ copyrightName: Kitsune Ral
+ copyrightEmail: <kitsune-ral@users.sf.net>
+# imports: { set: }
+
+templates:
+- "{{base}}.h.mustache"
+- "{{base}}.cpp.mustache"
+
+#outFilesList: apifiles.txt
+
diff --git a/jobs/mediathumbnailjob.cpp b/jobs/mediathumbnailjob.cpp
index c0d67a63..ec82f57b 100644
--- a/jobs/mediathumbnailjob.cpp
+++ b/jobs/mediathumbnailjob.cpp
@@ -17,23 +17,19 @@
*/
#include "mediathumbnailjob.h"
-#include "util.h"
-
-#include <QtCore/QDebug>
using namespace QMatrixClient;
-MediaThumbnailJob::MediaThumbnailJob(QUrl url, QSize requestedSize,
- ThumbnailType thumbnailType)
- : BaseJob(HttpVerb::Get, "MediaThumbnailJob",
- QStringLiteral("/_matrix/media/v1/thumbnail/%1%2")
- .arg(url.host(), url.path()),
- Query(
- { { "width", QString::number(requestedSize.width()) }
- , { "height", QString::number(requestedSize.height()) }
- , { "method",
- thumbnailType == ThumbnailType::Scale ? "scale" : "crop" }
- }))
+MediaThumbnailJob::MediaThumbnailJob(const QString& serverName,
+ const QString& mediaId, QSize requestedSize)
+ : GetContentThumbnailJob(serverName, mediaId,
+ requestedSize.width(), requestedSize.height())
+{ }
+
+MediaThumbnailJob::MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize)
+ : GetContentThumbnailJob(mxcUri.authority(),
+ mxcUri.path().mid(1), // sans leading '/'
+ requestedSize.width(), requestedSize.height())
{ }
QImage MediaThumbnailJob::thumbnail() const
@@ -47,9 +43,10 @@ QImage MediaThumbnailJob::scaledThumbnail(QSize toSize) const
Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
-BaseJob::Status MediaThumbnailJob::parseReply(QByteArray data)
+BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply)
{
- if( !_thumbnail.loadFromData(data) )
+ GetContentThumbnailJob::parseReply(reply);
+ if( !_thumbnail.loadFromData(content()->readAll()) )
{
qCDebug(JOBS) << "MediaThumbnailJob: could not read image data";
}
diff --git a/jobs/mediathumbnailjob.h b/jobs/mediathumbnailjob.h
index f8f36fe9..ef834cd7 100644
--- a/jobs/mediathumbnailjob.h
+++ b/jobs/mediathumbnailjob.h
@@ -18,25 +18,24 @@
#pragma once
-#include "basejob.h"
+#include "generated/content-repo.h"
#include <QtGui/QPixmap>
namespace QMatrixClient
{
- enum class ThumbnailType {Crop, Scale};
-
- class MediaThumbnailJob: public BaseJob
+ class MediaThumbnailJob: public GetContentThumbnailJob
{
public:
- MediaThumbnailJob(QUrl url, QSize requestedSize,
- ThumbnailType thumbnailType = ThumbnailType::Scale);
+ MediaThumbnailJob(const QString& serverName, const QString& mediaId,
+ QSize requestedSize);
+ MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize);
QImage thumbnail() const;
QImage scaledThumbnail(QSize toSize) const;
protected:
- Status parseReply(QByteArray data) override;
+ Status parseReply(QNetworkReply* reply) override;
private:
QImage _thumbnail;
diff --git a/jobs/preamble.mustache b/jobs/preamble.mustache
new file mode 100644
index 00000000..3ba87d61
--- /dev/null
+++ b/jobs/preamble.mustache
@@ -0,0 +1,3 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
diff --git a/jobs/requestdata.cpp b/jobs/requestdata.cpp
new file mode 100644
index 00000000..f5516c5f
--- /dev/null
+++ b/jobs/requestdata.cpp
@@ -0,0 +1,38 @@
+#include "requestdata.h"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+#include <QtCore/QJsonDocument>
+#include <QtCore/QBuffer>
+
+using namespace QMatrixClient;
+
+std::unique_ptr<QIODevice> fromData(const QByteArray& data)
+{
+ auto source = std::make_unique<QBuffer>();
+ source->open(QIODevice::WriteOnly);
+ source->write(data);
+ source->close();
+ return source;
+}
+
+template <typename JsonDataT>
+inline std::unique_ptr<QIODevice> fromJson(const JsonDataT& jdata)
+{
+ return fromData(QJsonDocument(jdata).toJson(QJsonDocument::Compact));
+}
+
+RequestData::RequestData(const QByteArray& a)
+ : _source(fromData(a))
+{ }
+
+RequestData::RequestData(const QJsonObject& jo)
+ : _source(fromJson(jo))
+{ }
+
+RequestData::RequestData(const QJsonArray& ja)
+ : _source(fromJson(ja))
+{ }
+
+RequestData::~RequestData() = default;
diff --git a/jobs/requestdata.h b/jobs/requestdata.h
new file mode 100644
index 00000000..aa03b744
--- /dev/null
+++ b/jobs/requestdata.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include <memory>
+
+class QByteArray;
+class QJsonObject;
+class QJsonArray;
+class QJsonDocument;
+class QIODevice;
+
+namespace QMatrixClient
+{
+ /**
+ * A simple wrapper that represents the request body.
+ * Provides a unified interface to dump an unstructured byte stream
+ * as well as JSON (and possibly other structures in the future) to
+ * a QByteArray consumed by QNetworkAccessManager request methods.
+ */
+ class RequestData
+ {
+ public:
+ RequestData() = default;
+ 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();
+
+ QIODevice* source() const
+ {
+ return _source.get();
+ }
+
+ private:
+ std::unique_ptr<QIODevice> _source;
+ };
+} // namespace QMatrixClient
diff --git a/jobs/{{base}}.cpp.mustache b/jobs/{{base}}.cpp.mustache
new file mode 100644
index 00000000..34e7faf3
--- /dev/null
+++ b/jobs/{{base}}.cpp.mustache
@@ -0,0 +1,103 @@
+{{#@filePartial}}preamble{{/@filePartial}}
+#include "{{filenameBase}}.h"
+{{^allModels}}
+#include "converters.h"
+{{/allModels}}{{#operations}}
+{{#producesNotJson?}}#include <QtNetwork/QNetworkReply>
+{{/producesNotJson?}}#include <QtCore/QStringBuilder>
+{{/operations}}
+using namespace QMatrixClient;
+{{#models.model}}{{^trivial?}}
+{{qualifiedName}}::operator QJsonValue() const
+{
+ QJsonObject o;
+ {{#vars}}o.insert("{{baseName}}", toJson({{nameCamelCase}}));
+ {{/vars}}
+ return o;
+}
+
+{{qualifiedName}} FromJson<{{qualifiedName}}>::operator()(QJsonValue jv)
+{
+ QJsonObject o = jv.toObject();
+ {{qualifiedName}} result;
+ {{#vars}}result.{{nameCamelCase}} =
+ fromJson<{{dataType.name}}>(o.value("{{baseName}}"));
+ {{/vars}}
+ return result;
+}
+{{/trivial?}}{{/models.model}}{{#operations}}
+static const auto basePath = QStringLiteral("{{basePathWithoutHost}}");
+{{# operation}}{{#models.model}}{{^trivial?}}
+{{qualifiedName}}::operator QJsonObject() const
+{
+ QJsonObject o;
+ {{#vars}}o.insert("{{baseName}}", toJson({{nameCamelCase}}));
+ {{/vars}}
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<{{qualifiedName}}>
+ {
+ {{qualifiedName}} operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ {{qualifiedName}} result;
+ {{#vars}}result.{{nameCamelCase}} =
+ fromJson<{{dataType.qualifiedName}}>(o.value("{{baseName}}"));
+ {{/vars}}
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+{{/ trivial?}}{{/models.model}}{{#responses}}{{#normalResponse?}}{{#allProperties?}}
+class {{camelCaseOperationId}}Job::Private
+{
+ public:{{#allProperties}}
+ {{dataType.name}} {{paramName}};{{/allProperties}}
+};
+{{/ allProperties?}}{{/normalResponse?}}{{/responses}}
+{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job({{#allParams}}{{>maybeCrefType}} {{paramName}}{{#@join}}, {{/@join}}{{/allParams}})
+ : BaseJob(HttpVerb::{{#@cap}}{{#@tolower}}{{httpMethod}}{{/@tolower}}{{/@cap}}, "{{camelCaseOperationId}}Job",
+ basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#skipAuth}}, false{{/skipAuth}}){{#responses}}{{#normalResponse?}}{{#allProperties?}}
+ , d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}}
+{
+{{#headerParams?}}{{#headerParams}} setRequestHeader("{{baseName}}", {{paramName}}.toLatin1());
+{{/headerParams}}
+{{/headerParams?}}{{!
+}}{{#queryParams?}} QUrlQuery _q;{{#queryParams}}
+{{^required?}}{{#string?}} if (!{{nameCamelCase}}.isEmpty())
+ {{/string?}}{{/required?}} _q.addQueryItem("{{baseName}}", {{>paramToString}});{{/queryParams}}
+ setRequestQuery(_q);
+{{/queryParams?}}{{#bodyParams?}}{{!
+}}{{#inlineBody}} setRequestData(Data({{nameCamelCase}}));{{/inlineBody}}{{!
+}}{{^inlineBody}} QJsonObject _data;{{#bodyParams}}
+{{^required?}}{{#string?}} if (!{{paramName}}.isEmpty())
+ {{/string?}}{{/required?}} _data.insert("{{baseName}}", toJson({{paramName}}));{{/bodyParams}}
+ setRequestData(_data);{{/inlineBody}}
+{{/bodyParams?}}{{#producesNotJson?}} setExpectedContentTypes({ {{#produces}}"{{_}}"{{#@join}}, {{/@join}}{{/produces}} });
+{{/producesNotJson?}}}{{!<- mind the actual brace}}
+{{# responses}}{{#normalResponse?}}{{#allProperties?}}
+{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default;
+{{# allProperties}}
+{{>qualifiedMaybeCrefType}} {{camelCaseOperationId}}Job::{{paramName}}() const
+{
+ return d->{{paramName}};
+}
+{{/ allProperties}}{{#producesNotJson?}}
+BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply)
+{
+ {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); {{! We don't check for required headers yet }}
+ {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}}
+ return Success;
+}{{/ producesNotJson?}}{{^producesNotJson?}}
+BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ {{# properties}}{{#required?}}if (!json.contains("{{baseName}}"))
+ return { JsonParseError,
+ "The key '{{baseName}}' not found in the response" };
+ {{/required?}}d->{{paramName}} = fromJson<{{dataType.name}}>(json.value("{{baseName}}"));
+ {{/ properties}}return Success;
+}{{/ producesNotJson?}}
+{{/allProperties?}}{{/normalResponse?}}{{/responses}}{{/operation}}{{/operations}}
diff --git a/jobs/{{base}}.h.mustache b/jobs/{{base}}.h.mustache
new file mode 100644
index 00000000..69e0e6d3
--- /dev/null
+++ b/jobs/{{base}}.h.mustache
@@ -0,0 +1,60 @@
+{{#@filePartial}}preamble{{/@filePartial}}
+#pragma once
+
+{{#operations}}#include "../basejob.h"
+{{/operations}}
+{{#imports}}#include {{_}}
+{{/imports}}
+{{#allModels}}#include "converters.h"
+{{/allModels}}
+namespace QMatrixClient
+{
+{{#models}} // Data structures
+{{# model}}{{#trivial?}}
+ using {{name}} = {{parent.name}};
+{{/ trivial?}}{{^trivial?}}
+ struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{#@join}}, {{/@join}}{{/parents}}{{/parents?}}
+ {
+ {{#vars}}{{dataType.name}} {{nameCamelCase}};
+ {{/vars}}
+ operator QJsonObject() const;
+ };
+
+ template <> struct FromJson<{{name}}>
+ {
+ {{name}} operator()(QJsonValue jv);
+ };
+{{/ trivial?}}{{/model}}
+{{/models}}{{#operations}} // Operations
+{{# operation}}
+ class {{camelCaseOperationId}}Job : public BaseJob
+ {
+ public:{{# models}}
+ // Inner data structures
+{{# model}}{{#trivial?}}
+ using {{name}} = {{parent.name}};
+{{/ trivial?}}{{^trivial?}}
+ struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{#@join}}, {{/@join}}{{/parents}}{{/parents?}}
+ {
+ {{#vars}}{{dataType.name}} {{nameCamelCase}};
+ {{/vars}}
+ operator QJsonObject() const;
+ };
+{{/ trivial?}}{{/model}}
+ // End of inner data structures
+{{/models}}
+ explicit {{camelCaseOperationId}}Job({{#allParams}}{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{#@join}}, {{/@join}}{{/allParams}});{{!skip EOL
+}}{{# responses}}{{#normalResponse?}}{{#allProperties?}}
+ ~{{camelCaseOperationId}}Job() override;
+{{#allProperties}}
+ {{>maybeCrefType}} {{paramName}}() const;{{/allProperties}}
+
+ protected:
+ Status {{#producesNotJson?}}parseReply(QNetworkReply* reply){{/producesNotJson?}}{{^producesNotJson?}}parseJson(const QJsonDocument& data){{/producesNotJson?}} override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;{{/allProperties?}}{{/normalResponse?}}{{/responses}}
+ };
+{{/operation}}{{/operations}}{{!skip EOL
+}}} // namespace QMatrixClient
diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri
index 8ee3634c..9e4cb279 100644
--- a/libqmatrixclient.pri
+++ b/libqmatrixclient.pri
@@ -20,6 +20,7 @@ HEADERS += \
$$PWD/events/typingevent.h \
$$PWD/events/receiptevent.h \
$$PWD/events/redactionevent.h \
+ $$PWD/jobs/requestdata.h \
$$PWD/jobs/basejob.h \
$$PWD/jobs/checkauthmethods.h \
$$PWD/jobs/passwordlogin.h \
@@ -34,7 +35,8 @@ HEADERS += \
$$PWD/logging.h \
$$PWD/settings.h \
$$PWD/networksettings.h \
- $$PWD/networkaccessmanager.h
+ $$PWD/networkaccessmanager.h \
+ $$PWD/jobs/downloadfilejob.h
SOURCES += \
$$PWD/connectiondata.cpp \
@@ -49,6 +51,7 @@ SOURCES += \
$$PWD/events/typingevent.cpp \
$$PWD/events/receiptevent.cpp \
$$PWD/events/redactionevent.cpp \
+ $$PWD/jobs/requestdata.cpp \
$$PWD/jobs/basejob.cpp \
$$PWD/jobs/checkauthmethods.cpp \
$$PWD/jobs/passwordlogin.cpp \
@@ -63,4 +66,5 @@ SOURCES += \
$$PWD/logging.cpp \
$$PWD/settings.cpp \
$$PWD/networksettings.cpp \
- $$PWD/networkaccessmanager.cpp
+ $$PWD/networkaccessmanager.cpp \
+ $$PWD/jobs/downloadfilejob.cpp