diff options
Diffstat (limited to 'lib/jobs')
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 |