aboutsummaryrefslogtreecommitdiff
path: root/lib/jobs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/jobs')
-rw-r--r--lib/jobs/basejob.cpp508
-rw-r--r--lib/jobs/basejob.h303
-rw-r--r--lib/jobs/checkauthmethods.cpp53
-rw-r--r--lib/jobs/checkauthmethods.h40
-rw-r--r--lib/jobs/downloadfilejob.cpp120
-rw-r--r--lib/jobs/downloadfilejob.h30
-rw-r--r--lib/jobs/generated/account-data.cpp28
-rw-r--r--lib/jobs/generated/account-data.h27
-rw-r--r--lib/jobs/generated/administrative_contact.cpp122
-rw-r--r--lib/jobs/generated/administrative_contact.h83
-rw-r--r--lib/jobs/generated/banning.cpp34
-rw-r--r--lib/jobs/generated/banning.h26
-rw-r--r--lib/jobs/generated/content-repo.cpp254
-rw-r--r--lib/jobs/generated/content-repo.h129
-rw-r--r--lib/jobs/generated/create_room.cpp115
-rw-r--r--lib/jobs/generated/create_room.h55
-rw-r--r--lib/jobs/generated/directory.cpp76
-rw-r--r--lib/jobs/generated/directory.h58
-rw-r--r--lib/jobs/generated/inviting.cpp23
-rw-r--r--lib/jobs/generated/inviting.h20
-rw-r--r--lib/jobs/generated/kicking.cpp25
-rw-r--r--lib/jobs/generated/kicking.h20
-rw-r--r--lib/jobs/generated/leaving.cpp38
-rw-r--r--lib/jobs/generated/leaving.h40
-rw-r--r--lib/jobs/generated/list_public_rooms.cpp266
-rw-r--r--lib/jobs/generated/list_public_rooms.h106
-rw-r--r--lib/jobs/generated/login.cpp79
-rw-r--r--lib/jobs/generated/login.h33
-rw-r--r--lib/jobs/generated/logout.cpp26
-rw-r--r--lib/jobs/generated/logout.h27
-rw-r--r--lib/jobs/generated/profile.cpp140
-rw-r--r--lib/jobs/generated/profile.h96
-rw-r--r--lib/jobs/generated/receipts.cpp21
-rw-r--r--lib/jobs/generated/receipts.h21
-rw-r--r--lib/jobs/generated/redaction.cpp45
-rw-r--r--lib/jobs/generated/redaction.h30
-rw-r--r--lib/jobs/generated/third_party_membership.cpp25
-rw-r--r--lib/jobs/generated/third_party_membership.h20
-rw-r--r--lib/jobs/generated/typing.cpp24
-rw-r--r--lib/jobs/generated/typing.h20
-rw-r--r--lib/jobs/generated/versions.cpp47
-rw-r--r--lib/jobs/generated/versions.h38
-rw-r--r--lib/jobs/generated/whoami.cpp50
-rw-r--r--lib/jobs/generated/whoami.h37
-rw-r--r--lib/jobs/joinroomjob.cpp58
-rw-r--r--lib/jobs/joinroomjob.h40
-rw-r--r--lib/jobs/mediathumbnailjob.cpp63
-rw-r--r--lib/jobs/mediathumbnailjob.h47
-rw-r--r--lib/jobs/passwordlogin.cpp74
-rw-r--r--lib/jobs/passwordlogin.h42
-rw-r--r--lib/jobs/postreadmarkersjob.h37
-rw-r--r--lib/jobs/postreceiptjob.cpp27
-rw-r--r--lib/jobs/postreceiptjob.h30
-rw-r--r--lib/jobs/requestdata.cpp38
-rw-r--r--lib/jobs/requestdata.h59
-rw-r--r--lib/jobs/roommessagesjob.cpp65
-rw-r--r--lib/jobs/roommessagesjob.h47
-rw-r--r--lib/jobs/sendeventjob.cpp45
-rw-r--r--lib/jobs/sendeventjob.h57
-rw-r--r--lib/jobs/setroomstatejob.cpp32
-rw-r--r--lib/jobs/setroomstatejob.h64
-rw-r--r--lib/jobs/syncjob.cpp133
-rw-r--r--lib/jobs/syncjob.h99
63 files changed, 4435 insertions, 0 deletions
diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp
new file mode 100644
index 00000000..3cde7c50
--- /dev/null
+++ b/lib/jobs/basejob.cpp
@@ -0,0 +1,508 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "basejob.h"
+
+#include "connectiondata.h"
+
+#include <QtNetwork/QNetworkAccessManager>
+#include <QtNetwork/QNetworkRequest>
+#include <QtNetwork/QNetworkReply>
+#include <QtCore/QTimer>
+#include <QtCore/QRegularExpression>
+//#include <QtCore/QStringBuilder>
+
+#include <array>
+
+using namespace QMatrixClient;
+
+struct NetworkReplyDeleter : public QScopedPointerDeleteLater
+{
+ static inline void cleanup(QNetworkReply* reply)
+ {
+ if (reply && reply->isRunning())
+ reply->abort();
+ QScopedPointerDeleteLater::cleanup(reply);
+ }
+};
+
+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)
+ : verb(v), apiEndpoint(std::move(endpoint))
+ , requestQuery(std::move(q)), requestData(std::move(data))
+ , needsToken(nt)
+ { }
+
+ void sendRequest();
+ const JobTimeoutConfig& getCurrentTimeoutConfig() const;
+
+ const ConnectionData* connection = nullptr;
+
+ // 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;
+
+ QTimer timer;
+ QTimer retryTimer;
+
+ QVector<JobTimeoutConfig> errorStrategy =
+ { { 90, 5 }, { 90, 10 }, { 120, 30 } };
+ int maxRetries = errorStrategy.size();
+ int retriesTaken = 0;
+
+ LoggingCategory logCat = JOBS;
+};
+
+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, 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);
+ connect (&d->retryTimer, &QTimer::timeout, this, &BaseJob::sendRequest);
+}
+
+BaseJob::~BaseJob()
+{
+ stop();
+ qCDebug(d->logCat) << this << "destroyed";
+}
+
+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;
+}
+
+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;
+}
+
+void BaseJob::setRequestQuery(const QUrlQuery& query)
+{
+ d->requestQuery = query;
+}
+
+const BaseJob::Data& BaseJob::requestData() const
+{
+ return d->requestData;
+}
+
+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->expectedContentTypes = contentTypes;
+}
+
+QUrl BaseJob::makeRequestUrl(QUrl baseUrl,
+ const QString& path, const QUrlQuery& query)
+{
+ auto pathBase = baseUrl.path();
+ if (!pathBase.endsWith('/') && !path.startsWith('/'))
+ pathBase.push_back('/');
+
+ baseUrl.setPath( pathBase + path );
+ baseUrl.setQuery(query);
+ return baseUrl;
+}
+
+void BaseJob::Private::sendRequest()
+{
+ QNetworkRequest req
+ { makeRequestUrl(connection->baseUrl(), apiEndpoint, requestQuery) };
+ 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.source()) );
+ break;
+ case HttpVerb::Put:
+ reply.reset( connection->nam()->put(req, requestData.source()) );
+ break;
+ case HttpVerb::Delete:
+ reply.reset( connection->nam()->deleteResource(req) );
+ break;
+ }
+}
+
+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);
+ if (status().good())
+ sendRequest();
+ if (status().good())
+ afterStart(connData, d->reply.data());
+ if (!status().good())
+ QTimer::singleShot(0, this, &BaseJob::finishJob);
+}
+
+void BaseJob::sendRequest()
+{
+ emit aboutToStart();
+ d->retryTimer.stop(); // In case we were counting down at the moment
+ qCDebug(d->logCat) << this << "sending request to" << d->apiEndpoint;
+ if (!d->requestQuery.isEmpty())
+ qCDebug(d->logCat) << " query:" << d->requestQuery.toString();
+ d->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();
+ }
+ else
+ qCWarning(d->logCat) << this << "request could not start";
+}
+
+void BaseJob::gotReply()
+{
+ setStatus(checkReply(d->reply.data()));
+ if (status().good())
+ setStatus(parseReply(d->reply.data()));
+ else
+ {
+ const auto body = d->reply->readAll();
+ if (!body.isEmpty())
+ {
+ qCDebug(d->logCat).noquote() << "Error body:" << body;
+ auto json = QJsonDocument::fromJson(body).object();
+ if (json.isEmpty())
+ setStatus(IncorrectRequestError, body);
+ else {
+ if (error() == TooManyRequestsError ||
+ json.value("errcode").toString() == "M_LIMIT_EXCEEDED")
+ {
+ QString msg = tr("Too many requests");
+ auto retryInterval = json.value("retry_after_ms").toInt(-1);
+ if (retryInterval != -1)
+ msg += tr(", next retry advised after %1 ms")
+ .arg(retryInterval);
+ else // We still have to figure some reasonable interval
+ retryInterval = getNextRetryInterval();
+
+ setStatus(TooManyRequestsError, msg);
+
+ // Shortcut to retry instead of executing finishJob()
+ stop();
+ qCWarning(d->logCat)
+ << this << "will retry in" << retryInterval;
+ d->retryTimer.start(retryInterval);
+ emit retryScheduled(d->retriesTaken, retryInterval);
+ return;
+ }
+ setStatus(IncorrectRequestError, json.value("error").toString());
+ }
+ }
+ }
+
+ finishJob();
+}
+
+bool checkContentType(const QByteArray& type, const QByteArrayList& patterns)
+{
+ if (patterns.isEmpty())
+ return true;
+
+ // ignore possible appendixes of the content type
+ const auto ctype = type.split(';').front();
+
+ for (const auto& pattern: patterns)
+ {
+ if (pattern.startsWith('*') || ctype == 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 (ctype.split('/').front() == patternParts.front() &&
+ patternParts.back() == "*")
+ return true; // Exact match already went on fast lane
+ }
+
+ return false;
+}
+
+BaseJob::Status BaseJob::checkReply(QNetworkReply* reply) const
+{
+ const auto httpCode =
+ reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ qCDebug(d->logCat).nospace().noquote() << this << " returned HTTP code "
+ << httpCode << ": "
+ << (reply->error() == QNetworkReply::NoError ?
+ "Success" : reply->errorString())
+ << " (URL: " << reply->url().toDisplayString() << ")";
+
+ if (httpCode == 429) // Qt doesn't know about it yet
+ return { TooManyRequestsError, tr("Too many requests") };
+
+ // Should we check httpCode instead? Maybe even use it in BaseJob::Status?
+ // That would make codes in logs slightly more readable.
+ switch( reply->error() )
+ {
+ case QNetworkReply::NoError:
+ if (checkContentType(reply->rawHeader("Content-Type"),
+ d->expectedContentTypes))
+ return NoError;
+ else // A warning in the logs might be more proper instead
+ 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(QNetworkReply* reply)
+{
+ QJsonParseError error;
+ QJsonDocument json = QJsonDocument::fromJson(reply->readAll(), &error);
+ if( error.error == QJsonParseError::NoError )
+ return parseJson(json);
+ else
+ return { JsonParseError, error.errorString() };
+}
+
+BaseJob::Status BaseJob::parseJson(const QJsonDocument&)
+{
+ return Success;
+}
+
+void BaseJob::stop()
+{
+ d->timer.stop();
+ if (d->reply)
+ {
+ d->reply->disconnect(this); // Ignore whatever comes from the reply
+ if (d->reply->isRunning())
+ {
+ qCWarning(d->logCat) << this << "stopped without ready network reply";
+ d->reply->abort();
+ }
+ }
+ else
+ qCWarning(d->logCat) << this << "stopped with empty network reply";
+}
+
+void BaseJob::finishJob()
+{
+ stop();
+ if ((error() == NetworkError || error() == TimeoutError)
+ && d->retriesTaken < d->maxRetries)
+ {
+ // TODO: The whole retrying thing should be put to ConnectionManager
+ // otherwise independently retrying jobs make a bit of notification
+ // storm towards the UI.
+ const auto retryInterval =
+ error() == TimeoutError ? 0 : getNextRetryInterval();
+ ++d->retriesTaken;
+ qCWarning(d->logCat) << this << "will retry" << d->retriesTaken
+ << "in" << retryInterval/1000 << "s";
+ d->retryTimer.start(retryInterval);
+ emit retryScheduled(d->retriesTaken, retryInterval);
+ return;
+ }
+
+ // Notify those interested in any completion of the job (including killing)
+ emit finished(this);
+
+ emit result(this);
+ if (error())
+ emit failure(this);
+ else
+ emit success(this);
+
+ deleteLater();
+}
+
+const JobTimeoutConfig& BaseJob::Private::getCurrentTimeoutConfig() const
+{
+ return errorStrategy[std::min(retriesTaken, errorStrategy.size() - 1)];
+}
+
+BaseJob::duration_t BaseJob::getCurrentTimeout() const
+{
+ return d->getCurrentTimeoutConfig().jobTimeout * 1000;
+}
+
+BaseJob::duration_t BaseJob::getNextRetryInterval() const
+{
+ return d->getCurrentTimeoutConfig().nextRetryInterval * 1000;
+}
+
+BaseJob::duration_t BaseJob::millisToRetry() const
+{
+ return d->retryTimer.isActive() ? d->retryTimer.remainingTime() : 0;
+}
+
+int BaseJob::maxRetries() const
+{
+ return d->maxRetries;
+}
+
+void BaseJob::setMaxRetries(int newMaxRetries)
+{
+ d->maxRetries = newMaxRetries;
+}
+
+BaseJob::Status BaseJob::status() const
+{
+ return d->status;
+}
+
+int BaseJob::error() const
+{
+ return d->status.code;
+}
+
+QString BaseJob::errorString() const
+{
+ return d->status.message;
+}
+
+void BaseJob::setStatus(Status s)
+{
+ d->status = s;
+ if (!s.good())
+ qCWarning(d->logCat) << this << "status" << s;
+}
+
+void BaseJob::setStatus(int code, QString message)
+{
+ message.replace(d->connection->accessToken(), "(REDACTED)");
+ setStatus({ code, message });
+}
+
+void BaseJob::abandon()
+{
+ beforeAbandon(d->reply.data());
+ setStatus(Abandoned);
+ this->disconnect();
+ if (d->reply)
+ d->reply->disconnect(this);
+ deleteLater();
+}
+
+void BaseJob::timeout()
+{
+ setStatus( TimeoutError, "The job has timed out" );
+ finishJob();
+}
+
+void BaseJob::setLoggingCategory(LoggingCategory lcf)
+{
+ d->logCat = lcf;
+}
diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h
new file mode 100644
index 00000000..ed630a67
--- /dev/null
+++ b/lib/jobs/basejob.h
@@ -0,0 +1,303 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#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>
+
+class QNetworkReply;
+class QSslError;
+
+namespace QMatrixClient
+{
+ class ConnectionData;
+
+ enum class HttpVerb { Get, Put, Post, Delete };
+
+ struct JobTimeoutConfig
+ {
+ int jobTimeout;
+ int nextRetryInterval;
+ };
+
+ class BaseJob: public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries)
+ public:
+ /* Just in case, the values are compatible with KJob
+ * (which BaseJob used to inherit from). */
+ enum StatusCode { NoError = 0 // To be compatible with Qt conventions
+ , Success = 0
+ , Pending = 1
+ , Abandoned = 50 //< A very brief period between abandoning and object deletion
+ , ErrorLevel = 100 //< Errors have codes starting from this
+ , NetworkError = 100
+ , JsonParseError
+ , TimeoutError
+ , ContentAccessError
+ , NotFoundError
+ , IncorrectRequestError
+ , IncorrectResponseError
+ , TooManyRequestsError
+ , UserDefinedError = 200
+ };
+
+ /**
+ * 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);
+ }
+ };
+
+ using Data = RequestData;
+
+ /**
+ * This structure stores the status of a server call job. The status consists
+ * of a code, that is described (but not delimited) by the respective enum,
+ * and a freeform message.
+ *
+ * To extend the list of error codes, define an (anonymous) enum
+ * along the lines of StatusCode, with additional values
+ * starting at UserDefinedError
+ */
+ class Status
+ {
+ public:
+ Status(StatusCode c) : code(c) { }
+ Status(int c, QString m) : code(c), message(std::move(m)) { }
+
+ bool good() const { return code < ErrorLevel; }
+ friend QDebug operator<<(QDebug dbg, const Status& s)
+ {
+ QDebug(dbg).noquote().nospace()
+ << s.code << ": " << s.message;
+ return dbg;
+ }
+
+ int code;
+ QString message;
+ };
+
+ using duration_t = int; // milliseconds
+
+ public:
+ BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
+ bool needsToken = true);
+ BaseJob(HttpVerb verb, const QString& name, const QString& endpoint,
+ const Query& query, Data&& data = {},
+ bool needsToken = true);
+
+ Status status() const;
+ int error() const;
+ virtual QString errorString() const;
+
+ int maxRetries() const;
+ void setMaxRetries(int newMaxRetries);
+
+ Q_INVOKABLE duration_t getCurrentTimeout() const;
+ Q_INVOKABLE duration_t getNextRetryInterval() const;
+ Q_INVOKABLE duration_t millisToRetry() const;
+
+ friend QDebug operator<<(QDebug dbg, const BaseJob* j)
+ {
+ return dbg << j->objectName();
+ }
+
+ public slots:
+ void start(const ConnectionData* connData);
+
+ /**
+ * Abandons the result of this job, arrived or unarrived.
+ *
+ * This aborts waiting for a reply from the server (if there was
+ * any pending) and deletes the job object. It is always done quietly
+ * (as opposed to KJob::kill() that can trigger emitting the result).
+ */
+ void abandon();
+
+ signals:
+ /** The job is about to send a network request */
+ void aboutToStart();
+
+ /** The job has sent a network request */
+ void started();
+
+ /**
+ * The previous network request has failed; the next attempt will
+ * be done in the specified time
+ * @param nextAttempt the 1-based number of attempt (will always be more than 1)
+ * @param inMilliseconds the interval after which the next attempt will be taken
+ */
+ void retryScheduled(int nextAttempt, int inMilliseconds);
+
+ /**
+ * Emitted when the job is finished, in any case. It is used to notify
+ * observers that the job is terminated and that progress can be hidden.
+ *
+ * This should not be emitted directly by subclasses;
+ * use finishJob() instead.
+ *
+ * In general, to be notified of a job's completion, client code
+ * should connect to success() and failure()
+ * rather than finished(), so that kill() is indeed quiet.
+ * However if you store a list of jobs and they might get killed
+ * silently, then you must connect to this instead of result(),
+ * to avoid dangling pointers in your list.
+ *
+ * @param job the job that emitted this signal
+ *
+ * @see success, failure
+ */
+ void finished(BaseJob* job);
+
+ /**
+ * Emitted when the job is finished (except when killed).
+ *
+ * Use error() to know if the job was finished with error.
+ *
+ * @param job the job that emitted this signal
+ *
+ * @see success, failure
+ */
+ void result(BaseJob* job);
+
+ /**
+ * Emitted together with result() but only if there's no error.
+ */
+ void success(BaseJob*);
+
+ /**
+ * Emitted together with result() if there's an error.
+ * Same as result(), this won't be emitted in case of kill().
+ */
+ 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(Data&& data);
+ const QByteArrayList& expectedContentTypes() const;
+ void addExpectedContentType(const QByteArray& contentType);
+ void setExpectedContentTypes(const QByteArrayList& contentTypes);
+
+ /** Construct a URL out of baseUrl, path and query
+ * The function automatically adds '/' between baseUrl's path and
+ * \p path if necessary. The query component of \p baseUrl
+ * is ignored.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& path,
+ const QUrlQuery& query = {});
+
+ 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
+ * issues such as network errors or access denial.
+ * Returning anything except NoError/Success prevents
+ * further parseReply()/parseJson() invocation.
+ *
+ * @param reply the reply received from the server
+ * @return the result of checking the reply
+ *
+ * @see gotReply
+ */
+ virtual Status checkReply(QNetworkReply* reply) const;
+
+ /**
+ * Processes the reply. By default, parses the reply into
+ * a QJsonDocument and calls parseJson() if it's a valid JSON.
+ *
+ * @param reply raw contents of a HTTP reply from the server (without headers)
+ *
+ * @see gotReply, parseJson
+ */
+ virtual Status parseReply(QNetworkReply* reply);
+
+ /**
+ * Processes the JSON document received from the Matrix server.
+ * By default returns succesful status without analysing the JSON.
+ *
+ * @param json valid JSON document received from the server
+ *
+ * @see parseReply
+ */
+ virtual Status parseJson(const QJsonDocument&);
+
+ void setStatus(Status s);
+ void setStatus(int code, QString message);
+
+ // Q_DECLARE_LOGGING_CATEGORY return different function types
+ // in different versions
+ using LoggingCategory = decltype(JOBS)*;
+ void setLoggingCategory(LoggingCategory lcf);
+
+ // Job objects should only be deleted via QObject::deleteLater
+ ~BaseJob() override;
+
+ protected slots:
+ void timeout();
+
+ private slots:
+ void sendRequest();
+ void gotReply();
+
+ private:
+ void stop();
+ void finishJob();
+
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ inline bool isJobRunning(BaseJob* job)
+ {
+ return job && job->error() == BaseJob::Pending;
+ }
+} // namespace QMatrixClient
diff --git a/lib/jobs/checkauthmethods.cpp b/lib/jobs/checkauthmethods.cpp
new file mode 100644
index 00000000..117def89
--- /dev/null
+++ b/lib/jobs/checkauthmethods.cpp
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "checkauthmethods.h"
+
+#include <QtCore/QJsonDocument>
+#include <QtCore/QJsonObject>
+
+using namespace QMatrixClient;
+
+class CheckAuthMethods::Private
+{
+ public:
+ QString session;
+};
+
+CheckAuthMethods::CheckAuthMethods()
+ : BaseJob(HttpVerb::Get, "CheckAuthMethods",
+ QStringLiteral("_matrix/client/r0/login"), Query(), Data(), false)
+ , d(new Private)
+{
+}
+
+CheckAuthMethods::~CheckAuthMethods()
+{
+ delete d;
+}
+
+QString CheckAuthMethods::session()
+{
+ return d->session;
+}
+
+BaseJob::Status CheckAuthMethods::parseJson(const QJsonDocument& data)
+{
+ // TODO
+ return { BaseJob::StatusCode::UserDefinedError, "Not implemented" };
+}
diff --git a/lib/jobs/checkauthmethods.h b/lib/jobs/checkauthmethods.h
new file mode 100644
index 00000000..647f3db6
--- /dev/null
+++ b/lib/jobs/checkauthmethods.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+namespace QMatrixClient
+{
+ class CheckAuthMethods : public BaseJob
+ {
+ public:
+ CheckAuthMethods();
+ virtual ~CheckAuthMethods();
+
+ QString session();
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ Private* d;
+ };
+}
diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp
new file mode 100644
index 00000000..6a3d8483
--- /dev/null
+++ b/lib/jobs/downloadfilejob.cpp
@@ -0,0 +1,120 @@
+#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;
+};
+
+QUrl DownloadFileJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri)
+{
+ return makeRequestUrl(baseUrl, mxcUri.authority(), mxcUri.path().mid(1));
+}
+
+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->isReadable() &&
+ !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->isReadable() && !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");
+ return;
+ }
+ 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/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h
new file mode 100644
index 00000000..1815a7c8
--- /dev/null
+++ b/lib/jobs/downloadfilejob.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "generated/content-repo.h"
+
+namespace QMatrixClient
+{
+ class DownloadFileJob : public GetContentJob
+ {
+ public:
+ enum { FileError = BaseJob::UserDefinedError + 1 };
+
+ using GetContentJob::makeRequestUrl;
+ static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri);
+
+ 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/lib/jobs/generated/account-data.cpp b/lib/jobs/generated/account-data.cpp
new file mode 100644
index 00000000..35ee94c0
--- /dev/null
+++ b/lib/jobs/generated/account-data.cpp
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "account-data.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content)
+ : BaseJob(HttpVerb::Put, "SetAccountDataJob",
+ basePath % "/user/" % userId % "/account_data/" % type)
+{
+ setRequestData(Data(content));
+}
+
+SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content)
+ : BaseJob(HttpVerb::Put, "SetAccountDataPerRoomJob",
+ basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type)
+{
+ setRequestData(Data(content));
+}
+
diff --git a/lib/jobs/generated/account-data.h b/lib/jobs/generated/account-data.h
new file mode 100644
index 00000000..69ad9fb4
--- /dev/null
+++ b/lib/jobs/generated/account-data.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QJsonObject>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class SetAccountDataJob : public BaseJob
+ {
+ public:
+ explicit SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content = {});
+ };
+
+ class SetAccountDataPerRoomJob : public BaseJob
+ {
+ public:
+ explicit SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content = {});
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/administrative_contact.cpp b/lib/jobs/generated/administrative_contact.cpp
new file mode 100644
index 00000000..1af57941
--- /dev/null
+++ b/lib/jobs/generated/administrative_contact.cpp
@@ -0,0 +1,122 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "administrative_contact.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+GetAccount3PIDsJob::ThirdPartyIdentifier::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("medium", toJson(medium));
+ o.insert("address", toJson(address));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<GetAccount3PIDsJob::ThirdPartyIdentifier>
+ {
+ GetAccount3PIDsJob::ThirdPartyIdentifier operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ GetAccount3PIDsJob::ThirdPartyIdentifier result;
+ result.medium =
+ fromJson<QString>(o.value("medium"));
+ result.address =
+ fromJson<QString>(o.value("address"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+class GetAccount3PIDsJob::Private
+{
+ public:
+ QVector<ThirdPartyIdentifier> threepids;
+};
+
+QUrl GetAccount3PIDsJob::makeRequestUrl(QUrl baseUrl)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/account/3pid");
+}
+
+GetAccount3PIDsJob::GetAccount3PIDsJob()
+ : BaseJob(HttpVerb::Get, "GetAccount3PIDsJob",
+ basePath % "/account/3pid")
+ , d(new Private)
+{
+}
+
+GetAccount3PIDsJob::~GetAccount3PIDsJob() = default;
+
+const QVector<GetAccount3PIDsJob::ThirdPartyIdentifier>& GetAccount3PIDsJob::threepids() const
+{
+ return d->threepids;
+}
+
+BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->threepids = fromJson<QVector<ThirdPartyIdentifier>>(json.value("threepids"));
+ return Success;
+}
+
+Post3PIDsJob::ThreePidCredentials::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("client_secret", toJson(clientSecret));
+ o.insert("id_server", toJson(idServer));
+ o.insert("sid", toJson(sid));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<Post3PIDsJob::ThreePidCredentials>
+ {
+ Post3PIDsJob::ThreePidCredentials operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ Post3PIDsJob::ThreePidCredentials result;
+ result.clientSecret =
+ fromJson<QString>(o.value("client_secret"));
+ result.idServer =
+ fromJson<QString>(o.value("id_server"));
+ result.sid =
+ fromJson<QString>(o.value("sid"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind)
+ : BaseJob(HttpVerb::Post, "Post3PIDsJob",
+ basePath % "/account/3pid")
+{
+ QJsonObject _data;
+ _data.insert("three_pid_creds", toJson(threePidCreds));
+ _data.insert("bind", toJson(bind));
+ setRequestData(_data);
+}
+
+QUrl RequestTokenTo3PIDJob::makeRequestUrl(QUrl baseUrl)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/account/3pid/email/requestToken");
+}
+
+RequestTokenTo3PIDJob::RequestTokenTo3PIDJob()
+ : BaseJob(HttpVerb::Post, "RequestTokenTo3PIDJob",
+ basePath % "/account/3pid/email/requestToken", false)
+{
+}
+
diff --git a/lib/jobs/generated/administrative_contact.h b/lib/jobs/generated/administrative_contact.h
new file mode 100644
index 00000000..c8429d39
--- /dev/null
+++ b/lib/jobs/generated/administrative_contact.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QVector>
+
+#include "converters.h"
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class GetAccount3PIDsJob : public BaseJob
+ {
+ public:
+ // Inner data structures
+
+ struct ThirdPartyIdentifier
+ {
+ QString medium;
+ QString address;
+
+ operator QJsonObject() const;
+ };
+
+ // End of inner data structures
+
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetAccount3PIDsJob. This function can be used when
+ * a URL for GetAccount3PIDsJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl);
+
+ explicit GetAccount3PIDsJob();
+ ~GetAccount3PIDsJob() override;
+
+ const QVector<ThirdPartyIdentifier>& threepids() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class Post3PIDsJob : public BaseJob
+ {
+ public:
+ // Inner data structures
+
+ struct ThreePidCredentials
+ {
+ QString clientSecret;
+ QString idServer;
+ QString sid;
+
+ operator QJsonObject() const;
+ };
+
+ // End of inner data structures
+
+ explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, bool bind = {});
+ };
+
+ class RequestTokenTo3PIDJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * RequestTokenTo3PIDJob. This function can be used when
+ * a URL for RequestTokenTo3PIDJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl);
+
+ explicit RequestTokenTo3PIDJob();
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/banning.cpp b/lib/jobs/generated/banning.cpp
new file mode 100644
index 00000000..f66b27b6
--- /dev/null
+++ b/lib/jobs/generated/banning.cpp
@@ -0,0 +1,34 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "banning.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+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")
+{
+ QJsonObject _data;
+ _data.insert("user_id", toJson(userId));
+ if (!reason.isEmpty())
+ _data.insert("reason", toJson(reason));
+ setRequestData(_data);
+}
+
+UnbanJob::UnbanJob(const QString& roomId, const QString& userId)
+ : BaseJob(HttpVerb::Post, "UnbanJob",
+ basePath % "/rooms/" % roomId % "/unban")
+{
+ QJsonObject _data;
+ _data.insert("user_id", toJson(userId));
+ setRequestData(_data);
+}
+
diff --git a/lib/jobs/generated/banning.h b/lib/jobs/generated/banning.h
new file mode 100644
index 00000000..2d6fbd9b
--- /dev/null
+++ b/lib/jobs/generated/banning.h
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class BanJob : public BaseJob
+ {
+ public:
+ explicit BanJob(const QString& roomId, const QString& userId, const QString& reason = {});
+ };
+
+ class UnbanJob : public BaseJob
+ {
+ public:
+ explicit UnbanJob(const QString& roomId, const QString& userId);
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/content-repo.cpp b/lib/jobs/generated/content-repo.cpp
new file mode 100644
index 00000000..51011251
--- /dev/null
+++ b/lib/jobs/generated/content-repo.cpp
@@ -0,0 +1,254 @@
+/******************************************************************************
+ * 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;
+};
+
+BaseJob::Query queryToUploadContent(const QString& filename)
+{
+ BaseJob::Query _q;
+ if (!filename.isEmpty())
+ _q.addQueryItem("filename", filename);
+ return _q;
+}
+
+UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, const QString& contentType)
+ : BaseJob(HttpVerb::Post, "UploadContentJob",
+ basePath % "/upload",
+ queryToUploadContent(filename))
+ , d(new Private)
+{
+ setRequestHeader("Content-Type", contentType.toLatin1());
+
+ 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;
+};
+
+QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/download/" % serverName % "/" % mediaId);
+}
+
+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;
+};
+
+QUrl GetContentOverrideNameJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, const QString& fileName)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/download/" % serverName % "/" % mediaId % "/" % fileName);
+}
+
+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;
+};
+
+BaseJob::Query queryToGetContentThumbnail(int width, int height, const QString& method)
+{
+ BaseJob::Query _q;
+ _q.addQueryItem("width", QString("%1").arg(width));
+ _q.addQueryItem("height", QString("%1").arg(height));
+ if (!method.isEmpty())
+ _q.addQueryItem("method", method);
+ return _q;
+}
+
+QUrl GetContentThumbnailJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, int width, int height, const QString& method)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/thumbnail/" % serverName % "/" % mediaId,
+ queryToGetContentThumbnail(width, height, method));
+}
+
+GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, int height, const QString& method)
+ : BaseJob(HttpVerb::Get, "GetContentThumbnailJob",
+ basePath % "/thumbnail/" % serverName % "/" % mediaId,
+ queryToGetContentThumbnail(width, height, method),
+ {}, false)
+ , d(new Private)
+{
+ 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;
+};
+
+BaseJob::Query queryToGetUrlPreview(const QString& url, double ts)
+{
+ BaseJob::Query _q;
+ _q.addQueryItem("url", url);
+ _q.addQueryItem("ts", QString("%1").arg(ts));
+ return _q;
+}
+
+QUrl GetUrlPreviewJob::makeRequestUrl(QUrl baseUrl, const QString& url, double ts)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/preview_url",
+ queryToGetUrlPreview(url, ts));
+}
+
+GetUrlPreviewJob::GetUrlPreviewJob(const QString& url, double ts)
+ : BaseJob(HttpVerb::Get, "GetUrlPreviewJob",
+ basePath % "/preview_url",
+ queryToGetUrlPreview(url, ts))
+ , d(new Private)
+{
+}
+
+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/lib/jobs/generated/content-repo.h b/lib/jobs/generated/content-repo.h
new file mode 100644
index 00000000..b4ea562f
--- /dev/null
+++ b/lib/jobs/generated/content-repo.h
@@ -0,0 +1,129 @@
+/******************************************************************************
+ * 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:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetContentJob. This function can be used when
+ * a URL for GetContentJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId);
+
+ 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:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetContentOverrideNameJob. This function can be used when
+ * a URL for GetContentOverrideNameJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, const QString& fileName);
+
+ 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:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetContentThumbnailJob. This function can be used when
+ * a URL for GetContentThumbnailJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, int width = {}, int height = {}, const QString& method = {});
+
+ 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:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetUrlPreviewJob. This function can be used when
+ * a URL for GetUrlPreviewJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& url, double ts = {});
+
+ 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/lib/jobs/generated/create_room.cpp b/lib/jobs/generated/create_room.cpp
new file mode 100644
index 00000000..de7807b5
--- /dev/null
+++ b/lib/jobs/generated/create_room.cpp
@@ -0,0 +1,115 @@
+/******************************************************************************
+ * 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, bool guestCanJoin)
+ : 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));
+ _data.insert("guest_can_join", toJson(guestCanJoin));
+ 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/lib/jobs/generated/create_room.h b/lib/jobs/generated/create_room.h
new file mode 100644
index 00000000..b479615a
--- /dev/null
+++ b/lib/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 = {}, bool guestCanJoin = {});
+ ~CreateRoomJob() override;
+
+ const QString& roomId() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/directory.cpp b/lib/jobs/generated/directory.cpp
new file mode 100644
index 00000000..9428dcee
--- /dev/null
+++ b/lib/jobs/generated/directory.cpp
@@ -0,0 +1,76 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "directory.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0/directory");
+
+SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId)
+ : BaseJob(HttpVerb::Put, "SetRoomAliasJob",
+ basePath % "/room/" % roomAlias)
+{
+ QJsonObject _data;
+ if (!roomId.isEmpty())
+ _data.insert("room_id", toJson(roomId));
+ setRequestData(_data);
+}
+
+class GetRoomIdByAliasJob::Private
+{
+ public:
+ QString roomId;
+ QVector<QString> servers;
+};
+
+QUrl GetRoomIdByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/room/" % roomAlias);
+}
+
+GetRoomIdByAliasJob::GetRoomIdByAliasJob(const QString& roomAlias)
+ : BaseJob(HttpVerb::Get, "GetRoomIdByAliasJob",
+ basePath % "/room/" % roomAlias, false)
+ , d(new Private)
+{
+}
+
+GetRoomIdByAliasJob::~GetRoomIdByAliasJob() = default;
+
+const QString& GetRoomIdByAliasJob::roomId() const
+{
+ return d->roomId;
+}
+
+const QVector<QString>& GetRoomIdByAliasJob::servers() const
+{
+ return d->servers;
+}
+
+BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->roomId = fromJson<QString>(json.value("room_id"));
+ d->servers = fromJson<QVector<QString>>(json.value("servers"));
+ return Success;
+}
+
+QUrl DeleteRoomAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/room/" % roomAlias);
+}
+
+DeleteRoomAliasJob::DeleteRoomAliasJob(const QString& roomAlias)
+ : BaseJob(HttpVerb::Delete, "DeleteRoomAliasJob",
+ basePath % "/room/" % roomAlias)
+{
+}
+
diff --git a/lib/jobs/generated/directory.h b/lib/jobs/generated/directory.h
new file mode 100644
index 00000000..87591240
--- /dev/null
+++ b/lib/jobs/generated/directory.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QVector>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class SetRoomAliasJob : public BaseJob
+ {
+ public:
+ explicit SetRoomAliasJob(const QString& roomAlias, const QString& roomId = {});
+ };
+
+ class GetRoomIdByAliasJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetRoomIdByAliasJob. This function can be used when
+ * a URL for GetRoomIdByAliasJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomAlias);
+
+ explicit GetRoomIdByAliasJob(const QString& roomAlias);
+ ~GetRoomIdByAliasJob() override;
+
+ const QString& roomId() const;
+ const QVector<QString>& servers() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class DeleteRoomAliasJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * DeleteRoomAliasJob. This function can be used when
+ * a URL for DeleteRoomAliasJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomAlias);
+
+ explicit DeleteRoomAliasJob(const QString& roomAlias);
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/inviting.cpp b/lib/jobs/generated/inviting.cpp
new file mode 100644
index 00000000..d2ee2107
--- /dev/null
+++ b/lib/jobs/generated/inviting.cpp
@@ -0,0 +1,23 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "inviting.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+InviteUserJob::InviteUserJob(const QString& roomId, const QString& userId)
+ : BaseJob(HttpVerb::Post, "InviteUserJob",
+ basePath % "/rooms/" % roomId % "/invite")
+{
+ QJsonObject _data;
+ _data.insert("user_id", toJson(userId));
+ setRequestData(_data);
+}
+
diff --git a/lib/jobs/generated/inviting.h b/lib/jobs/generated/inviting.h
new file mode 100644
index 00000000..eaa884df
--- /dev/null
+++ b/lib/jobs/generated/inviting.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class InviteUserJob : public BaseJob
+ {
+ public:
+ explicit InviteUserJob(const QString& roomId, const QString& userId);
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/kicking.cpp b/lib/jobs/generated/kicking.cpp
new file mode 100644
index 00000000..bf2490b7
--- /dev/null
+++ b/lib/jobs/generated/kicking.cpp
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "kicking.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+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")
+{
+ QJsonObject _data;
+ _data.insert("user_id", toJson(userId));
+ if (!reason.isEmpty())
+ _data.insert("reason", toJson(reason));
+ setRequestData(_data);
+}
+
diff --git a/lib/jobs/generated/kicking.h b/lib/jobs/generated/kicking.h
new file mode 100644
index 00000000..3814bef7
--- /dev/null
+++ b/lib/jobs/generated/kicking.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class KickJob : public BaseJob
+ {
+ public:
+ explicit KickJob(const QString& roomId, const QString& userId, const QString& reason = {});
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/leaving.cpp b/lib/jobs/generated/leaving.cpp
new file mode 100644
index 00000000..fbc40d11
--- /dev/null
+++ b/lib/jobs/generated/leaving.cpp
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "leaving.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+QUrl LeaveRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/rooms/" % roomId % "/leave");
+}
+
+LeaveRoomJob::LeaveRoomJob(const QString& roomId)
+ : BaseJob(HttpVerb::Post, "LeaveRoomJob",
+ basePath % "/rooms/" % roomId % "/leave")
+{
+}
+
+QUrl ForgetRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/rooms/" % roomId % "/forget");
+}
+
+ForgetRoomJob::ForgetRoomJob(const QString& roomId)
+ : BaseJob(HttpVerb::Post, "ForgetRoomJob",
+ basePath % "/rooms/" % roomId % "/forget")
+{
+}
+
diff --git a/lib/jobs/generated/leaving.h b/lib/jobs/generated/leaving.h
new file mode 100644
index 00000000..9bae2363
--- /dev/null
+++ b/lib/jobs/generated/leaving.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class LeaveRoomJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * LeaveRoomJob. This function can be used when
+ * a URL for LeaveRoomJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId);
+
+ explicit LeaveRoomJob(const QString& roomId);
+ };
+
+ class ForgetRoomJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * ForgetRoomJob. This function can be used when
+ * a URL for ForgetRoomJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId);
+
+ explicit ForgetRoomJob(const QString& roomId);
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/list_public_rooms.cpp b/lib/jobs/generated/list_public_rooms.cpp
new file mode 100644
index 00000000..39653300
--- /dev/null
+++ b/lib/jobs/generated/list_public_rooms.cpp
@@ -0,0 +1,266 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "list_public_rooms.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+GetPublicRoomsJob::PublicRoomsChunk::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("aliases", toJson(aliases));
+ o.insert("canonical_alias", toJson(canonicalAlias));
+ o.insert("name", toJson(name));
+ o.insert("num_joined_members", toJson(numJoinedMembers));
+ o.insert("room_id", toJson(roomId));
+ o.insert("topic", toJson(topic));
+ o.insert("world_readable", toJson(worldReadable));
+ o.insert("guest_can_join", toJson(guestCanJoin));
+ o.insert("avatar_url", toJson(avatarUrl));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<GetPublicRoomsJob::PublicRoomsChunk>
+ {
+ GetPublicRoomsJob::PublicRoomsChunk operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ GetPublicRoomsJob::PublicRoomsChunk result;
+ result.aliases =
+ fromJson<QVector<QString>>(o.value("aliases"));
+ result.canonicalAlias =
+ fromJson<QString>(o.value("canonical_alias"));
+ result.name =
+ fromJson<QString>(o.value("name"));
+ result.numJoinedMembers =
+ fromJson<double>(o.value("num_joined_members"));
+ result.roomId =
+ fromJson<QString>(o.value("room_id"));
+ result.topic =
+ fromJson<QString>(o.value("topic"));
+ result.worldReadable =
+ fromJson<bool>(o.value("world_readable"));
+ result.guestCanJoin =
+ fromJson<bool>(o.value("guest_can_join"));
+ result.avatarUrl =
+ fromJson<QString>(o.value("avatar_url"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+class GetPublicRoomsJob::Private
+{
+ public:
+ QVector<PublicRoomsChunk> chunk;
+ QString nextBatch;
+ QString prevBatch;
+ double totalRoomCountEstimate;
+};
+
+BaseJob::Query queryToGetPublicRooms(double limit, const QString& since, const QString& server)
+{
+ BaseJob::Query _q;
+ _q.addQueryItem("limit", QString("%1").arg(limit));
+ if (!since.isEmpty())
+ _q.addQueryItem("since", since);
+ if (!server.isEmpty())
+ _q.addQueryItem("server", server);
+ return _q;
+}
+
+QUrl GetPublicRoomsJob::makeRequestUrl(QUrl baseUrl, double limit, const QString& since, const QString& server)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/publicRooms",
+ queryToGetPublicRooms(limit, since, server));
+}
+
+GetPublicRoomsJob::GetPublicRoomsJob(double limit, const QString& since, const QString& server)
+ : BaseJob(HttpVerb::Get, "GetPublicRoomsJob",
+ basePath % "/publicRooms",
+ queryToGetPublicRooms(limit, since, server),
+ {}, false)
+ , d(new Private)
+{
+}
+
+GetPublicRoomsJob::~GetPublicRoomsJob() = default;
+
+const QVector<GetPublicRoomsJob::PublicRoomsChunk>& GetPublicRoomsJob::chunk() const
+{
+ return d->chunk;
+}
+
+const QString& GetPublicRoomsJob::nextBatch() const
+{
+ return d->nextBatch;
+}
+
+const QString& GetPublicRoomsJob::prevBatch() const
+{
+ return d->prevBatch;
+}
+
+double GetPublicRoomsJob::totalRoomCountEstimate() const
+{
+ return d->totalRoomCountEstimate;
+}
+
+BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ if (!json.contains("chunk"))
+ return { JsonParseError,
+ "The key 'chunk' not found in the response" };
+ d->chunk = fromJson<QVector<PublicRoomsChunk>>(json.value("chunk"));
+ d->nextBatch = fromJson<QString>(json.value("next_batch"));
+ d->prevBatch = fromJson<QString>(json.value("prev_batch"));
+ d->totalRoomCountEstimate = fromJson<double>(json.value("total_room_count_estimate"));
+ return Success;
+}
+
+QueryPublicRoomsJob::Filter::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("generic_search_term", toJson(genericSearchTerm));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<QueryPublicRoomsJob::Filter>
+ {
+ QueryPublicRoomsJob::Filter operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ QueryPublicRoomsJob::Filter result;
+ result.genericSearchTerm =
+ fromJson<QString>(o.value("generic_search_term"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+QueryPublicRoomsJob::PublicRoomsChunk::operator QJsonObject() const
+{
+ QJsonObject o;
+ o.insert("aliases", toJson(aliases));
+ o.insert("canonical_alias", toJson(canonicalAlias));
+ o.insert("name", toJson(name));
+ o.insert("num_joined_members", toJson(numJoinedMembers));
+ o.insert("room_id", toJson(roomId));
+ o.insert("topic", toJson(topic));
+ o.insert("world_readable", toJson(worldReadable));
+ o.insert("guest_can_join", toJson(guestCanJoin));
+ o.insert("avatar_url", toJson(avatarUrl));
+
+ return o;
+}
+namespace QMatrixClient
+{
+ template <> struct FromJson<QueryPublicRoomsJob::PublicRoomsChunk>
+ {
+ QueryPublicRoomsJob::PublicRoomsChunk operator()(QJsonValue jv)
+ {
+ QJsonObject o = jv.toObject();
+ QueryPublicRoomsJob::PublicRoomsChunk result;
+ result.aliases =
+ fromJson<QVector<QString>>(o.value("aliases"));
+ result.canonicalAlias =
+ fromJson<QString>(o.value("canonical_alias"));
+ result.name =
+ fromJson<QString>(o.value("name"));
+ result.numJoinedMembers =
+ fromJson<double>(o.value("num_joined_members"));
+ result.roomId =
+ fromJson<QString>(o.value("room_id"));
+ result.topic =
+ fromJson<QString>(o.value("topic"));
+ result.worldReadable =
+ fromJson<bool>(o.value("world_readable"));
+ result.guestCanJoin =
+ fromJson<bool>(o.value("guest_can_join"));
+ result.avatarUrl =
+ fromJson<QString>(o.value("avatar_url"));
+
+ return result;
+ }
+ };
+} // namespace QMatrixClient
+
+class QueryPublicRoomsJob::Private
+{
+ public:
+ QVector<PublicRoomsChunk> chunk;
+ QString nextBatch;
+ QString prevBatch;
+ double totalRoomCountEstimate;
+};
+
+BaseJob::Query queryToQueryPublicRooms(const QString& server)
+{
+ BaseJob::Query _q;
+ if (!server.isEmpty())
+ _q.addQueryItem("server", server);
+ return _q;
+}
+
+QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, double limit, const QString& since, const Filter& filter)
+ : BaseJob(HttpVerb::Post, "QueryPublicRoomsJob",
+ basePath % "/publicRooms",
+ queryToQueryPublicRooms(server))
+ , d(new Private)
+{
+ QJsonObject _data;
+ _data.insert("limit", toJson(limit));
+ if (!since.isEmpty())
+ _data.insert("since", toJson(since));
+ _data.insert("filter", toJson(filter));
+ setRequestData(_data);
+}
+
+QueryPublicRoomsJob::~QueryPublicRoomsJob() = default;
+
+const QVector<QueryPublicRoomsJob::PublicRoomsChunk>& QueryPublicRoomsJob::chunk() const
+{
+ return d->chunk;
+}
+
+const QString& QueryPublicRoomsJob::nextBatch() const
+{
+ return d->nextBatch;
+}
+
+const QString& QueryPublicRoomsJob::prevBatch() const
+{
+ return d->prevBatch;
+}
+
+double QueryPublicRoomsJob::totalRoomCountEstimate() const
+{
+ return d->totalRoomCountEstimate;
+}
+
+BaseJob::Status QueryPublicRoomsJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ if (!json.contains("chunk"))
+ return { JsonParseError,
+ "The key 'chunk' not found in the response" };
+ d->chunk = fromJson<QVector<PublicRoomsChunk>>(json.value("chunk"));
+ d->nextBatch = fromJson<QString>(json.value("next_batch"));
+ d->prevBatch = fromJson<QString>(json.value("prev_batch"));
+ d->totalRoomCountEstimate = fromJson<double>(json.value("total_room_count_estimate"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/list_public_rooms.h b/lib/jobs/generated/list_public_rooms.h
new file mode 100644
index 00000000..5c281de3
--- /dev/null
+++ b/lib/jobs/generated/list_public_rooms.h
@@ -0,0 +1,106 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QVector>
+
+#include "converters.h"
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class GetPublicRoomsJob : public BaseJob
+ {
+ public:
+ // Inner data structures
+
+ struct PublicRoomsChunk
+ {
+ QVector<QString> aliases;
+ QString canonicalAlias;
+ QString name;
+ double numJoinedMembers;
+ QString roomId;
+ QString topic;
+ bool worldReadable;
+ bool guestCanJoin;
+ QString avatarUrl;
+
+ operator QJsonObject() const;
+ };
+
+ // End of inner data structures
+
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetPublicRoomsJob. This function can be used when
+ * a URL for GetPublicRoomsJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, double limit = {}, const QString& since = {}, const QString& server = {});
+
+ explicit GetPublicRoomsJob(double limit = {}, const QString& since = {}, const QString& server = {});
+ ~GetPublicRoomsJob() override;
+
+ const QVector<PublicRoomsChunk>& chunk() const;
+ const QString& nextBatch() const;
+ const QString& prevBatch() const;
+ double totalRoomCountEstimate() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class QueryPublicRoomsJob : public BaseJob
+ {
+ public:
+ // Inner data structures
+
+ struct Filter
+ {
+ QString genericSearchTerm;
+
+ operator QJsonObject() const;
+ };
+
+ struct PublicRoomsChunk
+ {
+ QVector<QString> aliases;
+ QString canonicalAlias;
+ QString name;
+ double numJoinedMembers;
+ QString roomId;
+ QString topic;
+ bool worldReadable;
+ bool guestCanJoin;
+ QString avatarUrl;
+
+ operator QJsonObject() const;
+ };
+
+ // End of inner data structures
+
+ explicit QueryPublicRoomsJob(const QString& server = {}, double limit = {}, const QString& since = {}, const Filter& filter = {});
+ ~QueryPublicRoomsJob() override;
+
+ const QVector<PublicRoomsChunk>& chunk() const;
+ const QString& nextBatch() const;
+ const QString& prevBatch() const;
+ double totalRoomCountEstimate() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/login.cpp b/lib/jobs/generated/login.cpp
new file mode 100644
index 00000000..a4dab428
--- /dev/null
+++ b/lib/jobs/generated/login.cpp
@@ -0,0 +1,79 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "login.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+class LoginJob::Private
+{
+ public:
+ QString userId;
+ QString accessToken;
+ QString homeServer;
+ QString deviceId;
+};
+
+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", false)
+ , d(new Private)
+{
+ QJsonObject _data;
+ _data.insert("type", toJson(type));
+ if (!user.isEmpty())
+ _data.insert("user", toJson(user));
+ if (!medium.isEmpty())
+ _data.insert("medium", toJson(medium));
+ if (!address.isEmpty())
+ _data.insert("address", toJson(address));
+ if (!password.isEmpty())
+ _data.insert("password", toJson(password));
+ if (!token.isEmpty())
+ _data.insert("token", toJson(token));
+ if (!deviceId.isEmpty())
+ _data.insert("device_id", toJson(deviceId));
+ if (!initialDeviceDisplayName.isEmpty())
+ _data.insert("initial_device_display_name", toJson(initialDeviceDisplayName));
+ setRequestData(_data);
+}
+
+LoginJob::~LoginJob() = default;
+
+const QString& LoginJob::userId() const
+{
+ return d->userId;
+}
+
+const QString& LoginJob::accessToken() const
+{
+ return d->accessToken;
+}
+
+const QString& LoginJob::homeServer() const
+{
+ return d->homeServer;
+}
+
+const QString& LoginJob::deviceId() const
+{
+ return d->deviceId;
+}
+
+BaseJob::Status LoginJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->userId = fromJson<QString>(json.value("user_id"));
+ d->accessToken = fromJson<QString>(json.value("access_token"));
+ d->homeServer = fromJson<QString>(json.value("home_server"));
+ d->deviceId = fromJson<QString>(json.value("device_id"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/login.h b/lib/jobs/generated/login.h
new file mode 100644
index 00000000..3ac955d4
--- /dev/null
+++ b/lib/jobs/generated/login.h
@@ -0,0 +1,33 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class LoginJob : public BaseJob
+ {
+ public:
+ explicit 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 = {});
+ ~LoginJob() override;
+
+ const QString& userId() const;
+ const QString& accessToken() const;
+ const QString& homeServer() const;
+ const QString& deviceId() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/logout.cpp b/lib/jobs/generated/logout.cpp
new file mode 100644
index 00000000..83139842
--- /dev/null
+++ b/lib/jobs/generated/logout.cpp
@@ -0,0 +1,26 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "logout.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+QUrl LogoutJob::makeRequestUrl(QUrl baseUrl)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/logout");
+}
+
+LogoutJob::LogoutJob()
+ : BaseJob(HttpVerb::Post, "LogoutJob",
+ basePath % "/logout")
+{
+}
+
diff --git a/lib/jobs/generated/logout.h b/lib/jobs/generated/logout.h
new file mode 100644
index 00000000..7640ba55
--- /dev/null
+++ b/lib/jobs/generated/logout.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class LogoutJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * LogoutJob. This function can be used when
+ * a URL for LogoutJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl);
+
+ explicit LogoutJob();
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/profile.cpp b/lib/jobs/generated/profile.cpp
new file mode 100644
index 00000000..1f7092d7
--- /dev/null
+++ b/lib/jobs/generated/profile.cpp
@@ -0,0 +1,140 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "profile.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displayname)
+ : BaseJob(HttpVerb::Put, "SetDisplayNameJob",
+ basePath % "/profile/" % userId % "/displayname")
+{
+ QJsonObject _data;
+ if (!displayname.isEmpty())
+ _data.insert("displayname", toJson(displayname));
+ setRequestData(_data);
+}
+
+class GetDisplayNameJob::Private
+{
+ public:
+ QString displayname;
+};
+
+QUrl GetDisplayNameJob::makeRequestUrl(QUrl baseUrl, const QString& userId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/profile/" % userId % "/displayname");
+}
+
+GetDisplayNameJob::GetDisplayNameJob(const QString& userId)
+ : BaseJob(HttpVerb::Get, "GetDisplayNameJob",
+ basePath % "/profile/" % userId % "/displayname", false)
+ , d(new Private)
+{
+}
+
+GetDisplayNameJob::~GetDisplayNameJob() = default;
+
+const QString& GetDisplayNameJob::displayname() const
+{
+ return d->displayname;
+}
+
+BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->displayname = fromJson<QString>(json.value("displayname"));
+ return Success;
+}
+
+SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl)
+ : BaseJob(HttpVerb::Put, "SetAvatarUrlJob",
+ basePath % "/profile/" % userId % "/avatar_url")
+{
+ QJsonObject _data;
+ if (!avatarUrl.isEmpty())
+ _data.insert("avatar_url", toJson(avatarUrl));
+ setRequestData(_data);
+}
+
+class GetAvatarUrlJob::Private
+{
+ public:
+ QString avatarUrl;
+};
+
+QUrl GetAvatarUrlJob::makeRequestUrl(QUrl baseUrl, const QString& userId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/profile/" % userId % "/avatar_url");
+}
+
+GetAvatarUrlJob::GetAvatarUrlJob(const QString& userId)
+ : BaseJob(HttpVerb::Get, "GetAvatarUrlJob",
+ basePath % "/profile/" % userId % "/avatar_url", false)
+ , d(new Private)
+{
+}
+
+GetAvatarUrlJob::~GetAvatarUrlJob() = default;
+
+const QString& GetAvatarUrlJob::avatarUrl() const
+{
+ return d->avatarUrl;
+}
+
+BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->avatarUrl = fromJson<QString>(json.value("avatar_url"));
+ return Success;
+}
+
+class GetUserProfileJob::Private
+{
+ public:
+ QString avatarUrl;
+ QString displayname;
+};
+
+QUrl GetUserProfileJob::makeRequestUrl(QUrl baseUrl, const QString& userId)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/profile/" % userId);
+}
+
+GetUserProfileJob::GetUserProfileJob(const QString& userId)
+ : BaseJob(HttpVerb::Get, "GetUserProfileJob",
+ basePath % "/profile/" % userId, false)
+ , d(new Private)
+{
+}
+
+GetUserProfileJob::~GetUserProfileJob() = default;
+
+const QString& GetUserProfileJob::avatarUrl() const
+{
+ return d->avatarUrl;
+}
+
+const QString& GetUserProfileJob::displayname() const
+{
+ return d->displayname;
+}
+
+BaseJob::Status GetUserProfileJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->avatarUrl = fromJson<QString>(json.value("avatar_url"));
+ d->displayname = fromJson<QString>(json.value("displayname"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/profile.h b/lib/jobs/generated/profile.h
new file mode 100644
index 00000000..024130f5
--- /dev/null
+++ b/lib/jobs/generated/profile.h
@@ -0,0 +1,96 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class SetDisplayNameJob : public BaseJob
+ {
+ public:
+ explicit SetDisplayNameJob(const QString& userId, const QString& displayname = {});
+ };
+
+ class GetDisplayNameJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetDisplayNameJob. This function can be used when
+ * a URL for GetDisplayNameJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId);
+
+ explicit GetDisplayNameJob(const QString& userId);
+ ~GetDisplayNameJob() override;
+
+ const QString& displayname() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class SetAvatarUrlJob : public BaseJob
+ {
+ public:
+ explicit SetAvatarUrlJob(const QString& userId, const QString& avatarUrl = {});
+ };
+
+ class GetAvatarUrlJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetAvatarUrlJob. This function can be used when
+ * a URL for GetAvatarUrlJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId);
+
+ explicit GetAvatarUrlJob(const QString& userId);
+ ~GetAvatarUrlJob() override;
+
+ const QString& avatarUrl() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+
+ class GetUserProfileJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetUserProfileJob. This function can be used when
+ * a URL for GetUserProfileJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId);
+
+ explicit GetUserProfileJob(const QString& userId);
+ ~GetUserProfileJob() override;
+
+ const QString& avatarUrl() const;
+ const QString& displayname() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/receipts.cpp b/lib/jobs/generated/receipts.cpp
new file mode 100644
index 00000000..83c38b6f
--- /dev/null
+++ b/lib/jobs/generated/receipts.cpp
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "receipts.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+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)
+{
+ setRequestData(Data(receipt));
+}
+
diff --git a/lib/jobs/generated/receipts.h b/lib/jobs/generated/receipts.h
new file mode 100644
index 00000000..9eb7a489
--- /dev/null
+++ b/lib/jobs/generated/receipts.h
@@ -0,0 +1,21 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QJsonObject>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class PostReceiptJob : public BaseJob
+ {
+ public:
+ explicit PostReceiptJob(const QString& roomId, const QString& receiptType, const QString& eventId, const QJsonObject& receipt = {});
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/redaction.cpp b/lib/jobs/generated/redaction.cpp
new file mode 100644
index 00000000..0da35dfc
--- /dev/null
+++ b/lib/jobs/generated/redaction.cpp
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "redaction.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+class RedactEventJob::Private
+{
+ public:
+ QString eventId;
+};
+
+RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason)
+ : BaseJob(HttpVerb::Put, "RedactEventJob",
+ basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId)
+ , d(new Private)
+{
+ QJsonObject _data;
+ if (!reason.isEmpty())
+ _data.insert("reason", toJson(reason));
+ setRequestData(_data);
+}
+
+RedactEventJob::~RedactEventJob() = default;
+
+const QString& RedactEventJob::eventId() const
+{
+ return d->eventId;
+}
+
+BaseJob::Status RedactEventJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->eventId = fromJson<QString>(json.value("event_id"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/redaction.h b/lib/jobs/generated/redaction.h
new file mode 100644
index 00000000..e3b3ff4f
--- /dev/null
+++ b/lib/jobs/generated/redaction.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class RedactEventJob : public BaseJob
+ {
+ public:
+ explicit RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason = {});
+ ~RedactEventJob() override;
+
+ const QString& eventId() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/third_party_membership.cpp b/lib/jobs/generated/third_party_membership.cpp
new file mode 100644
index 00000000..b637d481
--- /dev/null
+++ b/lib/jobs/generated/third_party_membership.cpp
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "third_party_membership.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+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")
+{
+ QJsonObject _data;
+ _data.insert("id_server", toJson(idServer));
+ _data.insert("medium", toJson(medium));
+ _data.insert("address", toJson(address));
+ setRequestData(_data);
+}
+
diff --git a/lib/jobs/generated/third_party_membership.h b/lib/jobs/generated/third_party_membership.h
new file mode 100644
index 00000000..c7b5214e
--- /dev/null
+++ b/lib/jobs/generated/third_party_membership.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class InviteBy3PIDJob : public BaseJob
+ {
+ public:
+ explicit InviteBy3PIDJob(const QString& roomId, const QString& idServer, const QString& medium, const QString& address);
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/typing.cpp b/lib/jobs/generated/typing.cpp
new file mode 100644
index 00000000..fa700290
--- /dev/null
+++ b/lib/jobs/generated/typing.cpp
@@ -0,0 +1,24 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "typing.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+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)
+{
+ QJsonObject _data;
+ _data.insert("typing", toJson(typing));
+ _data.insert("timeout", toJson(timeout));
+ setRequestData(_data);
+}
+
diff --git a/lib/jobs/generated/typing.h b/lib/jobs/generated/typing.h
new file mode 100644
index 00000000..0495ed0a
--- /dev/null
+++ b/lib/jobs/generated/typing.h
@@ -0,0 +1,20 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class SetTypingJob : public BaseJob
+ {
+ public:
+ explicit SetTypingJob(const QString& userId, const QString& roomId, bool typing, int timeout = {});
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/versions.cpp b/lib/jobs/generated/versions.cpp
new file mode 100644
index 00000000..b12594ca
--- /dev/null
+++ b/lib/jobs/generated/versions.cpp
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "versions.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client");
+
+class GetVersionsJob::Private
+{
+ public:
+ QVector<QString> versions;
+};
+
+QUrl GetVersionsJob::makeRequestUrl(QUrl baseUrl)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/versions");
+}
+
+GetVersionsJob::GetVersionsJob()
+ : BaseJob(HttpVerb::Get, "GetVersionsJob",
+ basePath % "/versions", false)
+ , d(new Private)
+{
+}
+
+GetVersionsJob::~GetVersionsJob() = default;
+
+const QVector<QString>& GetVersionsJob::versions() const
+{
+ return d->versions;
+}
+
+BaseJob::Status GetVersionsJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ d->versions = fromJson<QVector<QString>>(json.value("versions"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/versions.h b/lib/jobs/generated/versions.h
new file mode 100644
index 00000000..18f6bb44
--- /dev/null
+++ b/lib/jobs/generated/versions.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+#include <QtCore/QVector>
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class GetVersionsJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetVersionsJob. This function can be used when
+ * a URL for GetVersionsJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl);
+
+ explicit GetVersionsJob();
+ ~GetVersionsJob() override;
+
+ const QVector<QString>& versions() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/generated/whoami.cpp b/lib/jobs/generated/whoami.cpp
new file mode 100644
index 00000000..cc38fa4d
--- /dev/null
+++ b/lib/jobs/generated/whoami.cpp
@@ -0,0 +1,50 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#include "whoami.h"
+
+#include "converters.h"
+
+#include <QtCore/QStringBuilder>
+
+using namespace QMatrixClient;
+
+static const auto basePath = QStringLiteral("/_matrix/client/r0");
+
+class GetTokenOwnerJob::Private
+{
+ public:
+ QString userId;
+};
+
+QUrl GetTokenOwnerJob::makeRequestUrl(QUrl baseUrl)
+{
+ return BaseJob::makeRequestUrl(baseUrl,
+ basePath % "/account/whoami");
+}
+
+GetTokenOwnerJob::GetTokenOwnerJob()
+ : BaseJob(HttpVerb::Get, "GetTokenOwnerJob",
+ basePath % "/account/whoami")
+ , d(new Private)
+{
+}
+
+GetTokenOwnerJob::~GetTokenOwnerJob() = default;
+
+const QString& GetTokenOwnerJob::userId() const
+{
+ return d->userId;
+}
+
+BaseJob::Status GetTokenOwnerJob::parseJson(const QJsonDocument& data)
+{
+ auto json = data.object();
+ if (!json.contains("user_id"))
+ return { JsonParseError,
+ "The key 'user_id' not found in the response" };
+ d->userId = fromJson<QString>(json.value("user_id"));
+ return Success;
+}
+
diff --git a/lib/jobs/generated/whoami.h b/lib/jobs/generated/whoami.h
new file mode 100644
index 00000000..835232ee
--- /dev/null
+++ b/lib/jobs/generated/whoami.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN
+ */
+
+#pragma once
+
+#include "../basejob.h"
+
+
+
+namespace QMatrixClient
+{
+ // Operations
+
+ class GetTokenOwnerJob : public BaseJob
+ {
+ public:
+ /** Construct a URL out of baseUrl and usual parameters passed to
+ * GetTokenOwnerJob. This function can be used when
+ * a URL for GetTokenOwnerJob is necessary but the job
+ * itself isn't.
+ */
+ static QUrl makeRequestUrl(QUrl baseUrl);
+
+ explicit GetTokenOwnerJob();
+ ~GetTokenOwnerJob() override;
+
+ const QString& userId() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ QScopedPointer<Private> d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/joinroomjob.cpp b/lib/jobs/joinroomjob.cpp
new file mode 100644
index 00000000..66a75089
--- /dev/null
+++ b/lib/jobs/joinroomjob.cpp
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "joinroomjob.h"
+#include "util.h"
+
+using namespace QMatrixClient;
+
+class JoinRoomJob::Private
+{
+ public:
+ QString roomId;
+};
+
+JoinRoomJob::JoinRoomJob(const QString& roomAlias)
+ : BaseJob(HttpVerb::Post, "JoinRoomJob",
+ QStringLiteral("_matrix/client/r0/join/%1").arg(roomAlias))
+ , d(new Private)
+{
+}
+
+JoinRoomJob::~JoinRoomJob()
+{
+ delete d;
+}
+
+QString JoinRoomJob::roomId()
+{
+ return d->roomId;
+}
+
+BaseJob::Status JoinRoomJob::parseJson(const QJsonDocument& data)
+{
+ QJsonObject json = data.object();
+ if( json.contains("room_id") )
+ {
+ d->roomId = json.value("room_id").toString();
+ return Success;
+ }
+
+ qCDebug(JOBS) << data;
+ return { UserDefinedError, "No room_id in the JSON response" };
+}
diff --git a/lib/jobs/joinroomjob.h b/lib/jobs/joinroomjob.h
new file mode 100644
index 00000000..f3ba216f
--- /dev/null
+++ b/lib/jobs/joinroomjob.h
@@ -0,0 +1,40 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+namespace QMatrixClient
+{
+ class JoinRoomJob: public BaseJob
+ {
+ public:
+ explicit JoinRoomJob(const QString& roomAlias);
+ virtual ~JoinRoomJob();
+
+ QString roomId();
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ Private* d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp
new file mode 100644
index 00000000..dda1cdb4
--- /dev/null
+++ b/lib/jobs/mediathumbnailjob.cpp
@@ -0,0 +1,63 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "mediathumbnailjob.h"
+
+using namespace QMatrixClient;
+
+QUrl MediaThumbnailJob::makeRequestUrl(QUrl baseUrl,
+ const QUrl& mxcUri, QSize requestedSize)
+{
+ return makeRequestUrl(baseUrl, mxcUri.authority(), mxcUri.path().mid(1),
+ requestedSize.width(), requestedSize.height());
+}
+
+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
+{
+ return _thumbnail;
+}
+
+QImage MediaThumbnailJob::scaledThumbnail(QSize toSize) const
+{
+ return _thumbnail.scaled(toSize,
+ Qt::KeepAspectRatio, Qt::SmoothTransformation);
+}
+
+BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply)
+{
+ auto result = GetContentThumbnailJob::parseReply(reply);
+ if (!result.good())
+ return result;
+
+ if( _thumbnail.loadFromData(content()->readAll()) )
+ return Success;
+
+ return { IncorrectResponseError, "Could not read image data" };
+}
diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h
new file mode 100644
index 00000000..6e0b94f3
--- /dev/null
+++ b/lib/jobs/mediathumbnailjob.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "generated/content-repo.h"
+
+#include <QtGui/QPixmap>
+
+namespace QMatrixClient
+{
+ class MediaThumbnailJob: public GetContentThumbnailJob
+ {
+ public:
+ using GetContentThumbnailJob::makeRequestUrl;
+ static QUrl makeRequestUrl(QUrl baseUrl,
+ const QUrl& mxcUri, QSize requestedSize);
+
+ 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(QNetworkReply* reply) override;
+
+ private:
+ QImage _thumbnail;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/passwordlogin.cpp b/lib/jobs/passwordlogin.cpp
new file mode 100644
index 00000000..8abfe66a
--- /dev/null
+++ b/lib/jobs/passwordlogin.cpp
@@ -0,0 +1,74 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "passwordlogin.h"
+
+using namespace QMatrixClient;
+
+class PasswordLogin::Private
+{
+ public:
+ QString returned_id;
+ QString returned_server;
+ QString returned_token;
+};
+
+PasswordLogin::PasswordLogin(QString user, QString password)
+ : BaseJob(HttpVerb::Post, "PasswordLogin",
+ "_matrix/client/r0/login", Query(), Data(), false)
+ , d(new Private)
+{
+ QJsonObject _data;
+ _data.insert("type", QStringLiteral("m.login.password"));
+ _data.insert("user", user);
+ _data.insert("password", password);
+ setRequestData(_data);
+}
+
+PasswordLogin::~PasswordLogin()
+{
+ delete d;
+}
+
+QString PasswordLogin::token() const
+{
+ return d->returned_token;
+}
+
+QString PasswordLogin::id() const
+{
+ return d->returned_id;
+}
+
+QString PasswordLogin::server() const
+{
+ return d->returned_server;
+}
+
+BaseJob::Status PasswordLogin::parseJson(const QJsonDocument& data)
+{
+ QJsonObject json = data.object();
+ if( !json.contains("access_token") || !json.contains("home_server") || !json.contains("user_id") )
+ {
+ return { UserDefinedError, "No expected data" };
+ }
+ d->returned_token = json.value("access_token").toString();
+ d->returned_server = json.value("home_server").toString();
+ d->returned_id = json.value("user_id").toString();
+ return Success;
+}
diff --git a/lib/jobs/passwordlogin.h b/lib/jobs/passwordlogin.h
new file mode 100644
index 00000000..fb8777a3
--- /dev/null
+++ b/lib/jobs/passwordlogin.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+namespace QMatrixClient
+{
+ class PasswordLogin : public BaseJob
+ {
+ public:
+ PasswordLogin(QString user, QString password);
+ virtual ~PasswordLogin();
+
+ QString token() const;
+ QString id() const;
+ QString server() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ Private* d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h
new file mode 100644
index 00000000..d0198821
--- /dev/null
+++ b/lib/jobs/postreadmarkersjob.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ * 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"
+
+using namespace QMatrixClient;
+
+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/postreceiptjob.cpp b/lib/jobs/postreceiptjob.cpp
new file mode 100644
index 00000000..4572d74c
--- /dev/null
+++ b/lib/jobs/postreceiptjob.cpp
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "postreceiptjob.h"
+
+using namespace QMatrixClient;
+
+PostReceiptJob::PostReceiptJob(const QString& roomId, const QString& eventId)
+ : BaseJob(HttpVerb::Post, "PostReceiptJob",
+ QStringLiteral("/_matrix/client/r0/rooms/%1/receipt/m.read/%2")
+ .arg(roomId, eventId))
+{ }
diff --git a/lib/jobs/postreceiptjob.h b/lib/jobs/postreceiptjob.h
new file mode 100644
index 00000000..23df7c05
--- /dev/null
+++ b/lib/jobs/postreceiptjob.h
@@ -0,0 +1,30 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+namespace QMatrixClient
+{
+ class PostReceiptJob: public BaseJob
+ {
+ public:
+ PostReceiptJob(const QString& roomId, const QString& eventId);
+ };
+}
diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp
new file mode 100644
index 00000000..5cb62221
--- /dev/null
+++ b/lib/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;
+
+auto 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 auto 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/lib/jobs/requestdata.h b/lib/jobs/requestdata.h
new file mode 100644
index 00000000..aa03b744
--- /dev/null
+++ b/lib/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/lib/jobs/roommessagesjob.cpp b/lib/jobs/roommessagesjob.cpp
new file mode 100644
index 00000000..e5568f17
--- /dev/null
+++ b/lib/jobs/roommessagesjob.cpp
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "roommessagesjob.h"
+
+using namespace QMatrixClient;
+
+class RoomMessagesJob::Private
+{
+ public:
+ RoomEvents events;
+ QString end;
+};
+
+RoomMessagesJob::RoomMessagesJob(const QString& roomId, const QString& from,
+ int limit, FetchDirection dir)
+ : BaseJob(HttpVerb::Get, "RoomMessagesJob",
+ QStringLiteral("/_matrix/client/r0/rooms/%1/messages").arg(roomId),
+ Query(
+ { { "from", from }
+ , { "dir", dir == FetchDirection::Backward ? "b" : "f" }
+ , { "limit", QString::number(limit) }
+ }))
+ , d(new Private)
+{
+ qCDebug(JOBS) << "Room messages query:" << query().toString(QUrl::PrettyDecoded);
+}
+
+RoomMessagesJob::~RoomMessagesJob()
+{
+ delete d;
+}
+
+RoomEvents&& RoomMessagesJob::releaseEvents()
+{
+ return move(d->events);
+}
+
+QString RoomMessagesJob::end() const
+{
+ return d->end;
+}
+
+BaseJob::Status RoomMessagesJob::parseJson(const QJsonDocument& data)
+{
+ const auto obj = data.object();
+ d->events.fromJson(obj, "chunk");
+ d->end = obj.value("end").toString();
+ return Success;
+}
diff --git a/lib/jobs/roommessagesjob.h b/lib/jobs/roommessagesjob.h
new file mode 100644
index 00000000..7b3fd9c9
--- /dev/null
+++ b/lib/jobs/roommessagesjob.h
@@ -0,0 +1,47 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+#include "../events/event.h"
+
+namespace QMatrixClient
+{
+ enum class FetchDirection { Backward, Forward };
+
+ class RoomMessagesJob: public BaseJob
+ {
+ public:
+ RoomMessagesJob(const QString& roomId, const QString& from,
+ int limit = 10,
+ FetchDirection dir = FetchDirection::Backward);
+ virtual ~RoomMessagesJob();
+
+ RoomEvents&& releaseEvents();
+ QString end() const;
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ class Private;
+ Private* d;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/sendeventjob.cpp b/lib/jobs/sendeventjob.cpp
new file mode 100644
index 00000000..f5190d4b
--- /dev/null
+++ b/lib/jobs/sendeventjob.cpp
@@ -0,0 +1,45 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "sendeventjob.h"
+
+#include "events/roommessageevent.h"
+
+using namespace QMatrixClient;
+
+SendEventJob::SendEventJob(const QString& roomId, const QString& type,
+ const QString& plainText)
+ : SendEventJob(roomId, RoomMessageEvent(plainText, type))
+{ }
+
+void SendEventJob::beforeStart(const ConnectionData* connData)
+{
+ BaseJob::beforeStart(connData);
+ setApiEndpoint(apiEndpoint() + connData->generateTxnId());
+}
+
+BaseJob::Status SendEventJob::parseJson(const QJsonDocument& data)
+{
+ _eventId = data.object().value("event_id").toString();
+ if (!_eventId.isEmpty())
+ return Success;
+
+ qCDebug(JOBS) << data;
+ return { UserDefinedError, "No event_id in the JSON response" };
+}
+
diff --git a/lib/jobs/sendeventjob.h b/lib/jobs/sendeventjob.h
new file mode 100644
index 00000000..3a11eb6a
--- /dev/null
+++ b/lib/jobs/sendeventjob.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+#include "connectiondata.h"
+
+namespace QMatrixClient
+{
+ class SendEventJob: public BaseJob
+ {
+ public:
+ /** Constructs a job that sends an arbitrary room event */
+ template <typename EvT>
+ SendEventJob(const QString& roomId, const EvT& event)
+ : BaseJob(HttpVerb::Put, QStringLiteral("SendEventJob"),
+ QStringLiteral("_matrix/client/r0/rooms/%1/send/%2/")
+ .arg(roomId, EvT::TypeId), // See also beforeStart()
+ Query(),
+ Data(event.toJson()))
+ { }
+
+ /**
+ * Constructs a plain text message job (for compatibility with
+ * the old PostMessageJob API).
+ */
+ SendEventJob(const QString& roomId, const QString& type,
+ const QString& plainText);
+
+ QString eventId() const { return _eventId; }
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ QString _eventId;
+
+ void beforeStart(const ConnectionData* connData) override;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/setroomstatejob.cpp b/lib/jobs/setroomstatejob.cpp
new file mode 100644
index 00000000..c2beb87b
--- /dev/null
+++ b/lib/jobs/setroomstatejob.cpp
@@ -0,0 +1,32 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "setroomstatejob.h"
+
+using namespace QMatrixClient;
+
+BaseJob::Status SetRoomStateJob::parseJson(const QJsonDocument& data)
+{
+ _eventId = data.object().value("event_id").toString();
+ if (!_eventId.isEmpty())
+ return Success;
+
+ qCDebug(JOBS) << data;
+ return { UserDefinedError, "No event_id in the JSON response" };
+}
+
diff --git a/lib/jobs/setroomstatejob.h b/lib/jobs/setroomstatejob.h
new file mode 100644
index 00000000..b7e6d4a1
--- /dev/null
+++ b/lib/jobs/setroomstatejob.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+#include "connectiondata.h"
+
+namespace QMatrixClient
+{
+ class SetRoomStateJob: public BaseJob
+ {
+ public:
+ /**
+ * Constructs a job that sets a state using an arbitrary room event
+ * with a state key.
+ */
+ template <typename EvT>
+ SetRoomStateJob(const QString& roomId, const QString& stateKey,
+ const EvT& event)
+ : BaseJob(HttpVerb::Put, "SetRoomStateJob",
+ QStringLiteral("_matrix/client/r0/rooms/%1/state/%2/%3")
+ .arg(roomId, EvT::TypeId, stateKey),
+ Query(),
+ Data(event.toJson()))
+ { }
+ /**
+ * Constructs a job that sets a state using an arbitrary room event
+ * without a state key.
+ */
+ template <typename EvT>
+ SetRoomStateJob(const QString& roomId, const EvT& event)
+ : BaseJob(HttpVerb::Put, "SetRoomStateJob",
+ QStringLiteral("_matrix/client/r0/rooms/%1/state/%2")
+ .arg(roomId, EvT::TypeId),
+ Query(),
+ Data(event.toJson()))
+ { }
+
+ QString eventId() const { return _eventId; }
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ QString _eventId;
+ };
+} // namespace QMatrixClient
diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp
new file mode 100644
index 00000000..435dfd0e
--- /dev/null
+++ b/lib/jobs/syncjob.cpp
@@ -0,0 +1,133 @@
+/******************************************************************************
+ * 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
+ */
+
+#include "syncjob.h"
+
+#include <QtCore/QElapsedTimer>
+
+using namespace QMatrixClient;
+
+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"))
+{
+ setLoggingCategory(SYNCJOB);
+ QUrlQuery query;
+ if( !filter.isEmpty() )
+ query.addQueryItem("filter", filter);
+ if( !presence.isEmpty() )
+ query.addQueryItem("set_presence", presence);
+ if( timeout >= 0 )
+ query.addQueryItem("timeout", QString::number(timeout));
+ if( !since.isEmpty() )
+ query.addQueryItem("since", since);
+ setRequestQuery(query);
+
+ setMaxRetries(std::numeric_limits<int>::max());
+}
+
+QString SyncData::nextBatch() const
+{
+ return nextBatch_;
+}
+
+SyncDataList&& SyncData::takeRoomData()
+{
+ return std::move(roomData);
+}
+
+SyncBatch<Event>&& SyncData::takeAccountData()
+{
+ return std::move(accountData);
+}
+
+BaseJob::Status SyncJob::parseJson(const QJsonDocument& data)
+{
+ return d.parseJson(data);
+}
+
+BaseJob::Status SyncData::parseJson(const QJsonDocument &data)
+{
+ QElapsedTimer et; et.start();
+
+ auto json = data.object();
+ nextBatch_ = json.value("next_batch").toString();
+ // TODO: presence
+ accountData.fromJson(json);
+
+ QJsonObject rooms = json.value("rooms").toObject();
+ JoinStates::Int ii = 1; // ii is used to make a JoinState value
+ for (size_t i = 0; i < JoinStateStrings.size(); ++i, ii <<= 1)
+ {
+ const auto rs = rooms.value(JoinStateStrings[i]).toObject();
+ // We have a Qt container on the right and an STL one on the left
+ roomData.reserve(static_cast<size_t>(rs.size()));
+ for(auto roomIt = rs.begin(); roomIt != rs.end(); ++roomIt)
+ roomData.emplace_back(roomIt.key(), JoinState(ii),
+ roomIt.value().toObject());
+ }
+ qCDebug(PROFILER) << "*** SyncData::parseJson(): batch with"
+ << rooms.size() << "room(s) in" << et;
+ return BaseJob::Success;
+}
+
+const QString SyncRoomData::UnreadCountKey =
+ QStringLiteral("x-qmatrixclient.unread_count");
+
+SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_,
+ const QJsonObject& room_)
+ : roomId(roomId_)
+ , joinState(joinState_)
+ , state(joinState == JoinState::Invite ? "invite_state" : "state")
+ , timeline("timeline")
+ , ephemeral("ephemeral")
+ , accountData("account_data")
+{
+ switch (joinState) {
+ case JoinState::Invite:
+ state.fromJson(room_);
+ break;
+ case JoinState::Join:
+ state.fromJson(room_);
+ timeline.fromJson(room_);
+ ephemeral.fromJson(room_);
+ accountData.fromJson(room_);
+ break;
+ case JoinState::Leave:
+ state.fromJson(room_);
+ timeline.fromJson(room_);
+ break;
+ default:
+ qCWarning(SYNCJOB) << "SyncRoomData: Unknown JoinState value, ignoring:" << int(joinState);
+ }
+
+ auto timelineJson = room_.value("timeline").toObject();
+ timelineLimited = timelineJson.value("limited").toBool();
+ timelinePrevBatch = timelineJson.value("prev_batch").toString();
+
+ auto unreadJson = room_.value("unread_notifications").toObject();
+ unreadCount = unreadJson.value(UnreadCountKey).toInt(-2);
+ highlightCount = unreadJson.value("highlight_count").toInt();
+ notificationCount = unreadJson.value("notification_count").toInt();
+ if (highlightCount > 0 || notificationCount > 0)
+ qCDebug(SYNCJOB) << "Highlights: " << highlightCount
+ << " Notifications:" << notificationCount;
+}
diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h
new file mode 100644
index 00000000..919060be
--- /dev/null
+++ b/lib/jobs/syncjob.h
@@ -0,0 +1,99 @@
+/******************************************************************************
+ * 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
+ */
+
+#pragma once
+
+#include "basejob.h"
+
+#include "joinstate.h"
+#include "events/event.h"
+#include "util.h"
+
+namespace QMatrixClient
+{
+ template <typename EventT>
+ class SyncBatch : public EventsBatch<EventT>
+ {
+ public:
+ explicit SyncBatch(QString k) : jsonKey(std::move(k)) { }
+ void fromJson(const QJsonObject& roomContents)
+ {
+ EventsBatch<EventT>::fromJson(
+ roomContents[jsonKey].toObject(), "events");
+ }
+
+ private:
+ QString jsonKey;
+ };
+
+ class SyncRoomData
+ {
+ public:
+ QString roomId;
+ JoinState joinState;
+ SyncBatch<RoomEvent> state;
+ SyncBatch<RoomEvent> timeline;
+ SyncBatch<Event> ephemeral;
+ SyncBatch<Event> accountData;
+
+ bool timelineLimited;
+ QString timelinePrevBatch;
+ int unreadCount;
+ int highlightCount;
+ int notificationCount;
+
+ SyncRoomData(const QString& roomId, JoinState joinState_,
+ const QJsonObject& room_);
+ SyncRoomData(SyncRoomData&&) = default;
+ SyncRoomData& operator=(SyncRoomData&&) = default;
+
+ static const QString UnreadCountKey;
+ };
+ // QVector cannot work with non-copiable objects, std::vector can.
+ using SyncDataList = std::vector<SyncRoomData>;
+
+ class SyncData
+ {
+ public:
+ BaseJob::Status parseJson(const QJsonDocument &data);
+ SyncBatch<Event>&& takeAccountData();
+ SyncDataList&& takeRoomData();
+ QString nextBatch() const;
+
+ private:
+ QString nextBatch_;
+ SyncBatch<Event> accountData { "account_data" };
+ SyncDataList roomData;
+ };
+
+ class SyncJob: public BaseJob
+ {
+ public:
+ explicit SyncJob(const QString& since = {},
+ const QString& filter = {},
+ int timeout = -1, const QString& presence = {});
+
+ SyncData &&takeData() { return std::move(d); }
+
+ protected:
+ Status parseJson(const QJsonDocument& data) override;
+
+ private:
+ SyncData d;
+ };
+} // namespace QMatrixClient