From aacc4bcb4a487871daae6717f77605aaba444341 Mon Sep 17 00:00:00 2001 From: Marc Deop Date: Sat, 2 Mar 2019 12:26:57 +0100 Subject: style: apply .clang-format to all .cpp and .h files --- lib/application-service/definitions/location.cpp | 3 +- lib/application-service/definitions/location.h | 9 +- lib/application-service/definitions/protocol.cpp | 19 +- lib/application-service/definitions/protocol.h | 64 +- lib/application-service/definitions/user.cpp | 9 +- lib/application-service/definitions/user.h | 9 +- lib/avatar.cpp | 133 +- lib/avatar.h | 49 +- lib/connection.cpp | 826 +++++----- lib/connection.h | 1339 +++++++-------- lib/connectiondata.cpp | 36 +- lib/connectiondata.h | 39 +- lib/converters.cpp | 44 +- lib/converters.h | 239 ++- lib/csapi/account-data.cpp | 17 +- lib/csapi/account-data.h | 62 +- lib/csapi/admin.cpp | 37 +- lib/csapi/admin.h | 139 +- lib/csapi/administrative_contact.cpp | 85 +- lib/csapi/administrative_contact.h | 336 ++-- lib/csapi/appservice_room_directory.cpp | 12 +- lib/csapi/appservice_room_directory.h | 41 +- lib/csapi/banning.cpp | 8 +- lib/csapi/banning.h | 58 +- lib/csapi/capabilities.cpp | 35 +- lib/csapi/capabilities.h | 104 +- lib/csapi/content-repo.cpp | 171 +- lib/csapi/content-repo.h | 383 +++-- lib/csapi/create_room.cpp | 49 +- lib/csapi/create_room.h | 416 ++--- lib/csapi/definitions/auth_data.cpp | 3 +- lib/csapi/definitions/auth_data.h | 14 +- lib/csapi/definitions/client_device.cpp | 8 +- lib/csapi/definitions/client_device.h | 23 +- lib/csapi/definitions/device_keys.cpp | 9 +- lib/csapi/definitions/device_keys.h | 22 +- lib/csapi/definitions/event_filter.cpp | 9 +- lib/csapi/definitions/event_filter.h | 25 +- lib/csapi/definitions/public_rooms_response.cpp | 20 +- lib/csapi/definitions/public_rooms_response.h | 17 +- lib/csapi/definitions/push_condition.cpp | 9 +- lib/csapi/definitions/push_condition.h | 21 +- lib/csapi/definitions/push_rule.cpp | 8 +- lib/csapi/definitions/push_rule.h | 23 +- lib/csapi/definitions/push_ruleset.cpp | 9 +- lib/csapi/definitions/push_ruleset.h | 11 +- lib/csapi/definitions/room_event_filter.cpp | 9 +- lib/csapi/definitions/room_event_filter.h | 22 +- lib/csapi/definitions/sync_filter.cpp | 33 +- lib/csapi/definitions/sync_filter.h | 46 +- lib/csapi/definitions/user_identifier.cpp | 9 +- lib/csapi/definitions/user_identifier.h | 12 +- lib/csapi/definitions/wellknown/full.cpp | 6 +- lib/csapi/definitions/wellknown/full.h | 19 +- lib/csapi/definitions/wellknown/homeserver.cpp | 3 +- lib/csapi/definitions/wellknown/homeserver.h | 10 +- .../definitions/wellknown/identity_server.cpp | 3 +- lib/csapi/definitions/wellknown/identity_server.h | 16 +- lib/csapi/device_management.cpp | 44 +- lib/csapi/device_management.h | 138 +- lib/csapi/directory.cpp | 33 +- lib/csapi/directory.h | 94 +- lib/csapi/event_context.cpp | 52 +- lib/csapi/event_context.h | 79 +- lib/csapi/filter.cpp | 31 +- lib/csapi/filter.h | 96 +- lib/csapi/inviting.cpp | 3 +- lib/csapi/inviting.h | 36 +- lib/csapi/joining.cpp | 63 +- lib/csapi/joining.h | 209 +-- lib/csapi/keys.cpp | 92 +- lib/csapi/keys.h | 303 ++-- lib/csapi/kicking.cpp | 6 +- lib/csapi/kicking.h | 38 +- lib/csapi/leaving.cpp | 9 +- lib/csapi/leaving.h | 64 +- lib/csapi/list_joined_rooms.cpp | 12 +- lib/csapi/list_joined_rooms.h | 34 +- lib/csapi/list_public_rooms.cpp | 98 +- lib/csapi/list_public_rooms.h | 233 +-- lib/csapi/login.cpp | 65 +- lib/csapi/login.h | 180 +- lib/csapi/logout.cpp | 12 +- lib/csapi/logout.h | 64 +- lib/csapi/message_pagination.cpp | 45 +- lib/csapi/message_pagination.h | 89 +- lib/csapi/notifications.cpp | 49 +- lib/csapi/notifications.h | 114 +- lib/csapi/openid.cpp | 37 +- lib/csapi/openid.h | 60 +- lib/csapi/peeking_events.cpp | 44 +- lib/csapi/peeking_events.h | 73 +- lib/csapi/presence.cpp | 34 +- lib/csapi/presence.h | 74 +- lib/csapi/profile.cpp | 59 +- lib/csapi/profile.h | 158 +- lib/csapi/pusher.cpp | 48 +- lib/csapi/pusher.h | 274 ++-- lib/csapi/pushrules.cpp | 140 +- lib/csapi/pushrules.h | 355 ++-- lib/csapi/read_markers.cpp | 7 +- lib/csapi/read_markers.h | 30 +- lib/csapi/receipts.cpp | 9 +- lib/csapi/receipts.h | 30 +- lib/csapi/redaction.cpp | 16 +- lib/csapi/redaction.h | 51 +- lib/csapi/registration.cpp | 174 +- lib/csapi/registration.h | 602 +++---- lib/csapi/report_content.cpp | 7 +- lib/csapi/report_content.h | 28 +- lib/csapi/room_send.cpp | 16 +- lib/csapi/room_send.h | 63 +- lib/csapi/room_state.cpp | 36 +- lib/csapi/room_state.h | 133 +- lib/csapi/room_upgrades.cpp | 9 +- lib/csapi/room_upgrades.h | 37 +- lib/csapi/rooms.cpp | 122 +- lib/csapi/rooms.h | 343 ++-- lib/csapi/search.cpp | 93 +- lib/csapi/search.h | 329 ++-- lib/csapi/sso_login_redirect.cpp | 10 +- lib/csapi/sso_login_redirect.h | 33 +- lib/csapi/tags.cpp | 37 +- lib/csapi/tags.h | 148 +- lib/csapi/third_party_lookup.cpp | 95 +- lib/csapi/third_party_lookup.h | 270 +-- lib/csapi/third_party_membership.cpp | 6 +- lib/csapi/third_party_membership.h | 65 +- lib/csapi/to_device.cpp | 7 +- lib/csapi/to_device.h | 34 +- lib/csapi/typing.cpp | 6 +- lib/csapi/typing.h | 28 +- lib/csapi/users.cpp | 36 +- lib/csapi/users.h | 89 +- lib/csapi/versions.cpp | 20 +- lib/csapi/versions.h | 54 +- lib/csapi/voip.cpp | 14 +- lib/csapi/voip.h | 33 +- lib/csapi/wellknown.cpp | 16 +- lib/csapi/wellknown.h | 37 +- lib/csapi/whoami.cpp | 16 +- lib/csapi/whoami.h | 46 +- lib/eventitem.cpp | 14 +- lib/eventitem.h | 169 +- lib/events/accountdataevents.h | 67 +- lib/events/callanswerevent.cpp | 26 +- lib/events/callanswerevent.h | 23 +- lib/events/callcandidatesevent.cpp | 7 +- lib/events/callcandidatesevent.h | 35 +- lib/events/callhangupevent.cpp | 6 +- lib/events/callhangupevent.h | 11 +- lib/events/callinviteevent.cpp | 15 +- lib/events/callinviteevent.h | 25 +- lib/events/directchatevent.cpp | 7 +- lib/events/directchatevent.h | 15 +- lib/events/event.cpp | 30 +- lib/events/event.h | 330 ++-- lib/events/eventcontent.cpp | 35 +- lib/events/eventcontent.h | 196 +-- lib/events/eventloader.h | 35 +- lib/events/receiptevent.cpp | 29 +- lib/events/receiptevent.h | 29 +- lib/events/redactionevent.h | 23 +- lib/events/roomavatarevent.h | 30 +- lib/events/roomcreateevent.cpp | 38 +- lib/events/roomcreateevent.h | 65 +- lib/events/roomevent.cpp | 59 +- lib/events/roomevent.h | 148 +- lib/events/roommemberevent.cpp | 44 +- lib/events/roommemberevent.h | 124 +- lib/events/roommessageevent.cpp | 144 +- lib/events/roommessageevent.h | 183 ++- lib/events/roomtombstoneevent.cpp | 32 +- lib/events/roomtombstoneevent.h | 54 +- lib/events/simplestateevents.h | 93 +- lib/events/stateevent.cpp | 41 +- lib/events/stateevent.h | 158 +- lib/events/typingevent.cpp | 8 +- lib/events/typingevent.h | 17 +- .../definitions/request_email_validation.cpp | 3 +- .../definitions/request_email_validation.h | 16 +- .../definitions/request_msisdn_validation.cpp | 3 +- .../definitions/request_msisdn_validation.h | 16 +- lib/identity/definitions/sid.cpp | 7 +- lib/identity/definitions/sid.h | 18 +- lib/jobs/basejob.cpp | 436 +++-- lib/jobs/basejob.h | 584 +++---- lib/jobs/downloadfilejob.cpp | 61 +- lib/jobs/downloadfilejob.h | 28 +- lib/jobs/mediathumbnailjob.cpp | 37 +- lib/jobs/mediathumbnailjob.h | 29 +- lib/jobs/postreadmarkersjob.h | 20 +- lib/jobs/requestdata.cpp | 19 +- lib/jobs/requestdata.h | 37 +- lib/jobs/syncjob.cpp | 18 +- lib/jobs/syncjob.h | 26 +- lib/joinstate.h | 18 +- lib/logging.cpp | 2 +- lib/logging.h | 19 +- lib/networkaccessmanager.cpp | 21 +- lib/networkaccessmanager.h | 31 +- lib/networksettings.cpp | 13 +- lib/networksettings.h | 27 +- lib/qt_connection_util.h | 69 +- lib/room.cpp | 1734 +++++++++----------- lib/room.h | 1071 ++++++------ lib/settings.cpp | 27 +- lib/settings.h | 206 +-- lib/syncdata.cpp | 122 +- lib/syncdata.h | 104 +- lib/user.cpp | 232 ++- lib/user.h | 227 +-- lib/util.cpp | 76 +- lib/util.h | 292 ++-- 214 files changed, 9998 insertions(+), 10182 deletions(-) (limited to 'lib') diff --git a/lib/application-service/definitions/location.cpp b/lib/application-service/definitions/location.cpp index a53db8d7..4da0e69d 100644 --- a/lib/application-service/definitions/location.cpp +++ b/lib/application-service/definitions/location.cpp @@ -15,10 +15,9 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, ThirdPartyLocation& result) + const QJsonObject& jo, ThirdPartyLocation& result) { fromJson(jo.value("alias"_ls), result.alias); fromJson(jo.value("protocol"_ls), result.protocol); fromJson(jo.value("fields"_ls), result.fields); } - diff --git a/lib/application-service/definitions/location.h b/lib/application-service/definitions/location.h index 5586cfc6..f92fa8c5 100644 --- a/lib/application-service/definitions/location.h +++ b/lib/application-service/definitions/location.h @@ -8,12 +8,10 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct ThirdPartyLocation - { + struct ThirdPartyLocation { /// An alias for a matrix room. QString alias; /// The protocol ID that the third party location is a part of. @@ -21,8 +19,7 @@ namespace QMatrixClient /// Information used to identify this third party location. QJsonObject fields; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod); }; diff --git a/lib/application-service/definitions/protocol.cpp b/lib/application-service/definitions/protocol.cpp index 2a62b15d..4c148796 100644 --- a/lib/application-service/definitions/protocol.cpp +++ b/lib/application-service/definitions/protocol.cpp @@ -6,22 +6,22 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const FieldType& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const FieldType& pod) { addParam<>(jo, QStringLiteral("regexp"), pod.regexp); addParam<>(jo, QStringLiteral("placeholder"), pod.placeholder); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, FieldType& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + FieldType& result) { fromJson(jo.value("regexp"_ls), result.regexp); fromJson(jo.value("placeholder"_ls), result.placeholder); } -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const ProtocolInstance& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const ProtocolInstance& pod) { addParam<>(jo, QStringLiteral("desc"), pod.desc); addParam(jo, QStringLiteral("icon"), pod.icon); @@ -29,8 +29,8 @@ void JsonObjectConverter::dumpTo( addParam<>(jo, QStringLiteral("network_id"), pod.networkId); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, ProtocolInstance& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + ProtocolInstance& result) { fromJson(jo.value("desc"_ls), result.desc); fromJson(jo.value("icon"_ls), result.icon); @@ -49,7 +49,7 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, ThirdPartyProtocol& result) + const QJsonObject& jo, ThirdPartyProtocol& result) { fromJson(jo.value("user_fields"_ls), result.userFields); fromJson(jo.value("location_fields"_ls), result.locationFields); @@ -57,4 +57,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("field_types"_ls), result.fieldTypes); fromJson(jo.value("instances"_ls), result.instances); } - diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h index 0a1f9a21..66012a13 100644 --- a/lib/application-service/definitions/protocol.h +++ b/lib/application-service/definitions/protocol.h @@ -6,76 +6,72 @@ #include "converters.h" +#include "converters.h" #include #include -#include "converters.h" #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Definition of valid values for a field. - struct FieldType - { - /// A regular expression for validation of a field's value. This may be relatively - /// coarse to verify the value as the application service providing this protocol - /// may apply additional validation or filtering. + struct FieldType { + /// A regular expression for validation of a field's value. This may be + /// relatively coarse to verify the value as the application service + /// providing this protocol may apply additional validation or + /// filtering. QString regexp; /// An placeholder serving as a valid example of the field value. QString placeholder; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const FieldType& pod); static void fillFrom(const QJsonObject& jo, FieldType& pod); }; - struct ProtocolInstance - { + struct ProtocolInstance { /// A human-readable description for the protocol, such as the name. QString desc; - /// An optional content URI representing the protocol. Overrides the one provided - /// at the higher level Protocol object. + /// An optional content URI representing the protocol. Overrides the one + /// provided at the higher level Protocol object. QString icon; /// Preset values for ``fields`` the client may use to search by. QJsonObject fields; /// A unique identifier across all instances. QString networkId; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod); static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod); }; - struct ThirdPartyProtocol - { - /// Fields which may be used to identify a third party user. These should be - /// ordered to suggest the way that entities may be grouped, where higher - /// groupings are ordered first. For example, the name of a network should be - /// searched before the nickname of a user. + struct ThirdPartyProtocol { + /// Fields which may be used to identify a third party user. These + /// should be ordered to suggest the way that entities may be grouped, + /// where higher groupings are ordered first. For example, the name of a + /// network should be searched before the nickname of a user. QStringList userFields; - /// Fields which may be used to identify a third party location. These should be - /// ordered to suggest the way that entities may be grouped, where higher - /// groupings are ordered first. For example, the name of a network should be - /// searched before the name of a channel. + /// Fields which may be used to identify a third party location. These + /// should be ordered to suggest the way that entities may be grouped, + /// where higher groupings are ordered first. For example, the name of a + /// network should be searched before the name of a channel. QStringList locationFields; /// A content URI representing an icon for the third party protocol. QString icon; - /// The type definitions for the fields defined in the ``user_fields`` and - /// ``location_fields``. Each entry in those arrays MUST have an entry here. The + /// The type definitions for the fields defined in the ``user_fields`` + /// and + /// ``location_fields``. Each entry in those arrays MUST have an entry + /// here. The /// ``string`` key for this object is field name itself. - /// + /// /// May be an empty object if no fields are defined. QHash fieldTypes; - /// A list of objects representing independent instances of configuration. - /// For example, multiple networks on IRC if multiple are provided by the - /// same application service. + /// A list of objects representing independent instances of + /// configuration. For example, multiple networks on IRC if multiple are + /// provided by the same application service. QVector instances; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod); }; diff --git a/lib/application-service/definitions/user.cpp b/lib/application-service/definitions/user.cpp index 8ba92321..0f3c3130 100644 --- a/lib/application-service/definitions/user.cpp +++ b/lib/application-service/definitions/user.cpp @@ -6,19 +6,18 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const ThirdPartyUser& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const ThirdPartyUser& pod) { addParam<>(jo, QStringLiteral("userid"), pod.userid); addParam<>(jo, QStringLiteral("protocol"), pod.protocol); addParam<>(jo, QStringLiteral("fields"), pod.fields); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, ThirdPartyUser& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + ThirdPartyUser& result) { fromJson(jo.value("userid"_ls), result.userid); fromJson(jo.value("protocol"_ls), result.protocol); fromJson(jo.value("fields"_ls), result.fields); } - diff --git a/lib/application-service/definitions/user.h b/lib/application-service/definitions/user.h index 062d2cac..9b314db1 100644 --- a/lib/application-service/definitions/user.h +++ b/lib/application-service/definitions/user.h @@ -8,12 +8,10 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct ThirdPartyUser - { + struct ThirdPartyUser { /// A Matrix User ID represting a third party user. QString userid; /// The protocol ID that the third party location is a part of. @@ -21,8 +19,7 @@ namespace QMatrixClient /// Information used to identify this third party location. QJsonObject fields; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyUser& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod); }; diff --git a/lib/avatar.cpp b/lib/avatar.cpp index c0ef3cba..5de43dd5 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -15,20 +15,20 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "avatar.h" -#include "jobs/mediathumbnailjob.h" -#include "events/eventcontent.h" #include "connection.h" +#include "events/eventcontent.h" +#include "jobs/mediathumbnailjob.h" -#include -#include #include +#include #include #include +#include using namespace QMatrixClient; using std::move; @@ -36,43 +36,37 @@ using std::move; class Avatar::Private { public: - explicit Private(QUrl url = {}) - : _url(move(url)) - { } - ~Private() - { - if (isJobRunning(_thumbnailRequest)) - _thumbnailRequest->abandon(); - if (isJobRunning(_uploadRequest)) - _uploadRequest->abandon(); - } - - QImage get(Connection* connection, QSize size, - get_callback_t callback) const; - bool upload(UploadContentJob* job, upload_callback_t callback); - - bool checkUrl(const QUrl& url) const; - QString localFile() const; - - QUrl _url; - - // The below are related to image caching, hence mutable - mutable QImage _originalImage; - mutable std::vector> _scaledImages; - mutable QSize _requestedSize; - mutable enum { Unknown, Cache, Network, Banned } _imageSource = Unknown; - mutable QPointer _thumbnailRequest = nullptr; - mutable QPointer _uploadRequest = nullptr; - mutable std::vector callbacks; + explicit Private(QUrl url = {}) : _url(move(url)) {} + ~Private() + { + if (isJobRunning(_thumbnailRequest)) + _thumbnailRequest->abandon(); + if (isJobRunning(_uploadRequest)) + _uploadRequest->abandon(); + } + + QImage get(Connection* connection, QSize size, + get_callback_t callback) const; + bool upload(UploadContentJob* job, upload_callback_t callback); + + bool checkUrl(const QUrl& url) const; + QString localFile() const; + + QUrl _url; + + // The below are related to image caching, hence mutable + mutable QImage _originalImage; + mutable std::vector> _scaledImages; + mutable QSize _requestedSize; + mutable enum { Unknown, Cache, Network, Banned } _imageSource = Unknown; + mutable QPointer _thumbnailRequest = nullptr; + mutable QPointer _uploadRequest = nullptr; + mutable std::vector callbacks; }; -Avatar::Avatar() - : d(std::make_unique()) -{ } +Avatar::Avatar() : d(std::make_unique()) {} -Avatar::Avatar(QUrl url) - : d(std::make_unique(std::move(url))) -{ } +Avatar::Avatar(QUrl url) : d(std::make_unique(std::move(url))) {} Avatar::Avatar(Avatar&&) = default; @@ -83,13 +77,13 @@ Avatar& Avatar::operator=(Avatar&&) = default; QImage Avatar::get(Connection* connection, int dimension, get_callback_t callback) const { - return d->get(connection, {dimension, dimension}, move(callback)); + return d->get(connection, { dimension, dimension }, move(callback)); } QImage Avatar::get(Connection* connection, int width, int height, get_callback_t callback) const { - return d->get(connection, {width, height}, move(callback)); + return d->get(connection, { width, height }, move(callback)); } bool Avatar::upload(Connection* connection, const QString& fileName, @@ -108,22 +102,17 @@ bool Avatar::upload(Connection* connection, QIODevice* source, return d->upload(connection->uploadContent(source), move(callback)); } -QString Avatar::mediaId() const -{ - return d->_url.authority() + d->_url.path(); -} +QString Avatar::mediaId() const { return d->_url.authority() + d->_url.path(); } QImage Avatar::Private::get(Connection* connection, QSize size, get_callback_t callback) const { - if (!callback) - { + if (!callback) { qCCritical(MAIN) << "Null callbacks are not allowed in Avatar::get"; Q_ASSERT(false); } - if (_imageSource == Unknown && _originalImage.load(localFile())) - { + if (_imageSource == Unknown && _originalImage.load(localFile())) { _imageSource = Cache; _requestedSize = _originalImage.size(); } @@ -132,9 +121,10 @@ QImage Avatar::Private::get(Connection* connection, QSize size, // to trick the below code into constantly getting another image from // the server because the existing one is alleged unsatisfactory. // Client authors can only blame themselves if they do so. - if (((_imageSource == Unknown && !_thumbnailRequest) || - size.width() > _requestedSize.width() || - size.height() > _requestedSize.height()) && checkUrl(_url)) { + if (((_imageSource == Unknown && !_thumbnailRequest) + || size.width() > _requestedSize.width() + || size.height() > _requestedSize.height()) + && checkUrl(_url)) { qCDebug(MAIN) << "Getting avatar from" << _url.toString(); _requestedSize = size; if (isJobRunning(_thumbnailRequest)) @@ -142,24 +132,27 @@ QImage Avatar::Private::get(Connection* connection, QSize size, if (callback) callbacks.emplace_back(move(callback)); _thumbnailRequest = connection->getThumbnail(_url, size); - QObject::connect( _thumbnailRequest, &MediaThumbnailJob::success, - _thumbnailRequest, [this] { - _imageSource = Network; - _originalImage = - _thumbnailRequest->scaledThumbnail(_requestedSize); - _originalImage.save(localFile()); - _scaledImages.clear(); - for (const auto& n: callbacks) - n(); - callbacks.clear(); - }); + QObject::connect(_thumbnailRequest, &MediaThumbnailJob::success, + _thumbnailRequest, [this] { + _imageSource = Network; + _originalImage = + _thumbnailRequest->scaledThumbnail( + _requestedSize); + _originalImage.save(localFile()); + _scaledImages.clear(); + for (const auto& n : callbacks) + n(); + callbacks.clear(); + }); } - for (const auto& p: _scaledImages) + for (const auto& p : _scaledImages) if (p.first == size) return p.second; - auto result = _originalImage.isNull() ? QImage() : _originalImage.scaled(size, - Qt::KeepAspectRatio, Qt::SmoothTransformation); + auto result = _originalImage.isNull() + ? QImage() + : _originalImage.scaled(size, Qt::KeepAspectRatio, + Qt::SmoothTransformation); _scaledImages.emplace_back(size, result); return result; } @@ -170,7 +163,7 @@ bool Avatar::Private::upload(UploadContentJob* job, upload_callback_t callback) if (!isJobRunning(_uploadRequest)) return false; _uploadRequest->connect(_uploadRequest, &BaseJob::success, _uploadRequest, - [job,callback] { callback(job->contentUri()); }); + [job, callback] { callback(job->contentUri()); }); return true; } @@ -181,8 +174,7 @@ bool Avatar::Private::checkUrl(const QUrl& url) const // FIXME: Make "mxc" a library-wide constant and maybe even make // the URL checker a Connection(?) method. - if (!url.isValid() || url.scheme() != "mxc" || url.path().count('/') != 1) - { + if (!url.isValid() || url.scheme() != "mxc" || url.path().count('/') != 1) { qCWarning(MAIN) << "Avatar URL is invalid or not mxc-based:" << url.toDisplayString(); _imageSource = Banned; @@ -190,7 +182,8 @@ bool Avatar::Private::checkUrl(const QUrl& url) const return _imageSource != Banned; } -QString Avatar::Private::localFile() const { +QString Avatar::Private::localFile() const +{ static const auto cachePath = cacheLocation("avatars"); return cachePath % _url.authority() % '_' % _url.fileName() % ".png"; } diff --git a/lib/avatar.h b/lib/avatar.h index c86345e3..2ca2c987 100644 --- a/lib/avatar.h +++ b/lib/avatar.h @@ -13,49 +13,48 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include #include +#include #include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Connection; class Avatar { public: - explicit Avatar(); - explicit Avatar(QUrl url); - Avatar(Avatar&&); - ~Avatar(); - Avatar& operator=(Avatar&&); + explicit Avatar(); + explicit Avatar(QUrl url); + Avatar(Avatar&&); + ~Avatar(); + Avatar& operator=(Avatar&&); - using get_callback_t = std::function; - using upload_callback_t = std::function; + using get_callback_t = std::function; + using upload_callback_t = std::function; - QImage get(Connection* connection, int dimension, - get_callback_t callback) const; - QImage get(Connection* connection, int w, int h, - get_callback_t callback) const; + QImage get(Connection* connection, int dimension, + get_callback_t callback) const; + QImage get(Connection* connection, int w, int h, + get_callback_t callback) const; - bool upload(Connection* connection, const QString& fileName, - upload_callback_t callback) const; - bool upload(Connection* connection, QIODevice* source, - upload_callback_t callback) const; + bool upload(Connection* connection, const QString& fileName, + upload_callback_t callback) const; + bool upload(Connection* connection, QIODevice* source, + upload_callback_t callback) const; - QString mediaId() const; - QUrl url() const; - bool updateUrl(const QUrl& newUrl); + QString mediaId() const; + QUrl url() const; + bool updateUrl(const QUrl& newUrl); private: - class Private; - std::unique_ptr d; + class Private; + std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/connection.cpp b/lib/connection.cpp index 26b40c03..2b1cc1b9 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -13,39 +13,39 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "connection.h" #include "connectiondata.h" -#include "user.h" -#include "events/directchatevent.h" -#include "events/eventloader.h" -#include "room.h" -#include "settings.h" -#include "csapi/login.h" +#include "csapi/account-data.h" #include "csapi/capabilities.h" +#include "csapi/joining.h" +#include "csapi/leaving.h" +#include "csapi/login.h" #include "csapi/logout.h" #include "csapi/receipts.h" -#include "csapi/leaving.h" -#include "csapi/account-data.h" -#include "csapi/joining.h" -#include "csapi/to_device.h" #include "csapi/room_send.h" -#include "jobs/syncjob.h" -#include "jobs/mediathumbnailjob.h" -#include "jobs/downloadfilejob.h" +#include "csapi/to_device.h" #include "csapi/voip.h" +#include "events/directchatevent.h" +#include "events/eventloader.h" +#include "jobs/downloadfilejob.h" +#include "jobs/mediathumbnailjob.h" +#include "jobs/syncjob.h" +#include "room.h" +#include "settings.h" +#include "user.h" -#include -#include +#include #include -#include -#include #include -#include +#include #include -#include +#include +#include +#include +#include using namespace QMatrixClient; @@ -54,10 +54,8 @@ template HashT erase_if(HashT& hashMap, Pred pred) { HashT removals; - for (auto it = hashMap.begin(); it != hashMap.end();) - { - if (pred(it)) - { + for (auto it = hashMap.begin(); it != hashMap.end();) { + if (pred(it)) { removals.insert(it.key(), it.value()); it = hashMap.erase(it); } else @@ -69,82 +67,81 @@ HashT erase_if(HashT& hashMap, Pred pred) class Connection::Private { public: - explicit Private(std::unique_ptr&& connection) - : data(move(connection)) - { } - Q_DISABLE_COPY(Private) - Private(Private&&) = delete; - Private operator=(Private&&) = delete; - - Connection* q = nullptr; - std::unique_ptr data; - // A complex key below is a pair of room name and whether its - // state is Invited. The spec mandates to keep Invited room state - // separately so we should, e.g., keep objects for Invite and - // Leave state of the same room. - QHash, Room*> roomMap; - // Mapping from aliases to room ids, as per the last sync - QHash roomAliasMap; - QVector roomIdsToForget; - QVector firstTimeRooms; - QVector pendingStateRoomIds; - QMap userMap; - DirectChatsMap directChats; - DirectChatUsersMap directChatUsers; - std::unordered_map accountData; - QString userId; - int syncLoopTimeout = -1; - - GetCapabilitiesJob* capabilitiesJob = nullptr; - GetCapabilitiesJob::Capabilities capabilities; - - SyncJob* syncJob = nullptr; - - bool cacheState = true; - bool cacheToBinary = SettingsGroup("libqmatrixclient") - .value("cache_type").toString() != "json"; - bool lazyLoading = false; - - void connectWithToken(const QString& user, const QString& accessToken, - const QString& deviceId); - void broadcastDirectChatUpdates(const DirectChatsMap& additions, - const DirectChatsMap& removals); - - template - EventT* unpackAccountData() const - { - const auto& eventIt = accountData.find(EventT::matrixTypeId()); - return eventIt == accountData.end() - ? nullptr : weakPtrCast(eventIt->second); - } + explicit Private(std::unique_ptr&& connection) + : data(move(connection)) + { + } + Q_DISABLE_COPY(Private) + Private(Private&&) = delete; + Private operator=(Private&&) = delete; + + Connection* q = nullptr; + std::unique_ptr data; + // A complex key below is a pair of room name and whether its + // state is Invited. The spec mandates to keep Invited room state + // separately so we should, e.g., keep objects for Invite and + // Leave state of the same room. + QHash, Room*> roomMap; + // Mapping from aliases to room ids, as per the last sync + QHash roomAliasMap; + QVector roomIdsToForget; + QVector firstTimeRooms; + QVector pendingStateRoomIds; + QMap userMap; + DirectChatsMap directChats; + DirectChatUsersMap directChatUsers; + std::unordered_map accountData; + QString userId; + int syncLoopTimeout = -1; + + GetCapabilitiesJob* capabilitiesJob = nullptr; + GetCapabilitiesJob::Capabilities capabilities; + + SyncJob* syncJob = nullptr; + + bool cacheState = true; + bool cacheToBinary = + SettingsGroup("libqmatrixclient").value("cache_type").toString() + != "json"; + bool lazyLoading = false; + + void connectWithToken(const QString& user, const QString& accessToken, + const QString& deviceId); + void broadcastDirectChatUpdates(const DirectChatsMap& additions, + const DirectChatsMap& removals); + + template EventT* unpackAccountData() const + { + const auto& eventIt = accountData.find(EventT::matrixTypeId()); + return eventIt == accountData.end() + ? nullptr + : weakPtrCast(eventIt->second); + } - void packAndSendAccountData(EventPtr&& event) - { - const auto eventType = event->matrixType(); - q->callApi(userId, eventType, - event->contentJson()); - accountData[eventType] = std::move(event); - emit q->accountDataChanged(eventType); - } + void packAndSendAccountData(EventPtr&& event) + { + const auto eventType = event->matrixType(); + q->callApi(userId, eventType, event->contentJson()); + accountData[eventType] = std::move(event); + emit q->accountDataChanged(eventType); + } - template - void packAndSendAccountData(ContentT&& content) - { - packAndSendAccountData( - makeEvent(std::forward(content))); - } + template + void packAndSendAccountData(ContentT&& content) + { + packAndSendAccountData( + makeEvent(std::forward(content))); + } }; Connection::Connection(const QUrl& server, QObject* parent) - : QObject(parent) - , d(std::make_unique(std::make_unique(server))) + : QObject(parent), + d(std::make_unique(std::make_unique(server))) { d->q = this; // All d initialization should occur before this line } -Connection::Connection(QObject* parent) - : Connection({}, parent) -{ } +Connection::Connection(QObject* parent) : Connection({}, parent) {} Connection::~Connection() { @@ -159,19 +156,20 @@ void Connection::resolveServer(const QString& mxidOrDomain) // Try to parse as an FQID; if there's no @ part, assume it's a domain name. QRegularExpression parser( - "^(@.+?:)?" // Optional username (allow everything for compatibility) - "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address - "(:\\d{1,5})?$", // Optional port - QRegularExpression::UseUnicodePropertiesOption); // Because asian digits + "^(@.+?:)?" // Optional username (allow everything for + // compatibility) + "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 + // address + "(:\\d{1,5})?$", // Optional port + QRegularExpression::UseUnicodePropertiesOption); // Because asian + // digits auto match = parser.match(mxidOrDomain); QUrl maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http" - if (!match.hasMatch() || !maybeBaseUrl.isValid()) - { - emit resolveError( - tr("%1 is not a valid homeserver address") - .arg(maybeBaseUrl.toString())); + if (!match.hasMatch() || !maybeBaseUrl.isValid()) { + emit resolveError(tr("%1 is not a valid homeserver address") + .arg(maybeBaseUrl.toString())); return; } @@ -188,16 +186,15 @@ void Connection::resolveServer(const QString& mxidOrDomain) dns->setType(QDnsLookup::SRV); dns->setName("_matrix._tcp." + domain); - connect(dns, &QDnsLookup::finished, [this,dns,maybeBaseUrl]() { + connect(dns, &QDnsLookup::finished, [this, dns, maybeBaseUrl]() { QUrl baseUrl { maybeBaseUrl }; - if (dns->error() == QDnsLookup::NoError && - dns->serviceRecords().isEmpty()) - { + if (dns->error() == QDnsLookup::NoError + && dns->serviceRecords().isEmpty()) { auto record = dns->serviceRecords().front(); baseUrl.setHost(record.target()); baseUrl.setPort(record.port()); - qCDebug(MAIN) << "SRV record for" << maybeBaseUrl.host() - << "is" << baseUrl.authority(); + qCDebug(MAIN) << "SRV record for" << maybeBaseUrl.host() << "is" + << baseUrl.authority(); } else { qCDebug(MAIN) << baseUrl.host() << "doesn't have SRV record" << dns->name() << "- using the hostname as is"; @@ -213,41 +210,37 @@ void Connection::connectToServer(const QString& user, const QString& password, const QString& initialDeviceName, const QString& deviceId) { - checkAndConnect(user, - [=] { - doConnectToServer(user, password, initialDeviceName, deviceId); - }); + checkAndConnect(user, [=] { + doConnectToServer(user, password, initialDeviceName, deviceId); + }); } void Connection::doConnectToServer(const QString& user, const QString& password, const QString& initialDeviceName, const QString& deviceId) { - auto loginJob = callApi(QStringLiteral("m.login.password"), + auto loginJob = callApi( + QStringLiteral("m.login.password"), UserIdentifier { QStringLiteral("m.id.user"), - {{ QStringLiteral("user"), user }} }, + { { QStringLiteral("user"), user } } }, password, /*token*/ "", deviceId, initialDeviceName); - connect(loginJob, &BaseJob::success, this, - [this, loginJob] { - d->connectWithToken(loginJob->userId(), loginJob->accessToken(), - loginJob->deviceId()); - }); - connect(loginJob, &BaseJob::failure, this, - [this, loginJob] { - emit loginError(loginJob->errorString(), loginJob->rawDataSample()); - }); + connect(loginJob, &BaseJob::success, this, [this, loginJob] { + d->connectWithToken(loginJob->userId(), loginJob->accessToken(), + loginJob->deviceId()); + }); + connect(loginJob, &BaseJob::failure, this, [this, loginJob] { + emit loginError(loginJob->errorString(), loginJob->rawDataSample()); + }); } -void Connection::syncLoopIteration() -{ - sync(d->syncLoopTimeout); -} +void Connection::syncLoopIteration() { sync(d->syncLoopTimeout); } void Connection::connectWithToken(const QString& userId, const QString& accessToken, const QString& deviceId) { - checkAndConnect(userId, - [=] { d->connectWithToken(userId, accessToken, deviceId); }); + checkAndConnect(userId, [=] { + d->connectWithToken(userId, accessToken, deviceId); + }); } void Connection::reloadCapabilities() @@ -259,18 +252,17 @@ void Connection::reloadCapabilities() else if (d->capabilitiesJob->error() == BaseJob::IncorrectRequestError) qCDebug(MAIN) << "Server doesn't support /capabilities"; - if (d->capabilities.roomVersions.omitted()) - { + if (d->capabilities.roomVersions.omitted()) { qCWarning(MAIN) << "Pinning supported room version to 1"; - d->capabilities.roomVersions = { "1", {{ "1", "stable" }} }; + d->capabilities.roomVersions = { "1", { { "1", "stable" } } }; } else { - qCDebug(MAIN) << "Room versions:" - << defaultRoomVersion() << "is default, full list:" - << availableRoomVersions(); + qCDebug(MAIN) << "Room versions:" << defaultRoomVersion() + << "is default, full list:" + << availableRoomVersions(); } Q_ASSERT(!d->capabilities.roomVersions.omitted()); emit capabilitiesLoaded(); - for (auto* r: d->roomMap) + for (auto* r : d->roomMap) if (r->joinState() == JoinState::Join && r->successorId().isEmpty()) r->checkVersion(); }); @@ -301,28 +293,26 @@ void Connection::Private::connectWithToken(const QString& user, void Connection::checkAndConnect(const QString& userId, std::function connectFn) { - if (d->data->baseUrl().isValid()) - { + if (d->data->baseUrl().isValid()) { connectFn(); return; } // Not good to go, try to fix the homeserver URL. - if (userId.startsWith('@') && userId.indexOf(':') != -1) - { - connectSingleShot(this, &Connection::homeserverChanged, this, connectFn); + if (userId.startsWith('@') && userId.indexOf(':') != -1) { + connectSingleShot(this, &Connection::homeserverChanged, this, + connectFn); // NB: doResolveServer can emit resolveError, so this is a part of // checkAndConnect function contract. resolveServer(userId); } else - emit resolveError( - tr("%1 is an invalid homeserver URL") - .arg(d->data->baseUrl().toString())); + emit resolveError(tr("%1 is an invalid homeserver URL") + .arg(d->data->baseUrl().toString())); } void Connection::logout() { auto job = callApi(); - connect( job, &LogoutJob::success, this, [this] { + connect(job, &LogoutJob::success, this, [this] { stopSync(); d->data->setToken({}); emit stateChanged(); @@ -338,28 +328,25 @@ void Connection::sync(int timeout) Filter filter; filter.room->timeline->limit = 100; filter.room->state->lazyLoadMembers = d->lazyLoading; - auto job = d->syncJob = callApi(BackgroundRequest, - d->data->lastEvent(), filter, timeout); - connect( job, &SyncJob::success, this, [this, job] { + auto job = d->syncJob = callApi( + BackgroundRequest, d->data->lastEvent(), filter, timeout); + connect(job, &SyncJob::success, this, [this, job] { onSyncSuccess(job->takeData()); d->syncJob = nullptr; emit syncDone(); }); - connect( job, &SyncJob::retryScheduled, this, - [this,job] (int retriesTaken, int nextInMilliseconds) - { - emit networkError(job->errorString(), job->rawDataSample(), - retriesTaken, nextInMilliseconds); - }); - connect( job, &SyncJob::failure, this, [this, job] { + connect(job, &SyncJob::retryScheduled, this, + [this, job](int retriesTaken, int nextInMilliseconds) { + emit networkError(job->errorString(), job->rawDataSample(), + retriesTaken, nextInMilliseconds); + }); + connect(job, &SyncJob::failure, this, [this, job] { d->syncJob = nullptr; - if (job->error() == BaseJob::ContentAccessError) - { - qCWarning(SYNCJOB) - << "Sync job failed with ContentAccessError - login expired?"; + if (job->error() == BaseJob::ContentAccessError) { + qCWarning(SYNCJOB) << "Sync job failed with ContentAccessError - " + "login expired?"; emit loginError(job->errorString(), job->rawDataSample()); - } - else + } else emit syncError(job->errorString(), job->rawDataSample()); }); } @@ -371,65 +358,59 @@ void Connection::syncLoop(int timeout) syncLoopIteration(); // initial sync to start the loop } -void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { +void Connection::onSyncSuccess(SyncData&& data, bool fromCache) +{ d->data->setLastEvent(data.nextBatch()); - for (auto&& roomData: data.takeRoomData()) - { + for (auto&& roomData : data.takeRoomData()) { const auto forgetIdx = d->roomIdsToForget.indexOf(roomData.roomId); - if (forgetIdx != -1) - { + if (forgetIdx != -1) { d->roomIdsToForget.removeAt(forgetIdx); - if (roomData.joinState == JoinState::Leave) - { - qDebug(MAIN) << "Room" << roomData.roomId - << "has been forgotten, ignoring /sync response for it"; + if (roomData.joinState == JoinState::Leave) { + qDebug(MAIN) + << "Room" << roomData.roomId + << "has been forgotten, ignoring /sync response for it"; continue; } qWarning(MAIN) << "Room" << roomData.roomId - << "has just been forgotten but /sync returned it in" - << toCString(roomData.joinState) - << "state - suspiciously fast turnaround"; + << "has just been forgotten but /sync returned it in" + << toCString(roomData.joinState) + << "state - suspiciously fast turnaround"; } - if ( auto* r = provideRoom(roomData.roomId, roomData.joinState) ) - { + if (auto* r = provideRoom(roomData.roomId, roomData.joinState)) { d->pendingStateRoomIds.removeOne(roomData.roomId); r->updateData(std::move(roomData), fromCache); - if (d->firstTimeRooms.removeOne(r)) - { + if (d->firstTimeRooms.removeOne(r)) { emit loadedRoomState(r); if (!d->capabilities.roomVersions.omitted()) r->checkVersion(); - // Otherwise, the version will be checked in reloadCapabilities() + // Otherwise, the version will be checked in + // reloadCapabilities() } } // Let UI update itself after updating each room QCoreApplication::processEvents(); } - for (auto&& accountEvent: data.takeAccountData()) - { - if (is(*accountEvent)) - { + for (auto&& accountEvent : data.takeAccountData()) { + if (is(*accountEvent)) { const auto usersToDCs = ptrCast(move(accountEvent)) - ->usersToDirectChats(); + ->usersToDirectChats(); DirectChatsMap removals = - erase_if(d->directChats, [&usersToDCs] (auto it) { - return !usersToDCs.contains(it.key()->id(), it.value()); - }); - erase_if(d->directChatUsers, [&usersToDCs] (auto it) { + erase_if(d->directChats, [&usersToDCs](auto it) { + return !usersToDCs.contains(it.key()->id(), it.value()); + }); + erase_if(d->directChatUsers, [&usersToDCs](auto it) { return !usersToDCs.contains(it.value()->id(), it.key()); }); if (MAIN().isDebugEnabled()) for (auto it = removals.begin(); it != removals.end(); ++it) - qCDebug(MAIN) << it.value() - << "is no more a direct chat with" << it.key()->id(); + qCDebug(MAIN) + << it.value() << "is no more a direct chat with" + << it.key()->id(); DirectChatsMap additions; - for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) - { - if (auto* u = user(it.key())) - { - if (!d->directChats.contains(u, it.value())) - { + for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { + if (auto* u = user(it.key())) { + if (!d->directChats.contains(u, it.value())) { Q_ASSERT(!d->directChatUsers.contains(it.value(), u)); additions.insert(u, it.value()); d->directChats.insert(u, it.value()); @@ -448,14 +429,13 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { } if (is(*accountEvent)) qCDebug(MAIN) << "Users ignored by" << d->userId << "updated:" - << QStringList::fromSet(ignoredUsers()).join(','); + << QStringList::fromSet(ignoredUsers()).join(','); auto& currentData = d->accountData[accountEvent->matrixType()]; // A polymorphic event-specific comparison might be a bit more // efficient; maaybe do it another day - if (!currentData || - currentData->contentJson() != accountEvent->contentJson()) - { + if (!currentData + || currentData->contentJson() != accountEvent->contentJson()) { currentData = std::move(accountEvent); qCDebug(MAIN) << "Updated account data of type" << currentData->matrixType(); @@ -467,8 +447,8 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { void Connection::stopSync() { // If there's a sync loop, break it - disconnect(this, &Connection::syncDone, - this, &Connection::syncLoopIteration); + disconnect(this, &Connection::syncDone, this, + &Connection::syncLoopIteration); if (d->syncJob) // If there's an ongoing sync job, stop it too { d->syncJob->abandon(); @@ -476,10 +456,7 @@ void Connection::stopSync() } } -QString Connection::nextBatchToken() const -{ - return d->data->lastEvent(); -} +QString Connection::nextBatchToken() const { return d->data->lastEvent(); } PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const { @@ -492,8 +469,8 @@ JoinRoomJob* Connection::joinRoom(const QString& roomAlias, auto job = callApi(roomAlias, serverNames); // Upon completion, ensure a room object in Join state is created but only // if it's not already there due to a sync completing earlier. - connect(job, &JoinRoomJob::success, - this, [this, job] { provideRoom(job->roomId()); }); + connect(job, &JoinRoomJob::success, this, + [this, job] { provideRoom(job->roomId()); }); return job; } @@ -501,15 +478,13 @@ LeaveRoomJob* Connection::leaveRoom(Room* room) { const auto& roomId = room->id(); const auto job = callApi(roomId); - if (room->joinState() == JoinState::Invite) - { + if (room->joinState() == JoinState::Invite) { // Workaround matrix-org/synapse#2181 - if the room is in invite state // the invite may have been cancelled but Synapse didn't send it in // `/sync`. See also #273 for the discussion in the library context. d->pendingStateRoomIds.push_back(roomId); - connect(job, &LeaveRoomJob::success, this, [this,roomId] { - if (d->pendingStateRoomIds.removeOne(roomId)) - { + connect(job, &LeaveRoomJob::success, this, [this, roomId] { + if (d->pendingStateRoomIds.removeOne(roomId)) { qCDebug(MAIN) << "Forcing the room to Leave status"; provideRoom(roomId, JoinState::Leave); } @@ -522,40 +497,44 @@ inline auto splitMediaId(const QString& mediaId) { auto idParts = mediaId.split('/'); Q_ASSERT_X(idParts.size() == 2, __FUNCTION__, - ("'" + mediaId + - "' doesn't look like 'serverName/localMediaId'").toLatin1()); + ("'" + mediaId + "' doesn't look like 'serverName/localMediaId'") + .toLatin1()); return idParts; } MediaThumbnailJob* Connection::getThumbnail(const QString& mediaId, - QSize requestedSize, RunningPolicy policy) const + QSize requestedSize, + RunningPolicy policy) const { auto idParts = splitMediaId(mediaId); - return callApi(policy, - idParts.front(), idParts.back(), requestedSize); + return callApi(policy, idParts.front(), idParts.back(), + requestedSize); } MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, - QSize requestedSize, RunningPolicy policy) const + QSize requestedSize, + RunningPolicy policy) const { return getThumbnail(url.authority() + url.path(), requestedSize, policy); } -MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, - int requestedWidth, int requestedHeight, RunningPolicy policy) const +MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, int requestedWidth, + int requestedHeight, + RunningPolicy policy) const { return getThumbnail(url, QSize(requestedWidth, requestedHeight), policy); } -UploadContentJob* Connection::uploadContent(QIODevice* contentSource, - const QString& filename, const QString& overrideContentType) const +UploadContentJob* +Connection::uploadContent(QIODevice* contentSource, const QString& filename, + const QString& overrideContentType) const { auto contentType = overrideContentType; - if (contentType.isEmpty()) - { + if (contentType.isEmpty()) { contentType = - QMimeDatabase().mimeTypeForFileNameAndData(filename, contentSource) - .name(); + QMimeDatabase() + .mimeTypeForFileNameAndData(filename, contentSource) + .name(); contentSource->open(QIODevice::ReadOnly); } return callApi(contentSource, filename, contentType); @@ -565,8 +544,7 @@ UploadContentJob* Connection::uploadFile(const QString& fileName, const QString& overrideContentType) { auto sourceFile = new QFile(fileName); - if (!sourceFile->open(QIODevice::ReadOnly)) - { + if (!sourceFile->open(QIODevice::ReadOnly)) { qCWarning(MAIN) << "Couldn't open" << sourceFile->fileName() << "for reading"; return nullptr; @@ -596,13 +574,14 @@ DownloadFileJob* Connection::downloadFile(const QUrl& url, return job; } -CreateRoomJob* Connection::createRoom(RoomVisibility visibility, - const QString& alias, const QString& name, const QString& topic, - QStringList invites, const QString& presetName, - const QString& roomVersion, bool isDirect, - const QVector& initialState, - const QVector& invite3pids, - const QJsonObject& creationContent) +CreateRoomJob* +Connection::createRoom(RoomVisibility visibility, const QString& alias, + const QString& name, const QString& topic, + QStringList invites, const QString& presetName, + const QString& roomVersion, bool isDirect, + const QVector& initialState, + const QVector& invite3pids, + const QJsonObject& creationContent) { invites.removeOne(d->userId); // The creator is by definition in the room auto job = callApi( @@ -610,7 +589,7 @@ CreateRoomJob* Connection::createRoom(RoomVisibility visibility, : QStringLiteral("private"), alias, name, topic, invites, invite3pids, roomVersion, creationContent, initialState, presetName, isDirect); - connect(job, &BaseJob::success, this, [this,job] { + connect(job, &BaseJob::success, this, [this, job] { emit createdRoom(provideRoom(job->roomId(), JoinState::Join)); }); return job; @@ -621,14 +600,14 @@ void Connection::requestDirectChat(const QString& userId) if (auto* u = user(userId)) requestDirectChat(u); else - qCCritical(MAIN) - << "Connection::requestDirectChat: Couldn't get a user object for" - << userId; + qCCritical(MAIN) << "Connection::requestDirectChat: Couldn't get a " + "user object for" + << userId; } void Connection::requestDirectChat(User* u) { - doInDirectChat(u, [this] (Room* r) { emit directChatAvailable(r); }); + doInDirectChat(u, [this](Room* r) { emit directChatAvailable(r); }); } void Connection::doInDirectChat(const QString& userId, @@ -638,8 +617,8 @@ void Connection::doInDirectChat(const QString& userId, doInDirectChat(u, operation); else qCCritical(MAIN) - << "Connection::doInDirectChat: Couldn't get a user object for" - << userId; + << "Connection::doInDirectChat: Couldn't get a user object for" + << userId; } void Connection::doInDirectChat(User* u, @@ -651,11 +630,9 @@ void Connection::doInDirectChat(User* u, // (left/forgotten) ones along the way. DirectChatsMap removals; for (auto it = d->directChats.find(u); - it != d->directChats.end() && it.key() == u; ++it) - { + it != d->directChats.end() && it.key() == u; ++it) { const auto& roomId = *it; - if (auto r = room(roomId, JoinState::Join)) - { + if (auto r = room(roomId, JoinState::Join)) { Q_ASSERT(r->id() == roomId); // A direct chat with yourself should only involve yourself :) if (userId == d->userId && r->totalMemberCount() > 1) @@ -665,15 +642,16 @@ void Connection::doInDirectChat(User* u, operation(r); return; } - if (auto ir = invitation(roomId)) - { + if (auto ir = invitation(roomId)) { Q_ASSERT(ir->id() == roomId); auto j = joinRoom(ir->id()); - connect(j, &BaseJob::success, this, [this,roomId,userId,operation] { - qCDebug(MAIN) << "Joined the already invited direct chat with" - << userId << "as" << roomId; - operation(room(roomId, JoinState::Join)); - }); + connect(j, &BaseJob::success, this, + [this, roomId, userId, operation] { + qCDebug(MAIN) + << "Joined the already invited direct chat with" + << userId << "as" << roomId; + operation(room(roomId, JoinState::Join)); + }); return; } // Avoid reusing previously left chats but don't remove them @@ -686,10 +664,8 @@ void Connection::doInDirectChat(User* u, // Postpone actual deletion until we finish iterating d->directChats. removals.insert(it.key(), it.value()); } - if (!removals.isEmpty()) - { - for (auto it = removals.cbegin(); it != removals.cend(); ++it) - { + if (!removals.isEmpty()) { + for (auto it = removals.cbegin(); it != removals.cend(); ++it) { d->directChats.remove(it.key(), it.value()); d->directChatUsers.remove(it.value(), const_cast(it.key())); // FIXME @@ -698,18 +674,18 @@ void Connection::doInDirectChat(User* u, } auto j = createDirectChat(userId); - connect(j, &BaseJob::success, this, [this,j,userId,operation] { - qCDebug(MAIN) << "Direct chat with" << userId - << "has been created as" << j->roomId(); + connect(j, &BaseJob::success, this, [this, j, userId, operation] { + qCDebug(MAIN) << "Direct chat with" << userId << "has been created as" + << j->roomId(); operation(room(j->roomId(), JoinState::Join)); }); - } CreateRoomJob* Connection::createDirectChat(const QString& userId, - const QString& topic, const QString& name) + const QString& topic, + const QString& name) { - return createRoom(UnpublishRoom, "", name, topic, {userId}, + return createRoom(UnpublishRoom, "", name, topic, { userId }, "trusted_private_chat", {}, true); } @@ -723,11 +699,10 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) // a ForgetRoomJob is created in advance and can be returned in a probably // not-yet-started state (it will start once /leave completes). auto forgetJob = new ForgetRoomJob(id); - auto room = d->roomMap.value({id, false}); + auto room = d->roomMap.value({ id, false }); if (!room) - room = d->roomMap.value({id, true}); - if (room && room->joinState() != JoinState::Leave) - { + room = d->roomMap.value({ id, true }); + if (room && room->joinState() != JoinState::Leave) { auto leaveJob = room->leaveRoom(); connect(leaveJob, &BaseJob::success, this, [this, forgetJob, room] { forgetJob->start(connectionData()); @@ -737,18 +712,14 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) d->roomIdsToForget.push_back(room->id()); }); connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon); - } - else + } else forgetJob->start(connectionData()); - connect(forgetJob, &BaseJob::success, this, [this, id] - { + connect(forgetJob, &BaseJob::success, this, [this, id] { // Delete whatever instances of the room are still in the map. - for (auto f: {false, true}) - if (auto r = d->roomMap.take({ id, f })) - { - qCDebug(MAIN) << "Room" << r->objectName() - << "in state" << toCString(r->joinState()) - << "will be deleted"; + for (auto f : { false, true }) + if (auto r = d->roomMap.take({ id, f })) { + qCDebug(MAIN) << "Room" << r->objectName() << "in state" + << toCString(r->joinState()) << "will be deleted"; emit r->beforeDestruction(r); r->deleteLater(); } @@ -756,52 +727,52 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) return forgetJob; } -SendToDeviceJob* Connection::sendToDevices(const QString& eventType, - const UsersToDevicesToEvents& eventsMap) const +SendToDeviceJob* +Connection::sendToDevices(const QString& eventType, + const UsersToDevicesToEvents& eventsMap) const { QHash> json; json.reserve(int(eventsMap.size())); - std::for_each(eventsMap.begin(), eventsMap.end(), - [&json] (const auto& userTodevicesToEvents) { - auto& jsonUser = json[userTodevicesToEvents.first]; - const auto& devicesToEvents = userTodevicesToEvents.second; - std::for_each(devicesToEvents.begin(), devicesToEvents.end(), - [&jsonUser] (const auto& deviceToEvents) { - jsonUser.insert(deviceToEvents.first, - deviceToEvents.second.contentJson()); - }); - }); - return callApi(BackgroundRequest, - eventType, generateTxnId(), json); + std::for_each( + eventsMap.begin(), eventsMap.end(), + [&json](const auto& userTodevicesToEvents) { + auto& jsonUser = json[userTodevicesToEvents.first]; + const auto& devicesToEvents = userTodevicesToEvents.second; + std::for_each(devicesToEvents.begin(), devicesToEvents.end(), + [&jsonUser](const auto& deviceToEvents) { + jsonUser.insert( + deviceToEvents.first, + deviceToEvents.second.contentJson()); + }); + }); + return callApi(BackgroundRequest, eventType, + generateTxnId(), json); } SendMessageJob* Connection::sendMessage(const QString& roomId, const RoomEvent& event) const { - const auto txnId = event.transactionId().isEmpty() - ? generateTxnId() : event.transactionId(); - return callApi(roomId, event.matrixType(), - txnId, event.contentJson()); + const auto txnId = event.transactionId().isEmpty() ? generateTxnId() + : event.transactionId(); + return callApi(roomId, event.matrixType(), txnId, + event.contentJson()); } -QUrl Connection::homeserver() const -{ - return d->data->baseUrl(); -} +QUrl Connection::homeserver() const { return d->data->baseUrl(); } Room* Connection::room(const QString& roomId, JoinStates states) const { - Room* room = d->roomMap.value({roomId, false}, nullptr); - if (states.testFlag(JoinState::Join) && - room && room->joinState() == JoinState::Join) + Room* room = d->roomMap.value({ roomId, false }, nullptr); + if (states.testFlag(JoinState::Join) && room + && room->joinState() == JoinState::Join) return room; if (states.testFlag(JoinState::Invite)) if (Room* invRoom = invitation(roomId)) return invRoom; - if (states.testFlag(JoinState::Leave) && - room && room->joinState() == JoinState::Leave) + if (states.testFlag(JoinState::Leave) && room + && room->joinState() == JoinState::Leave) return room; return nullptr; @@ -821,22 +792,21 @@ void Connection::updateRoomAliases(const QString& roomId, const QStringList& previousRoomAliases, const QStringList& roomAliases) { - for (const auto& a: previousRoomAliases) + for (const auto& a : previousRoomAliases) if (d->roomAliasMap.remove(a) == 0) - qCWarning(MAIN) << "Alias" << a << "is not found (already deleted?)"; + qCWarning(MAIN) + << "Alias" << a << "is not found (already deleted?)"; - for (const auto& a: roomAliases) - { + for (const auto& a : roomAliases) { auto& mappedId = d->roomAliasMap[a]; - if (!mappedId.isEmpty()) - { + if (!mappedId.isEmpty()) { if (mappedId == roomId) qCDebug(MAIN) << "Alias" << a << "is already mapped to room" << roomId; else - qCWarning(MAIN) << "Alias" << a - << "will be force-remapped from room" - << mappedId << "to" << roomId; + qCWarning(MAIN) + << "Alias" << a << "will be force-remapped from room" + << mappedId << "to" << roomId; } mappedId = roomId; } @@ -844,19 +814,18 @@ void Connection::updateRoomAliases(const QString& roomId, Room* Connection::invitation(const QString& roomId) const { - return d->roomMap.value({roomId, true}, nullptr); + return d->roomMap.value({ roomId, true }, nullptr); } User* Connection::user(const QString& userId) { if (userId.isEmpty()) return nullptr; - if (!userId.startsWith('@') || !userId.contains(':')) - { + if (!userId.startsWith('@') || !userId.contains(':')) { qCCritical(MAIN) << "Malformed userId:" << userId; return nullptr; } - if( d->userMap.contains(userId) ) + if (d->userMap.contains(userId)) return d->userMap.value(userId); auto* user = userFactory()(this, userId); d->userMap.insert(userId, user); @@ -869,47 +838,29 @@ const User* Connection::user() const return d->userMap.value(d->userId, nullptr); } -User* Connection::user() -{ - return user(d->userId); -} +User* Connection::user() { return user(d->userId); } -QString Connection::userId() const -{ - return d->userId; -} +QString Connection::userId() const { return d->userId; } -QString Connection::deviceId() const -{ - return d->data->deviceId(); -} +QString Connection::deviceId() const { return d->data->deviceId(); } -QString Connection::token() const -{ - return accessToken(); -} +QString Connection::token() const { return accessToken(); } -QByteArray Connection::accessToken() const -{ - return d->data->accessToken(); -} +QByteArray Connection::accessToken() const { return d->data->accessToken(); } -SyncJob* Connection::syncJob() const -{ - return d->syncJob; -} +SyncJob* Connection::syncJob() const { return d->syncJob; } int Connection::millisToReconnect() const { return d->syncJob ? d->syncJob->millisToRetry() : 0; } -QHash< QPair, Room* > Connection::roomMap() const +QHash, Room*> Connection::roomMap() const { - // Copy-on-write-and-remove-elements is faster than copying elements one by one. - QHash< QPair, Room* > roomMap = d->roomMap; - for (auto it = roomMap.begin(); it != roomMap.end(); ) - { + // Copy-on-write-and-remove-elements is faster than copying elements one by + // one. + QHash, Room*> roomMap = d->roomMap; + for (auto it = roomMap.begin(); it != roomMap.end();) { if (it.value()->joinState() == JoinState::Leave) it = roomMap.erase(it); else @@ -949,24 +900,22 @@ void Connection::setAccountData(const QString& type, const QJsonObject& content) QHash> Connection::tagsToRooms() const { QHash> result; - for (auto* r: qAsConst(d->roomMap)) - { - for (const auto& tagName: r->tagNames()) + for (auto* r : qAsConst(d->roomMap)) { + for (const auto& tagName : r->tagNames()) result[tagName].push_back(r); } for (auto it = result.begin(); it != result.end(); ++it) - std::sort(it->begin(), it->end(), - [t=it.key()] (Room* r1, Room* r2) { - return r1->tags().value(t) < r2->tags().value(t); - }); + std::sort(it->begin(), it->end(), [t = it.key()](Room* r1, Room* r2) { + return r1->tags().value(t) < r2->tags().value(t); + }); return result; } QStringList Connection::tagNames() const { - QStringList tags ({FavouriteTag}); - for (auto* r: qAsConst(d->roomMap)) - for (const auto& tag: r->tagNames()) + QStringList tags({ FavouriteTag }); + for (auto* r : qAsConst(d->roomMap)) + for (const auto& tag : r->tagNames()) if (tag != LowPriorityTag && !tags.contains(tag)) tags.push_back(tag); tags.push_back(LowPriorityTag); @@ -976,8 +925,9 @@ QStringList Connection::tagNames() const QVector Connection::roomsWithTag(const QString& tagName) const { QVector rooms; - std::copy_if(d->roomMap.begin(), d->roomMap.end(), std::back_inserter(rooms), - [&tagName] (Room* r) { return r->tags().contains(tagName); }); + std::copy_if(d->roomMap.begin(), d->roomMap.end(), + std::back_inserter(rooms), + [&tagName](Room* r) { return r->tags().contains(tagName); }); return rooms; } @@ -989,8 +939,7 @@ Connection::DirectChatsMap Connection::directChats() const QJsonObject toJson(const Connection::DirectChatsMap& directChats) { QJsonObject json; - for (auto it = directChats.begin(); it != directChats.end();) - { + for (auto it = directChats.begin(); it != directChats.end();) { QJsonArray roomIds; const auto* user = it.key(); for (; it != directChats.end() && it.key() == user; ++it) @@ -1000,8 +949,8 @@ QJsonObject toJson(const Connection::DirectChatsMap& directChats) return json; } -void Connection::Private::broadcastDirectChatUpdates(const DirectChatsMap& additions, - const DirectChatsMap& removals) +void Connection::Private::broadcastDirectChatUpdates( + const DirectChatsMap& additions, const DirectChatsMap& removals) { q->callApi(userId, QStringLiteral("m.direct"), toJson(directChats)); @@ -1023,19 +972,19 @@ void Connection::addToDirectChats(const Room* room, User* user) void Connection::removeFromDirectChats(const QString& roomId, User* user) { Q_ASSERT(!roomId.isEmpty()); - if ((user != nullptr && !d->directChats.contains(user, roomId)) || - d->directChats.key(roomId) == nullptr) + if ((user != nullptr && !d->directChats.contains(user, roomId)) + || d->directChats.key(roomId) == nullptr) return; DirectChatsMap removals; - if (user != nullptr) - { + if (user != nullptr) { removals.insert(user, roomId); d->directChats.remove(user, roomId); d->directChatUsers.remove(roomId, user); } else { - removals = erase_if(d->directChats, - [&roomId] (auto it) { return it.value() == roomId; }); + removals = erase_if(d->directChats, [&roomId](auto it) { + return it.value() == roomId; + }); d->directChatUsers.remove(roomId); } d->broadcastDirectChatUpdates({}, removals); @@ -1068,11 +1017,10 @@ void Connection::addToIgnoredUsers(const User* user) Q_ASSERT(user != nullptr); auto ignoreList = ignoredUsers(); - if (!ignoreList.contains(user->id())) - { + if (!ignoreList.contains(user->id())) { ignoreList.insert(user->id()); d->packAndSendAccountData(ignoreList); - emit ignoredUsersListChanged({{ user->id() }}, {}); + emit ignoredUsersListChanged({ { user->id() } }, {}); } } @@ -1081,17 +1029,13 @@ void Connection::removeFromIgnoredUsers(const User* user) Q_ASSERT(user != nullptr); auto ignoreList = ignoredUsers(); - if (ignoreList.remove(user->id()) != 0) - { + if (ignoreList.remove(user->id()) != 0) { d->packAndSendAccountData(ignoreList); - emit ignoredUsersListChanged({}, {{ user->id() }}); + emit ignoredUsersListChanged({}, { { user->id() } }); } } -QMap Connection::users() const -{ - return d->userMap; -} +QMap Connection::users() const { return d->userMap; } const ConnectionData* Connection::connectionData() const { @@ -1106,58 +1050,52 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) // If joinState.omitted(), all joinState == comparisons below are false. const auto roomKey = qMakePair(id, joinState == JoinState::Invite); auto* room = d->roomMap.value(roomKey, nullptr); - if (room) - { + if (room) { // Leave is a special case because in transition (5a) (see the .h file) // joinState == room->joinState but we still have to preempt the Invite // and emit a signal. For Invite and Join, there's no such problem. if (room->joinState() == joinState && joinState != JoinState::Leave) return room; - } else if (joinState.omitted()) - { + } else if (joinState.omitted()) { // No Join and Leave, maybe Invite? - room = d->roomMap.value({id, true}, nullptr); + room = d->roomMap.value({ id, true }, nullptr); if (room) return room; // No Invite either, setup a new room object below } - if (!room) - { + if (!room) { room = roomFactory()(this, id, - joinState.omitted() ? JoinState::Join : joinState.value()); - if (!room) - { + joinState.omitted() ? JoinState::Join + : joinState.value()); + if (!room) { qCCritical(MAIN) << "Failed to create a room" << id; return nullptr; } d->roomMap.insert(roomKey, room); d->firstTimeRooms.push_back(room); - connect(room, &Room::beforeDestruction, - this, &Connection::aboutToDeleteRoom); + connect(room, &Room::beforeDestruction, this, + &Connection::aboutToDeleteRoom); emit newRoom(room); } if (joinState.omitted()) return room; - if (joinState == JoinState::Invite) - { + if (joinState == JoinState::Invite) { // prev is either Leave or nullptr - auto* prev = d->roomMap.value({id, false}, nullptr); + auto* prev = d->roomMap.value({ id, false }, nullptr); emit invitedRoom(room, prev); - } - else - { + } else { room->setJoinState(joinState.value()); // Preempt the Invite room (if any) with a room in Join/Leave state. - auto* prevInvite = d->roomMap.take({id, true}); + auto* prevInvite = d->roomMap.take({ id, true }); if (joinState == JoinState::Join) emit joinedRoom(room, prevInvite); else if (joinState == JoinState::Leave) emit leftRoom(room, prevInvite); - if (prevInvite) - { - qCDebug(MAIN) << "Deleting Invite state for room" << prevInvite->id(); + if (prevInvite) { + qCDebug(MAIN) << "Deleting Invite state for room" + << prevInvite->id(); emit prevInvite->beforeDestruction(prevInvite); prevInvite->deleteLater(); } @@ -1176,15 +1114,9 @@ void Connection::setUserFactory(user_factory_t f) _userFactory = std::move(f); } -room_factory_t Connection::roomFactory() -{ - return _roomFactory; -} +room_factory_t Connection::roomFactory() { return _roomFactory; } -user_factory_t Connection::userFactory() -{ - return _userFactory; -} +user_factory_t Connection::userFactory() { return _userFactory; } room_factory_t Connection::_roomFactory = defaultRoomFactory<>(); user_factory_t Connection::_userFactory = defaultUserFactory<>(); @@ -1210,16 +1142,15 @@ void Connection::saveRoomState(Room* r) const return; QFile outRoomFile { stateCachePath() % SyncData::fileNameForRoom(r->id()) }; - if (outRoomFile.open(QFile::WriteOnly)) - { + if (outRoomFile.open(QFile::WriteOnly)) { QJsonDocument json { r->toJson() }; auto data = d->cacheToBinary ? json.toBinaryData() : json.toJson(QJsonDocument::Compact); outRoomFile.write(data.data(), data.size()); qCDebug(MAIN) << "Room state cache saved to" << outRoomFile.fileName(); } else { - qCWarning(MAIN) << "Error opening" << outRoomFile.fileName() - << ":" << outRoomFile.errorString(); + qCWarning(MAIN) << "Error opening" << outRoomFile.fileName() << ":" + << outRoomFile.errorString(); } } @@ -1228,29 +1159,31 @@ void Connection::saveState() const if (!d->cacheState) return; - QElapsedTimer et; et.start(); + QElapsedTimer et; + et.start(); QFile outFile { stateCachePath() % "state.json" }; - if (!outFile.open(QFile::WriteOnly)) - { - qCWarning(MAIN) << "Error opening" << outFile.fileName() - << ":" << outFile.errorString(); + if (!outFile.open(QFile::WriteOnly)) { + qCWarning(MAIN) << "Error opening" << outFile.fileName() << ":" + << outFile.errorString(); qCWarning(MAIN) << "Caching the rooms state disabled"; d->cacheState = false; return; } QJsonObject rootObj { - { QStringLiteral("cache_version"), QJsonObject { - { QStringLiteral("major"), SyncData::cacheVersion().first }, - { QStringLiteral("minor"), SyncData::cacheVersion().second } - }}}; + { QStringLiteral("cache_version"), + QJsonObject { + { QStringLiteral("major"), SyncData::cacheVersion().first }, + { QStringLiteral("minor"), + SyncData::cacheVersion().second } } } + }; { QJsonObject rooms; QJsonObject inviteRooms; for (const auto* i : roomMap()) // Pass on rooms in Leave state (i->joinState() == JoinState::Invite ? inviteRooms : rooms) - .insert(i->id(), QJsonValue::Null); + .insert(i->id(), QJsonValue::Null); QJsonObject roomObj; if (!rooms.isEmpty()) @@ -1262,20 +1195,20 @@ void Connection::saveState() const rootObj.insert("rooms", roomObj); } { - QJsonArray accountDataEvents { - basicEventJson(QStringLiteral("m.direct"), toJson(d->directChats)) - }; - for (const auto &e : d->accountData) + QJsonArray accountDataEvents { basicEventJson( + QStringLiteral("m.direct"), toJson(d->directChats)) }; + for (const auto& e : d->accountData) accountDataEvents.append( - basicEventJson(e.first, e.second->contentJson())); + basicEventJson(e.first, e.second->contentJson())); rootObj.insert("account_data", - QJsonObject {{ QStringLiteral("events"), accountDataEvents }}); + QJsonObject { { QStringLiteral("events"), + accountDataEvents } }); } QJsonDocument json { rootObj }; - auto data = d->cacheToBinary ? json.toBinaryData() : - json.toJson(QJsonDocument::Compact); + auto data = d->cacheToBinary ? json.toBinaryData() + : json.toJson(QJsonDocument::Compact); qCDebug(PROFILER) << "Cache for" << userId() << "generated in" << et; outFile.write(data.data(), data.size()); @@ -1287,14 +1220,14 @@ void Connection::loadState() if (!d->cacheState) return; - QElapsedTimer et; et.start(); + QElapsedTimer et; + et.start(); SyncData sync { stateCachePath() % "state.json" }; if (sync.nextBatch().isEmpty()) // No token means no cache by definition return; - if (!sync.unresolvedRooms().isEmpty()) - { + if (!sync.unresolvedRooms().isEmpty()) { qCWarning(MAIN) << "State cache incomplete, discarding"; return; } @@ -1302,7 +1235,8 @@ void Connection::loadState() // 1. Do initial sync on failed rooms without saving the nextBatch token // 2. Do the sync across all rooms as normal onSyncSuccess(std::move(sync), true); - qCDebug(PROFILER) << "*** Cached state for" << userId() << "loaded in" << et; + qCDebug(PROFILER) << "*** Cached state for" << userId() << "loaded in" + << et; } QString Connection::stateCachePath() const @@ -1312,29 +1246,21 @@ QString Connection::stateCachePath() const return cacheLocation(safeUserId); } -bool Connection::cacheState() const -{ - return d->cacheState; -} +bool Connection::cacheState() const { return d->cacheState; } void Connection::setCacheState(bool newValue) { - if (d->cacheState != newValue) - { + if (d->cacheState != newValue) { d->cacheState = newValue; emit cacheStateChanged(); } } -bool QMatrixClient::Connection::lazyLoading() const -{ - return d->lazyLoading; -} +bool QMatrixClient::Connection::lazyLoading() const { return d->lazyLoading; } void QMatrixClient::Connection::setLazyLoading(bool newValue) { - if (d->lazyLoading != newValue) - { + if (d->lazyLoading != newValue) { d->lazyLoading = newValue; emit lazyLoadingChanged(); } @@ -1343,8 +1269,8 @@ void QMatrixClient::Connection::setLazyLoading(bool newValue) void Connection::getTurnServers() { auto job = callApi(); - connect(job, &GetTurnServerJob::success, - this, [=] { emit turnServersChanged(job->data()); }); + connect(job, &GetTurnServerJob::success, this, + [=] { emit turnServersChanged(job->data()); }); } const QString Connection::SupportedRoomVersion::StableTag = @@ -1376,7 +1302,8 @@ inline bool roomVersionLess(const Connection::SupportedRoomVersion& v1, return ok1 && ok2 ? vNum1 < vNum2 : v1.id < v2.id; } -QVector Connection::availableRoomVersions() const +QVector +Connection::availableRoomVersions() const { Q_ASSERT(!d->capabilities.roomVersions.omitted()); QVector result; @@ -1386,8 +1313,9 @@ QVector Connection::availableRoomVersions() co result.push_back({ it.key(), it.value() }); // Put stable versions over unstable; within each group, // sort numeric versions as numbers, the rest as strings. - const auto mid = std::partition(result.begin(), result.end(), - std::mem_fn(&SupportedRoomVersion::isStable)); + const auto mid = + std::partition(result.begin(), result.end(), + std::mem_fn(&SupportedRoomVersion::isStable)); std::sort(result.begin(), mid, roomVersionLess); std::sort(mid, result.end(), roomVersionLess); diff --git a/lib/connection.h b/lib/connection.h index b22d63da..50f7b71b 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -13,25 +13,24 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "csapi/create_room.h" -#include "joinstate.h" #include "events/accountdataevents.h" +#include "joinstate.h" #include "qt_connection_util.h" #include -#include #include +#include #include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Room; class User; class ConnectionData; @@ -53,8 +52,8 @@ namespace QMatrixClient class Connection; - using room_factory_t = std::function; + using room_factory_t = + std::function; using user_factory_t = std::function; /** The default factory to create room objects @@ -65,10 +64,9 @@ namespace QMatrixClient template static inline room_factory_t defaultRoomFactory() { - return [](Connection* c, const QString& id, JoinState js) - { - return new T(c, id, js); - }; + return [](Connection* c, const QString& id, JoinState js) { + return new T(c, id, js); + }; } /** The default factory to create user objects @@ -79,10 +77,7 @@ namespace QMatrixClient template static inline user_factory_t defaultUserFactory() { - return [](Connection* c, const QString& id) - { - return new T(id, c); - }; + return [](Connection* c, const QString& id) { return new T(id, c); }; } /** Enumeration with flags defining the network job running policy @@ -92,667 +87,685 @@ namespace QMatrixClient */ enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; - class Connection: public QObject { - Q_OBJECT - - /** Whether or not the rooms state should be cached locally - * \sa loadState(), saveState() - */ - Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) - Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged) - Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) - Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) - Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded) - Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) - Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) - Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) + class Connection : public QObject + { + Q_OBJECT + + /** Whether or not the rooms state should be cached locally + * \sa loadState(), saveState() + */ + Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) + Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged) + Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) + Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) + Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY + capabilitiesLoaded) + Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY + homeserverChanged) + Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY + cacheStateChanged) + Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY + lazyLoadingChanged) public: - // Room ids, rather than room pointers, are used in the direct chat - // map types because the library keeps Invite rooms separate from - // rooms in Join and Leave state; and direct chats in account data - // are stored with no regard to their state. - using DirectChatsMap = QMultiHash; - using DirectChatUsersMap = QMultiHash; - using IgnoredUsersList = IgnoredUsersEvent::content_type; - - using UsersToDevicesToEvents = + // Room ids, rather than room pointers, are used in the direct chat + // map types because the library keeps Invite rooms separate from + // rooms in Join and Leave state; and direct chats in account data + // are stored with no regard to their state. + using DirectChatsMap = QMultiHash; + using DirectChatUsersMap = QMultiHash; + using IgnoredUsersList = IgnoredUsersEvent::content_type; + + using UsersToDevicesToEvents = std::unordered_map>; - - enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob - - explicit Connection(QObject* parent = nullptr); - explicit Connection(const QUrl& server, QObject* parent = nullptr); - virtual ~Connection(); - - /** Get all Invited and Joined rooms - * \return a hashmap from a composite key - room name and whether - * it's an Invite rather than Join - to room pointers - */ - QHash, Room*> roomMap() const; - - /** Check whether the account has data of the given type - * Direct chats map is not supported by this method _yet_. - */ - bool hasAccountData(const QString& type) const; - - /** Get a generic account data event of the given type - * This returns an account data event of the given type - * stored on the server. Direct chats map cannot be retrieved - * using this method _yet_; use directChats() instead. - */ - const EventPtr& accountData(const QString& type) const; - - /** Get a generic account data event of the given type - * This returns an account data event of the given type - * stored on the server. Direct chats map cannot be retrieved - * using this method _yet_; use directChats() instead. - */ - template - const typename EventT::content_type accountData() const - { - if (const auto& eventPtr = accountData(EventT::matrixTypeId())) - return eventPtr->content(); - return {}; - } - - /** Get account data as a JSON object - * This returns the content part of the account data event - * of the given type. Direct chats map cannot be retrieved using - * this method _yet_; use directChats() instead. - */ - Q_INVOKABLE QJsonObject accountDataJson(const QString& type) const; - - /** Set a generic account data event of the given type */ - void setAccountData(EventPtr&& event); - - Q_INVOKABLE void setAccountData(const QString& type, - const QJsonObject& content); - - /** Get all Invited and Joined rooms grouped by tag - * \return a hashmap from tag name to a vector of room pointers, - * sorted by their order in the tag - details are at - * https://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#id95 - */ - QHash> tagsToRooms() const; - - /** Get all room tags known on this connection */ - QStringList tagNames() const; - - /** Get the list of rooms with the specified tag */ - QVector roomsWithTag(const QString& tagName) const; - - /** Mark the room as a direct chat with the user - * This function marks \p room as a direct chat with \p user. - * Emits the signal synchronously, without waiting to complete - * synchronisation with the server. - * - * \sa directChatsListChanged - */ - void addToDirectChats(const Room* room, User* user); - - /** Unmark the room from direct chats - * This function removes the room id from direct chats either for - * a specific \p user or for all users if \p user in nullptr. - * The room id is used to allow removal of, e.g., ids of forgotten - * rooms; a Room object need not exist. Emits the signal - * immediately, without waiting to complete synchronisation with - * the server. - * - * \sa directChatsListChanged - */ - void removeFromDirectChats(const QString& roomId, - User* user = nullptr); - - /** Check whether the room id corresponds to a direct chat */ - bool isDirectChat(const QString& roomId) const; - - /** Get the whole map from users to direct chat rooms */ - DirectChatsMap directChats() const; - - /** Retrieve the list of users the room is a direct chat with - * @return The list of users for which this room is marked as - * a direct chat; an empty list if the room is not a direct chat - */ - QList directChatUsers(const Room* room) const; - - /** Check whether a particular user is in the ignore list */ - bool isIgnored(const User* user) const; - - /** Get the whole list of ignored users */ - IgnoredUsersList ignoredUsers() const; - - /** Add the user to the ignore list - * The change signal is emitted synchronously, without waiting - * to complete synchronisation with the server. - * - * \sa ignoredUsersListChanged - */ - void addToIgnoredUsers(const User* user); - - /** Remove the user from the ignore list */ - /** Similar to adding, the change signal is emitted synchronously. - * - * \sa ignoredUsersListChanged - */ - void removeFromIgnoredUsers(const User* user); - - /** Get the full list of users known to this account */ - QMap users() const; - - QUrl homeserver() const; - /** Find a room by its id and a mask of applicable states */ - Q_INVOKABLE Room* room(const QString& roomId, - JoinStates states = JoinState::Invite|JoinState::Join) const; - /** Find a room by its alias and a mask of applicable states */ - Q_INVOKABLE Room* roomByAlias(const QString& roomAlias, - JoinStates states = JoinState::Invite|JoinState::Join) const; - /** Update the internal map of room aliases to IDs */ - /// This is used for internal bookkeeping of rooms. Do NOT use - /// it to try change aliases, use Room::setAliases instead - void updateRoomAliases(const QString& roomId, - const QStringList& previousRoomAliases, - const QStringList& roomAliases); - Q_INVOKABLE Room* invitation(const QString& roomId) const; - Q_INVOKABLE User* user(const QString& userId); - const User* user() const; - User* user(); - QString userId() const; - QString deviceId() const; - QByteArray accessToken() const; - Q_INVOKABLE SyncJob* syncJob() const; - Q_INVOKABLE int millisToReconnect() const; - - [[deprecated("Use accessToken() instead")]] - Q_INVOKABLE QString token() const; - Q_INVOKABLE void getTurnServers(); - - struct SupportedRoomVersion - { - QString id; - QString status; - - static const QString StableTag; // "stable", as of CS API 0.5 - bool isStable() const { return status == StableTag; } - - friend QDebug operator<<(QDebug dbg, - const SupportedRoomVersion& v) - { - QDebugStateSaver _(dbg); - return dbg.nospace() << v.id << '/' << v.status; - } - }; - - /// Get the room version recommended by the server - /** Only works after server capabilities have been loaded. - * \sa loadingCapabilities */ - QString defaultRoomVersion() const; - /// Get the room version considered stable by the server - /** Only works after server capabilities have been loaded. - * \sa loadingCapabilities */ - QStringList stableRoomVersions() const; - /// Get all room versions supported by the server - /** Only works after server capabilities have been loaded. - * \sa loadingCapabilities */ - QVector availableRoomVersions() const; - - /** - * Call this before first sync to load from previously saved file. - * - * \param fromFile A local path to read the state from. Uses QUrl - * to be QML-friendly. Empty parameter means using a path - * defined by stateCachePath(). - */ - Q_INVOKABLE void loadState(); - /** - * This method saves the current state of rooms (but not messages - * in them) to a local cache file, so that it could be loaded by - * loadState() on a next run of the client. - * - * \param toFile A local path to save the state to. Uses QUrl to be - * QML-friendly. Empty parameter means using a path defined by - * stateCachePath(). - */ - Q_INVOKABLE void saveState() const; - - /// This method saves the current state of a single room. - void saveRoomState(Room* r) const; - - /** - * The default path to store the cached room state, defined as - * follows: - * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) + _safeUserId + "_state.json" - * where `_safeUserId` is userId() with `:` (colon) replaced with - * `_` (underscore) - * /see loadState(), saveState() - */ - Q_INVOKABLE QString stateCachePath() const; - - bool cacheState() const; - void setCacheState(bool newValue); - - bool lazyLoading() const; - void setLazyLoading(bool newValue); - - /** Start a job of a specified type with specified arguments and policy - * - * This is a universal method to start a job of a type passed - * as a template parameter. The policy allows to fine-tune the way - * the job is executed - as of this writing it means a choice - * between "foreground" and "background". - * - * \param runningPolicy controls how the job is executed - * \param jobArgs arguments to the job constructor - * - * \sa BaseJob::isBackground. QNetworkRequest::BackgroundRequestAttribute - */ - template - JobT* callApi(RunningPolicy runningPolicy, - JobArgTs&&... jobArgs) const + std::unordered_map>; + + enum RoomVisibility { + PublishRoom, + UnpublishRoom + }; // FIXME: Should go inside CreateRoomJob + + explicit Connection(QObject* parent = nullptr); + explicit Connection(const QUrl& server, QObject* parent = nullptr); + virtual ~Connection(); + + /** Get all Invited and Joined rooms + * \return a hashmap from a composite key - room name and whether + * it's an Invite rather than Join - to room pointers + */ + QHash, Room*> roomMap() const; + + /** Check whether the account has data of the given type + * Direct chats map is not supported by this method _yet_. + */ + bool hasAccountData(const QString& type) const; + + /** Get a generic account data event of the given type + * This returns an account data event of the given type + * stored on the server. Direct chats map cannot be retrieved + * using this method _yet_; use directChats() instead. + */ + const EventPtr& accountData(const QString& type) const; + + /** Get a generic account data event of the given type + * This returns an account data event of the given type + * stored on the server. Direct chats map cannot be retrieved + * using this method _yet_; use directChats() instead. + */ + template + const typename EventT::content_type accountData() const + { + if (const auto& eventPtr = accountData(EventT::matrixTypeId())) + return eventPtr->content(); + return {}; + } + + /** Get account data as a JSON object + * This returns the content part of the account data event + * of the given type. Direct chats map cannot be retrieved using + * this method _yet_; use directChats() instead. + */ + Q_INVOKABLE QJsonObject accountDataJson(const QString& type) const; + + /** Set a generic account data event of the given type */ + void setAccountData(EventPtr&& event); + + Q_INVOKABLE void setAccountData(const QString& type, + const QJsonObject& content); + + /** Get all Invited and Joined rooms grouped by tag + * \return a hashmap from tag name to a vector of room pointers, + * sorted by their order in the tag - details are at + * https://matrix.org/speculator/spec/drafts%2Fe2e/client_server/unstable.html#id95 + */ + QHash> tagsToRooms() const; + + /** Get all room tags known on this connection */ + QStringList tagNames() const; + + /** Get the list of rooms with the specified tag */ + QVector roomsWithTag(const QString& tagName) const; + + /** Mark the room as a direct chat with the user + * This function marks \p room as a direct chat with \p user. + * Emits the signal synchronously, without waiting to complete + * synchronisation with the server. + * + * \sa directChatsListChanged + */ + void addToDirectChats(const Room* room, User* user); + + /** Unmark the room from direct chats + * This function removes the room id from direct chats either for + * a specific \p user or for all users if \p user in nullptr. + * The room id is used to allow removal of, e.g., ids of forgotten + * rooms; a Room object need not exist. Emits the signal + * immediately, without waiting to complete synchronisation with + * the server. + * + * \sa directChatsListChanged + */ + void removeFromDirectChats(const QString& roomId, User* user = nullptr); + + /** Check whether the room id corresponds to a direct chat */ + bool isDirectChat(const QString& roomId) const; + + /** Get the whole map from users to direct chat rooms */ + DirectChatsMap directChats() const; + + /** Retrieve the list of users the room is a direct chat with + * @return The list of users for which this room is marked as + * a direct chat; an empty list if the room is not a direct chat + */ + QList directChatUsers(const Room* room) const; + + /** Check whether a particular user is in the ignore list */ + bool isIgnored(const User* user) const; + + /** Get the whole list of ignored users */ + IgnoredUsersList ignoredUsers() const; + + /** Add the user to the ignore list + * The change signal is emitted synchronously, without waiting + * to complete synchronisation with the server. + * + * \sa ignoredUsersListChanged + */ + void addToIgnoredUsers(const User* user); + + /** Remove the user from the ignore list */ + /** Similar to adding, the change signal is emitted synchronously. + * + * \sa ignoredUsersListChanged + */ + void removeFromIgnoredUsers(const User* user); + + /** Get the full list of users known to this account */ + QMap users() const; + + QUrl homeserver() const; + /** Find a room by its id and a mask of applicable states */ + Q_INVOKABLE Room* room(const QString& roomId, + JoinStates states = JoinState::Invite + | JoinState::Join) const; + /** Find a room by its alias and a mask of applicable states */ + Q_INVOKABLE Room* roomByAlias(const QString& roomAlias, + JoinStates states = JoinState::Invite + | JoinState::Join) const; + /** Update the internal map of room aliases to IDs */ + /// This is used for internal bookkeeping of rooms. Do NOT use + /// it to try change aliases, use Room::setAliases instead + void updateRoomAliases(const QString& roomId, + const QStringList& previousRoomAliases, + const QStringList& roomAliases); + Q_INVOKABLE Room* invitation(const QString& roomId) const; + Q_INVOKABLE User* user(const QString& userId); + const User* user() const; + User* user(); + QString userId() const; + QString deviceId() const; + QByteArray accessToken() const; + Q_INVOKABLE SyncJob* syncJob() const; + Q_INVOKABLE int millisToReconnect() const; + + [[deprecated("Use accessToken() instead")]] Q_INVOKABLE QString + token() const; + Q_INVOKABLE void getTurnServers(); + + struct SupportedRoomVersion { + QString id; + QString status; + + static const QString StableTag; // "stable", as of CS API 0.5 + bool isStable() const { return status == StableTag; } + + friend QDebug operator<<(QDebug dbg, const SupportedRoomVersion& v) { - auto job = new JobT(std::forward(jobArgs)...); - connect(job, &BaseJob::failure, this, &Connection::requestFailed); - job->start(connectionData(), runningPolicy&BackgroundRequest); - return job; + QDebugStateSaver _(dbg); + return dbg.nospace() << v.id << '/' << v.status; } - - /** Start a job of a specified type with specified arguments - * - * This is an overload that calls the job with "foreground" policy. - */ - template - JobT* callApi(JobArgTs&&... jobArgs) const - { - return callApi(ForegroundRequest, - std::forward(jobArgs)...); - } - - /** Generate a new transaction id. Transaction id's are unique within - * a single Connection object - */ - Q_INVOKABLE QByteArray generateTxnId() const; - - /// Set a room factory function - static void setRoomFactory(room_factory_t f); - - /// Set a user factory function - static void setUserFactory(user_factory_t f); - - /// Get a room factory function - static room_factory_t roomFactory(); - - /// Get a user factory function - static user_factory_t userFactory(); - - /// Set the room factory to default with the overriden room type - template - static void setRoomType() { setRoomFactory(defaultRoomFactory()); } - - /// Set the user factory to default with the overriden user type - template - static void setUserType() { setUserFactory(defaultUserFactory()); } + }; + + /// Get the room version recommended by the server + /** Only works after server capabilities have been loaded. + * \sa loadingCapabilities */ + QString defaultRoomVersion() const; + /// Get the room version considered stable by the server + /** Only works after server capabilities have been loaded. + * \sa loadingCapabilities */ + QStringList stableRoomVersions() const; + /// Get all room versions supported by the server + /** Only works after server capabilities have been loaded. + * \sa loadingCapabilities */ + QVector availableRoomVersions() const; + + /** + * Call this before first sync to load from previously saved file. + * + * \param fromFile A local path to read the state from. Uses QUrl + * to be QML-friendly. Empty parameter means using a path + * defined by stateCachePath(). + */ + Q_INVOKABLE void loadState(); + /** + * This method saves the current state of rooms (but not messages + * in them) to a local cache file, so that it could be loaded by + * loadState() on a next run of the client. + * + * \param toFile A local path to save the state to. Uses QUrl to be + * QML-friendly. Empty parameter means using a path defined by + * stateCachePath(). + */ + Q_INVOKABLE void saveState() const; + + /// This method saves the current state of a single room. + void saveRoomState(Room* r) const; + + /** + * The default path to store the cached room state, defined as + * follows: + * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) + * + _safeUserId + "_state.json" where `_safeUserId` is userId() with + * `:` (colon) replaced with + * `_` (underscore) + * /see loadState(), saveState() + */ + Q_INVOKABLE QString stateCachePath() const; + + bool cacheState() const; + void setCacheState(bool newValue); + + bool lazyLoading() const; + void setLazyLoading(bool newValue); + + /** Start a job of a specified type with specified arguments and policy + * + * This is a universal method to start a job of a type passed + * as a template parameter. The policy allows to fine-tune the way + * the job is executed - as of this writing it means a choice + * between "foreground" and "background". + * + * \param runningPolicy controls how the job is executed + * \param jobArgs arguments to the job constructor + * + * \sa BaseJob::isBackground. + * QNetworkRequest::BackgroundRequestAttribute + */ + template + JobT* callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs) const + { + auto job = new JobT(std::forward(jobArgs)...); + connect(job, &BaseJob::failure, this, &Connection::requestFailed); + job->start(connectionData(), runningPolicy & BackgroundRequest); + return job; + } + + /** Start a job of a specified type with specified arguments + * + * This is an overload that calls the job with "foreground" policy. + */ + template + JobT* callApi(JobArgTs&&... jobArgs) const + { + return callApi(ForegroundRequest, + std::forward(jobArgs)...); + } + + /** Generate a new transaction id. Transaction id's are unique within + * a single Connection object + */ + Q_INVOKABLE QByteArray generateTxnId() const; + + /// Set a room factory function + static void setRoomFactory(room_factory_t f); + + /// Set a user factory function + static void setUserFactory(user_factory_t f); + + /// Get a room factory function + static room_factory_t roomFactory(); + + /// Get a user factory function + static user_factory_t userFactory(); + + /// Set the room factory to default with the overriden room type + template static void setRoomType() + { + setRoomFactory(defaultRoomFactory()); + } + + /// Set the user factory to default with the overriden user type + template static void setUserType() + { + setUserFactory(defaultUserFactory()); + } public slots: - /** Set the homeserver base URL */ - void setHomeserver(const QUrl& baseUrl); - - /** Determine and set the homeserver from domain or MXID */ - void resolveServer(const QString& mxidOrDomain); - - void connectToServer(const QString& user, const QString& password, - const QString& initialDeviceName, - const QString& deviceId = {}); - void connectWithToken(const QString& userId, const QString& accessToken, - const QString& deviceId); - /// Explicitly request capabilities from the server - void reloadCapabilities(); - - /// Find out if capabilites are still loading from the server - bool loadingCapabilities() const; - - /** @deprecated Use stopSync() instead */ - void disconnectFromServer() { stopSync(); } - void logout(); - - void sync(int timeout = -1); - void syncLoop(int timeout = -1); - - void stopSync(); - QString nextBatchToken() const; - - virtual MediaThumbnailJob* getThumbnail(const QString& mediaId, - QSize requestedSize, RunningPolicy policy = BackgroundRequest) const; - MediaThumbnailJob* getThumbnail(const QUrl& url, - QSize requestedSize, RunningPolicy policy = BackgroundRequest) const; - MediaThumbnailJob* getThumbnail(const QUrl& url, - int requestedWidth, int requestedHeight, - RunningPolicy policy = BackgroundRequest) const; - - // QIODevice* should already be open - UploadContentJob* uploadContent(QIODevice* contentSource, - const QString& filename = {}, - const QString& overrideContentType = {}) const; - UploadContentJob* uploadFile(const QString& fileName, - const QString& overrideContentType = {}); - GetContentJob* getContent(const QString& mediaId) const; - GetContentJob* getContent(const QUrl& url) const; - // If localFilename is empty, a temporary file will be created - DownloadFileJob* downloadFile(const QUrl& url, - const QString& localFilename = {}) const; - - /** - * \brief Create a room (generic method) - * This method allows to customize room entirely to your liking, - * providing all the attributes the original CS API provides. - */ - CreateRoomJob* createRoom(RoomVisibility visibility, - const QString& alias, const QString& name, const QString& topic, - QStringList invites, const QString& presetName = {}, - const QString& roomVersion = {}, bool isDirect = false, - const QVector& initialState = {}, - const QVector& invite3pids = {}, - const QJsonObject& creationContent = {}); - - /** Get a direct chat with a single user - * This method may return synchronously or asynchoronously depending - * on whether a direct chat room with the respective person exists - * already. - * - * \sa directChatAvailable - */ - void requestDirectChat(const QString& userId); - - /** Get a direct chat with a single user - * This method may return synchronously or asynchoronously depending - * on whether a direct chat room with the respective person exists - * already. - * - * \sa directChatAvailable - */ - void requestDirectChat(User* u); - - /** Run an operation in a direct chat with the user - * This method may return synchronously or asynchoronously depending - * on whether a direct chat room with the respective person exists - * already. Instead of emitting a signal it executes the passed - * function object with the direct chat room as its parameter. - */ - void doInDirectChat(const QString& userId, - const std::function& operation); - - /** Run an operation in a direct chat with the user - * This method may return synchronously or asynchoronously depending - * on whether a direct chat room with the respective person exists - * already. Instead of emitting a signal it executes the passed - * function object with the direct chat room as its parameter. - */ - void doInDirectChat(User* u, - const std::function& operation); - - /** Create a direct chat with a single user, optional name and topic - * A room will always be created, unlike in requestDirectChat. - * It is advised to use requestDirectChat as a default way of getting - * one-on-one with a person, and only use createDirectChat when - * a new creation is explicitly desired. - */ - CreateRoomJob* createDirectChat(const QString& userId, - const QString& topic = {}, const QString& name = {}); - - virtual JoinRoomJob* joinRoom(const QString& roomAlias, - const QStringList& serverNames = {}); - - /** Sends /forget to the server and also deletes room locally. - * This method is in Connection, not in Room, since it's a - * room lifecycle operation, and Connection is an acting room manager. - * It ensures that the local user is not a member of a room (running /leave, - * if necessary) then issues a /forget request and if that one doesn't fail - * deletion of the local Room object is ensured. - * \param id - the room id to forget - * \return - the ongoing /forget request to the server; note that the - * success() signal of this request is connected to deleteLater() - * of a respective room so by the moment this finishes, there might be no - * Room object anymore. - */ - ForgetRoomJob* forgetRoom(const QString& id); - - SendToDeviceJob* sendToDevices(const QString& eventType, - const UsersToDevicesToEvents& eventsMap) const; - - /** \deprecated This method is experimental and may be removed any time */ - SendMessageJob* sendMessage(const QString& roomId, - const RoomEvent& event) const; - - /** \deprecated Do not use this directly, use Room::leaveRoom() instead */ - virtual LeaveRoomJob* leaveRoom( Room* room ); - - // Old API that will be abolished any time soon. DO NOT USE. - - /** @deprecated Use callApi() or Room::postReceipt() instead */ - virtual PostReceiptJob* postReceipt(Room* room, - RoomEvent* event) const; + /** Set the homeserver base URL */ + void setHomeserver(const QUrl& baseUrl); + + /** Determine and set the homeserver from domain or MXID */ + void resolveServer(const QString& mxidOrDomain); + + void connectToServer(const QString& user, const QString& password, + const QString& initialDeviceName, + const QString& deviceId = {}); + void connectWithToken(const QString& userId, const QString& accessToken, + const QString& deviceId); + /// Explicitly request capabilities from the server + void reloadCapabilities(); + + /// Find out if capabilites are still loading from the server + bool loadingCapabilities() const; + + /** @deprecated Use stopSync() instead */ + void disconnectFromServer() { stopSync(); } + void logout(); + + void sync(int timeout = -1); + void syncLoop(int timeout = -1); + + void stopSync(); + QString nextBatchToken() const; + + virtual MediaThumbnailJob* + getThumbnail(const QString& mediaId, QSize requestedSize, + RunningPolicy policy = BackgroundRequest) const; + MediaThumbnailJob* + getThumbnail(const QUrl& url, QSize requestedSize, + RunningPolicy policy = BackgroundRequest) const; + MediaThumbnailJob* + getThumbnail(const QUrl& url, int requestedWidth, int requestedHeight, + RunningPolicy policy = BackgroundRequest) const; + + // QIODevice* should already be open + UploadContentJob* + uploadContent(QIODevice* contentSource, const QString& filename = {}, + const QString& overrideContentType = {}) const; + UploadContentJob* uploadFile(const QString& fileName, + const QString& overrideContentType = {}); + GetContentJob* getContent(const QString& mediaId) const; + GetContentJob* getContent(const QUrl& url) const; + // If localFilename is empty, a temporary file will be created + DownloadFileJob* downloadFile(const QUrl& url, + const QString& localFilename = {}) const; + + /** + * \brief Create a room (generic method) + * This method allows to customize room entirely to your liking, + * providing all the attributes the original CS API provides. + */ + CreateRoomJob* + createRoom(RoomVisibility visibility, const QString& alias, + const QString& name, const QString& topic, + QStringList invites, const QString& presetName = {}, + const QString& roomVersion = {}, bool isDirect = false, + const QVector& initialState = {}, + const QVector& invite3pids = {}, + const QJsonObject& creationContent = {}); + + /** Get a direct chat with a single user + * This method may return synchronously or asynchoronously depending + * on whether a direct chat room with the respective person exists + * already. + * + * \sa directChatAvailable + */ + void requestDirectChat(const QString& userId); + + /** Get a direct chat with a single user + * This method may return synchronously or asynchoronously depending + * on whether a direct chat room with the respective person exists + * already. + * + * \sa directChatAvailable + */ + void requestDirectChat(User* u); + + /** Run an operation in a direct chat with the user + * This method may return synchronously or asynchoronously depending + * on whether a direct chat room with the respective person exists + * already. Instead of emitting a signal it executes the passed + * function object with the direct chat room as its parameter. + */ + void doInDirectChat(const QString& userId, + const std::function& operation); + + /** Run an operation in a direct chat with the user + * This method may return synchronously or asynchoronously depending + * on whether a direct chat room with the respective person exists + * already. Instead of emitting a signal it executes the passed + * function object with the direct chat room as its parameter. + */ + void doInDirectChat(User* u, + const std::function& operation); + + /** Create a direct chat with a single user, optional name and topic + * A room will always be created, unlike in requestDirectChat. + * It is advised to use requestDirectChat as a default way of getting + * one-on-one with a person, and only use createDirectChat when + * a new creation is explicitly desired. + */ + CreateRoomJob* createDirectChat(const QString& userId, + const QString& topic = {}, + const QString& name = {}); + + virtual JoinRoomJob* joinRoom(const QString& roomAlias, + const QStringList& serverNames = {}); + + /** Sends /forget to the server and also deletes room locally. + * This method is in Connection, not in Room, since it's a + * room lifecycle operation, and Connection is an acting room manager. + * It ensures that the local user is not a member of a room (running + * /leave, if necessary) then issues a /forget request and if that one + * doesn't fail deletion of the local Room object is ensured. \param id + * - the room id to forget \return - the ongoing /forget request to the + * server; note that the success() signal of this request is connected + * to deleteLater() of a respective room so by the moment this finishes, + * there might be no Room object anymore. + */ + ForgetRoomJob* forgetRoom(const QString& id); + + SendToDeviceJob* + sendToDevices(const QString& eventType, + const UsersToDevicesToEvents& eventsMap) const; + + /** \deprecated This method is experimental and may be removed any time + */ + SendMessageJob* sendMessage(const QString& roomId, + const RoomEvent& event) const; + + /** \deprecated Do not use this directly, use Room::leaveRoom() instead + */ + virtual LeaveRoomJob* leaveRoom(Room* room); + + // Old API that will be abolished any time soon. DO NOT USE. + + /** @deprecated Use callApi() or Room::postReceipt() + * instead */ + virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event) const; signals: - /** - * @deprecated - * This was a signal resulting from a successful resolveServer(). - * Since Connection now provides setHomeserver(), the HS URL - * may change even without resolveServer() invocation. Use - * homeserverChanged() instead of resolved(). You can also use - * connectToServer and connectWithToken without the HS URL set in - * advance (i.e. without calling resolveServer), as they now trigger - * server name resolution from MXID if the server URL is not valid. - */ - void resolved(); - void resolveError(QString error); - - void homeserverChanged(QUrl baseUrl); - void capabilitiesLoaded(); - - void connected(); - void reconnected(); //< \deprecated Use connected() instead - void loggedOut(); - /** Login data or state have changed - * - * This is a common change signal for userId, deviceId and - * accessToken - these properties normally only change at - * a successful login and logout and are constant at other times. - */ - void stateChanged(); - void loginError(QString message, QString details); - - /** A network request (job) failed - * - * @param request - the pointer to the failed job - */ - void requestFailed(BaseJob* request); - - /** A network request (job) failed due to network problems - * - * This is _only_ emitted when the job will retry on its own; - * once it gives up, requestFailed() will be emitted. - * - * @param message - message about the network problem - * @param details - raw error details, if any available - * @param retriesTaken - how many retries have already been taken - * @param nextRetryInMilliseconds - when the job will retry again - */ - void networkError(QString message, QString details, - int retriesTaken, int nextRetryInMilliseconds); - - void syncDone(); - void syncError(QString message, QString details); - - void newUser(User* user); - - /** - * \group Signals emitted on room transitions - * - * Note: Rooms in Invite state are always stored separately from - * rooms in Join/Leave state, because of special treatment of - * invite_state in Matrix CS API (see The Spec on /sync for details). - * Therefore, objects below are: r - room in Join/Leave state; - * i - room in Invite state - * - * 1. none -> Invite: newRoom(r), invitedRoom(r,nullptr) - * 2. none -> Join: newRoom(r), joinedRoom(r,nullptr) - * 3. none -> Leave: newRoom(r), leftRoom(r,nullptr) - * 4. Invite -> Join: - * newRoom(r), joinedRoom(r,i), aboutToDeleteRoom(i) - * 4a. Leave and Invite -> Join: - * joinedRoom(r,i), aboutToDeleteRoom(i) - * 5. Invite -> Leave: - * newRoom(r), leftRoom(r,i), aboutToDeleteRoom(i) - * 5a. Leave and Invite -> Leave: - * leftRoom(r,i), aboutToDeleteRoom(i) - * 6. Join -> Leave: leftRoom(r) - * 7. Leave -> Invite: newRoom(i), invitedRoom(i,r) - * 8. Leave -> Join: joinedRoom(r) - * The following transitions are only possible via forgetRoom() - * so far; if a room gets forgotten externally, sync won't tell - * about it: - * 9. any -> none: as any -> Leave, then aboutToDeleteRoom(r) - */ - - /** A new room object has been created */ - void newRoom(Room* room); - - /** A room invitation is seen for the first time - * - * If the same room is in Left state, it's passed in prev. Beware - * that initial sync will trigger this signal for all rooms in - * Invite state. - */ - void invitedRoom(Room* room, Room* prev); - - /** A joined room is seen for the first time - * - * It's not the same as receiving a room in "join" section of sync - * response (rooms will be there even after joining); it's also - * not (exactly) the same as actual joining action of a user (all - * rooms coming in initial sync will trigger this signal too). If - * this room was in Invite state before, the respective object is - * passed in prev (and it will be deleted shortly afterwards). - */ - void joinedRoom(Room* room, Room* prev); - - /** A room has just been left - * - * If this room has been in Invite state (as in case of rejecting - * an invitation), the respective object will be passed in prev - * (and will be deleted shortly afterwards). Note that, similar - * to invitedRoom and joinedRoom, this signal is triggered for all - * Left rooms upon initial sync (not only those that were left - * right before the sync). - */ - void leftRoom(Room* room, Room* prev); - - /** The room object is about to be deleted */ - void aboutToDeleteRoom(Room* room); - - /** The room has just been created by createRoom or requestDirectChat - * - * This signal is not emitted in usual room state transitions, - * only as an outcome of room creation operations invoked by - * the client. - * \note requestDirectChat doesn't necessarily create a new chat; - * use directChatAvailable signal if you just need to obtain - * a direct chat room. - */ - void createdRoom(Room* room); - - /** The first sync for the room has been completed - * - * This signal is emitted after the room has been synced the first - * time. This is the right signal to connect to if you need to - * access the room state (name, aliases, members); state transition - * signals (newRoom, joinedRoom etc.) come earlier, when the room - * has just been created. - */ - void loadedRoomState(Room* room); - - /** Account data (except direct chats) have changed */ - void accountDataChanged(QString type); - - /** The direct chat room is ready for using - * This signal is emitted upon any successful outcome from - * requestDirectChat. - */ - void directChatAvailable(Room* directChat); - - /** The list of direct chats has changed - * This signal is emitted every time when the mapping of users - * to direct chat rooms is changed (because of either local updates - * or a different list arrived from the server). - */ - void directChatsListChanged(DirectChatsMap additions, - DirectChatsMap removals); - - void ignoredUsersListChanged(IgnoredUsersList additions, - IgnoredUsersList removals); - - void cacheStateChanged(); - void lazyLoadingChanged(); - void turnServersChanged(const QJsonObject& servers); + /** + * @deprecated + * This was a signal resulting from a successful resolveServer(). + * Since Connection now provides setHomeserver(), the HS URL + * may change even without resolveServer() invocation. Use + * homeserverChanged() instead of resolved(). You can also use + * connectToServer and connectWithToken without the HS URL set in + * advance (i.e. without calling resolveServer), as they now trigger + * server name resolution from MXID if the server URL is not valid. + */ + void resolved(); + void resolveError(QString error); + + void homeserverChanged(QUrl baseUrl); + void capabilitiesLoaded(); + + void connected(); + void reconnected(); //< \deprecated Use connected() instead + void loggedOut(); + /** Login data or state have changed + * + * This is a common change signal for userId, deviceId and + * accessToken - these properties normally only change at + * a successful login and logout and are constant at other times. + */ + void stateChanged(); + void loginError(QString message, QString details); + + /** A network request (job) failed + * + * @param request - the pointer to the failed job + */ + void requestFailed(BaseJob* request); + + /** A network request (job) failed due to network problems + * + * This is _only_ emitted when the job will retry on its own; + * once it gives up, requestFailed() will be emitted. + * + * @param message - message about the network problem + * @param details - raw error details, if any available + * @param retriesTaken - how many retries have already been taken + * @param nextRetryInMilliseconds - when the job will retry again + */ + void networkError(QString message, QString details, int retriesTaken, + int nextRetryInMilliseconds); + + void syncDone(); + void syncError(QString message, QString details); + + void newUser(User* user); + + /** + * \group Signals emitted on room transitions + * + * Note: Rooms in Invite state are always stored separately from + * rooms in Join/Leave state, because of special treatment of + * invite_state in Matrix CS API (see The Spec on /sync for details). + * Therefore, objects below are: r - room in Join/Leave state; + * i - room in Invite state + * + * 1. none -> Invite: newRoom(r), invitedRoom(r,nullptr) + * 2. none -> Join: newRoom(r), joinedRoom(r,nullptr) + * 3. none -> Leave: newRoom(r), leftRoom(r,nullptr) + * 4. Invite -> Join: + * newRoom(r), joinedRoom(r,i), aboutToDeleteRoom(i) + * 4a. Leave and Invite -> Join: + * joinedRoom(r,i), aboutToDeleteRoom(i) + * 5. Invite -> Leave: + * newRoom(r), leftRoom(r,i), aboutToDeleteRoom(i) + * 5a. Leave and Invite -> Leave: + * leftRoom(r,i), aboutToDeleteRoom(i) + * 6. Join -> Leave: leftRoom(r) + * 7. Leave -> Invite: newRoom(i), invitedRoom(i,r) + * 8. Leave -> Join: joinedRoom(r) + * The following transitions are only possible via forgetRoom() + * so far; if a room gets forgotten externally, sync won't tell + * about it: + * 9. any -> none: as any -> Leave, then aboutToDeleteRoom(r) + */ + + /** A new room object has been created */ + void newRoom(Room* room); + + /** A room invitation is seen for the first time + * + * If the same room is in Left state, it's passed in prev. Beware + * that initial sync will trigger this signal for all rooms in + * Invite state. + */ + void invitedRoom(Room* room, Room* prev); + + /** A joined room is seen for the first time + * + * It's not the same as receiving a room in "join" section of sync + * response (rooms will be there even after joining); it's also + * not (exactly) the same as actual joining action of a user (all + * rooms coming in initial sync will trigger this signal too). If + * this room was in Invite state before, the respective object is + * passed in prev (and it will be deleted shortly afterwards). + */ + void joinedRoom(Room* room, Room* prev); + + /** A room has just been left + * + * If this room has been in Invite state (as in case of rejecting + * an invitation), the respective object will be passed in prev + * (and will be deleted shortly afterwards). Note that, similar + * to invitedRoom and joinedRoom, this signal is triggered for all + * Left rooms upon initial sync (not only those that were left + * right before the sync). + */ + void leftRoom(Room* room, Room* prev); + + /** The room object is about to be deleted */ + void aboutToDeleteRoom(Room* room); + + /** The room has just been created by createRoom or requestDirectChat + * + * This signal is not emitted in usual room state transitions, + * only as an outcome of room creation operations invoked by + * the client. + * \note requestDirectChat doesn't necessarily create a new chat; + * use directChatAvailable signal if you just need to obtain + * a direct chat room. + */ + void createdRoom(Room* room); + + /** The first sync for the room has been completed + * + * This signal is emitted after the room has been synced the first + * time. This is the right signal to connect to if you need to + * access the room state (name, aliases, members); state transition + * signals (newRoom, joinedRoom etc.) come earlier, when the room + * has just been created. + */ + void loadedRoomState(Room* room); + + /** Account data (except direct chats) have changed */ + void accountDataChanged(QString type); + + /** The direct chat room is ready for using + * This signal is emitted upon any successful outcome from + * requestDirectChat. + */ + void directChatAvailable(Room* directChat); + + /** The list of direct chats has changed + * This signal is emitted every time when the mapping of users + * to direct chat rooms is changed (because of either local updates + * or a different list arrived from the server). + */ + void directChatsListChanged(DirectChatsMap additions, + DirectChatsMap removals); + + void ignoredUsersListChanged(IgnoredUsersList additions, + IgnoredUsersList removals); + + void cacheStateChanged(); + void lazyLoadingChanged(); + void turnServersChanged(const QJsonObject& servers); protected: - /** - * @brief Access the underlying ConnectionData class - */ - const ConnectionData* connectionData() const; - - /** Get a Room object for the given id in the given state - * - * Use this method when you need a Room object in the local list - * of rooms, with the given state. Note that this does not interact - * with the server; in particular, does not automatically create - * rooms on the server. This call performs necessary join state - * transitions; e.g., if it finds a room in Invite but - * `joinState == JoinState::Join` then the Invite room object - * will be deleted and a new room object with Join state created. - * In contrast, switching between Join and Leave happens within - * the same object. - * \param roomId room id (not alias!) - * \param joinState desired (target) join state of the room; if - * omitted, any state will be found and return unchanged, or a - * new Join room created. - * @return a pointer to a Room object with the specified id and the - * specified state; nullptr if roomId is empty or if roomFactory() - * failed to create a Room object. - */ - Room* provideRoom(const QString& roomId, - Omittable joinState = none); - - /** - * Completes loading sync data. - */ - void onSyncSuccess(SyncData &&data, bool fromCache = false); + /** + * @brief Access the underlying ConnectionData class + */ + const ConnectionData* connectionData() const; + + /** Get a Room object for the given id in the given state + * + * Use this method when you need a Room object in the local list + * of rooms, with the given state. Note that this does not interact + * with the server; in particular, does not automatically create + * rooms on the server. This call performs necessary join state + * transitions; e.g., if it finds a room in Invite but + * `joinState == JoinState::Join` then the Invite room object + * will be deleted and a new room object with Join state created. + * In contrast, switching between Join and Leave happens within + * the same object. + * \param roomId room id (not alias!) + * \param joinState desired (target) join state of the room; if + * omitted, any state will be found and return unchanged, or a + * new Join room created. + * @return a pointer to a Room object with the specified id and the + * specified state; nullptr if roomId is empty or if roomFactory() + * failed to create a Room object. + */ + Room* provideRoom(const QString& roomId, + Omittable joinState = none); + + /** + * Completes loading sync data. + */ + void onSyncSuccess(SyncData&& data, bool fromCache = false); protected slots: - void syncLoopIteration(); + void syncLoopIteration(); private: - class Private; - std::unique_ptr d; - - /** - * A single entry for functions that need to check whether the - * homeserver is valid before running. May either execute connectFn - * synchronously or asynchronously (if tryResolve is true and - * a DNS lookup is initiated); in case of errors, emits resolveError - * if the homeserver URL is not valid and cannot be resolved from - * userId. - * - * @param userId - fully-qualified MXID to resolve HS from - * @param connectFn - a function to execute once the HS URL is good - */ - void checkAndConnect(const QString& userId, - std::function connectFn); - void doConnectToServer(const QString& user, const QString& password, - const QString& initialDeviceName, - const QString& deviceId = {}); - - static room_factory_t _roomFactory; - static user_factory_t _userFactory; + class Private; + std::unique_ptr d; + + /** + * A single entry for functions that need to check whether the + * homeserver is valid before running. May either execute connectFn + * synchronously or asynchronously (if tryResolve is true and + * a DNS lookup is initiated); in case of errors, emits resolveError + * if the homeserver URL is not valid and cannot be resolved from + * userId. + * + * @param userId - fully-qualified MXID to resolve HS from + * @param connectFn - a function to execute once the HS URL is good + */ + void checkAndConnect(const QString& userId, + std::function connectFn); + void doConnectToServer(const QString& user, const QString& password, + const QString& initialDeviceName, + const QString& deviceId = {}); + + static room_factory_t _roomFactory; + static user_factory_t _userFactory; }; -} // namespace QMatrixClient +} // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Connection*) diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index eb516ef7..513e497e 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -13,19 +13,18 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "connectiondata.h" -#include "networkaccessmanager.h" #include "logging.h" +#include "networkaccessmanager.h" using namespace QMatrixClient; -struct ConnectionData::Private -{ - explicit Private(const QUrl& url) : baseUrl(url) { } +struct ConnectionData::Private { + explicit Private(const QUrl& url) : baseUrl(url) {} QUrl baseUrl; QByteArray accessToken; @@ -38,19 +37,14 @@ struct ConnectionData::Private ConnectionData::ConnectionData(QUrl baseUrl) : d(std::make_unique(baseUrl)) -{ } +{ +} ConnectionData::~ConnectionData() = default; -QByteArray ConnectionData::accessToken() const -{ - return d->accessToken; -} +QByteArray ConnectionData::accessToken() const { return d->accessToken; } -QUrl ConnectionData::baseUrl() const -{ - return d->baseUrl; -} +QUrl ConnectionData::baseUrl() const { return d->baseUrl; } QNetworkAccessManager* ConnectionData::nam() const { @@ -80,10 +74,7 @@ void ConnectionData::setPort(int port) qCDebug(MAIN) << "updated baseUrl to" << d->baseUrl; } -const QString& ConnectionData::deviceId() const -{ - return d->deviceId; -} +const QString& ConnectionData::deviceId() const { return d->deviceId; } void ConnectionData::setDeviceId(const QString& deviceId) { @@ -91,10 +82,7 @@ void ConnectionData::setDeviceId(const QString& deviceId) qCDebug(MAIN) << "updated deviceId to" << d->deviceId; } -QString ConnectionData::lastEvent() const -{ - return d->lastEvent; -} +QString ConnectionData::lastEvent() const { return d->lastEvent; } void ConnectionData::setLastEvent(QString identifier) { @@ -103,6 +91,6 @@ void ConnectionData::setLastEvent(QString identifier) QByteArray ConnectionData::generateTxnId() const { - return QByteArray::number(d->id) + 'q' + - QByteArray::number(++d->txnCounter); + return QByteArray::number(d->id) + 'q' + + QByteArray::number(++d->txnCounter); } diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 7a2f2e90..fcb67e2d 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -24,32 +24,31 @@ class QNetworkAccessManager; -namespace QMatrixClient -{ +namespace QMatrixClient { class ConnectionData { public: - explicit ConnectionData(QUrl baseUrl); - virtual ~ConnectionData(); + explicit ConnectionData(QUrl baseUrl); + virtual ~ConnectionData(); - QByteArray accessToken() const; - QUrl baseUrl() const; - const QString& deviceId() const; + QByteArray accessToken() const; + QUrl baseUrl() const; + const QString& deviceId() const; - QNetworkAccessManager* nam() const; - void setBaseUrl(QUrl baseUrl); - void setToken(QByteArray accessToken); - void setHost( QString host ); - void setPort( int port ); - void setDeviceId(const QString& deviceId); + QNetworkAccessManager* nam() const; + void setBaseUrl(QUrl baseUrl); + void setToken(QByteArray accessToken); + void setHost(QString host); + void setPort(int port); + void setDeviceId(const QString& deviceId); - QString lastEvent() const; - void setLastEvent( QString identifier ); + QString lastEvent() const; + void setLastEvent(QString identifier); - QByteArray generateTxnId() const; + QByteArray generateTxnId() const; private: - struct Private; - std::unique_ptr d; + struct Private; + std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/converters.cpp b/lib/converters.cpp index 88f5267e..8f45ba3b 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 "converters.h" @@ -36,20 +36,20 @@ QJsonObject JsonConverter::dump(const variant_map_t& map) { return #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QJsonObject::fromVariantHash + QJsonObject::fromVariantHash #else - QJsonObject::fromVariantMap + QJsonObject::fromVariantMap #endif - (map); + (map); } variant_map_t JsonConverter::load(const QJsonValue& jv) { return jv.toObject(). #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - toVariantHash + toVariantHash #else - toVariantMap + toVariantMap #endif - (); + (); } diff --git a/lib/converters.h b/lib/converters.h index af2be645..68a841cf 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -1,31 +1,31 @@ /****************************************************************************** -* Copyright (C) 2017 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2017 Kitsune Ral + * + * 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 "util.h" -#include +#include #include // Includes #include -#include -#include +#include #include +#include #include #include @@ -38,35 +38,29 @@ using optional = std::experimental::optional; #endif // Enable std::unordered_map -namespace std -{ - template <> struct hash - { +namespace std { + template <> struct hash { size_t operator()(const QString& s) const Q_DECL_NOEXCEPT { return qHash(s #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - , uint(qGlobalQHashSeed()) + , + uint(qGlobalQHashSeed()) #endif - ); + ); } }; } class QVariant; -namespace QMatrixClient -{ - template - struct JsonObjectConverter - { +namespace QMatrixClient { + template struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod; } static void fillFrom(const QJsonObject& jo, T& pod) { pod = jo; } }; - template - struct JsonConverter - { + template struct JsonConverter { static QJsonObject dump(const T& pod) { QJsonObject jo; @@ -83,39 +77,33 @@ namespace QMatrixClient static T load(const QJsonDocument& jd) { return doLoad(jd.object()); } }; - template - inline auto toJson(const T& pod) + template inline auto toJson(const T& pod) { return JsonConverter::dump(pod); } - template - inline auto fillJson(QJsonObject& json, const T& data) + template inline auto fillJson(QJsonObject& json, const T& data) { JsonObjectConverter::dumpTo(json, data); } - template - inline auto fromJson(const QJsonValue& jv) + template inline auto fromJson(const QJsonValue& jv) { return JsonConverter::load(jv); } - template - inline T fromJson(const QJsonDocument& jd) + template inline T fromJson(const QJsonDocument& jd) { return JsonConverter::load(jd); } - template - inline void fromJson(const QJsonValue& jv, T& pod) + template inline void fromJson(const QJsonValue& jv, T& pod) { if (!jv.isUndefined()) pod = fromJson(jv); } - template - inline void fromJson(const QJsonDocument& jd, T& pod) + template inline void fromJson(const QJsonDocument& jd, T& pod) { pod = fromJson(jd); } @@ -130,8 +118,7 @@ namespace QMatrixClient pod = fromJson(jv); } - template - inline void fillFromJson(const QJsonValue& jv, T& pod) + template inline void fillFromJson(const QJsonValue& jv, T& pod) { if (jv.isObject()) JsonObjectConverter::fillFrom(jv.toObject(), pod); @@ -139,59 +126,49 @@ namespace QMatrixClient // JsonConverter<> specialisations - template - struct TrivialJsonDumper - { + template struct TrivialJsonDumper { // Works for: QJsonValue (and all things it can consume), // QJsonObject, QJsonArray static auto dump(const T& val) { return val; } }; - template <> struct JsonConverter : public TrivialJsonDumper - { + template <> struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toBool(); } }; - template <> struct JsonConverter : public TrivialJsonDumper - { + template <> struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toInt(); } }; - template <> struct JsonConverter - : public TrivialJsonDumper - { + template <> + struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toDouble(); } }; - template <> struct JsonConverter : public TrivialJsonDumper - { + template <> struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return float(jv.toDouble()); } }; - template <> struct JsonConverter - : public TrivialJsonDumper - { + template <> + struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); } }; - template <> struct JsonConverter - : public TrivialJsonDumper - { + template <> + struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toString(); } }; - template <> struct JsonConverter - { + template <> struct JsonConverter { static auto dump(const QDateTime& val) = delete; // not provided yet static auto load(const QJsonValue& jv) { - return QDateTime::fromMSecsSinceEpoch( - fromJson(jv), Qt::UTC); + return QDateTime::fromMSecsSinceEpoch(fromJson(jv), + Qt::UTC); } }; - template <> struct JsonConverter - { + template <> struct JsonConverter { static auto dump(const QDate& val) = delete; // not provided yet static auto load(const QJsonValue& jv) { @@ -199,14 +176,12 @@ namespace QMatrixClient } }; - template <> struct JsonConverter - : public TrivialJsonDumper - { + template <> + struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toArray(); } }; - template <> struct JsonConverter - { + template <> struct JsonConverter { static QString dump(const QByteArray& ba) { return ba.constData(); } static auto load(const QJsonValue& jv) { @@ -214,19 +189,16 @@ namespace QMatrixClient } }; - template <> struct JsonConverter - { + template <> struct JsonConverter { static QJsonValue dump(const QVariant& v); static QVariant load(const QJsonValue& jv); }; - template - struct JsonArrayConverter - { + template + struct JsonArrayConverter { static void dumpTo(QJsonArray& ar, const VectorT& vals) { - for (const auto& v: vals) + for (const auto& v : vals) ar.push_back(toJson(v)); } static auto dump(const VectorT& vals) @@ -237,8 +209,9 @@ namespace QMatrixClient } static auto load(const QJsonArray& ja) { - VectorT vect; vect.reserve(typename VectorT::size_type(ja.size())); - for (const auto& i: ja) + VectorT vect; + vect.reserve(typename VectorT::size_type(ja.size())); + for (const auto& i : ja) vect.push_back(fromJson(i)); return vect; } @@ -246,33 +219,32 @@ namespace QMatrixClient static auto load(const QJsonDocument& jd) { return load(jd.array()); } }; - template struct JsonConverter> - : public JsonArrayConverter> - { }; + template + struct JsonConverter> + : public JsonArrayConverter> { + }; - template struct JsonConverter> - : public JsonArrayConverter> - { }; + template + struct JsonConverter> : public JsonArrayConverter> { + }; - template struct JsonConverter> - : public JsonArrayConverter> - { }; + template + struct JsonConverter> : public JsonArrayConverter> { + }; - template <> struct JsonConverter - : public JsonConverter> - { + template <> + struct JsonConverter : public JsonConverter> { static auto dump(const QStringList& sl) { return QJsonArray::fromStringList(sl); } }; - template <> struct JsonObjectConverter> - { + template <> struct JsonObjectConverter> { static void dumpTo(QJsonObject& json, const QSet& s) { - for (const auto& e: s) - json.insert(toJson(e), QJsonObject{}); + for (const auto& e : s) + json.insert(toJson(e), QJsonObject {}); } static auto fillFrom(const QJsonObject& json, QSet& s) { @@ -283,9 +255,7 @@ namespace QMatrixClient } }; - template - struct HashMapFromJson - { + template struct HashMapFromJson { static void dumpTo(QJsonObject& json, const HashMapT& hashMap) { for (auto it = hashMap.begin(); it != hashMap.end(); ++it) @@ -296,19 +266,19 @@ namespace QMatrixClient h.reserve(jo.size()); for (auto it = jo.begin(); it != jo.end(); ++it) h[it.key()] = - fromJson(it.value()); + fromJson(it.value()); } }; template struct JsonObjectConverter> - : public HashMapFromJson> - { }; + : public HashMapFromJson> { + }; template struct JsonObjectConverter> - : public HashMapFromJson> - { }; + : public HashMapFromJson> { + }; // We could use std::conditional<> below but QT_VERSION* macros in C++ code // cause (kinda valid but useless and noisy) compiler warnings about @@ -319,35 +289,38 @@ namespace QMatrixClient #else QVariantMap; #endif - template <> struct JsonConverter - { + template <> struct JsonConverter { static QJsonObject dump(const variant_map_t& vh); static QVariantHash load(const QJsonValue& jv); }; // Conditional insertion into a QJsonObject - namespace _impl - { + namespace _impl { template inline void addTo(QJsonObject& o, const QString& k, ValT&& v) - { o.insert(k, toJson(v)); } + { + o.insert(k, toJson(v)); + } template inline void addTo(QUrlQuery& q, const QString& k, ValT&& v) - { q.addQueryItem(k, QStringLiteral("%1").arg(v)); } + { + q.addQueryItem(k, QStringLiteral("%1").arg(v)); + } // OpenAPI is entirely JSON-based, which means representing bools as // textual true/false, rather than 1/0. inline void addTo(QUrlQuery& q, const QString& k, bool v) { - q.addQueryItem(k, v ? QStringLiteral("true") - : QStringLiteral("false")); + q.addQueryItem( + k, v ? QStringLiteral("true") : QStringLiteral("false")); } - inline void addTo(QUrlQuery& q, const QString& k, const QStringList& vals) + inline void addTo(QUrlQuery& q, const QString& k, + const QStringList& vals) { - for (const auto& v: vals) + for (const auto& v : vals) q.addQueryItem(k, v); } @@ -359,8 +332,7 @@ namespace QMatrixClient // This one is for types that don't have isEmpty() template - struct AddNode - { + struct AddNode { template static void impl(ContT& container, const QString& key, ForwardedT&& value) @@ -371,31 +343,28 @@ namespace QMatrixClient // This one is for types that have isEmpty() template - struct AddNode().isEmpty())> - { + struct AddNode().isEmpty())> { template static void impl(ContT& container, const QString& key, ForwardedT&& value) { if (!value.isEmpty()) - AddNode::impl(container, - key, std::forward(value)); + AddNode::impl(container, key, + std::forward(value)); } }; // This is a special one that unfolds Omittable<> template - struct AddNode, Force> - { + struct AddNode, Force> { template - static void impl(ContT& container, - const QString& key, const OmittableT& value) + static void impl(ContT& container, const QString& key, + const OmittableT& value) { if (!value.omitted()) AddNode::impl(container, key, value.value()); else if (Force) // Edge case, no value but must put something - AddNode::impl(container, key, QString{}); + AddNode::impl(container, key, QString {}); } }; @@ -416,14 +385,14 @@ namespace QMatrixClient }; #endif - } // namespace _impl + } // namespace _impl static constexpr bool IfNotEmpty = false; template inline void addParam(ContT& container, const QString& key, ValT&& value) { - _impl::AddNode, Force> - ::impl(container, key, std::forward(value)); + _impl::AddNode, Force>::impl( + container, key, std::forward(value)); } -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/csapi/account-data.cpp b/lib/csapi/account-data.cpp index 5021c73a..40388673 100644 --- a/lib/csapi/account-data.cpp +++ b/lib/csapi/account-data.cpp @@ -14,19 +14,24 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SetAccountDataJobName = QStringLiteral("SetAccountDataJob"); -SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content) +SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, + const QJsonObject& content) : BaseJob(HttpVerb::Put, SetAccountDataJobName, - basePath % "/user/" % userId % "/account_data/" % type) + basePath % "/user/" % userId % "/account_data/" % type) { setRequestData(Data(toJson(content))); } -static const auto SetAccountDataPerRoomJobName = QStringLiteral("SetAccountDataPerRoomJob"); +static const auto SetAccountDataPerRoomJobName = + QStringLiteral("SetAccountDataPerRoomJob"); -SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content) +SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, + const QString& roomId, + const QString& type, + const QJsonObject& content) : BaseJob(HttpVerb::Put, SetAccountDataPerRoomJobName, - basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type) + basePath % "/user/" % userId % "/rooms/" % roomId + % "/account_data/" % type) { setRequestData(Data(toJson(content))); } - diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index f3656a14..669a4e2c 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -8,51 +8,49 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Set some account_data for the user. /// - /// Set some account_data for the client. This config is only visible to the user - /// that set the account_data. The config will be synced to clients in the - /// top-level ``account_data``. + /// Set some account_data for the client. This config is only visible to the + /// user that set the account_data. The config will be synced to clients in + /// the top-level ``account_data``. class SetAccountDataJob : public BaseJob { public: - /*! Set some account_data for the user. - * \param userId - * The id of the user to set account_data for. The access token must be - * authorized to make requests for this user id. - * \param type - * The event type of the account_data to set. Custom types should be - * namespaced to avoid clashes. - * \param content - * The content of the account_data - */ - explicit SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content = {}); + /*! Set some account_data for the user. + * \param userId + * The id of the user to set account_data for. The access token must + * be authorized to make requests for this user id. \param type The + * event type of the account_data to set. Custom types should be + * namespaced to avoid clashes. + * \param content + * The content of the account_data + */ + explicit SetAccountDataJob(const QString& userId, const QString& type, + const QJsonObject& content = {}); }; /// Set some account_data for the user. /// - /// Set some account_data for the client on a given room. This config is only - /// visible to the user that set the account_data. The config will be synced to - /// clients in the per-room ``account_data``. + /// Set some account_data for the client on a given room. This config is + /// only visible to the user that set the account_data. The config will be + /// synced to clients in the per-room ``account_data``. class SetAccountDataPerRoomJob : public BaseJob { public: - /*! Set some account_data for the user. - * \param userId - * The id of the user to set account_data for. The access token must be - * authorized to make requests for this user id. - * \param roomId - * The id of the room to set account_data on. - * \param type - * The event type of the account_data to set. Custom types should be - * namespaced to avoid clashes. - * \param content - * The content of the account_data - */ - explicit SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content = {}); + /*! Set some account_data for the user. + * \param userId + * The id of the user to set account_data for. The access token must + * be authorized to make requests for this user id. \param roomId The id + * of the room to set account_data on. \param type The event type of the + * account_data to set. Custom types should be namespaced to avoid + * clashes. \param content The content of the account_data + */ + explicit SetAccountDataPerRoomJob(const QString& userId, + const QString& roomId, + const QString& type, + const QJsonObject& content = {}); }; } // namespace QMatrixClient diff --git a/lib/csapi/admin.cpp b/lib/csapi/admin.cpp index ce06a56d..7922ffe3 100644 --- a/lib/csapi/admin.cpp +++ b/lib/csapi/admin.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetWhoIsJob::ConnectionInfo& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetWhoIsJob::ConnectionInfo& result) { fromJson(jo.value("ip"_ls), result.ip); fromJson(jo.value("last_seen"_ls), result.lastSeen); @@ -26,17 +25,17 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetWhoIsJob::SessionInfo& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetWhoIsJob::SessionInfo& result) { fromJson(jo.value("connections"_ls), result.connections); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetWhoIsJob::DeviceInfo& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetWhoIsJob::DeviceInfo& result) { fromJson(jo.value("sessions"_ls), result.sessions); } @@ -46,31 +45,28 @@ namespace QMatrixClient class GetWhoIsJob::Private { public: - QString userId; - QHash devices; + QString userId; + QHash devices; }; QUrl GetWhoIsJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/admin/whois/" % userId); + basePath % "/admin/whois/" % userId); } static const auto GetWhoIsJobName = QStringLiteral("GetWhoIsJob"); GetWhoIsJob::GetWhoIsJob(const QString& userId) : BaseJob(HttpVerb::Get, GetWhoIsJobName, - basePath % "/admin/whois/" % userId) - , d(new Private) + basePath % "/admin/whois/" % userId), + d(new Private) { } GetWhoIsJob::~GetWhoIsJob() = default; -const QString& GetWhoIsJob::userId() const -{ - return d->userId; -} +const QString& GetWhoIsJob::userId() const { return d->userId; } const QHash& GetWhoIsJob::devices() const { @@ -84,4 +80,3 @@ BaseJob::Status GetWhoIsJob::parseJson(const QJsonDocument& data) fromJson(json.value("devices"_ls), d->devices); return Success; } - diff --git a/lib/csapi/admin.h b/lib/csapi/admin.h index d35f3ee3..3c21e2cb 100644 --- a/lib/csapi/admin.h +++ b/lib/csapi/admin.h @@ -6,93 +6,90 @@ #include "jobs/basejob.h" -#include -#include #include "converters.h" +#include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets information about a particular user. /// /// Gets information about a particular user. - /// + /// /// This API may be restricted to only be called by the user being looked /// up, or by a server admin. Server-local administrator privileges are not /// specified in this document. class GetWhoIsJob : public BaseJob { public: - // Inner data structures - - /// Gets information about a particular user. - /// - /// This API may be restricted to only be called by the user being looked - /// up, or by a server admin. Server-local administrator privileges are not - /// specified in this document. - struct ConnectionInfo - { - /// Most recently seen IP address of the session. - QString ip; - /// Unix timestamp that the session was last active. - Omittable lastSeen; - /// User agent string last seen in the session. - QString userAgent; - }; - - /// Gets information about a particular user. - /// - /// This API may be restricted to only be called by the user being looked - /// up, or by a server admin. Server-local administrator privileges are not - /// specified in this document. - struct SessionInfo - { - /// Information particular connections in the session. - QVector connections; - }; - - /// Gets information about a particular user. - /// - /// This API may be restricted to only be called by the user being looked - /// up, or by a server admin. Server-local administrator privileges are not - /// specified in this document. - struct DeviceInfo - { - /// A user's sessions (i.e. what they did with an access token from one login). - QVector sessions; - }; - - // Construction/destruction - - /*! Gets information about a particular user. - * \param userId - * The user to look up. - */ - explicit GetWhoIsJob(const QString& userId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetWhoIsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - - ~GetWhoIsJob() override; - - // Result properties - - /// The Matrix user ID of the user. - const QString& userId() const; - /// Each key is an identitfier for one of the user's devices. - const QHash& devices() const; + // Inner data structures + + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being + /// looked up, or by a server admin. Server-local administrator + /// privileges are not specified in this document. + struct ConnectionInfo { + /// Most recently seen IP address of the session. + QString ip; + /// Unix timestamp that the session was last active. + Omittable lastSeen; + /// User agent string last seen in the session. + QString userAgent; + }; + + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being + /// looked up, or by a server admin. Server-local administrator + /// privileges are not specified in this document. + struct SessionInfo { + /// Information particular connections in the session. + QVector connections; + }; + + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being + /// looked up, or by a server admin. Server-local administrator + /// privileges are not specified in this document. + struct DeviceInfo { + /// A user's sessions (i.e. what they did with an access token from + /// one login). + QVector sessions; + }; + + // Construction/destruction + + /*! Gets information about a particular user. + * \param userId + * The user to look up. + */ + explicit GetWhoIsJob(const QString& userId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetWhoIsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); + + ~GetWhoIsJob() override; + + // Result properties + + /// The Matrix user ID of the user. + const QString& userId() const; + /// Each key is an identitfier for one of the user's devices. + const QHash& devices() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index 11385dff..f64f2723 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -12,13 +12,13 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetAccount3PIDsJob::ThirdPartyIdentifier& result) + template <> + struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetAccount3PIDsJob::ThirdPartyIdentifier& result) { fromJson(jo.value("medium"_ls), result.medium); fromJson(jo.value("address"_ls), result.address); @@ -31,27 +31,28 @@ namespace QMatrixClient class GetAccount3PIDsJob::Private { public: - QVector threepids; + QVector threepids; }; QUrl GetAccount3PIDsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/account/3pid"); + basePath % "/account/3pid"); } static const auto GetAccount3PIDsJobName = QStringLiteral("GetAccount3PIDsJob"); GetAccount3PIDsJob::GetAccount3PIDsJob() : BaseJob(HttpVerb::Get, GetAccount3PIDsJobName, - basePath % "/account/3pid") - , d(new Private) + basePath % "/account/3pid"), + d(new Private) { } GetAccount3PIDsJob::~GetAccount3PIDsJob() = default; -const QVector& GetAccount3PIDsJob::threepids() const +const QVector& +GetAccount3PIDsJob::threepids() const { return d->threepids; } @@ -63,13 +64,12 @@ BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data) return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const Post3PIDsJob::ThreePidCredentials& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const Post3PIDsJob::ThreePidCredentials& pod) { addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); addParam<>(jo, QStringLiteral("id_server"), pod.idServer); @@ -80,9 +80,9 @@ namespace QMatrixClient static const auto Post3PIDsJobName = QStringLiteral("Post3PIDsJob"); -Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable bind) - : BaseJob(HttpVerb::Post, Post3PIDsJobName, - basePath % "/account/3pid") +Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, + Omittable bind) + : BaseJob(HttpVerb::Post, Post3PIDsJobName, basePath % "/account/3pid") { QJsonObject _data; addParam<>(_data, QStringLiteral("three_pid_creds"), threePidCreds); @@ -90,11 +90,13 @@ Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable(_data, QStringLiteral("medium"), medium); @@ -105,15 +107,18 @@ Delete3pidFromAccountJob::Delete3pidFromAccountJob(const QString& medium, const class RequestTokenTo3PIDEmailJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenTo3PIDEmailJobName = QStringLiteral("RequestTokenTo3PIDEmailJob"); +static const auto RequestTokenTo3PIDEmailJobName = + QStringLiteral("RequestTokenTo3PIDEmailJob"); -RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob( + const QString& clientSecret, const QString& email, int sendAttempt, + const QString& idServer, const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenTo3PIDEmailJobName, - basePath % "/account/3pid/email/requestToken", false) - , d(new Private) + basePath % "/account/3pid/email/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -126,10 +131,7 @@ RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob(const QString& clientSecr RequestTokenTo3PIDEmailJob::~RequestTokenTo3PIDEmailJob() = default; -const Sid& RequestTokenTo3PIDEmailJob::data() const -{ - return d->data; -} +const Sid& RequestTokenTo3PIDEmailJob::data() const { return d->data; } BaseJob::Status RequestTokenTo3PIDEmailJob::parseJson(const QJsonDocument& data) { @@ -140,15 +142,19 @@ BaseJob::Status RequestTokenTo3PIDEmailJob::parseJson(const QJsonDocument& data) class RequestTokenTo3PIDMSISDNJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenTo3PIDMSISDNJobName = QStringLiteral("RequestTokenTo3PIDMSISDNJob"); +static const auto RequestTokenTo3PIDMSISDNJobName = + QStringLiteral("RequestTokenTo3PIDMSISDNJob"); -RequestTokenTo3PIDMSISDNJob::RequestTokenTo3PIDMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenTo3PIDMSISDNJob::RequestTokenTo3PIDMSISDNJob( + const QString& clientSecret, const QString& country, + const QString& phoneNumber, int sendAttempt, const QString& idServer, + const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenTo3PIDMSISDNJobName, - basePath % "/account/3pid/msisdn/requestToken", false) - , d(new Private) + basePath % "/account/3pid/msisdn/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -162,14 +168,11 @@ RequestTokenTo3PIDMSISDNJob::RequestTokenTo3PIDMSISDNJob(const QString& clientSe RequestTokenTo3PIDMSISDNJob::~RequestTokenTo3PIDMSISDNJob() = default; -const Sid& RequestTokenTo3PIDMSISDNJob::data() const -{ - return d->data; -} +const Sid& RequestTokenTo3PIDMSISDNJob::data() const { return d->data; } -BaseJob::Status RequestTokenTo3PIDMSISDNJob::parseJson(const QJsonDocument& data) +BaseJob::Status +RequestTokenTo3PIDMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index 02aeee4d..d99fde42 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -6,82 +6,83 @@ #include "jobs/basejob.h" -#include "csapi/../identity/definitions/sid.h" #include "converters.h" +#include "csapi/../identity/definitions/sid.h" #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets a list of a user's third party identifiers. /// /// Gets a list of the third party identifiers that the homeserver has /// associated with the user's account. - /// + /// /// This is *not* the same as the list of third party identifiers bound to /// the user's Matrix ID in identity servers. - /// + /// /// Identifiers in this list may be used by the homeserver as, for example, /// identifiers that it will accept to reset the user's account password. class GetAccount3PIDsJob : public BaseJob { public: - // Inner data structures - - /// Gets a list of the third party identifiers that the homeserver has - /// associated with the user's account. - /// - /// This is *not* the same as the list of third party identifiers bound to - /// the user's Matrix ID in identity servers. - /// - /// Identifiers in this list may be used by the homeserver as, for example, - /// identifiers that it will accept to reset the user's account password. - struct ThirdPartyIdentifier - { - /// The medium of the third party identifier. - QString medium; - /// The third party identifier address. - QString address; - /// The timestamp, in milliseconds, when the identifier was - /// validated by the identity server. - qint64 validatedAt; - /// The timestamp, in milliseconds, when the homeserver associated the third party identifier with the user. - qint64 addedAt; - }; - - // Construction/destruction - - explicit GetAccount3PIDsJob(); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetAccount3PIDsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); - - ~GetAccount3PIDsJob() override; - - // Result properties - - /// Gets a list of the third party identifiers that the homeserver has - /// associated with the user's account. - /// - /// This is *not* the same as the list of third party identifiers bound to - /// the user's Matrix ID in identity servers. - /// - /// Identifiers in this list may be used by the homeserver as, for example, - /// identifiers that it will accept to reset the user's account password. - const QVector& threepids() const; + // Inner data structures + + /// Gets a list of the third party identifiers that the homeserver has + /// associated with the user's account. + /// + /// This is *not* the same as the list of third party identifiers bound + /// to the user's Matrix ID in identity servers. + /// + /// Identifiers in this list may be used by the homeserver as, for + /// example, identifiers that it will accept to reset the user's account + /// password. + struct ThirdPartyIdentifier { + /// The medium of the third party identifier. + QString medium; + /// The third party identifier address. + QString address; + /// The timestamp, in milliseconds, when the identifier was + /// validated by the identity server. + qint64 validatedAt; + /// The timestamp, in milliseconds, when the homeserver associated + /// the third party identifier with the user. + qint64 addedAt; + }; + + // Construction/destruction + + explicit GetAccount3PIDsJob(); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetAccount3PIDsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); + + ~GetAccount3PIDsJob() override; + + // Result properties + + /// Gets a list of the third party identifiers that the homeserver has + /// associated with the user's account. + /// + /// This is *not* the same as the list of third party identifiers bound + /// to the user's Matrix ID in identity servers. + /// + /// Identifiers in this list may be used by the homeserver as, for + /// example, identifiers that it will accept to reset the user's account + /// password. + const QVector& threepids() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Adds contact information to the user's account. @@ -90,30 +91,30 @@ namespace QMatrixClient class Post3PIDsJob : public BaseJob { public: - // Inner data structures - - /// The third party credentials to associate with the account. - struct ThreePidCredentials - { - /// The client secret used in the session with the identity server. - QString clientSecret; - /// The identity server to use. - QString idServer; - /// The session identifier given by the identity server. - QString sid; - }; - - // Construction/destruction - - /*! Adds contact information to the user's account. - * \param threePidCreds - * The third party credentials to associate with the account. - * \param bind - * Whether the homeserver should also bind this third party - * identifier to the account's Matrix ID with the passed identity - * server. Default: ``false``. - */ - explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable bind = none); + // Inner data structures + + /// The third party credentials to associate with the account. + struct ThreePidCredentials { + /// The client secret used in the session with the identity server. + QString clientSecret; + /// The identity server to use. + QString idServer; + /// The session identifier given by the identity server. + QString sid; + }; + + // Construction/destruction + + /*! Adds contact information to the user's account. + * \param threePidCreds + * The third party credentials to associate with the account. + * \param bind + * Whether the homeserver should also bind this third party + * identifier to the account's Matrix ID with the passed identity + * server. Default: ``false``. + */ + explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, + Omittable bind = none); }; /// Deletes a third party identifier from the user's account @@ -123,115 +124,126 @@ namespace QMatrixClient class Delete3pidFromAccountJob : public BaseJob { public: - /*! Deletes a third party identifier from the user's account - * \param medium - * The medium of the third party identifier being removed. - * \param address - * The third party address being removed. - */ - explicit Delete3pidFromAccountJob(const QString& medium, const QString& address); + /*! Deletes a third party identifier from the user's account + * \param medium + * The medium of the third party identifier being removed. + * \param address + * The third party address being removed. + */ + explicit Delete3pidFromAccountJob(const QString& medium, + const QString& address); }; - /// Begins the validation process for an email address for association with the user's account. + /// Begins the validation process for an email address for association with + /// the user's account. /// /// Proxies the Identity Service API ``validate/email/requestToken``, but /// first checks that the given email address is **not** already associated /// with an account on this homeserver. This API should be used to request /// validation tokens when adding an email address to an account. This API's - /// parameters and response are identical to that of the |/register/email/requestToken|_ - /// endpoint. + /// parameters and response are identical to that of the + /// |/register/email/requestToken|_ endpoint. class RequestTokenTo3PIDEmailJob : public BaseJob { public: - /*! Begins the validation process for an email address for association with the user's account. - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenTo3PIDEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenTo3PIDEmailJob() override; - - // Result properties - - /// An email was sent to the given address. - const Sid& data() const; + /*! Begins the validation process for an email address for association with the user's account. + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param email The email address to validate. + * \param sendAttempt + * The server will only send an email if the ``send_attempt`` + * is a number greater than the most recent one which it has seen, + * scoped to that ``email`` + ``client_secret`` pair. This is to + * avoid repeatedly sending the same email in the case of request + * retries between the POSTing user and the identity server. + * The client should increment this value if they desire a new + * email (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenTo3PIDEmailJob(const QString& clientSecret, + const QString& email, + int sendAttempt, + const QString& idServer, + const QString& nextLink = {}); + ~RequestTokenTo3PIDEmailJob() override; + + // Result properties + + /// An email was sent to the given address. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; - /// Begins the validation process for a phone number for association with the user's account. + /// Begins the validation process for a phone number for association with + /// the user's account. /// /// Proxies the Identity Service API ``validate/msisdn/requestToken``, but /// first checks that the given phone number is **not** already associated /// with an account on this homeserver. This API should be used to request /// validation tokens when adding a phone number to an account. This API's - /// parameters and response are identical to that of the |/register/msisdn/requestToken|_ - /// endpoint. + /// parameters and response are identical to that of the + /// |/register/msisdn/requestToken|_ endpoint. class RequestTokenTo3PIDMSISDNJob : public BaseJob { public: - /*! Begins the validation process for a phone number for association with the user's account. - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenTo3PIDMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenTo3PIDMSISDNJob() override; - - // Result properties - - /// An SMS message was sent to the given phone number. - const Sid& data() const; + /*! Begins the validation process for a phone number for association with the user's account. + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param country The two-letter uppercase ISO + * country code that the number in + * ``phone_number`` should be parsed as if it were dialled from. + * \param phoneNumber + * The phone number to validate. + * \param sendAttempt + * The server will only send an SMS if the ``send_attempt`` is a + * number greater than the most recent one which it has seen, + * scoped to that ``country`` + ``phone_number`` + ``client_secret`` + * triple. This is to avoid repeatedly sending the same SMS in + * the case of request retries between the POSTing user and the + * identity server. The client should increment this value if + * they desire a new SMS (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenTo3PIDMSISDNJob(const QString& clientSecret, + const QString& country, + const QString& phoneNumber, + int sendAttempt, + const QString& idServer, + const QString& nextLink = {}); + ~RequestTokenTo3PIDMSISDNJob() override; + + // Result properties + + /// An SMS message was sent to the given phone number. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/appservice_room_directory.cpp b/lib/csapi/appservice_room_directory.cpp index f40e2f05..cf76874c 100644 --- a/lib/csapi/appservice_room_directory.cpp +++ b/lib/csapi/appservice_room_directory.cpp @@ -12,14 +12,18 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto UpdateAppserviceRoomDirectoryVsibilityJobName = QStringLiteral("UpdateAppserviceRoomDirectoryVsibilityJob"); +static const auto UpdateAppserviceRoomDirectoryVsibilityJobName = + QStringLiteral("UpdateAppserviceRoomDirectoryVsibilityJob"); -UpdateAppserviceRoomDirectoryVsibilityJob::UpdateAppserviceRoomDirectoryVsibilityJob(const QString& networkId, const QString& roomId, const QString& visibility) +UpdateAppserviceRoomDirectoryVsibilityJob:: + UpdateAppserviceRoomDirectoryVsibilityJob(const QString& networkId, + const QString& roomId, + const QString& visibility) : BaseJob(HttpVerb::Put, UpdateAppserviceRoomDirectoryVsibilityJobName, - basePath % "/directory/list/appservice/" % networkId % "/" % roomId) + basePath % "/directory/list/appservice/" % networkId % "/" + % roomId) { QJsonObject _data; addParam<>(_data, QStringLiteral("visibility"), visibility); setRequestData(_data); } - diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h index f35198b3..2ee680c9 100644 --- a/lib/csapi/appservice_room_directory.h +++ b/lib/csapi/appservice_room_directory.h @@ -6,36 +6,37 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Updates a room's visibility in the application service's room directory. /// /// Updates the visibility of a given room on the application service's room /// directory. - /// + /// /// This API is similar to the room directory visibility API used by clients /// to update the homeserver's more general room directory. - /// - /// This API requires the use of an application service access token (``as_token``) - /// instead of a typical client's access_token. This API cannot be invoked by - /// users who are not identified as application services. + /// + /// This API requires the use of an application service access token + /// (``as_token``) instead of a typical client's access_token. This API + /// cannot be invoked by users who are not identified as application + /// services. class UpdateAppserviceRoomDirectoryVsibilityJob : public BaseJob { public: - /*! Updates a room's visibility in the application service's room directory. - * \param networkId - * The protocol (network) ID to update the room list for. This would - * have been provided by the application service as being listed as - * a supported protocol. - * \param roomId - * The room ID to add to the directory. - * \param visibility - * Whether the room should be visible (public) in the directory - * or not (private). - */ - explicit UpdateAppserviceRoomDirectoryVsibilityJob(const QString& networkId, const QString& roomId, const QString& visibility); + /*! Updates a room's visibility in the application service's room directory. + * \param networkId + * The protocol (network) ID to update the room list for. This would + * have been provided by the application service as being listed as + * a supported protocol. + * \param roomId + * The room ID to add to the directory. + * \param visibility + * Whether the room should be visible (public) in the directory + * or not (private). + */ + explicit UpdateAppserviceRoomDirectoryVsibilityJob( + const QString& networkId, const QString& roomId, + const QString& visibility); }; } // namespace QMatrixClient diff --git a/lib/csapi/banning.cpp b/lib/csapi/banning.cpp index 4065207b..201126c3 100644 --- a/lib/csapi/banning.cpp +++ b/lib/csapi/banning.cpp @@ -14,9 +14,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto BanJobName = QStringLiteral("BanJob"); -BanJob::BanJob(const QString& roomId, const QString& userId, const QString& reason) +BanJob::BanJob(const QString& roomId, const QString& userId, + const QString& reason) : BaseJob(HttpVerb::Post, BanJobName, - basePath % "/rooms/" % roomId % "/ban") + basePath % "/rooms/" % roomId % "/ban") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); @@ -28,10 +29,9 @@ static const auto UnbanJobName = QStringLiteral("UnbanJob"); UnbanJob::UnbanJob(const QString& roomId, const QString& userId) : BaseJob(HttpVerb::Post, UnbanJobName, - basePath % "/rooms/" % roomId % "/unban") + basePath % "/rooms/" % roomId % "/unban") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); setRequestData(_data); } - diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h index 237bd2a0..e1886f0e 100644 --- a/lib/csapi/banning.h +++ b/lib/csapi/banning.h @@ -6,47 +6,51 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Ban a user in the room. /// - /// Ban a user in the room. If the user is currently in the room, also kick them. - /// - /// When a user is banned from a room, they may not join it or be invited to it until they are unbanned. - /// - /// The caller must have the required power level in order to perform this operation. + /// Ban a user in the room. If the user is currently in the room, also kick + /// them. + /// + /// When a user is banned from a room, they may not join it or be invited to + /// it until they are unbanned. + /// + /// The caller must have the required power level in order to perform this + /// operation. class BanJob : public BaseJob { public: - /*! Ban a user in the room. - * \param roomId - * The room identifier (not alias) from which the user should be banned. - * \param userId - * The fully qualified user ID of the user being banned. - * \param reason - * The reason the user has been banned. This will be supplied as the ``reason`` on the target's updated `m.room.member`_ event. - */ - explicit BanJob(const QString& roomId, const QString& userId, const QString& reason = {}); + /*! Ban a user in the room. + * \param roomId + * The room identifier (not alias) from which the user should be + * banned. \param userId The fully qualified user ID of the user being + * banned. \param reason The reason the user has been banned. This will + * be supplied as the ``reason`` on the target's updated + * `m.room.member`_ event. + */ + explicit BanJob(const QString& roomId, const QString& userId, + const QString& reason = {}); }; /// Unban a user from the room. /// /// Unban a user from the room. This allows them to be invited to the room, - /// and join if they would otherwise be allowed to join according to its join rules. - /// - /// The caller must have the required power level in order to perform this operation. + /// and join if they would otherwise be allowed to join according to its + /// join rules. + /// + /// The caller must have the required power level in order to perform this + /// operation. class UnbanJob : public BaseJob { public: - /*! Unban a user from the room. - * \param roomId - * The room identifier (not alias) from which the user should be unbanned. - * \param userId - * The fully qualified user ID of the user being unbanned. - */ - explicit UnbanJob(const QString& roomId, const QString& userId); + /*! Unban a user from the room. + * \param roomId + * The room identifier (not alias) from which the user should be + * unbanned. \param userId The fully qualified user ID of the user being + * unbanned. + */ + explicit UnbanJob(const QString& roomId, const QString& userId); }; } // namespace QMatrixClient diff --git a/lib/csapi/capabilities.cpp b/lib/csapi/capabilities.cpp index 210423f5..0fb9fbae 100644 --- a/lib/csapi/capabilities.cpp +++ b/lib/csapi/capabilities.cpp @@ -12,30 +12,32 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetCapabilitiesJob::ChangePasswordCapability& result) + template <> + struct JsonObjectConverter { + static void + fillFrom(const QJsonObject& jo, + GetCapabilitiesJob::ChangePasswordCapability& result) { fromJson(jo.value("enabled"_ls), result.enabled); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetCapabilitiesJob::RoomVersionsCapability& result) + template <> + struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetCapabilitiesJob::RoomVersionsCapability& result) { fromJson(jo.value("default"_ls), result.defaultVersion); fromJson(jo.value("available"_ls), result.available); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(QJsonObject jo, GetCapabilitiesJob::Capabilities& result) + template <> struct JsonObjectConverter { + static void fillFrom(QJsonObject jo, + GetCapabilitiesJob::Capabilities& result) { fromJson(jo.take("m.change_password"_ls), result.changePassword); fromJson(jo.take("m.room_versions"_ls), result.roomVersions); @@ -47,21 +49,21 @@ namespace QMatrixClient class GetCapabilitiesJob::Private { public: - Capabilities capabilities; + Capabilities capabilities; }; QUrl GetCapabilitiesJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/capabilities"); + basePath % "/capabilities"); } static const auto GetCapabilitiesJobName = QStringLiteral("GetCapabilitiesJob"); GetCapabilitiesJob::GetCapabilitiesJob() : BaseJob(HttpVerb::Get, GetCapabilitiesJobName, - basePath % "/capabilities") - , d(new Private) + basePath % "/capabilities"), + d(new Private) { } @@ -77,8 +79,7 @@ BaseJob::Status GetCapabilitiesJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("capabilities"_ls)) return { JsonParseError, - "The key 'capabilities' not found in the response" }; + "The key 'capabilities' not found in the response" }; fromJson(json.value("capabilities"_ls), d->capabilities); return Success; } - diff --git a/lib/csapi/capabilities.h b/lib/csapi/capabilities.h index 39e2f4d1..6282c2fd 100644 --- a/lib/csapi/capabilities.h +++ b/lib/csapi/capabilities.h @@ -6,12 +6,11 @@ #include "jobs/basejob.h" -#include #include "converters.h" #include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets information about the server's capabilities. @@ -21,62 +20,59 @@ namespace QMatrixClient class GetCapabilitiesJob : public BaseJob { public: - // Inner data structures - + // Inner data structures + + /// Capability to indicate if the user can change their password. + struct ChangePasswordCapability { + /// True if the user can change their password, false otherwise. + bool enabled; + }; + + /// The room versions the server supports. + struct RoomVersionsCapability { + /// The default room version the server is using for new rooms. + QString defaultVersion; + /// A detailed description of the room versions the server supports. + QHash available; + }; + + /// Gets information about the server's supported feature set + /// and other relevant capabilities. + struct Capabilities { /// Capability to indicate if the user can change their password. - struct ChangePasswordCapability - { - /// True if the user can change their password, false otherwise. - bool enabled; - }; - + Omittable changePassword; /// The room versions the server supports. - struct RoomVersionsCapability - { - /// The default room version the server is using for new rooms. - QString defaultVersion; - /// A detailed description of the room versions the server supports. - QHash available; - }; - - /// Gets information about the server's supported feature set - /// and other relevant capabilities. - struct Capabilities - { - /// Capability to indicate if the user can change their password. - Omittable changePassword; - /// The room versions the server supports. - Omittable roomVersions; - /// The custom capabilities the server supports, using the - /// Java package naming convention. - QHash additionalProperties; - }; - - // Construction/destruction - - explicit GetCapabilitiesJob(); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetCapabilitiesJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); - - ~GetCapabilitiesJob() override; - - // Result properties - - /// Gets information about the server's supported feature set - /// and other relevant capabilities. - const Capabilities& capabilities() const; + Omittable roomVersions; + /// The custom capabilities the server supports, using the + /// Java package naming convention. + QHash additionalProperties; + }; + + // Construction/destruction + + explicit GetCapabilitiesJob(); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetCapabilitiesJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); + + ~GetCapabilitiesJob() override; + + // Result properties + + /// Gets information about the server's supported feature set + /// and other relevant capabilities. + const Capabilities& capabilities() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index 22223985..d59449b9 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -6,8 +6,8 @@ #include "converters.h" -#include #include +#include using namespace QMatrixClient; @@ -16,7 +16,7 @@ static const auto basePath = QStringLiteral("/_matrix/media/r0"); class UploadContentJob::Private { public: - QString contentUri; + QString contentUri; }; BaseJob::Query queryToUploadContent(const QString& filename) @@ -28,11 +28,11 @@ BaseJob::Query queryToUploadContent(const QString& filename) static const auto UploadContentJobName = QStringLiteral("UploadContentJob"); -UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, const QString& contentType) - : BaseJob(HttpVerb::Post, UploadContentJobName, - basePath % "/upload", - queryToUploadContent(filename)) - , d(new Private) +UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, + const QString& contentType) + : BaseJob(HttpVerb::Post, UploadContentJobName, basePath % "/upload", + queryToUploadContent(filename)), + d(new Private) { setRequestHeader("Content-Type", contentType.toLatin1()); @@ -41,17 +41,14 @@ UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, UploadContentJob::~UploadContentJob() = default; -const QString& UploadContentJob::contentUri() const -{ - return d->contentUri; -} +const QString& UploadContentJob::contentUri() const { return d->contentUri; } BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("content_uri"_ls)) return { JsonParseError, - "The key 'content_uri' not found in the response" }; + "The key 'content_uri' not found in the response" }; fromJson(json.value("content_uri"_ls), d->contentUri); return Success; } @@ -59,9 +56,9 @@ BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data) class GetContentJob::Private { public: - QString contentType; - QString contentDisposition; - QIODevice* data; + QString contentType; + QString contentDisposition; + QIODevice* data; }; BaseJob::Query queryToGetContent(bool allowRemote) @@ -71,41 +68,37 @@ BaseJob::Query queryToGetContent(bool allowRemote) return _q; } -QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, bool allowRemote) +QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, + const QString& mediaId, bool allowRemote) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/download/" % serverName % "/" % mediaId, - queryToGetContent(allowRemote)); + basePath % "/download/" % serverName % "/" + % mediaId, + queryToGetContent(allowRemote)); } static const auto GetContentJobName = QStringLiteral("GetContentJob"); -GetContentJob::GetContentJob(const QString& serverName, const QString& mediaId, bool allowRemote) +GetContentJob::GetContentJob(const QString& serverName, const QString& mediaId, + bool allowRemote) : BaseJob(HttpVerb::Get, GetContentJobName, - basePath % "/download/" % serverName % "/" % mediaId, - queryToGetContent(allowRemote), - {}, false) - , d(new Private) + basePath % "/download/" % serverName % "/" % mediaId, + queryToGetContent(allowRemote), {}, false), + d(new Private) { setExpectedContentTypes({ "*/*" }); } GetContentJob::~GetContentJob() = default; -const QString& GetContentJob::contentType() const -{ - return d->contentType; -} +const QString& GetContentJob::contentType() const { return d->contentType; } const QString& GetContentJob::contentDisposition() const { return d->contentDisposition; } -QIODevice* GetContentJob::data() const -{ - return d->data; -} +QIODevice* GetContentJob::data() const { return d->data; } BaseJob::Status GetContentJob::parseReply(QNetworkReply* reply) { @@ -118,9 +111,9 @@ BaseJob::Status GetContentJob::parseReply(QNetworkReply* reply) class GetContentOverrideNameJob::Private { public: - QString contentType; - QString contentDisposition; - QIODevice* data; + QString contentType; + QString contentDisposition; + QIODevice* data; }; BaseJob::Query queryToGetContentOverrideName(bool allowRemote) @@ -130,21 +123,30 @@ BaseJob::Query queryToGetContentOverrideName(bool allowRemote) return _q; } -QUrl GetContentOverrideNameJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, const QString& fileName, bool allowRemote) +QUrl GetContentOverrideNameJob::makeRequestUrl(QUrl baseUrl, + const QString& serverName, + const QString& mediaId, + const QString& fileName, + bool allowRemote) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/download/" % serverName % "/" % mediaId % "/" % fileName, - queryToGetContentOverrideName(allowRemote)); + basePath % "/download/" % serverName % "/" + % mediaId % "/" % fileName, + queryToGetContentOverrideName(allowRemote)); } -static const auto GetContentOverrideNameJobName = QStringLiteral("GetContentOverrideNameJob"); +static const auto GetContentOverrideNameJobName = + QStringLiteral("GetContentOverrideNameJob"); -GetContentOverrideNameJob::GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, const QString& fileName, bool allowRemote) +GetContentOverrideNameJob::GetContentOverrideNameJob(const QString& serverName, + const QString& mediaId, + const QString& fileName, + bool allowRemote) : BaseJob(HttpVerb::Get, GetContentOverrideNameJobName, - basePath % "/download/" % serverName % "/" % mediaId % "/" % fileName, - queryToGetContentOverrideName(allowRemote), - {}, false) - , d(new Private) + basePath % "/download/" % serverName % "/" % mediaId % "/" + % fileName, + queryToGetContentOverrideName(allowRemote), {}, false), + d(new Private) { setExpectedContentTypes({ "*/*" }); } @@ -161,10 +163,7 @@ const QString& GetContentOverrideNameJob::contentDisposition() const return d->contentDisposition; } -QIODevice* GetContentOverrideNameJob::data() const -{ - return d->data; -} +QIODevice* GetContentOverrideNameJob::data() const { return d->data; } BaseJob::Status GetContentOverrideNameJob::parseReply(QNetworkReply* reply) { @@ -177,11 +176,13 @@ BaseJob::Status GetContentOverrideNameJob::parseReply(QNetworkReply* reply) class GetContentThumbnailJob::Private { public: - QString contentType; - QIODevice* data; + QString contentType; + QIODevice* data; }; -BaseJob::Query queryToGetContentThumbnail(int width, int height, const QString& method, bool allowRemote) +BaseJob::Query queryToGetContentThumbnail(int width, int height, + const QString& method, + bool allowRemote) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("width"), width); @@ -191,21 +192,31 @@ BaseJob::Query queryToGetContentThumbnail(int width, int height, const QString& return _q; } -QUrl GetContentThumbnailJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, int width, int height, const QString& method, bool allowRemote) +QUrl GetContentThumbnailJob::makeRequestUrl(QUrl baseUrl, + const QString& serverName, + const QString& mediaId, int width, + int height, const QString& method, + bool allowRemote) { - return BaseJob::makeRequestUrl(std::move(baseUrl), + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/thumbnail/" % serverName % "/" % mediaId, queryToGetContentThumbnail(width, height, method, allowRemote)); } -static const auto GetContentThumbnailJobName = QStringLiteral("GetContentThumbnailJob"); +static const auto GetContentThumbnailJobName = + QStringLiteral("GetContentThumbnailJob"); -GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, int height, const QString& method, bool allowRemote) +GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, + const QString& mediaId, + int width, int height, + const QString& method, + bool allowRemote) : BaseJob(HttpVerb::Get, GetContentThumbnailJobName, - basePath % "/thumbnail/" % serverName % "/" % mediaId, - queryToGetContentThumbnail(width, height, method, allowRemote), - {}, false) - , d(new Private) + basePath % "/thumbnail/" % serverName % "/" % mediaId, + queryToGetContentThumbnail(width, height, method, allowRemote), + {}, false), + d(new Private) { setExpectedContentTypes({ "image/jpeg", "image/png" }); } @@ -217,10 +228,7 @@ const QString& GetContentThumbnailJob::contentType() const return d->contentType; } -QIODevice* GetContentThumbnailJob::data() const -{ - return d->data; -} +QIODevice* GetContentThumbnailJob::data() const { return d->data; } BaseJob::Status GetContentThumbnailJob::parseReply(QNetworkReply* reply) { @@ -232,8 +240,8 @@ BaseJob::Status GetContentThumbnailJob::parseReply(QNetworkReply* reply) class GetUrlPreviewJob::Private { public: - Omittable matrixImageSize; - QString ogImage; + Omittable matrixImageSize; + QString ogImage; }; BaseJob::Query queryToGetUrlPreview(const QString& url, Omittable ts) @@ -244,20 +252,20 @@ BaseJob::Query queryToGetUrlPreview(const QString& url, Omittable ts) return _q; } -QUrl GetUrlPreviewJob::makeRequestUrl(QUrl baseUrl, const QString& url, Omittable ts) +QUrl GetUrlPreviewJob::makeRequestUrl(QUrl baseUrl, const QString& url, + Omittable ts) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/preview_url", - queryToGetUrlPreview(url, ts)); + basePath % "/preview_url", + queryToGetUrlPreview(url, ts)); } static const auto GetUrlPreviewJobName = QStringLiteral("GetUrlPreviewJob"); GetUrlPreviewJob::GetUrlPreviewJob(const QString& url, Omittable ts) - : BaseJob(HttpVerb::Get, GetUrlPreviewJobName, - basePath % "/preview_url", - queryToGetUrlPreview(url, ts)) - , d(new Private) + : BaseJob(HttpVerb::Get, GetUrlPreviewJobName, basePath % "/preview_url", + queryToGetUrlPreview(url, ts)), + d(new Private) { } @@ -268,10 +276,7 @@ Omittable GetUrlPreviewJob::matrixImageSize() const return d->matrixImageSize; } -const QString& GetUrlPreviewJob::ogImage() const -{ - return d->ogImage; -} +const QString& GetUrlPreviewJob::ogImage() const { return d->ogImage; } BaseJob::Status GetUrlPreviewJob::parseJson(const QJsonDocument& data) { @@ -284,30 +289,25 @@ BaseJob::Status GetUrlPreviewJob::parseJson(const QJsonDocument& data) class GetConfigJob::Private { public: - Omittable uploadSize; + Omittable uploadSize; }; QUrl GetConfigJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/config"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/config"); } static const auto GetConfigJobName = QStringLiteral("GetConfigJob"); GetConfigJob::GetConfigJob() - : BaseJob(HttpVerb::Get, GetConfigJobName, - basePath % "/config") - , d(new Private) + : BaseJob(HttpVerb::Get, GetConfigJobName, basePath % "/config"), + d(new Private) { } GetConfigJob::~GetConfigJob() = default; -Omittable GetConfigJob::uploadSize() const -{ - return d->uploadSize; -} +Omittable GetConfigJob::uploadSize() const { return d->uploadSize; } BaseJob::Status GetConfigJob::parseJson(const QJsonDocument& data) { @@ -315,4 +315,3 @@ BaseJob::Status GetConfigJob::parseJson(const QJsonDocument& data) fromJson(json.value("m.upload.size"_ls), d->uploadSize); return Success; } - diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h index 5ef2e0d6..1bef6380 100644 --- a/lib/csapi/content-repo.h +++ b/lib/csapi/content-repo.h @@ -9,252 +9,271 @@ #include "converters.h" #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Upload some content to the content repository. class UploadContentJob : public BaseJob { public: - /*! Upload some content to the content repository. - * \param content - * \param filename - * The name of the file being uploaded - * \param contentType - * The content type of the file being uploaded - */ - explicit UploadContentJob(QIODevice* content, const QString& filename = {}, const QString& contentType = {}); - ~UploadContentJob() override; - - // Result properties - - /// The MXC URI to the uploaded content. - const QString& contentUri() const; + /*! Upload some content to the content repository. + * \param content + * \param filename + * The name of the file being uploaded + * \param contentType + * The content type of the file being uploaded + */ + explicit UploadContentJob(QIODevice* content, + const QString& filename = {}, + const QString& contentType = {}); + ~UploadContentJob() override; + + // Result properties + + /// The MXC URI to the uploaded content. + const QString& contentUri() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Download content from the content repository. class GetContentJob : public BaseJob { public: - /*! Download content from the content repository. - * \param serverName - * The server name from the ``mxc://`` URI (the authoritory component) - * \param mediaId - * The media ID from the ``mxc://`` URI (the path component) - * \param allowRemote - * Indicates to the server that it should not attempt to fetch the media if it is deemed - * remote. This is to prevent routing loops where the server contacts itself. Defaults to - * true if not provided. - */ - explicit GetContentJob(const QString& serverName, const QString& mediaId, bool allowRemote = true); - - /*! Construct a URL without creating a full-fledged job object - * - * 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, bool allowRemote = true); - - ~GetContentJob() override; - - // Result properties - - /// The content type of the file that was previously uploaded. - const QString& contentType() const; - /// The name of the file that was previously uploaded, if set. - const QString& contentDisposition() const; - /// The content that was previously uploaded. - QIODevice* data() const; + /*! Download content from the content repository. + * \param serverName + * The server name from the ``mxc://`` URI (the authoritory component) + * \param mediaId + * The media ID from the ``mxc://`` URI (the path component) + * \param allowRemote + * Indicates to the server that it should not attempt to fetch the + * media if it is deemed remote. This is to prevent routing loops where + * the server contacts itself. Defaults to true if not provided. + */ + explicit GetContentJob(const QString& serverName, + const QString& mediaId, bool allowRemote = true); + + /*! Construct a URL without creating a full-fledged job object + * + * 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, + bool allowRemote = true); + + ~GetContentJob() override; + + // Result properties + + /// The content type of the file that was previously uploaded. + const QString& contentType() const; + /// The name of the file that was previously uploaded, if set. + const QString& contentDisposition() const; + /// The content that was previously uploaded. + QIODevice* data() const; protected: - Status parseReply(QNetworkReply* reply) override; + Status parseReply(QNetworkReply* reply) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Download content from the content repository as a given filename. class GetContentOverrideNameJob : public BaseJob { public: - /*! Download content from the content repository as a given filename. - * \param serverName - * The server name from the ``mxc://`` URI (the authoritory component) - * \param mediaId - * The media ID from the ``mxc://`` URI (the path component) - * \param fileName - * The filename to give in the Content-Disposition - * \param allowRemote - * Indicates to the server that it should not attempt to fetch the media if it is deemed - * remote. This is to prevent routing loops where the server contacts itself. Defaults to - * true if not provided. - */ - explicit GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, const QString& fileName, bool allowRemote = true); - - /*! Construct a URL without creating a full-fledged job object - * - * 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, bool allowRemote = true); - - ~GetContentOverrideNameJob() override; - - // Result properties - - /// The content type of the file that was previously uploaded. - const QString& contentType() const; - /// The name of file given in the request - const QString& contentDisposition() const; - /// The content that was previously uploaded. - QIODevice* data() const; + /*! Download content from the content repository as a given filename. + * \param serverName + * The server name from the ``mxc://`` URI (the authoritory component) + * \param mediaId + * The media ID from the ``mxc://`` URI (the path component) + * \param fileName + * The filename to give in the Content-Disposition + * \param allowRemote + * Indicates to the server that it should not attempt to fetch the + * media if it is deemed remote. This is to prevent routing loops where + * the server contacts itself. Defaults to true if not provided. + */ + explicit GetContentOverrideNameJob(const QString& serverName, + const QString& mediaId, + const QString& fileName, + bool allowRemote = true); + + /*! Construct a URL without creating a full-fledged job object + * + * 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, + bool allowRemote = true); + + ~GetContentOverrideNameJob() override; + + // Result properties + + /// The content type of the file that was previously uploaded. + const QString& contentType() const; + /// The name of file given in the request + const QString& contentDisposition() const; + /// The content that was previously uploaded. + QIODevice* data() const; protected: - Status parseReply(QNetworkReply* reply) override; + Status parseReply(QNetworkReply* reply) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Download a thumbnail of the content from the content repository. class GetContentThumbnailJob : public BaseJob { public: - /*! Download a thumbnail of the content from the content repository. - * \param serverName - * The server name from the ``mxc://`` URI (the authoritory component) - * \param mediaId - * The media ID from the ``mxc://`` URI (the path component) - * \param width - * The *desired* width of the thumbnail. The actual thumbnail may not - * match the size specified. - * \param height - * The *desired* height of the thumbnail. The actual thumbnail may not - * match the size specified. - * \param method - * The desired resizing method. - * \param allowRemote - * Indicates to the server that it should not attempt to fetch the media if it is deemed - * remote. This is to prevent routing loops where the server contacts itself. Defaults to - * true if not provided. - */ - explicit GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, int height, const QString& method = {}, bool allowRemote = true); - - /*! Construct a URL without creating a full-fledged job object - * - * 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 = {}, bool allowRemote = true); - - ~GetContentThumbnailJob() override; - - // Result properties - - /// The content type of the thumbnail. - const QString& contentType() const; - /// A thumbnail of the requested content. - QIODevice* data() const; + /*! Download a thumbnail of the content from the content repository. + * \param serverName + * The server name from the ``mxc://`` URI (the authoritory component) + * \param mediaId + * The media ID from the ``mxc://`` URI (the path component) + * \param width + * The *desired* width of the thumbnail. The actual thumbnail may not + * match the size specified. + * \param height + * The *desired* height of the thumbnail. The actual thumbnail may not + * match the size specified. + * \param method + * The desired resizing method. + * \param allowRemote + * Indicates to the server that it should not attempt to fetch the + * media if it is deemed remote. This is to prevent routing loops where + * the server contacts itself. Defaults to true if not provided. + */ + explicit GetContentThumbnailJob(const QString& serverName, + const QString& mediaId, int width, + int height, const QString& method = {}, + bool allowRemote = true); + + /*! Construct a URL without creating a full-fledged job object + * + * 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 = {}, + bool allowRemote = true); + + ~GetContentThumbnailJob() override; + + // Result properties + + /// The content type of the thumbnail. + const QString& contentType() const; + /// A thumbnail of the requested content. + QIODevice* data() const; protected: - Status parseReply(QNetworkReply* reply) override; + Status parseReply(QNetworkReply* reply) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get information about a URL for a client class GetUrlPreviewJob : public BaseJob { public: - /*! Get information about a URL for a client - * \param url - * The URL to get a preview of - * \param ts - * The preferred point in time to return a preview for. The server may - * return a newer version if it does not have the requested version - * available. - */ - explicit GetUrlPreviewJob(const QString& url, Omittable ts = none); - - /*! Construct a URL without creating a full-fledged job object - * - * 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, Omittable ts = none); - - ~GetUrlPreviewJob() override; - - // Result properties - - /// The byte-size of the image. Omitted if there is no image attached. - Omittable matrixImageSize() const; - /// An MXC URI to the image. Omitted if there is no image. - const QString& ogImage() const; + /*! Get information about a URL for a client + * \param url + * The URL to get a preview of + * \param ts + * The preferred point in time to return a preview for. The server may + * return a newer version if it does not have the requested version + * available. + */ + explicit GetUrlPreviewJob(const QString& url, + Omittable ts = none); + + /*! Construct a URL without creating a full-fledged job object + * + * 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, + Omittable ts = none); + + ~GetUrlPreviewJob() override; + + // Result properties + + /// The byte-size of the image. Omitted if there is no image attached. + Omittable matrixImageSize() const; + /// An MXC URI to the image. Omitted if there is no image. + const QString& ogImage() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get the configuration for the content repository. /// - /// This endpoint allows clients to retrieve the configuration of the content - /// repository, such as upload limitations. - /// Clients SHOULD use this as a guide when using content repository endpoints. - /// All values are intentionally left optional. Clients SHOULD follow - /// the advice given in the field description when the field is not available. - /// - /// **NOTE:** Both clients and server administrators should be aware that proxies - /// between the client and the server may affect the apparent behaviour of content - /// repository APIs, for example, proxies may enforce a lower upload size limit - /// than is advertised by the server on this endpoint. + /// This endpoint allows clients to retrieve the configuration of the + /// content repository, such as upload limitations. Clients SHOULD use this + /// as a guide when using content repository endpoints. All values are + /// intentionally left optional. Clients SHOULD follow the advice given in + /// the field description when the field is not available. + /// + /// **NOTE:** Both clients and server administrators should be aware that + /// proxies between the client and the server may affect the apparent + /// behaviour of content repository APIs, for example, proxies may enforce a + /// lower upload size limit than is advertised by the server on this + /// endpoint. class GetConfigJob : public BaseJob { public: - explicit GetConfigJob(); + explicit GetConfigJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetConfigJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetConfigJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetConfigJob() override; + ~GetConfigJob() override; - // Result properties + // Result properties - /// The maximum size an upload can be in bytes. - /// Clients SHOULD use this as a guide when uploading content. - /// If not listed or null, the size limit should be treated as unknown. - Omittable uploadSize() const; + /// The maximum size an upload can be in bytes. + /// Clients SHOULD use this as a guide when uploading content. + /// If not listed or null, the size limit should be treated as unknown. + Omittable uploadSize() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index 448547ae..47a13d8e 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const CreateRoomJob::Invite3pid& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const CreateRoomJob::Invite3pid& pod) { addParam<>(jo, QStringLiteral("id_server"), pod.idServer); addParam<>(jo, QStringLiteral("medium"), pod.medium); @@ -26,9 +25,9 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const CreateRoomJob::StateEvent& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const CreateRoomJob::StateEvent& pod) { addParam<>(jo, QStringLiteral("type"), pod.type); addParam(jo, QStringLiteral("state_key"), pod.stateKey); @@ -40,46 +39,52 @@ namespace QMatrixClient class CreateRoomJob::Private { public: - QString roomId; + QString roomId; }; static const auto CreateRoomJobName = QStringLiteral("CreateRoomJob"); -CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QStringList& invite, const QVector& invite3pid, const QString& roomVersion, const QJsonObject& creationContent, const QVector& initialState, const QString& preset, Omittable isDirect, const QJsonObject& powerLevelContentOverride) - : BaseJob(HttpVerb::Post, CreateRoomJobName, - basePath % "/createRoom") - , d(new Private) +CreateRoomJob::CreateRoomJob(const QString& visibility, + const QString& roomAliasName, const QString& name, + const QString& topic, const QStringList& invite, + const QVector& invite3pid, + const QString& roomVersion, + const QJsonObject& creationContent, + const QVector& initialState, + const QString& preset, Omittable isDirect, + const QJsonObject& powerLevelContentOverride) + : BaseJob(HttpVerb::Post, CreateRoomJobName, basePath % "/createRoom"), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("visibility"), visibility); - addParam(_data, QStringLiteral("room_alias_name"), roomAliasName); + addParam(_data, QStringLiteral("room_alias_name"), + roomAliasName); addParam(_data, QStringLiteral("name"), name); addParam(_data, QStringLiteral("topic"), topic); addParam(_data, QStringLiteral("invite"), invite); addParam(_data, QStringLiteral("invite_3pid"), invite3pid); addParam(_data, QStringLiteral("room_version"), roomVersion); - addParam(_data, QStringLiteral("creation_content"), creationContent); + addParam(_data, QStringLiteral("creation_content"), + creationContent); addParam(_data, QStringLiteral("initial_state"), initialState); addParam(_data, QStringLiteral("preset"), preset); addParam(_data, QStringLiteral("is_direct"), isDirect); - addParam(_data, QStringLiteral("power_level_content_override"), powerLevelContentOverride); + addParam(_data, QStringLiteral("power_level_content_override"), + powerLevelContentOverride); setRequestData(_data); } CreateRoomJob::~CreateRoomJob() = default; -const QString& CreateRoomJob::roomId() const -{ - return d->roomId; -} +const QString& CreateRoomJob::roomId() const { return d->roomId; } BaseJob::Status CreateRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) return { JsonParseError, - "The key 'room_id' not found in the response" }; + "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; } - diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index d7c01d00..4348bd63 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -6,229 +6,267 @@ #include "jobs/basejob.h" -#include #include "converters.h" +#include #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Create a new room /// /// Create a new room with various configuration options. - /// + /// /// The server MUST apply the normal state resolution rules when creating /// the new room, including checking power levels for each event. It MUST /// apply the events implied by the request in the following order: - /// + /// /// 0. A default ``m.room.power_levels`` event, giving the room creator /// (and not other members) permission to send state events. Overridden /// by the ``power_level_content_override`` parameter. - /// - /// 1. Events set by the ``preset``. Currently these are the ``m.room.join_rules``, - /// ``m.room.history_visibility``, and ``m.room.guest_access`` state events. - /// + /// + /// 1. Events set by the ``preset``. Currently these are the + /// ``m.room.join_rules``, + /// ``m.room.history_visibility``, and ``m.room.guest_access`` state + /// events. + /// /// 2. Events listed in ``initial_state``, in the order that they are /// listed. - /// - /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic`` + /// + /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// ``m.room.topic`` /// state events). - /// - /// 4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with + /// + /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// (``m.room.member`` with /// ``membership: invite`` and ``m.room.third_party_invite``). - /// + /// /// The available presets do the following with respect to room state: - /// - /// ======================== ============== ====================== ================ ========= - /// Preset ``join_rules`` ``history_visibility`` ``guest_access`` Other - /// ======================== ============== ====================== ================ ========= - /// ``private_chat`` ``invite`` ``shared`` ``can_join`` - /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All invitees are given the same power level as the room creator. - /// ``public_chat`` ``public`` ``shared`` ``forbidden`` - /// ======================== ============== ====================== ================ ========= - /// + /// + /// ======================== ============== ====================== + /// ================ ========= + /// Preset ``join_rules`` ``history_visibility`` + /// ``guest_access`` Other + /// ======================== ============== ====================== + /// ================ ========= + /// ``private_chat`` ``invite`` ``shared`` ``can_join`` + /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All + /// invitees are given the same power level as the room creator. + /// ``public_chat`` ``public`` ``shared`` ``forbidden`` + /// ======================== ============== ====================== + /// ================ ========= + /// /// The server will create a ``m.room.create`` event in the room with the /// requesting user as the creator, alongside other keys provided in the /// ``creation_content``. class CreateRoomJob : public BaseJob { public: - // Inner data structures + // Inner data structures - /// Create a new room with various configuration options. - /// - /// The server MUST apply the normal state resolution rules when creating - /// the new room, including checking power levels for each event. It MUST - /// apply the events implied by the request in the following order: - /// - /// 0. A default ``m.room.power_levels`` event, giving the room creator - /// (and not other members) permission to send state events. Overridden - /// by the ``power_level_content_override`` parameter. - /// - /// 1. Events set by the ``preset``. Currently these are the ``m.room.join_rules``, - /// ``m.room.history_visibility``, and ``m.room.guest_access`` state events. - /// - /// 2. Events listed in ``initial_state``, in the order that they are - /// listed. - /// - /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic`` - /// state events). - /// - /// 4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with - /// ``membership: invite`` and ``m.room.third_party_invite``). - /// - /// The available presets do the following with respect to room state: - /// - /// ======================== ============== ====================== ================ ========= - /// Preset ``join_rules`` ``history_visibility`` ``guest_access`` Other - /// ======================== ============== ====================== ================ ========= - /// ``private_chat`` ``invite`` ``shared`` ``can_join`` - /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All invitees are given the same power level as the room creator. - /// ``public_chat`` ``public`` ``shared`` ``forbidden`` - /// ======================== ============== ====================== ================ ========= - /// - /// The server will create a ``m.room.create`` event in the room with the - /// requesting user as the creator, alongside other keys provided in the - /// ``creation_content``. - struct Invite3pid - { - /// The hostname+port of the identity server which should be used for third party identifier lookups. - QString idServer; - /// The kind of address being passed in the address field, for example ``email``. - QString medium; - /// The invitee's third party identifier. - QString address; - }; + /// Create a new room with various configuration options. + /// + /// The server MUST apply the normal state resolution rules when + /// creating the new room, including checking power levels for each + /// event. It MUST apply the events implied by the request in the + /// following order: + /// + /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// (and not other members) permission to send state events. + /// Overridden by the ``power_level_content_override`` parameter. + /// + /// 1. Events set by the ``preset``. Currently these are the + /// ``m.room.join_rules``, + /// ``m.room.history_visibility``, and ``m.room.guest_access`` state + /// events. + /// + /// 2. Events listed in ``initial_state``, in the order that they are + /// listed. + /// + /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// ``m.room.topic`` + /// state events). + /// + /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// (``m.room.member`` with + /// ``membership: invite`` and ``m.room.third_party_invite``). + /// + /// The available presets do the following with respect to room state: + /// + /// ======================== ============== ====================== + /// ================ ========= + /// Preset ``join_rules`` ``history_visibility`` + /// ``guest_access`` Other + /// ======================== ============== ====================== + /// ================ ========= + /// ``private_chat`` ``invite`` ``shared`` ``can_join`` + /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` + /// All invitees are given the same power level as the room creator. + /// ``public_chat`` ``public`` ``shared`` ``forbidden`` + /// ======================== ============== ====================== + /// ================ ========= + /// + /// The server will create a ``m.room.create`` event in the room with + /// the requesting user as the creator, alongside other keys provided in + /// the + /// ``creation_content``. + struct Invite3pid { + /// The hostname+port of the identity server which should be used + /// for third party identifier lookups. + QString idServer; + /// The kind of address being passed in the address field, for + /// example ``email``. + QString medium; + /// The invitee's third party identifier. + QString address; + }; - /// Create a new room with various configuration options. - /// - /// The server MUST apply the normal state resolution rules when creating - /// the new room, including checking power levels for each event. It MUST - /// apply the events implied by the request in the following order: - /// - /// 0. A default ``m.room.power_levels`` event, giving the room creator - /// (and not other members) permission to send state events. Overridden - /// by the ``power_level_content_override`` parameter. - /// - /// 1. Events set by the ``preset``. Currently these are the ``m.room.join_rules``, - /// ``m.room.history_visibility``, and ``m.room.guest_access`` state events. - /// - /// 2. Events listed in ``initial_state``, in the order that they are - /// listed. - /// - /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and ``m.room.topic`` - /// state events). - /// - /// 4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` with - /// ``membership: invite`` and ``m.room.third_party_invite``). - /// - /// The available presets do the following with respect to room state: - /// - /// ======================== ============== ====================== ================ ========= - /// Preset ``join_rules`` ``history_visibility`` ``guest_access`` Other - /// ======================== ============== ====================== ================ ========= - /// ``private_chat`` ``invite`` ``shared`` ``can_join`` - /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All invitees are given the same power level as the room creator. - /// ``public_chat`` ``public`` ``shared`` ``forbidden`` - /// ======================== ============== ====================== ================ ========= - /// - /// The server will create a ``m.room.create`` event in the room with the - /// requesting user as the creator, alongside other keys provided in the - /// ``creation_content``. - struct StateEvent - { - /// The type of event to send. - QString type; - /// The state_key of the state event. Defaults to an empty string. - QString stateKey; - /// The content of the event. - QJsonObject content; - }; + /// Create a new room with various configuration options. + /// + /// The server MUST apply the normal state resolution rules when + /// creating the new room, including checking power levels for each + /// event. It MUST apply the events implied by the request in the + /// following order: + /// + /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// (and not other members) permission to send state events. + /// Overridden by the ``power_level_content_override`` parameter. + /// + /// 1. Events set by the ``preset``. Currently these are the + /// ``m.room.join_rules``, + /// ``m.room.history_visibility``, and ``m.room.guest_access`` state + /// events. + /// + /// 2. Events listed in ``initial_state``, in the order that they are + /// listed. + /// + /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// ``m.room.topic`` + /// state events). + /// + /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// (``m.room.member`` with + /// ``membership: invite`` and ``m.room.third_party_invite``). + /// + /// The available presets do the following with respect to room state: + /// + /// ======================== ============== ====================== + /// ================ ========= + /// Preset ``join_rules`` ``history_visibility`` + /// ``guest_access`` Other + /// ======================== ============== ====================== + /// ================ ========= + /// ``private_chat`` ``invite`` ``shared`` ``can_join`` + /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` + /// All invitees are given the same power level as the room creator. + /// ``public_chat`` ``public`` ``shared`` ``forbidden`` + /// ======================== ============== ====================== + /// ================ ========= + /// + /// The server will create a ``m.room.create`` event in the room with + /// the requesting user as the creator, alongside other keys provided in + /// the + /// ``creation_content``. + struct StateEvent { + /// The type of event to send. + QString type; + /// The state_key of the state event. Defaults to an empty string. + QString stateKey; + /// The content of the event. + QJsonObject content; + }; - // Construction/destruction + // Construction/destruction - /*! Create a new room - * \param visibility - * A ``public`` visibility indicates that the room will be shown - * in the published room list. A ``private`` visibility will hide - * the room from the published room list. Rooms default to - * ``private`` visibility if this key is not included. NB: This - * should not be confused with ``join_rules`` which also uses the - * word ``public``. - * \param roomAliasName - * The desired room alias **local part**. If this is included, a - * room alias will be created and mapped to the newly created - * room. The alias will belong on the *same* homeserver which - * created the room. For example, if this was set to "foo" and - * sent to the homeserver "example.com" the complete room alias - * would be ``#foo:example.com``. - * - * The complete room alias will become the canonical alias for - * the room. - * \param name - * If this is included, an ``m.room.name`` event will be sent - * into the room to indicate the name of the room. See Room - * Events for more information on ``m.room.name``. - * \param topic - * If this is included, an ``m.room.topic`` event will be sent - * into the room to indicate the topic for the room. See Room - * Events for more information on ``m.room.topic``. - * \param invite - * A list of user IDs to invite to the room. This will tell the - * server to invite everyone in the list to the newly created room. - * \param invite3pid - * A list of objects representing third party IDs to invite into - * the room. - * \param roomVersion - * The room version to set for the room. If not provided, the homeserver is - * to use its configured default. If provided, the homeserver will return a - * 400 error with the errcode ``M_UNSUPPORTED_ROOM_VERSION`` if it does not - * support the room version. - * \param creationContent - * Extra keys, such as ``m.federate``, to be added to the content - * of the `m.room.create`_ event. The server will clobber the following - * keys: ``creator``, ``room_version``. Future versions of the specification - * may allow the server to clobber other keys. - * \param initialState - * A list of state events to set in the new room. This allows - * the user to override the default state events set in the new - * room. The expected format of the state events are an object - * with type, state_key and content keys set. - * - * Takes precedence over events set by ``preset``, but gets - * overriden by ``name`` and ``topic`` keys. - * \param preset - * Convenience parameter for setting various default state events - * based on a preset. - * - * If unspecified, the server should use the ``visibility`` to determine - * which preset to use. A visbility of ``public`` equates to a preset of - * ``public_chat`` and ``private`` visibility equates to a preset of - * ``private_chat``. - * \param isDirect - * This flag makes the server set the ``is_direct`` flag on the - * ``m.room.member`` events sent to the users in ``invite`` and - * ``invite_3pid``. See `Direct Messaging`_ for more information. - * \param powerLevelContentOverride - * The power level content to override in the default power level - * event. This object is applied on top of the generated `m.room.power_levels`_ - * event content prior to it being sent to the room. Defaults to - * overriding nothing. - */ - explicit CreateRoomJob(const QString& visibility = {}, const QString& roomAliasName = {}, const QString& name = {}, const QString& topic = {}, const QStringList& invite = {}, const QVector& invite3pid = {}, const QString& roomVersion = {}, const QJsonObject& creationContent = {}, const QVector& initialState = {}, const QString& preset = {}, Omittable isDirect = none, const QJsonObject& powerLevelContentOverride = {}); - ~CreateRoomJob() override; + /*! Create a new room + * \param visibility + * A ``public`` visibility indicates that the room will be shown + * in the published room list. A ``private`` visibility will hide + * the room from the published room list. Rooms default to + * ``private`` visibility if this key is not included. NB: This + * should not be confused with ``join_rules`` which also uses the + * word ``public``. + * \param roomAliasName + * The desired room alias **local part**. If this is included, a + * room alias will be created and mapped to the newly created + * room. The alias will belong on the *same* homeserver which + * created the room. For example, if this was set to "foo" and + * sent to the homeserver "example.com" the complete room alias + * would be ``#foo:example.com``. + * + * The complete room alias will become the canonical alias for + * the room. + * \param name + * If this is included, an ``m.room.name`` event will be sent + * into the room to indicate the name of the room. See Room + * Events for more information on ``m.room.name``. + * \param topic + * If this is included, an ``m.room.topic`` event will be sent + * into the room to indicate the topic for the room. See Room + * Events for more information on ``m.room.topic``. + * \param invite + * A list of user IDs to invite to the room. This will tell the + * server to invite everyone in the list to the newly created room. + * \param invite3pid + * A list of objects representing third party IDs to invite into + * the room. + * \param roomVersion + * The room version to set for the room. If not provided, the + * homeserver is to use its configured default. If provided, the + * homeserver will return a 400 error with the errcode + * ``M_UNSUPPORTED_ROOM_VERSION`` if it does not support the room + * version. \param creationContent Extra keys, such as ``m.federate``, + * to be added to the content of the `m.room.create`_ event. The server + * will clobber the following keys: ``creator``, ``room_version``. + * Future versions of the specification may allow the server to clobber + * other keys. \param initialState A list of state events to set in the + * new room. This allows the user to override the default state events + * set in the new room. The expected format of the state events are an + * object with type, state_key and content keys set. + * + * Takes precedence over events set by ``preset``, but gets + * overriden by ``name`` and ``topic`` keys. + * \param preset + * Convenience parameter for setting various default state events + * based on a preset. + * + * If unspecified, the server should use the ``visibility`` to + * determine which preset to use. A visbility of ``public`` equates to a + * preset of + * ``public_chat`` and ``private`` visibility equates to a preset of + * ``private_chat``. + * \param isDirect + * This flag makes the server set the ``is_direct`` flag on the + * ``m.room.member`` events sent to the users in ``invite`` and + * ``invite_3pid``. See `Direct Messaging`_ for more information. + * \param powerLevelContentOverride + * The power level content to override in the default power level + * event. This object is applied on top of the generated + * `m.room.power_levels`_ event content prior to it being sent to the + * room. Defaults to overriding nothing. + */ + explicit CreateRoomJob( + const QString& visibility = {}, + const QString& roomAliasName = {}, const QString& name = {}, + const QString& topic = {}, const QStringList& invite = {}, + const QVector& invite3pid = {}, + const QString& roomVersion = {}, + const QJsonObject& creationContent = {}, + const QVector& initialState = {}, + const QString& preset = {}, Omittable isDirect = none, + const QJsonObject& powerLevelContentOverride = {}); + ~CreateRoomJob() override; - // Result properties + // Result properties - /// The created room's ID. - const QString& roomId() const; + /// The created room's ID. + const QString& roomId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/auth_data.cpp b/lib/csapi/definitions/auth_data.cpp index 006b8c7e..f40a3a90 100644 --- a/lib/csapi/definitions/auth_data.cpp +++ b/lib/csapi/definitions/auth_data.cpp @@ -15,11 +15,10 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - QJsonObject jo, AuthenticationData& result) + QJsonObject jo, AuthenticationData& result) { fromJson(jo.take("type"_ls), result.type); fromJson(jo.take("session"_ls), result.session); fromJson(jo, result.authInfo); } - diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index 26eb205c..e25dff7a 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -6,16 +6,15 @@ #include "converters.h" -#include #include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - /// Used by clients to submit authentication information to the interactive-authentication API - struct AuthenticationData - { + /// Used by clients to submit authentication information to the + /// interactive-authentication API + struct AuthenticationData { /// The login type that the client is attempting to complete. QString type; /// The value of the session key given by the homeserver. @@ -23,8 +22,7 @@ namespace QMatrixClient /// Keys dependent on the login type QHash authInfo; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const AuthenticationData& pod); static void fillFrom(QJsonObject jo, AuthenticationData& pod); }; diff --git a/lib/csapi/definitions/client_device.cpp b/lib/csapi/definitions/client_device.cpp index 752b806a..2ca58e2f 100644 --- a/lib/csapi/definitions/client_device.cpp +++ b/lib/csapi/definitions/client_device.cpp @@ -6,8 +6,7 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const Device& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, const Device& pod) { addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); addParam(jo, QStringLiteral("display_name"), pod.displayName); @@ -15,12 +14,11 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("last_seen_ts"), pod.lastSeenTs); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, Device& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + Device& result) { fromJson(jo.value("device_id"_ls), result.deviceId); fromJson(jo.value("display_name"_ls), result.displayName); fromJson(jo.value("last_seen_ip"_ls), result.lastSeenIp); fromJson(jo.value("last_seen_ts"_ls), result.lastSeenTs); } - diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index a6224f71..b473a037 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -8,28 +8,25 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// A client device - struct Device - { + struct Device { /// Identifier of this device. QString deviceId; - /// Display name set by the user for this device. Absent if no name has been - /// set. + /// Display name set by the user for this device. Absent if no name has + /// been set. QString displayName; - /// The IP address where this device was last seen. (May be a few minutes out - /// of date, for efficiency reasons). + /// The IP address where this device was last seen. (May be a few + /// minutes out of date, for efficiency reasons). QString lastSeenIp; - /// The timestamp (in milliseconds since the unix epoch) when this devices - /// was last seen. (May be a few minutes out of date, for efficiency - /// reasons). + /// The timestamp (in milliseconds since the unix epoch) when this + /// devices was last seen. (May be a few minutes out of date, for + /// efficiency reasons). Omittable lastSeenTs; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Device& pod); static void fillFrom(const QJsonObject& jo, Device& pod); }; diff --git a/lib/csapi/definitions/device_keys.cpp b/lib/csapi/definitions/device_keys.cpp index 1e79499f..cc5262b7 100644 --- a/lib/csapi/definitions/device_keys.cpp +++ b/lib/csapi/definitions/device_keys.cpp @@ -6,8 +6,8 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const DeviceKeys& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const DeviceKeys& pod) { addParam<>(jo, QStringLiteral("user_id"), pod.userId); addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); @@ -16,8 +16,8 @@ void JsonObjectConverter::dumpTo( addParam<>(jo, QStringLiteral("signatures"), pod.signatures); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, DeviceKeys& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + DeviceKeys& result) { fromJson(jo.value("user_id"_ls), result.userId); fromJson(jo.value("device_id"_ls), result.deviceId); @@ -25,4 +25,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("keys"_ls), result.keys); fromJson(jo.value("signatures"_ls), result.signatures); } - diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index 8ebe1125..6c417ce7 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -8,18 +8,16 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Device identity keys - struct DeviceKeys - { - /// The ID of the user the device belongs to. Must match the user ID used - /// when logging in. + struct DeviceKeys { + /// The ID of the user the device belongs to. Must match the user ID + /// used when logging in. QString userId; - /// The ID of the device these keys belong to. Must match the device ID used - /// when logging in. + /// The ID of the device these keys belong to. Must match the device ID + /// used when logging in. QString deviceId; /// The encryption algorithms supported by this device. QStringList algorithms; @@ -27,15 +25,15 @@ namespace QMatrixClient /// format ``:``. The keys themselves should be /// encoded as specified by the key algorithm. QHash keys; - /// Signatures for the device key object. A map from user ID, to a map from + /// Signatures for the device key object. A map from user ID, to a map + /// from /// ``:`` to the signature. - /// + /// /// The signature is calculated using the process described at `Signing /// JSON`_. QHash> signatures; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DeviceKeys& pod); static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); }; diff --git a/lib/csapi/definitions/event_filter.cpp b/lib/csapi/definitions/event_filter.cpp index b20d7807..9b2c7a33 100644 --- a/lib/csapi/definitions/event_filter.cpp +++ b/lib/csapi/definitions/event_filter.cpp @@ -6,8 +6,8 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const EventFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const EventFilter& pod) { addParam(jo, QStringLiteral("limit"), pod.limit); addParam(jo, QStringLiteral("not_senders"), pod.notSenders); @@ -16,8 +16,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("types"), pod.types); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, EventFilter& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + EventFilter& result) { fromJson(jo.value("limit"_ls), result.limit); fromJson(jo.value("not_senders"_ls), result.notSenders); @@ -25,4 +25,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("senders"_ls), result.senders); fromJson(jo.value("types"_ls), result.types); } - diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index 6de1fe79..5a1a831b 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -8,25 +8,30 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct EventFilter - { + struct EventFilter { /// The maximum number of events to return. Omittable limit; - /// A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the ``'senders'`` filter. + /// A list of sender IDs to exclude. If this list is absent then no + /// senders are excluded. A matching sender will be excluded even if it + /// is listed in the ``'senders'`` filter. QStringList notSenders; - /// A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the ``'types'`` filter. A '*' can be used as a wildcard to match any sequence of characters. + /// A list of event types to exclude. If this list is absent then no + /// event types are excluded. A matching type will be excluded even if + /// it is listed in the ``'types'`` filter. A '*' can be used as a + /// wildcard to match any sequence of characters. QStringList notTypes; - /// A list of senders IDs to include. If this list is absent then all senders are included. + /// A list of senders IDs to include. If this list is absent then all + /// senders are included. QStringList senders; - /// A list of event types to include. If this list is absent then all event types are included. A ``'*'`` can be used as a wildcard to match any sequence of characters. + /// A list of event types to include. If this list is absent then all + /// event types are included. A ``'*'`` can be used as a wildcard to + /// match any sequence of characters. QStringList types; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const EventFilter& pod); static void fillFrom(const QJsonObject& jo, EventFilter& pod); }; diff --git a/lib/csapi/definitions/public_rooms_response.cpp b/lib/csapi/definitions/public_rooms_response.cpp index 0d26662c..199a7a93 100644 --- a/lib/csapi/definitions/public_rooms_response.cpp +++ b/lib/csapi/definitions/public_rooms_response.cpp @@ -6,11 +6,12 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const PublicRoomsChunk& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PublicRoomsChunk& pod) { addParam(jo, QStringLiteral("aliases"), pod.aliases); - addParam(jo, QStringLiteral("canonical_alias"), pod.canonicalAlias); + addParam(jo, QStringLiteral("canonical_alias"), + pod.canonicalAlias); addParam(jo, QStringLiteral("name"), pod.name); addParam<>(jo, QStringLiteral("num_joined_members"), pod.numJoinedMembers); addParam<>(jo, QStringLiteral("room_id"), pod.roomId); @@ -20,8 +21,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("avatar_url"), pod.avatarUrl); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PublicRoomsChunk& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PublicRoomsChunk& result) { fromJson(jo.value("aliases"_ls), result.aliases); fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); @@ -40,15 +41,16 @@ void JsonObjectConverter::dumpTo( addParam<>(jo, QStringLiteral("chunk"), pod.chunk); addParam(jo, QStringLiteral("next_batch"), pod.nextBatch); addParam(jo, QStringLiteral("prev_batch"), pod.prevBatch); - addParam(jo, QStringLiteral("total_room_count_estimate"), pod.totalRoomCountEstimate); + addParam(jo, QStringLiteral("total_room_count_estimate"), + pod.totalRoomCountEstimate); } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PublicRoomsResponse& result) + const QJsonObject& jo, PublicRoomsResponse& result) { fromJson(jo.value("chunk"_ls), result.chunk); fromJson(jo.value("next_batch"_ls), result.nextBatch); fromJson(jo.value("prev_batch"_ls), result.prevBatch); - fromJson(jo.value("total_room_count_estimate"_ls), result.totalRoomCountEstimate); + fromJson(jo.value("total_room_count_estimate"_ls), + result.totalRoomCountEstimate); } - diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index 4c54ac25..6d8caf98 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -6,15 +6,13 @@ #include "converters.h" -#include #include "converters.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct PublicRoomsChunk - { + struct PublicRoomsChunk { /// Aliases of the room. May be empty. QStringList aliases; /// The canonical alias of the room, if any. @@ -36,15 +34,13 @@ namespace QMatrixClient /// The URL for the room's avatar, if one is set. QString avatarUrl; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod); static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod); }; /// A list of the rooms on the server. - struct PublicRoomsResponse - { + struct PublicRoomsResponse { /// A paginated chunk of public rooms. QVector chunk; /// A pagination token for the response. The absence of this token @@ -59,8 +55,7 @@ namespace QMatrixClient /// server has an estimate. Omittable totalRoomCountEstimate; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod); static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); }; diff --git a/lib/csapi/definitions/push_condition.cpp b/lib/csapi/definitions/push_condition.cpp index ace02755..5bcb845e 100644 --- a/lib/csapi/definitions/push_condition.cpp +++ b/lib/csapi/definitions/push_condition.cpp @@ -6,8 +6,8 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const PushCondition& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PushCondition& pod) { addParam<>(jo, QStringLiteral("kind"), pod.kind); addParam(jo, QStringLiteral("key"), pod.key); @@ -15,12 +15,11 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("is"), pod.is); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PushCondition& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushCondition& result) { fromJson(jo.value("kind"_ls), result.kind); fromJson(jo.value("key"_ls), result.key); fromJson(jo.value("pattern"_ls), result.pattern); fromJson(jo.value("is"_ls), result.is); } - diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index e45526d2..a4e44e93 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -6,16 +6,13 @@ #include "converters.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct PushCondition - { + struct PushCondition { QString kind; - /// Required for ``event_match`` conditions. The dot-separated field of the - /// event to match. + /// Required for ``event_match`` conditions. The dot-separated field of + /// the event to match. QString key; /// Required for ``event_match`` conditions. The glob-style pattern to /// match against. Patterns with no special glob characters should be @@ -23,13 +20,13 @@ namespace QMatrixClient /// condition. QString pattern; /// Required for ``room_member_count`` conditions. A decimal integer - /// optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches - /// rooms where the member count is strictly less than the given number and - /// so forth. If no prefix is present, this parameter defaults to ==. + /// optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < + /// matches rooms where the member count is strictly less than the given + /// number and so forth. If no prefix is present, this parameter + /// defaults to ==. QString is; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushCondition& pod); static void fillFrom(const QJsonObject& jo, PushCondition& pod); }; diff --git a/lib/csapi/definitions/push_rule.cpp b/lib/csapi/definitions/push_rule.cpp index abbb04b5..fc2be2c7 100644 --- a/lib/csapi/definitions/push_rule.cpp +++ b/lib/csapi/definitions/push_rule.cpp @@ -6,8 +6,7 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const PushRule& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRule& pod) { addParam<>(jo, QStringLiteral("actions"), pod.actions); addParam<>(jo, QStringLiteral("default"), pod.isDefault); @@ -17,8 +16,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("pattern"), pod.pattern); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PushRule& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushRule& result) { fromJson(jo.value("actions"_ls), result.actions); fromJson(jo.value("default"_ls), result.isDefault); @@ -27,4 +26,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("conditions"_ls), result.conditions); fromJson(jo.value("pattern"_ls), result.pattern); } - diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index bea13e96..d8d2cc0f 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -6,18 +6,16 @@ #include "converters.h" +#include "converters.h" #include "csapi/definitions/push_condition.h" #include -#include #include -#include "converters.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct PushRule - { + struct PushRule { /// The actions to perform when this rule is matched. QVector actions; /// Whether this is a default rule, or has been set explicitly. @@ -26,16 +24,15 @@ namespace QMatrixClient bool enabled; /// The ID of this rule. QString ruleId; - /// The conditions that must hold true for an event in order for a rule to be - /// applied to an event. A rule with no conditions always matches. Only - /// applicable to ``underride`` and ``override`` rules. + /// The conditions that must hold true for an event in order for a rule + /// to be applied to an event. A rule with no conditions always matches. + /// Only applicable to ``underride`` and ``override`` rules. QVector conditions; - /// The glob-style pattern to match against. Only applicable to ``content`` - /// rules. + /// The glob-style pattern to match against. Only applicable to + /// ``content`` rules. QString pattern; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRule& pod); static void fillFrom(const QJsonObject& jo, PushRule& pod); }; diff --git a/lib/csapi/definitions/push_ruleset.cpp b/lib/csapi/definitions/push_ruleset.cpp index f1bad882..6f48d27b 100644 --- a/lib/csapi/definitions/push_ruleset.cpp +++ b/lib/csapi/definitions/push_ruleset.cpp @@ -6,8 +6,8 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const PushRuleset& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PushRuleset& pod) { addParam(jo, QStringLiteral("content"), pod.content); addParam(jo, QStringLiteral("override"), pod.override); @@ -16,8 +16,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("underride"), pod.underride); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PushRuleset& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushRuleset& result) { fromJson(jo.value("content"_ls), result.content); fromJson(jo.value("override"_ls), result.override); @@ -25,4 +25,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("sender"_ls), result.sender); fromJson(jo.value("underride"_ls), result.underride); } - diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index f2d937c0..b2f791c4 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -6,24 +6,21 @@ #include "converters.h" -#include #include "converters.h" #include "csapi/definitions/push_rule.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct PushRuleset - { + struct PushRuleset { QVector content; QVector override; QVector room; QVector sender; QVector underride; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRuleset& pod); static void fillFrom(const QJsonObject& jo, PushRuleset& pod); }; diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp index df92e684..bd38ebc7 100644 --- a/lib/csapi/definitions/room_event_filter.cpp +++ b/lib/csapi/definitions/room_event_filter.cpp @@ -6,8 +6,8 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const RoomEventFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const RoomEventFilter& pod) { fillJson(jo, pod); addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); @@ -15,12 +15,11 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("contains_url"), pod.containsUrl); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RoomEventFilter& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + RoomEventFilter& result) { fillFromJson(jo, result); fromJson(jo.value("not_rooms"_ls), result.notRooms); fromJson(jo.value("rooms"_ls), result.rooms); fromJson(jo.value("contains_url"_ls), result.containsUrl); } - diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 6eb9a390..13c82341 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -6,24 +6,26 @@ #include "converters.h" -#include "csapi/definitions/event_filter.h" #include "converters.h" +#include "csapi/definitions/event_filter.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct RoomEventFilter : EventFilter - { - /// A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` filter. + struct RoomEventFilter : EventFilter { + /// A list of room IDs to exclude. If this list is absent then no rooms + /// are excluded. A matching room will be excluded even if it is listed + /// in the ``'rooms'`` filter. QStringList notRooms; - /// A list of room IDs to include. If this list is absent then all rooms are included. + /// A list of room IDs to include. If this list is absent then all rooms + /// are included. QStringList rooms; - /// If ``true``, includes only events with a ``url`` key in their content. If ``false``, excludes those events. If omitted, ``url`` key is not considered for filtering. + /// If ``true``, includes only events with a ``url`` key in their + /// content. If ``false``, excludes those events. If omitted, ``url`` + /// key is not considered for filtering. Omittable containsUrl; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod); static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); }; diff --git a/lib/csapi/definitions/sync_filter.cpp b/lib/csapi/definitions/sync_filter.cpp index 32752d1f..c06c16ca 100644 --- a/lib/csapi/definitions/sync_filter.cpp +++ b/lib/csapi/definitions/sync_filter.cpp @@ -6,24 +6,27 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const StateFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const StateFilter& pod) { fillJson(jo, pod); - addParam(jo, QStringLiteral("lazy_load_members"), pod.lazyLoadMembers); - addParam(jo, QStringLiteral("include_redundant_members"), pod.includeRedundantMembers); + addParam(jo, QStringLiteral("lazy_load_members"), + pod.lazyLoadMembers); + addParam(jo, QStringLiteral("include_redundant_members"), + pod.includeRedundantMembers); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, StateFilter& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + StateFilter& result) { fillFromJson(jo, result); fromJson(jo.value("lazy_load_members"_ls), result.lazyLoadMembers); - fromJson(jo.value("include_redundant_members"_ls), result.includeRedundantMembers); + fromJson(jo.value("include_redundant_members"_ls), + result.includeRedundantMembers); } -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const RoomFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const RoomFilter& pod) { addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); @@ -34,8 +37,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("account_data"), pod.accountData); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RoomFilter& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + RoomFilter& result) { fromJson(jo.value("not_rooms"_ls), result.notRooms); fromJson(jo.value("rooms"_ls), result.rooms); @@ -46,8 +49,7 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("account_data"_ls), result.accountData); } -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const Filter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, const Filter& pod) { addParam(jo, QStringLiteral("event_fields"), pod.eventFields); addParam(jo, QStringLiteral("event_format"), pod.eventFormat); @@ -56,8 +58,8 @@ void JsonObjectConverter::dumpTo( addParam(jo, QStringLiteral("room"), pod.room); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, Filter& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + Filter& result) { fromJson(jo.value("event_fields"_ls), result.eventFields); fromJson(jo.value("event_format"_ls), result.eventFormat); @@ -65,4 +67,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("account_data"_ls), result.accountData); fromJson(jo.value("room"_ls), result.room); } - diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index d94c74d7..d523c388 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -6,17 +6,15 @@ #include "converters.h" -#include "csapi/definitions/room_event_filter.h" #include "converters.h" #include "csapi/definitions/event_filter.h" +#include "csapi/definitions/room_event_filter.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// The state events to include for rooms. - struct StateFilter : RoomEventFilter - { + struct StateFilter : RoomEventFilter { /// If ``true``, the only ``m.room.member`` events returned in /// the ``state`` section of the ``/sync`` response are those /// which are definitely necessary for a client to display @@ -39,20 +37,24 @@ namespace QMatrixClient /// If ``lazy_load_members`` is ``false`` this field is ignored. Omittable includeRedundantMembers; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const StateFilter& pod); static void fillFrom(const QJsonObject& jo, StateFilter& pod); }; /// Filters to be applied to room data. - struct RoomFilter - { - /// A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` filter. This filter is applied before the filters in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` + struct RoomFilter { + /// A list of room IDs to exclude. If this list is absent then no rooms + /// are excluded. A matching room will be excluded even if it is listed + /// in the ``'rooms'`` filter. This filter is applied before the filters + /// in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` QStringList notRooms; - /// A list of room IDs to include. If this list is absent then all rooms are included. This filter is applied before the filters in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` + /// A list of room IDs to include. If this list is absent then all rooms + /// are included. This filter is applied before the filters in + /// ``ephemeral``, ``state``, ``timeline`` or ``account_data`` QStringList rooms; - /// The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms. + /// The events that aren't recorded in the room history, e.g. typing and + /// receipts, to include for rooms. Omittable ephemeral; /// Include rooms that the user has left in the sync, default false Omittable includeLeave; @@ -63,17 +65,22 @@ namespace QMatrixClient /// The per user account data to include for rooms. Omittable accountData; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomFilter& pod); static void fillFrom(const QJsonObject& jo, RoomFilter& pod); }; - struct Filter - { - /// List of event fields to include. If this list is absent then all fields are included. The entries may include '.' charaters to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' object. A literal '.' character in a field name may be escaped using a '\\'. A server may include more fields than were requested. + struct Filter { + /// List of event fields to include. If this list is absent then all + /// fields are included. The entries may include '.' charaters to + /// indicate sub-fields. So ['content.body'] will include the 'body' + /// field of the 'content' object. A literal '.' character in a field + /// name may be escaped using a '\\'. A server may include more fields + /// than were requested. QStringList eventFields; - /// The format to use for events. 'client' will return the events in a format suitable for clients. 'federation' will return the raw event as receieved over federation. The default is 'client'. + /// The format to use for events. 'client' will return the events in a + /// format suitable for clients. 'federation' will return the raw event + /// as receieved over federation. The default is 'client'. QString eventFormat; /// The presence updates to include. Omittable presence; @@ -82,8 +89,7 @@ namespace QMatrixClient /// Filters to be applied to room data. Omittable room; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Filter& pod); static void fillFrom(const QJsonObject& jo, Filter& pod); }; diff --git a/lib/csapi/definitions/user_identifier.cpp b/lib/csapi/definitions/user_identifier.cpp index 05a27c1c..998f1b85 100644 --- a/lib/csapi/definitions/user_identifier.cpp +++ b/lib/csapi/definitions/user_identifier.cpp @@ -6,18 +6,17 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const UserIdentifier& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const UserIdentifier& pod) { fillJson(jo, pod.additionalProperties); addParam<>(jo, QStringLiteral("type"), pod.type); } -void JsonObjectConverter::fillFrom( - QJsonObject jo, UserIdentifier& result) +void JsonObjectConverter::fillFrom(QJsonObject jo, + UserIdentifier& result) { fromJson(jo.take("type"_ls), result.type); fromJson(jo, result.additionalProperties); } - diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index cbb1550f..4a9ce684 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -8,20 +8,18 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Identification information for a user - struct UserIdentifier - { - /// The type of identification. See `Identifier types`_ for supported values and additional property descriptions. + struct UserIdentifier { + /// The type of identification. See `Identifier types`_ for supported + /// values and additional property descriptions. QString type; /// Identification information for a user QVariantHash additionalProperties; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const UserIdentifier& pod); static void fillFrom(QJsonObject jo, UserIdentifier& pod); }; diff --git a/lib/csapi/definitions/wellknown/full.cpp b/lib/csapi/definitions/wellknown/full.cpp index 5ecef34f..35c0df5b 100644 --- a/lib/csapi/definitions/wellknown/full.cpp +++ b/lib/csapi/definitions/wellknown/full.cpp @@ -11,15 +11,15 @@ void JsonObjectConverter::dumpTo( { fillJson(jo, pod.additionalProperties); addParam<>(jo, QStringLiteral("m.homeserver"), pod.homeserver); - addParam(jo, QStringLiteral("m.identity_server"), pod.identityServer); + addParam(jo, QStringLiteral("m.identity_server"), + pod.identityServer); } void JsonObjectConverter::fillFrom( - QJsonObject jo, DiscoveryInformation& result) + QJsonObject jo, DiscoveryInformation& result) { fromJson(jo.take("m.homeserver"_ls), result.homeserver); fromJson(jo.take("m.identity_server"_ls), result.identityServer); fromJson(jo, result.additionalProperties); } - diff --git a/lib/csapi/definitions/wellknown/full.h b/lib/csapi/definitions/wellknown/full.h index d9346acb..9b920eda 100644 --- a/lib/csapi/definitions/wellknown/full.h +++ b/lib/csapi/definitions/wellknown/full.h @@ -6,31 +6,28 @@ #include "converters.h" -#include #include "converters.h" #include "csapi/definitions/wellknown/homeserver.h" #include "csapi/definitions/wellknown/identity_server.h" #include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Used by clients to determine the homeserver, identity server, and other /// optional components they should be interacting with. - struct DiscoveryInformation - { - /// Used by clients to determine the homeserver, identity server, and other - /// optional components they should be interacting with. + struct DiscoveryInformation { + /// Used by clients to determine the homeserver, identity server, and + /// other optional components they should be interacting with. HomeserverInformation homeserver; - /// Used by clients to determine the homeserver, identity server, and other - /// optional components they should be interacting with. + /// Used by clients to determine the homeserver, identity server, and + /// other optional components they should be interacting with. Omittable identityServer; /// Application-dependent keys using Java package naming convention. QHash additionalProperties; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DiscoveryInformation& pod); static void fillFrom(QJsonObject jo, DiscoveryInformation& pod); }; diff --git a/lib/csapi/definitions/wellknown/homeserver.cpp b/lib/csapi/definitions/wellknown/homeserver.cpp index 0783f11b..a7337520 100644 --- a/lib/csapi/definitions/wellknown/homeserver.cpp +++ b/lib/csapi/definitions/wellknown/homeserver.cpp @@ -13,8 +13,7 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, HomeserverInformation& result) + const QJsonObject& jo, HomeserverInformation& result) { fromJson(jo.value("base_url"_ls), result.baseUrl); } - diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index f6761c30..8bd3c150 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -6,19 +6,15 @@ #include "converters.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Used by clients to discover homeserver information. - struct HomeserverInformation - { + struct HomeserverInformation { /// The base URL for the homeserver for client-server connections. QString baseUrl; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod); static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); }; diff --git a/lib/csapi/definitions/wellknown/identity_server.cpp b/lib/csapi/definitions/wellknown/identity_server.cpp index 99f36641..46a614d8 100644 --- a/lib/csapi/definitions/wellknown/identity_server.cpp +++ b/lib/csapi/definitions/wellknown/identity_server.cpp @@ -13,8 +13,7 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, IdentityServerInformation& result) + const QJsonObject& jo, IdentityServerInformation& result) { fromJson(jo.value("base_url"_ls), result.baseUrl); } - diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index 67d8b08d..fd53dfc1 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -6,21 +6,19 @@ #include "converters.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures /// Used by clients to discover identity server information. - struct IdentityServerInformation - { + struct IdentityServerInformation { /// The base URL for the identity server for client-server connections. QString baseUrl; }; - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod); - static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const IdentityServerInformation& pod); + static void fillFrom(const QJsonObject& jo, + IdentityServerInformation& pod); }; } // namespace QMatrixClient diff --git a/lib/csapi/device_management.cpp b/lib/csapi/device_management.cpp index 9c31db5d..7d15bb2b 100644 --- a/lib/csapi/device_management.cpp +++ b/lib/csapi/device_management.cpp @@ -15,30 +15,25 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetDevicesJob::Private { public: - QVector devices; + QVector devices; }; QUrl GetDevicesJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/devices"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/devices"); } static const auto GetDevicesJobName = QStringLiteral("GetDevicesJob"); GetDevicesJob::GetDevicesJob() - : BaseJob(HttpVerb::Get, GetDevicesJobName, - basePath % "/devices") - , d(new Private) + : BaseJob(HttpVerb::Get, GetDevicesJobName, basePath % "/devices"), + d(new Private) { } GetDevicesJob::~GetDevicesJob() = default; -const QVector& GetDevicesJob::devices() const -{ - return d->devices; -} +const QVector& GetDevicesJob::devices() const { return d->devices; } BaseJob::Status GetDevicesJob::parseJson(const QJsonDocument& data) { @@ -50,30 +45,27 @@ BaseJob::Status GetDevicesJob::parseJson(const QJsonDocument& data) class GetDeviceJob::Private { public: - Device data; + Device data; }; QUrl GetDeviceJob::makeRequestUrl(QUrl baseUrl, const QString& deviceId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/devices/" % deviceId); + basePath % "/devices/" % deviceId); } static const auto GetDeviceJobName = QStringLiteral("GetDeviceJob"); GetDeviceJob::GetDeviceJob(const QString& deviceId) : BaseJob(HttpVerb::Get, GetDeviceJobName, - basePath % "/devices/" % deviceId) - , d(new Private) + basePath % "/devices/" % deviceId), + d(new Private) { } GetDeviceJob::~GetDeviceJob() = default; -const Device& GetDeviceJob::data() const -{ - return d->data; -} +const Device& GetDeviceJob::data() const { return d->data; } BaseJob::Status GetDeviceJob::parseJson(const QJsonDocument& data) { @@ -83,9 +75,10 @@ BaseJob::Status GetDeviceJob::parseJson(const QJsonDocument& data) static const auto UpdateDeviceJobName = QStringLiteral("UpdateDeviceJob"); -UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, const QString& displayName) +UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, + const QString& displayName) : BaseJob(HttpVerb::Put, UpdateDeviceJobName, - basePath % "/devices/" % deviceId) + basePath % "/devices/" % deviceId) { QJsonObject _data; addParam(_data, QStringLiteral("display_name"), displayName); @@ -94,9 +87,10 @@ UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, const QString& display static const auto DeleteDeviceJobName = QStringLiteral("DeleteDeviceJob"); -DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, const Omittable& auth) +DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, + const Omittable& auth) : BaseJob(HttpVerb::Delete, DeleteDeviceJobName, - basePath % "/devices/" % deviceId) + basePath % "/devices/" % deviceId) { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); @@ -105,13 +99,13 @@ DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, const Omittable& auth) +DeleteDevicesJob::DeleteDevicesJob(const QStringList& devices, + const Omittable& auth) : BaseJob(HttpVerb::Post, DeleteDevicesJobName, - basePath % "/delete_devices") + basePath % "/delete_devices") { QJsonObject _data; addParam<>(_data, QStringLiteral("devices"), devices); addParam(_data, QStringLiteral("auth"), auth); setRequestData(_data); } - diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h index f41efdbc..628f26d2 100644 --- a/lib/csapi/device_management.h +++ b/lib/csapi/device_management.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -#include "csapi/definitions/auth_data.h" -#include #include "converters.h" +#include "csapi/definitions/auth_data.h" #include "csapi/definitions/client_device.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// List registered devices for the current user @@ -21,29 +20,29 @@ namespace QMatrixClient class GetDevicesJob : public BaseJob { public: - explicit GetDevicesJob(); + explicit GetDevicesJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetDevicesJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetDevicesJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetDevicesJob() override; + ~GetDevicesJob() override; - // Result properties + // Result properties - /// A list of all registered devices for this user. - const QVector& devices() const; + /// A list of all registered devices for this user. + const QVector& devices() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get a single device @@ -52,33 +51,33 @@ namespace QMatrixClient class GetDeviceJob : public BaseJob { public: - /*! Get a single device - * \param deviceId - * The device to retrieve. - */ - explicit GetDeviceJob(const QString& deviceId); + /*! Get a single device + * \param deviceId + * The device to retrieve. + */ + explicit GetDeviceJob(const QString& deviceId); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetDeviceJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& deviceId); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetDeviceJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& deviceId); - ~GetDeviceJob() override; + ~GetDeviceJob() override; - // Result properties + // Result properties - /// Device information - const Device& data() const; + /// Device information + const Device& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Update a device @@ -87,49 +86,56 @@ namespace QMatrixClient class UpdateDeviceJob : public BaseJob { public: - /*! Update a device - * \param deviceId - * The device to update. - * \param displayName - * The new display name for this device. If not given, the - * display name is unchanged. - */ - explicit UpdateDeviceJob(const QString& deviceId, const QString& displayName = {}); + /*! Update a device + * \param deviceId + * The device to update. + * \param displayName + * The new display name for this device. If not given, the + * display name is unchanged. + */ + explicit UpdateDeviceJob(const QString& deviceId, + const QString& displayName = {}); }; /// Delete a device /// /// This API endpoint uses the `User-Interactive Authentication API`_. - /// - /// Deletes the given device, and invalidates any access token associated with it. + /// + /// Deletes the given device, and invalidates any access token associated + /// with it. class DeleteDeviceJob : public BaseJob { public: - /*! Delete a device - * \param deviceId - * The device to delete. - * \param auth - * Additional authentication information for the - * user-interactive authentication API. - */ - explicit DeleteDeviceJob(const QString& deviceId, const Omittable& auth = none); + /*! Delete a device + * \param deviceId + * The device to delete. + * \param auth + * Additional authentication information for the + * user-interactive authentication API. + */ + explicit DeleteDeviceJob( + const QString& deviceId, + const Omittable& auth = none); }; /// Bulk deletion of devices /// /// This API endpoint uses the `User-Interactive Authentication API`_. - /// - /// Deletes the given devices, and invalidates any access token associated with them. + /// + /// Deletes the given devices, and invalidates any access token associated + /// with them. class DeleteDevicesJob : public BaseJob { public: - /*! Bulk deletion of devices - * \param devices - * The list of device IDs to delete. - * \param auth - * Additional authentication information for the - * user-interactive authentication API. - */ - explicit DeleteDevicesJob(const QStringList& devices, const Omittable& auth = none); + /*! Bulk deletion of devices + * \param devices + * The list of device IDs to delete. + * \param auth + * Additional authentication information for the + * user-interactive authentication API. + */ + explicit DeleteDevicesJob( + const QStringList& devices, + const Omittable& auth = none); }; } // namespace QMatrixClient diff --git a/lib/csapi/directory.cpp b/lib/csapi/directory.cpp index 4af86f7b..b4282ffb 100644 --- a/lib/csapi/directory.cpp +++ b/lib/csapi/directory.cpp @@ -14,9 +14,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0/directory"); static const auto SetRoomAliasJobName = QStringLiteral("SetRoomAliasJob"); -SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId) +SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, + const QString& roomId) : BaseJob(HttpVerb::Put, SetRoomAliasJobName, - basePath % "/room/" % roomAlias) + basePath % "/room/" % roomAlias) { QJsonObject _data; addParam<>(_data, QStringLiteral("room_id"), roomId); @@ -26,36 +27,31 @@ SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId class GetRoomIdByAliasJob::Private { public: - QString roomId; - QStringList servers; + QString roomId; + QStringList servers; }; QUrl GetRoomIdByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/room/" % roomAlias); + basePath % "/room/" % roomAlias); } -static const auto GetRoomIdByAliasJobName = QStringLiteral("GetRoomIdByAliasJob"); +static const auto GetRoomIdByAliasJobName = + QStringLiteral("GetRoomIdByAliasJob"); GetRoomIdByAliasJob::GetRoomIdByAliasJob(const QString& roomAlias) : BaseJob(HttpVerb::Get, GetRoomIdByAliasJobName, - basePath % "/room/" % roomAlias, false) - , d(new Private) + basePath % "/room/" % roomAlias, false), + d(new Private) { } GetRoomIdByAliasJob::~GetRoomIdByAliasJob() = default; -const QString& GetRoomIdByAliasJob::roomId() const -{ - return d->roomId; -} +const QString& GetRoomIdByAliasJob::roomId() const { return d->roomId; } -const QStringList& GetRoomIdByAliasJob::servers() const -{ - return d->servers; -} +const QStringList& GetRoomIdByAliasJob::servers() const { return d->servers; } BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data) { @@ -68,14 +64,13 @@ BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data) QUrl DeleteRoomAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/room/" % roomAlias); + basePath % "/room/" % roomAlias); } static const auto DeleteRoomAliasJobName = QStringLiteral("DeleteRoomAliasJob"); DeleteRoomAliasJob::DeleteRoomAliasJob(const QString& roomAlias) : BaseJob(HttpVerb::Delete, DeleteRoomAliasJobName, - basePath % "/room/" % roomAlias) + basePath % "/room/" % roomAlias) { } - diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h index 39e86635..6bf5ad14 100644 --- a/lib/csapi/directory.h +++ b/lib/csapi/directory.h @@ -6,86 +6,86 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Create a new mapping from room alias to room ID. class SetRoomAliasJob : public BaseJob { public: - /*! Create a new mapping from room alias to room ID. - * \param roomAlias - * The room alias to set. - * \param roomId - * The room ID to set. - */ - explicit SetRoomAliasJob(const QString& roomAlias, const QString& roomId); + /*! Create a new mapping from room alias to room ID. + * \param roomAlias + * The room alias to set. + * \param roomId + * The room ID to set. + */ + explicit SetRoomAliasJob(const QString& roomAlias, + const QString& roomId); }; /// Get the room ID corresponding to this room alias. /// /// Requests that the server resolve a room alias to a room ID. - /// + /// /// The server will use the federation API to resolve the alias if the /// domain part of the alias does not correspond to the server's own /// domain. class GetRoomIdByAliasJob : public BaseJob { public: - /*! Get the room ID corresponding to this room alias. - * \param roomAlias - * The room alias. - */ - explicit GetRoomIdByAliasJob(const QString& roomAlias); + /*! Get the room ID corresponding to this room alias. + * \param roomAlias + * The room alias. + */ + explicit GetRoomIdByAliasJob(const QString& roomAlias); - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Construct a URL without creating a full-fledged job object + * + * 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); - ~GetRoomIdByAliasJob() override; + ~GetRoomIdByAliasJob() override; - // Result properties + // Result properties - /// The room ID for this room alias. - const QString& roomId() const; - /// A list of servers that are aware of this room alias. - const QStringList& servers() const; + /// The room ID for this room alias. + const QString& roomId() const; + /// A list of servers that are aware of this room alias. + const QStringList& servers() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Remove a mapping of room alias to room ID. /// /// Remove a mapping of room alias to room ID. - /// - /// Servers may choose to implement additional access control checks here, for instance that room aliases can only be deleted by their creator or a server administrator. + /// + /// Servers may choose to implement additional access control checks here, + /// for instance that room aliases can only be deleted by their creator or a + /// server administrator. class DeleteRoomAliasJob : public BaseJob { public: - /*! Remove a mapping of room alias to room ID. - * \param roomAlias - * The room alias to remove. - */ - explicit DeleteRoomAliasJob(const QString& roomAlias); - - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Remove a mapping of room alias to room ID. + * \param roomAlias + * The room alias to remove. + */ + explicit DeleteRoomAliasJob(const QString& roomAlias); + /*! Construct a URL without creating a full-fledged job object + * + * 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); }; } // namespace QMatrixClient diff --git a/lib/csapi/event_context.cpp b/lib/csapi/event_context.cpp index bb1f5301..9ead6ac6 100644 --- a/lib/csapi/event_context.cpp +++ b/lib/csapi/event_context.cpp @@ -15,12 +15,12 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetEventContextJob::Private { public: - QString begin; - QString end; - RoomEvents eventsBefore; - RoomEventPtr event; - RoomEvents eventsAfter; - StateEvents state; + QString begin; + QString end; + RoomEvents eventsBefore; + RoomEventPtr event; + RoomEvents eventsAfter; + StateEvents state; }; BaseJob::Query queryToGetEventContext(Omittable limit) @@ -30,54 +30,47 @@ BaseJob::Query queryToGetEventContext(Omittable limit) return _q; } -QUrl GetEventContextJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId, Omittable limit) +QUrl GetEventContextJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventId, + Omittable limit) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/context/" % eventId, - queryToGetEventContext(limit)); + basePath % "/rooms/" % roomId % "/context/" + % eventId, + queryToGetEventContext(limit)); } static const auto GetEventContextJobName = QStringLiteral("GetEventContextJob"); -GetEventContextJob::GetEventContextJob(const QString& roomId, const QString& eventId, Omittable limit) +GetEventContextJob::GetEventContextJob(const QString& roomId, + const QString& eventId, + Omittable limit) : BaseJob(HttpVerb::Get, GetEventContextJobName, - basePath % "/rooms/" % roomId % "/context/" % eventId, - queryToGetEventContext(limit)) - , d(new Private) + basePath % "/rooms/" % roomId % "/context/" % eventId, + queryToGetEventContext(limit)), + d(new Private) { } GetEventContextJob::~GetEventContextJob() = default; -const QString& GetEventContextJob::begin() const -{ - return d->begin; -} +const QString& GetEventContextJob::begin() const { return d->begin; } -const QString& GetEventContextJob::end() const -{ - return d->end; -} +const QString& GetEventContextJob::end() const { return d->end; } RoomEvents&& GetEventContextJob::eventsBefore() { return std::move(d->eventsBefore); } -RoomEventPtr&& GetEventContextJob::event() -{ - return std::move(d->event); -} +RoomEventPtr&& GetEventContextJob::event() { return std::move(d->event); } RoomEvents&& GetEventContextJob::eventsAfter() { return std::move(d->eventsAfter); } -StateEvents&& GetEventContextJob::state() -{ - return std::move(d->state); -} +StateEvents&& GetEventContextJob::state() { return std::move(d->state); } BaseJob::Status GetEventContextJob::parseJson(const QJsonDocument& data) { @@ -90,4 +83,3 @@ BaseJob::Status GetEventContextJob::parseJson(const QJsonDocument& data) fromJson(json.value("state"_ls), d->state); return Success; } - diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index a5fda7ea..cfefc550 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include "events/eventloader.h" #include "converters.h" +#include "events/eventloader.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Get events and state around the specified event. @@ -21,48 +20,52 @@ namespace QMatrixClient class GetEventContextJob : public BaseJob { public: - /*! Get events and state around the specified event. - * \param roomId - * The room to get events from. - * \param eventId - * The event to get context around. - * \param limit - * The maximum number of events to return. Default: 10. - */ - explicit GetEventContextJob(const QString& roomId, const QString& eventId, Omittable limit = none); + /*! Get events and state around the specified event. + * \param roomId + * The room to get events from. + * \param eventId + * The event to get context around. + * \param limit + * The maximum number of events to return. Default: 10. + */ + explicit GetEventContextJob(const QString& roomId, + const QString& eventId, + Omittable limit = none); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetEventContextJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId, Omittable limit = none); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetEventContextJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventId, + Omittable limit = none); - ~GetEventContextJob() override; + ~GetEventContextJob() override; - // Result properties + // Result properties - /// A token that can be used to paginate backwards with. - const QString& begin() const; - /// A token that can be used to paginate forwards with. - const QString& end() const; - /// A list of room events that happened just before the - /// requested event, in reverse-chronological order. - RoomEvents&& eventsBefore(); - /// Details of the requested event. - RoomEventPtr&& event(); - /// A list of room events that happened just after the - /// requested event, in chronological order. - RoomEvents&& eventsAfter(); - /// The state of the room at the last event returned. - StateEvents&& state(); + /// A token that can be used to paginate backwards with. + const QString& begin() const; + /// A token that can be used to paginate forwards with. + const QString& end() const; + /// A list of room events that happened just before the + /// requested event, in reverse-chronological order. + RoomEvents&& eventsBefore(); + /// Details of the requested event. + RoomEventPtr&& event(); + /// A list of room events that happened just after the + /// requested event, in chronological order. + RoomEvents&& eventsAfter(); + /// The state of the room at the last event returned. + StateEvents&& state(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index 982e60b5..40743de4 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -15,32 +15,29 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class DefineFilterJob::Private { public: - QString filterId; + QString filterId; }; static const auto DefineFilterJobName = QStringLiteral("DefineFilterJob"); DefineFilterJob::DefineFilterJob(const QString& userId, const Filter& filter) : BaseJob(HttpVerb::Post, DefineFilterJobName, - basePath % "/user/" % userId % "/filter") - , d(new Private) + basePath % "/user/" % userId % "/filter"), + d(new Private) { setRequestData(Data(toJson(filter))); } DefineFilterJob::~DefineFilterJob() = default; -const QString& DefineFilterJob::filterId() const -{ - return d->filterId; -} +const QString& DefineFilterJob::filterId() const { return d->filterId; } BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("filter_id"_ls)) return { JsonParseError, - "The key 'filter_id' not found in the response" }; + "The key 'filter_id' not found in the response" }; fromJson(json.value("filter_id"_ls), d->filterId); return Success; } @@ -48,34 +45,32 @@ BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) class GetFilterJob::Private { public: - Filter data; + Filter data; }; -QUrl GetFilterJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& filterId) +QUrl GetFilterJob::makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& filterId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId % "/filter/" % filterId); + basePath % "/user/" % userId % "/filter/" + % filterId); } static const auto GetFilterJobName = QStringLiteral("GetFilterJob"); GetFilterJob::GetFilterJob(const QString& userId, const QString& filterId) : BaseJob(HttpVerb::Get, GetFilterJobName, - basePath % "/user/" % userId % "/filter/" % filterId) - , d(new Private) + basePath % "/user/" % userId % "/filter/" % filterId), + d(new Private) { } GetFilterJob::~GetFilterJob() = default; -const Filter& GetFilterJob::data() const -{ - return d->data; -} +const Filter& GetFilterJob::data() const { return d->data; } BaseJob::Status GetFilterJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h index 0ca7e953..85e05667 100644 --- a/lib/csapi/filter.h +++ b/lib/csapi/filter.h @@ -9,8 +9,7 @@ #include "converters.h" #include "csapi/definitions/sync_filter.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Upload a new filter. @@ -21,65 +20,66 @@ namespace QMatrixClient class DefineFilterJob : public BaseJob { public: - /*! Upload a new filter. - * \param userId - * The id of the user uploading the filter. The access token must be authorized to make requests for this user id. - * \param filter - * Uploads a new filter definition to the homeserver. - * Returns a filter ID that may be used in future requests to - * restrict which events are returned to the client. - */ - explicit DefineFilterJob(const QString& userId, const Filter& filter); - ~DefineFilterJob() override; - - // Result properties - - /// The ID of the filter that was created. Cannot start - /// with a ``{`` as this character is used to determine - /// if the filter provided is inline JSON or a previously - /// declared filter by homeservers on some APIs. - const QString& filterId() const; + /*! Upload a new filter. + * \param userId + * The id of the user uploading the filter. The access token must be + * authorized to make requests for this user id. \param filter Uploads a + * new filter definition to the homeserver. Returns a filter ID that may + * be used in future requests to restrict which events are returned to + * the client. + */ + explicit DefineFilterJob(const QString& userId, const Filter& filter); + ~DefineFilterJob() override; + + // Result properties + + /// The ID of the filter that was created. Cannot start + /// with a ``{`` as this character is used to determine + /// if the filter provided is inline JSON or a previously + /// declared filter by homeservers on some APIs. + const QString& filterId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Download a filter class GetFilterJob : public BaseJob { public: - /*! Download a filter - * \param userId - * The user ID to download a filter for. - * \param filterId - * The filter ID to download. - */ - explicit GetFilterJob(const QString& userId, const QString& filterId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetFilterJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& filterId); - - ~GetFilterJob() override; - - // Result properties - - /// "The filter defintion" - const Filter& data() const; + /*! Download a filter + * \param userId + * The user ID to download a filter for. + * \param filterId + * The filter ID to download. + */ + explicit GetFilterJob(const QString& userId, const QString& filterId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetFilterJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& filterId); + + ~GetFilterJob() override; + + // Result properties + + /// "The filter defintion" + const Filter& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/inviting.cpp b/lib/csapi/inviting.cpp index 7dc33b18..4ddbe5d0 100644 --- a/lib/csapi/inviting.cpp +++ b/lib/csapi/inviting.cpp @@ -16,10 +16,9 @@ static const auto InviteUserJobName = QStringLiteral("InviteUserJob"); InviteUserJob::InviteUserJob(const QString& roomId, const QString& userId) : BaseJob(HttpVerb::Post, InviteUserJobName, - basePath % "/rooms/" % roomId % "/invite") + basePath % "/rooms/" % roomId % "/invite") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); setRequestData(_data); } - diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h index 6d5d2e99..12cf1b58 100644 --- a/lib/csapi/inviting.h +++ b/lib/csapi/inviting.h @@ -6,40 +6,38 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Invite a user to participate in a particular room. /// /// .. _invite-by-user-id-endpoint: - /// - /// *Note that there are two forms of this API, which are documented separately. - /// This version of the API requires that the inviter knows the Matrix - /// identifier of the invitee. The other is documented in the* - /// `third party invites section`_. - /// + /// + /// *Note that there are two forms of this API, which are documented + /// separately. This version of the API requires that the inviter knows the + /// Matrix identifier of the invitee. The other is documented in the* `third + /// party invites section`_. + /// /// This API invites a user to participate in a particular room. /// They do not start participating in the room until they actually join the /// room. - /// + /// /// Only users currently in a particular room can invite other users to /// join that room. - /// + /// /// If the user was invited to the room, the homeserver will append a /// ``m.room.member`` event to the room. - /// + /// /// .. _third party invites section: `invite-by-third-party-id-endpoint`_ class InviteUserJob : public BaseJob { public: - /*! Invite a user to participate in a particular room. - * \param roomId - * The room identifier (not alias) to which to invite the user. - * \param userId - * The fully qualified user ID of the invitee. - */ - explicit InviteUserJob(const QString& roomId, const QString& userId); + /*! Invite a user to participate in a particular room. + * \param roomId + * The room identifier (not alias) to which to invite the user. + * \param userId + * The fully qualified user ID of the invitee. + */ + explicit InviteUserJob(const QString& roomId, const QString& userId); }; } // namespace QMatrixClient diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index 00d930fa..fe4c83e3 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const JoinRoomByIdJob::ThirdPartySigned& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const JoinRoomByIdJob::ThirdPartySigned& pod) { addParam<>(jo, QStringLiteral("sender"), pod.sender); addParam<>(jo, QStringLiteral("mxid"), pod.mxid); @@ -31,44 +30,42 @@ namespace QMatrixClient class JoinRoomByIdJob::Private { public: - QString roomId; + QString roomId; }; static const auto JoinRoomByIdJobName = QStringLiteral("JoinRoomByIdJob"); -JoinRoomByIdJob::JoinRoomByIdJob(const QString& roomId, const Omittable& thirdPartySigned) +JoinRoomByIdJob::JoinRoomByIdJob( + const QString& roomId, + const Omittable& thirdPartySigned) : BaseJob(HttpVerb::Post, JoinRoomByIdJobName, - basePath % "/rooms/" % roomId % "/join") - , d(new Private) + basePath % "/rooms/" % roomId % "/join"), + d(new Private) { QJsonObject _data; - addParam(_data, QStringLiteral("third_party_signed"), thirdPartySigned); + addParam(_data, QStringLiteral("third_party_signed"), + thirdPartySigned); setRequestData(_data); } JoinRoomByIdJob::~JoinRoomByIdJob() = default; -const QString& JoinRoomByIdJob::roomId() const -{ - return d->roomId; -} +const QString& JoinRoomByIdJob::roomId() const { return d->roomId; } BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) return { JsonParseError, - "The key 'room_id' not found in the response" }; + "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const JoinRoomJob::Signed& pod) { addParam<>(jo, QStringLiteral("sender"), pod.sender); @@ -78,9 +75,9 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const JoinRoomJob::ThirdPartySigned& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const JoinRoomJob::ThirdPartySigned& pod) { addParam<>(jo, QStringLiteral("signed"), pod.signedData); } @@ -90,7 +87,7 @@ namespace QMatrixClient class JoinRoomJob::Private { public: - QString roomId; + QString roomId; }; BaseJob::Query queryToJoinRoom(const QStringList& serverName) @@ -102,31 +99,29 @@ BaseJob::Query queryToJoinRoom(const QStringList& serverName) static const auto JoinRoomJobName = QStringLiteral("JoinRoomJob"); -JoinRoomJob::JoinRoomJob(const QString& roomIdOrAlias, const QStringList& serverName, const Omittable& thirdPartySigned) +JoinRoomJob::JoinRoomJob(const QString& roomIdOrAlias, + const QStringList& serverName, + const Omittable& thirdPartySigned) : BaseJob(HttpVerb::Post, JoinRoomJobName, - basePath % "/join/" % roomIdOrAlias, - queryToJoinRoom(serverName)) - , d(new Private) + basePath % "/join/" % roomIdOrAlias, queryToJoinRoom(serverName)), + d(new Private) { QJsonObject _data; - addParam(_data, QStringLiteral("third_party_signed"), thirdPartySigned); + addParam(_data, QStringLiteral("third_party_signed"), + thirdPartySigned); setRequestData(_data); } JoinRoomJob::~JoinRoomJob() = default; -const QString& JoinRoomJob::roomId() const -{ - return d->roomId; -} +const QString& JoinRoomJob::roomId() const { return d->roomId; } BaseJob::Status JoinRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) return { JsonParseError, - "The key 'room_id' not found in the response" }; + "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; } - diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index 52c8ea42..5d118dab 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -9,143 +9,156 @@ #include "converters.h" #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Start the requesting user participating in a particular room. /// - /// *Note that this API requires a room ID, not alias.* ``/join/{roomIdOrAlias}`` *exists if you have a room alias.* - /// + /// *Note that this API requires a room ID, not alias.* + /// ``/join/{roomIdOrAlias}`` *exists if you have a room alias.* + /// /// This API starts a user participating in a particular room, if that user /// is allowed to participate in that room. After this call, the client is /// allowed to see all current state events in the room, and all subsequent /// events associated with the room until the user leaves the room. - /// + /// /// After a user has joined a room, the room will appear as an entry in the /// response of the |/initialSync|_ and |/sync|_ APIs. - /// + /// /// If a ``third_party_signed`` was supplied, the homeserver must verify /// that it matches a pending ``m.room.third_party_invite`` event in the /// room, and perform key validity checking if required by the event. class JoinRoomByIdJob : public BaseJob { public: - // Inner data structures - - /// A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - struct ThirdPartySigned - { - /// The Matrix ID of the user who issued the invite. - QString sender; - /// The Matrix ID of the invitee. - QString mxid; - /// The state key of the m.third_party_invite event. - QString token; - /// A signatures object containing a signature of the entire signed object. - QJsonObject signatures; - }; - - // Construction/destruction - - /*! Start the requesting user participating in a particular room. - * \param roomId - * The room identifier (not alias) to join. - * \param thirdPartySigned - * A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - */ - explicit JoinRoomByIdJob(const QString& roomId, const Omittable& thirdPartySigned = none); - ~JoinRoomByIdJob() override; - - // Result properties - - /// The joined room ID. - const QString& roomId() const; + // Inner data structures + + /// A signature of an ``m.third_party_invite`` token to prove that this + /// user owns a third party identity which has been invited to the room. + struct ThirdPartySigned { + /// The Matrix ID of the user who issued the invite. + QString sender; + /// The Matrix ID of the invitee. + QString mxid; + /// The state key of the m.third_party_invite event. + QString token; + /// A signatures object containing a signature of the entire signed + /// object. + QJsonObject signatures; + }; + + // Construction/destruction + + /*! Start the requesting user participating in a particular room. + * \param roomId + * The room identifier (not alias) to join. + * \param thirdPartySigned + * A signature of an ``m.third_party_invite`` token to prove that this + * user owns a third party identity which has been invited to the room. + */ + explicit JoinRoomByIdJob( + const QString& roomId, + const Omittable& thirdPartySigned = none); + ~JoinRoomByIdJob() override; + + // Result properties + + /// The joined room ID. + const QString& roomId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Start the requesting user participating in a particular room. /// - /// *Note that this API takes either a room ID or alias, unlike* ``/room/{roomId}/join``. - /// + /// *Note that this API takes either a room ID or alias, unlike* + /// ``/room/{roomId}/join``. + /// /// This API starts a user participating in a particular room, if that user /// is allowed to participate in that room. After this call, the client is /// allowed to see all current state events in the room, and all subsequent /// events associated with the room until the user leaves the room. - /// + /// /// After a user has joined a room, the room will appear as an entry in the /// response of the |/initialSync|_ and |/sync|_ APIs. - /// + /// /// If a ``third_party_signed`` was supplied, the homeserver must verify /// that it matches a pending ``m.room.third_party_invite`` event in the /// room, and perform key validity checking if required by the event. class JoinRoomJob : public BaseJob { public: - // Inner data structures - - /// *Note that this API takes either a room ID or alias, unlike* ``/room/{roomId}/join``. - /// - /// This API starts a user participating in a particular room, if that user - /// is allowed to participate in that room. After this call, the client is - /// allowed to see all current state events in the room, and all subsequent - /// events associated with the room until the user leaves the room. - /// - /// After a user has joined a room, the room will appear as an entry in the - /// response of the |/initialSync|_ and |/sync|_ APIs. - /// - /// If a ``third_party_signed`` was supplied, the homeserver must verify - /// that it matches a pending ``m.room.third_party_invite`` event in the - /// room, and perform key validity checking if required by the event. - struct Signed - { - /// The Matrix ID of the user who issued the invite. - QString sender; - /// The Matrix ID of the invitee. - QString mxid; - /// The state key of the m.third_party_invite event. - QString token; - /// A signatures object containing a signature of the entire signed object. - QJsonObject signatures; - }; - - /// A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - struct ThirdPartySigned - { - /// A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - Signed signedData; - }; - - // Construction/destruction - - /*! Start the requesting user participating in a particular room. - * \param roomIdOrAlias - * The room identifier or alias to join. - * \param serverName - * The servers to attempt to join the room through. One of the servers - * must be participating in the room. - * \param thirdPartySigned - * A signature of an ``m.third_party_invite`` token to prove that this user owns a third party identity which has been invited to the room. - */ - explicit JoinRoomJob(const QString& roomIdOrAlias, const QStringList& serverName = {}, const Omittable& thirdPartySigned = none); - ~JoinRoomJob() override; - - // Result properties - - /// The joined room ID. - const QString& roomId() const; + // Inner data structures + + /// *Note that this API takes either a room ID or alias, unlike* + /// ``/room/{roomId}/join``. + /// + /// This API starts a user participating in a particular room, if that + /// user is allowed to participate in that room. After this call, the + /// client is allowed to see all current state events in the room, and + /// all subsequent events associated with the room until the user leaves + /// the room. + /// + /// After a user has joined a room, the room will appear as an entry in + /// the response of the |/initialSync|_ and |/sync|_ APIs. + /// + /// If a ``third_party_signed`` was supplied, the homeserver must verify + /// that it matches a pending ``m.room.third_party_invite`` event in the + /// room, and perform key validity checking if required by the event. + struct Signed { + /// The Matrix ID of the user who issued the invite. + QString sender; + /// The Matrix ID of the invitee. + QString mxid; + /// The state key of the m.third_party_invite event. + QString token; + /// A signatures object containing a signature of the entire signed + /// object. + QJsonObject signatures; + }; + + /// A signature of an ``m.third_party_invite`` token to prove that this + /// user owns a third party identity which has been invited to the room. + struct ThirdPartySigned { + /// A signature of an ``m.third_party_invite`` token to prove that + /// this user owns a third party identity which has been invited to + /// the room. + Signed signedData; + }; + + // Construction/destruction + + /*! Start the requesting user participating in a particular room. + * \param roomIdOrAlias + * The room identifier or alias to join. + * \param serverName + * The servers to attempt to join the room through. One of the servers + * must be participating in the room. + * \param thirdPartySigned + * A signature of an ``m.third_party_invite`` token to prove that this + * user owns a third party identity which has been invited to the room. + */ + explicit JoinRoomJob( + const QString& roomIdOrAlias, + const QStringList& serverName = {}, + const Omittable& thirdPartySigned = none); + ~JoinRoomJob() override; + + // Result properties + + /// The joined room ID. + const QString& roomId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index 6c16a8a3..862366a3 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -15,15 +15,15 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class UploadKeysJob::Private { public: - QHash oneTimeKeyCounts; + QHash oneTimeKeyCounts; }; static const auto UploadKeysJobName = QStringLiteral("UploadKeysJob"); -UploadKeysJob::UploadKeysJob(const Omittable& deviceKeys, const QHash& oneTimeKeys) - : BaseJob(HttpVerb::Post, UploadKeysJobName, - basePath % "/keys/upload") - , d(new Private) +UploadKeysJob::UploadKeysJob(const Omittable& deviceKeys, + const QHash& oneTimeKeys) + : BaseJob(HttpVerb::Post, UploadKeysJobName, basePath % "/keys/upload"), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("device_keys"), deviceKeys); @@ -43,29 +43,29 @@ BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("one_time_key_counts"_ls)) return { JsonParseError, - "The key 'one_time_key_counts' not found in the response" }; + "The key 'one_time_key_counts' not found in the response" }; fromJson(json.value("one_time_key_counts"_ls), d->oneTimeKeyCounts); return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, QueryKeysJob::UnsignedDeviceInfo& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + QueryKeysJob::UnsignedDeviceInfo& result) { - fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); + fromJson(jo.value("device_display_name"_ls), + result.deviceDisplayName); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, QueryKeysJob::DeviceInformation& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + QueryKeysJob::DeviceInformation& result) { fillFromJson(jo, result); - fromJson(jo.value("unsigned"_ls), result.unsignedData); + fromJson(jo.value("unsigned"_ls), result.unsignedData); } }; } // namespace QMatrixClient @@ -73,16 +73,16 @@ namespace QMatrixClient class QueryKeysJob::Private { public: - QHash failures; - QHash> deviceKeys; + QHash failures; + QHash> deviceKeys; }; static const auto QueryKeysJobName = QStringLiteral("QueryKeysJob"); -QueryKeysJob::QueryKeysJob(const QHash& deviceKeys, Omittable timeout, const QString& token) - : BaseJob(HttpVerb::Post, QueryKeysJobName, - basePath % "/keys/query") - , d(new Private) +QueryKeysJob::QueryKeysJob(const QHash& deviceKeys, + Omittable timeout, const QString& token) + : BaseJob(HttpVerb::Post, QueryKeysJobName, basePath % "/keys/query"), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("timeout"), timeout); @@ -98,7 +98,8 @@ const QHash& QueryKeysJob::failures() const return d->failures; } -const QHash>& QueryKeysJob::deviceKeys() const +const QHash>& +QueryKeysJob::deviceKeys() const { return d->deviceKeys; } @@ -114,16 +115,17 @@ BaseJob::Status QueryKeysJob::parseJson(const QJsonDocument& data) class ClaimKeysJob::Private { public: - QHash failures; - QHash> oneTimeKeys; + QHash failures; + QHash> oneTimeKeys; }; static const auto ClaimKeysJobName = QStringLiteral("ClaimKeysJob"); -ClaimKeysJob::ClaimKeysJob(const QHash>& oneTimeKeys, Omittable timeout) - : BaseJob(HttpVerb::Post, ClaimKeysJobName, - basePath % "/keys/claim") - , d(new Private) +ClaimKeysJob::ClaimKeysJob( + const QHash>& oneTimeKeys, + Omittable timeout) + : BaseJob(HttpVerb::Post, ClaimKeysJobName, basePath % "/keys/claim"), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("timeout"), timeout); @@ -138,7 +140,8 @@ const QHash& ClaimKeysJob::failures() const return d->failures; } -const QHash>& ClaimKeysJob::oneTimeKeys() const +const QHash>& +ClaimKeysJob::oneTimeKeys() const { return d->oneTimeKeys; } @@ -154,8 +157,8 @@ BaseJob::Status ClaimKeysJob::parseJson(const QJsonDocument& data) class GetKeysChangesJob::Private { public: - QStringList changed; - QStringList left; + QStringList changed; + QStringList left; }; BaseJob::Query queryToGetKeysChanges(const QString& from, const QString& to) @@ -166,34 +169,28 @@ BaseJob::Query queryToGetKeysChanges(const QString& from, const QString& to) return _q; } -QUrl GetKeysChangesJob::makeRequestUrl(QUrl baseUrl, const QString& from, const QString& to) +QUrl GetKeysChangesJob::makeRequestUrl(QUrl baseUrl, const QString& from, + const QString& to) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/keys/changes", - queryToGetKeysChanges(from, to)); + basePath % "/keys/changes", + queryToGetKeysChanges(from, to)); } static const auto GetKeysChangesJobName = QStringLiteral("GetKeysChangesJob"); GetKeysChangesJob::GetKeysChangesJob(const QString& from, const QString& to) - : BaseJob(HttpVerb::Get, GetKeysChangesJobName, - basePath % "/keys/changes", - queryToGetKeysChanges(from, to)) - , d(new Private) + : BaseJob(HttpVerb::Get, GetKeysChangesJobName, basePath % "/keys/changes", + queryToGetKeysChanges(from, to)), + d(new Private) { } GetKeysChangesJob::~GetKeysChangesJob() = default; -const QStringList& GetKeysChangesJob::changed() const -{ - return d->changed; -} +const QStringList& GetKeysChangesJob::changed() const { return d->changed; } -const QStringList& GetKeysChangesJob::left() const -{ - return d->left; -} +const QStringList& GetKeysChangesJob::left() const { return d->left; } BaseJob::Status GetKeysChangesJob::parseJson(const QJsonDocument& data) { @@ -202,4 +199,3 @@ BaseJob::Status GetKeysChangesJob::parseJson(const QJsonDocument& data) fromJson(json.value("left"_ls), d->left); return Success; } - diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h index e59b1dae..a01cd33b 100644 --- a/lib/csapi/keys.h +++ b/lib/csapi/keys.h @@ -6,14 +6,13 @@ #include "jobs/basejob.h" +#include "converters.h" #include "csapi/definitions/device_keys.h" #include -#include "converters.h" -#include #include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Upload end-to-end encryption keys. @@ -22,33 +21,35 @@ namespace QMatrixClient class UploadKeysJob : public BaseJob { public: - /*! Upload end-to-end encryption keys. - * \param deviceKeys - * Identity keys for the device. May be absent if no new - * identity keys are required. - * \param oneTimeKeys - * One-time public keys for "pre-key" messages. The names of - * the properties should be in the format - * ``:``. The format of the key is determined - * by the key algorithm. - * - * May be absent if no new one-time keys are required. - */ - explicit UploadKeysJob(const Omittable& deviceKeys = none, const QHash& oneTimeKeys = {}); - ~UploadKeysJob() override; - - // Result properties - - /// For each key algorithm, the number of unclaimed one-time keys - /// of that type currently held on the server for this device. - const QHash& oneTimeKeyCounts() const; + /*! Upload end-to-end encryption keys. + * \param deviceKeys + * Identity keys for the device. May be absent if no new + * identity keys are required. + * \param oneTimeKeys + * One-time public keys for "pre-key" messages. The names of + * the properties should be in the format + * ``:``. The format of the key is determined + * by the key algorithm. + * + * May be absent if no new one-time keys are required. + */ + explicit UploadKeysJob( + const Omittable& deviceKeys = none, + const QHash& oneTimeKeys = {}); + ~UploadKeysJob() override; + + // Result properties + + /// For each key algorithm, the number of unclaimed one-time keys + /// of that type currently held on the server for this device. + const QHash& oneTimeKeyCounts() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Download device identity keys. @@ -57,68 +58,70 @@ namespace QMatrixClient class QueryKeysJob : public BaseJob { public: - // Inner data structures - + // Inner data structures + + /// Additional data added to the device key information + /// by intermediate servers, and not covered by the + /// signatures. + struct UnsignedDeviceInfo { + /// The display name which the user set on the device. + QString deviceDisplayName; + }; + + /// Returns the current devices and identity keys for the given users. + struct DeviceInformation : DeviceKeys { /// Additional data added to the device key information /// by intermediate servers, and not covered by the /// signatures. - struct UnsignedDeviceInfo - { - /// The display name which the user set on the device. - QString deviceDisplayName; - }; - - /// Returns the current devices and identity keys for the given users. - struct DeviceInformation : DeviceKeys - { - /// Additional data added to the device key information - /// by intermediate servers, and not covered by the - /// signatures. - Omittable unsignedData; - }; - - // Construction/destruction - - /*! Download device identity keys. - * \param deviceKeys - * The keys to be downloaded. A map from user ID, to a list of - * device IDs, or to an empty list to indicate all devices for the - * corresponding user. - * \param timeout - * The time (in milliseconds) to wait when downloading keys from - * remote servers. 10 seconds is the recommended default. - * \param token - * If the client is fetching keys as a result of a device update received - * in a sync request, this should be the 'since' token of that sync request, - * or any later sync token. This allows the server to ensure its response - * contains the keys advertised by the notification in that sync. - */ - explicit QueryKeysJob(const QHash& deviceKeys, Omittable timeout = none, const QString& token = {}); - ~QueryKeysJob() override; - - // Result properties - - /// If any remote homeservers could not be reached, they are - /// recorded here. The names of the properties are the names of - /// the unreachable servers. - /// - /// If the homeserver could be reached, but the user or device - /// was unknown, no failure is recorded. Instead, the corresponding - /// user or device is missing from the ``device_keys`` result. - const QHash& failures() const; - /// Information on the queried devices. A map from user ID, to a - /// map from device ID to device information. For each device, - /// the information returned will be the same as uploaded via - /// ``/keys/upload``, with the addition of an ``unsigned`` - /// property. - const QHash>& deviceKeys() const; + Omittable unsignedData; + }; + + // Construction/destruction + + /*! Download device identity keys. + * \param deviceKeys + * The keys to be downloaded. A map from user ID, to a list of + * device IDs, or to an empty list to indicate all devices for the + * corresponding user. + * \param timeout + * The time (in milliseconds) to wait when downloading keys from + * remote servers. 10 seconds is the recommended default. + * \param token + * If the client is fetching keys as a result of a device update + * received in a sync request, this should be the 'since' token of that + * sync request, or any later sync token. This allows the server to + * ensure its response contains the keys advertised by the notification + * in that sync. + */ + explicit QueryKeysJob(const QHash& deviceKeys, + Omittable timeout = none, + const QString& token = {}); + ~QueryKeysJob() override; + + // Result properties + + /// If any remote homeservers could not be reached, they are + /// recorded here. The names of the properties are the names of + /// the unreachable servers. + /// + /// If the homeserver could be reached, but the user or device + /// was unknown, no failure is recorded. Instead, the corresponding + /// user or device is missing from the ``device_keys`` result. + const QHash& failures() const; + /// Information on the queried devices. A map from user ID, to a + /// map from device ID to device information. For each device, + /// the information returned will be the same as uploaded via + /// ``/keys/upload``, with the addition of an ``unsigned`` + /// property. + const QHash>& + deviceKeys() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Claim one-time encryption keys. @@ -127,46 +130,49 @@ namespace QMatrixClient class ClaimKeysJob : public BaseJob { public: - /*! Claim one-time encryption keys. - * \param oneTimeKeys - * The keys to be claimed. A map from user ID, to a map from - * device ID to algorithm name. - * \param timeout - * The time (in milliseconds) to wait when downloading keys from - * remote servers. 10 seconds is the recommended default. - */ - explicit ClaimKeysJob(const QHash>& oneTimeKeys, Omittable timeout = none); - ~ClaimKeysJob() override; - - // Result properties - - /// If any remote homeservers could not be reached, they are - /// recorded here. The names of the properties are the names of - /// the unreachable servers. - /// - /// If the homeserver could be reached, but the user or device - /// was unknown, no failure is recorded. Instead, the corresponding - /// user or device is missing from the ``one_time_keys`` result. - const QHash& failures() const; - /// One-time keys for the queried devices. A map from user ID, to a - /// map from devices to a map from ``:`` to the key object. - const QHash>& oneTimeKeys() const; + /*! Claim one-time encryption keys. + * \param oneTimeKeys + * The keys to be claimed. A map from user ID, to a map from + * device ID to algorithm name. + * \param timeout + * The time (in milliseconds) to wait when downloading keys from + * remote servers. 10 seconds is the recommended default. + */ + explicit ClaimKeysJob( + const QHash>& oneTimeKeys, + Omittable timeout = none); + ~ClaimKeysJob() override; + + // Result properties + + /// If any remote homeservers could not be reached, they are + /// recorded here. The names of the properties are the names of + /// the unreachable servers. + /// + /// If the homeserver could be reached, but the user or device + /// was unknown, no failure is recorded. Instead, the corresponding + /// user or device is missing from the ``one_time_keys`` result. + const QHash& failures() const; + /// One-time keys for the queried devices. A map from user ID, to a + /// map from devices to a map from ``:`` to the key + /// object. + const QHash>& oneTimeKeys() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Query users with recent device key updates. /// /// Gets a list of users who have updated their device identity keys since a /// previous sync token. - /// + /// /// The server should include in the results any users who: - /// + /// /// * currently share a room with the calling user (ie, both users have /// membership state ``join``); *and* /// * added new device identity keys or removed an existing device with @@ -174,46 +180,47 @@ namespace QMatrixClient class GetKeysChangesJob : public BaseJob { public: - /*! Query users with recent device key updates. - * \param from - * The desired start point of the list. Should be the ``next_batch`` field - * from a response to an earlier call to |/sync|. Users who have not - * uploaded new device identity keys since this point, nor deleted - * existing devices with identity keys since then, will be excluded - * from the results. - * \param to - * The desired end point of the list. Should be the ``next_batch`` - * field from a recent call to |/sync| - typically the most recent - * such call. This may be used by the server as a hint to check its - * caches are up to date. - */ - explicit GetKeysChangesJob(const QString& from, const QString& to); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetKeysChangesJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& from, const QString& to); - - ~GetKeysChangesJob() override; - - // Result properties - - /// The Matrix User IDs of all users who updated their device - /// identity keys. - const QStringList& changed() const; - /// The Matrix User IDs of all users who may have left all - /// the end-to-end encrypted rooms they previously shared - /// with the user. - const QStringList& left() const; + /*! Query users with recent device key updates. + * \param from + * The desired start point of the list. Should be the ``next_batch`` + * field from a response to an earlier call to |/sync|. Users who have + * not uploaded new device identity keys since this point, nor deleted + * existing devices with identity keys since then, will be excluded + * from the results. + * \param to + * The desired end point of the list. Should be the ``next_batch`` + * field from a recent call to |/sync| - typically the most recent + * such call. This may be used by the server as a hint to check its + * caches are up to date. + */ + explicit GetKeysChangesJob(const QString& from, const QString& to); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetKeysChangesJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& from, + const QString& to); + + ~GetKeysChangesJob() override; + + // Result properties + + /// The Matrix User IDs of all users who updated their device + /// identity keys. + const QStringList& changed() const; + /// The Matrix User IDs of all users who may have left all + /// the end-to-end encrypted rooms they previously shared + /// with the user. + const QStringList& left() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/kicking.cpp b/lib/csapi/kicking.cpp index 1d6d5543..05c4c581 100644 --- a/lib/csapi/kicking.cpp +++ b/lib/csapi/kicking.cpp @@ -14,13 +14,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto KickJobName = QStringLiteral("KickJob"); -KickJob::KickJob(const QString& roomId, const QString& userId, const QString& reason) +KickJob::KickJob(const QString& roomId, const QString& userId, + const QString& reason) : BaseJob(HttpVerb::Post, KickJobName, - basePath % "/rooms/" % roomId % "/kick") + basePath % "/rooms/" % roomId % "/kick") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); addParam(_data, QStringLiteral("reason"), reason); setRequestData(_data); } - diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h index 714079cf..d75b8df3 100644 --- a/lib/csapi/kicking.h +++ b/lib/csapi/kicking.h @@ -6,32 +6,32 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Kick a user from the room. /// /// Kick a user from the room. - /// - /// The caller must have the required power level in order to perform this operation. - /// - /// Kicking a user adjusts the target member's membership state to be ``leave`` with an - /// optional ``reason``. Like with other membership changes, a user can directly adjust - /// the target member's state by making a request to ``/rooms//state/m.room.member/``. + /// + /// The caller must have the required power level in order to perform this + /// operation. + /// + /// Kicking a user adjusts the target member's membership state to be + /// ``leave`` with an optional ``reason``. Like with other membership + /// changes, a user can directly adjust the target member's state by making + /// a request to ``/rooms//state/m.room.member/``. class KickJob : public BaseJob { public: - /*! Kick a user from the room. - * \param roomId - * The room identifier (not alias) from which the user should be kicked. - * \param userId - * The fully qualified user ID of the user being kicked. - * \param reason - * The reason the user has been kicked. This will be supplied as the - * ``reason`` on the target's updated `m.room.member`_ event. - */ - explicit KickJob(const QString& roomId, const QString& userId, const QString& reason = {}); + /*! Kick a user from the room. + * \param roomId + * The room identifier (not alias) from which the user should be + * kicked. \param userId The fully qualified user ID of the user being + * kicked. \param reason The reason the user has been kicked. This will + * be supplied as the + * ``reason`` on the target's updated `m.room.member`_ event. + */ + explicit KickJob(const QString& roomId, const QString& userId, + const QString& reason = {}); }; } // namespace QMatrixClient diff --git a/lib/csapi/leaving.cpp b/lib/csapi/leaving.cpp index 09e5f83b..c46567d7 100644 --- a/lib/csapi/leaving.cpp +++ b/lib/csapi/leaving.cpp @@ -15,28 +15,27 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); QUrl LeaveRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/leave"); + basePath % "/rooms/" % roomId % "/leave"); } static const auto LeaveRoomJobName = QStringLiteral("LeaveRoomJob"); LeaveRoomJob::LeaveRoomJob(const QString& roomId) : BaseJob(HttpVerb::Post, LeaveRoomJobName, - basePath % "/rooms/" % roomId % "/leave") + basePath % "/rooms/" % roomId % "/leave") { } QUrl ForgetRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/forget"); + basePath % "/rooms/" % roomId % "/forget"); } static const auto ForgetRoomJobName = QStringLiteral("ForgetRoomJob"); ForgetRoomJob::ForgetRoomJob(const QString& roomId) : BaseJob(HttpVerb::Post, ForgetRoomJobName, - basePath % "/rooms/" % roomId % "/forget") + basePath % "/rooms/" % roomId % "/forget") { } - diff --git a/lib/csapi/leaving.h b/lib/csapi/leaving.h index 3a340034..374d27d4 100644 --- a/lib/csapi/leaving.h +++ b/lib/csapi/leaving.h @@ -6,70 +6,66 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Stop the requesting user participating in a particular room. /// /// This API stops a user participating in a particular room. - /// + /// /// If the user was already in the room, they will no longer be able to see /// new events in the room. If the room requires an invite to join, they /// will need to be re-invited before they can re-join. - /// + /// /// If the user was invited to the room, but had not joined, this call /// serves to reject the invite. - /// + /// /// The user will still be allowed to retrieve history from the room which /// they were previously allowed to see. class LeaveRoomJob : public BaseJob { public: - /*! Stop the requesting user participating in a particular room. - * \param roomId - * The room identifier to leave. - */ - explicit LeaveRoomJob(const QString& roomId); - - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Stop the requesting user participating in a particular room. + * \param roomId + * The room identifier to leave. + */ + explicit LeaveRoomJob(const QString& roomId); + /*! Construct a URL without creating a full-fledged job object + * + * 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); }; /// Stop the requesting user remembering about a particular room. /// /// This API stops a user remembering about a particular room. - /// + /// /// In general, history is a first class citizen in Matrix. After this API /// is called, however, a user will no longer be able to retrieve history /// for this room. If all users on a homeserver forget a room, the room is /// eligible for deletion from that homeserver. - /// + /// /// If the user is currently joined to the room, they must leave the room /// before calling this API. class ForgetRoomJob : public BaseJob { public: - /*! Stop the requesting user remembering about a particular room. - * \param roomId - * The room identifier to forget. - */ - explicit ForgetRoomJob(const QString& roomId); - - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Stop the requesting user remembering about a particular room. + * \param roomId + * The room identifier to forget. + */ + explicit ForgetRoomJob(const QString& roomId); + /*! Construct a URL without creating a full-fledged job object + * + * 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); }; } // namespace QMatrixClient diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index 85a9cae4..145e91ad 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -15,21 +15,20 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetJoinedRoomsJob::Private { public: - QStringList joinedRooms; + QStringList joinedRooms; }; QUrl GetJoinedRoomsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/joined_rooms"); + basePath % "/joined_rooms"); } static const auto GetJoinedRoomsJobName = QStringLiteral("GetJoinedRoomsJob"); GetJoinedRoomsJob::GetJoinedRoomsJob() - : BaseJob(HttpVerb::Get, GetJoinedRoomsJobName, - basePath % "/joined_rooms") - , d(new Private) + : BaseJob(HttpVerb::Get, GetJoinedRoomsJobName, basePath % "/joined_rooms"), + d(new Private) { } @@ -45,8 +44,7 @@ BaseJob::Status GetJoinedRoomsJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("joined_rooms"_ls)) return { JsonParseError, - "The key 'joined_rooms' not found in the response" }; + "The key 'joined_rooms' not found in the response" }; fromJson(json.value("joined_rooms"_ls), d->joinedRooms); return Success; } - diff --git a/lib/csapi/list_joined_rooms.h b/lib/csapi/list_joined_rooms.h index 881a97b4..6f4169e8 100644 --- a/lib/csapi/list_joined_rooms.h +++ b/lib/csapi/list_joined_rooms.h @@ -6,9 +6,7 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Lists the user's current rooms. @@ -17,28 +15,28 @@ namespace QMatrixClient class GetJoinedRoomsJob : public BaseJob { public: - explicit GetJoinedRoomsJob(); + explicit GetJoinedRoomsJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetJoinedRoomsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetJoinedRoomsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetJoinedRoomsJob() override; + ~GetJoinedRoomsJob() override; - // Result properties + // Result properties - /// The ID of each room in which the user has ``joined`` membership. - const QStringList& joinedRooms() const; + /// The ID of each room in which the user has ``joined`` membership. + const QStringList& joinedRooms() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 71b3c541..2649b9f8 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -15,21 +15,24 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetRoomVisibilityOnDirectoryJob::Private { public: - QString visibility; + QString visibility; }; -QUrl GetRoomVisibilityOnDirectoryJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) +QUrl GetRoomVisibilityOnDirectoryJob::makeRequestUrl(QUrl baseUrl, + const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/directory/list/room/" % roomId); + basePath % "/directory/list/room/" % roomId); } -static const auto GetRoomVisibilityOnDirectoryJobName = QStringLiteral("GetRoomVisibilityOnDirectoryJob"); +static const auto GetRoomVisibilityOnDirectoryJobName = + QStringLiteral("GetRoomVisibilityOnDirectoryJob"); -GetRoomVisibilityOnDirectoryJob::GetRoomVisibilityOnDirectoryJob(const QString& roomId) +GetRoomVisibilityOnDirectoryJob::GetRoomVisibilityOnDirectoryJob( + const QString& roomId) : BaseJob(HttpVerb::Get, GetRoomVisibilityOnDirectoryJobName, - basePath % "/directory/list/room/" % roomId, false) - , d(new Private) + basePath % "/directory/list/room/" % roomId, false), + d(new Private) { } @@ -40,18 +43,21 @@ const QString& GetRoomVisibilityOnDirectoryJob::visibility() const return d->visibility; } -BaseJob::Status GetRoomVisibilityOnDirectoryJob::parseJson(const QJsonDocument& data) +BaseJob::Status +GetRoomVisibilityOnDirectoryJob::parseJson(const QJsonDocument& data) { auto json = data.object(); fromJson(json.value("visibility"_ls), d->visibility); return Success; } -static const auto SetRoomVisibilityOnDirectoryJobName = QStringLiteral("SetRoomVisibilityOnDirectoryJob"); +static const auto SetRoomVisibilityOnDirectoryJobName = + QStringLiteral("SetRoomVisibilityOnDirectoryJob"); -SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob(const QString& roomId, const QString& visibility) +SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob( + const QString& roomId, const QString& visibility) : BaseJob(HttpVerb::Put, SetRoomVisibilityOnDirectoryJobName, - basePath % "/directory/list/room/" % roomId) + basePath % "/directory/list/room/" % roomId) { QJsonObject _data; addParam(_data, QStringLiteral("visibility"), visibility); @@ -61,10 +67,11 @@ SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob(const QString& class GetPublicRoomsJob::Private { public: - PublicRoomsResponse data; + PublicRoomsResponse data; }; -BaseJob::Query queryToGetPublicRooms(Omittable limit, const QString& since, const QString& server) +BaseJob::Query queryToGetPublicRooms(Omittable limit, const QString& since, + const QString& server) { BaseJob::Query _q; addParam(_q, QStringLiteral("limit"), limit); @@ -73,30 +80,28 @@ BaseJob::Query queryToGetPublicRooms(Omittable limit, const QString& since, return _q; } -QUrl GetPublicRoomsJob::makeRequestUrl(QUrl baseUrl, Omittable limit, const QString& since, const QString& server) +QUrl GetPublicRoomsJob::makeRequestUrl(QUrl baseUrl, Omittable limit, + const QString& since, + const QString& server) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/publicRooms", - queryToGetPublicRooms(limit, since, server)); + basePath % "/publicRooms", + queryToGetPublicRooms(limit, since, server)); } static const auto GetPublicRoomsJobName = QStringLiteral("GetPublicRoomsJob"); -GetPublicRoomsJob::GetPublicRoomsJob(Omittable limit, const QString& since, const QString& server) - : BaseJob(HttpVerb::Get, GetPublicRoomsJobName, - basePath % "/publicRooms", - queryToGetPublicRooms(limit, since, server), - {}, false) - , d(new Private) +GetPublicRoomsJob::GetPublicRoomsJob(Omittable limit, const QString& since, + const QString& server) + : BaseJob(HttpVerb::Get, GetPublicRoomsJobName, basePath % "/publicRooms", + queryToGetPublicRooms(limit, since, server), {}, false), + d(new Private) { } GetPublicRoomsJob::~GetPublicRoomsJob() = default; -const PublicRoomsResponse& GetPublicRoomsJob::data() const -{ - return d->data; -} +const PublicRoomsResponse& GetPublicRoomsJob::data() const { return d->data; } BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) { @@ -104,15 +109,15 @@ BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const QueryPublicRoomsJob::Filter& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const QueryPublicRoomsJob::Filter& pod) { - addParam(jo, QStringLiteral("generic_search_term"), pod.genericSearchTerm); + addParam(jo, QStringLiteral("generic_search_term"), + pod.genericSearchTerm); } }; } // namespace QMatrixClient @@ -120,7 +125,7 @@ namespace QMatrixClient class QueryPublicRoomsJob::Private { public: - PublicRoomsResponse data; + PublicRoomsResponse data; }; BaseJob::Query queryToQueryPublicRooms(const QString& server) @@ -130,33 +135,36 @@ BaseJob::Query queryToQueryPublicRooms(const QString& server) return _q; } -static const auto QueryPublicRoomsJobName = QStringLiteral("QueryPublicRoomsJob"); +static const auto QueryPublicRoomsJobName = + QStringLiteral("QueryPublicRoomsJob"); -QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, Omittable limit, const QString& since, const Omittable& filter, Omittable includeAllNetworks, const QString& thirdPartyInstanceId) +QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, + Omittable limit, + const QString& since, + const Omittable& filter, + Omittable includeAllNetworks, + const QString& thirdPartyInstanceId) : BaseJob(HttpVerb::Post, QueryPublicRoomsJobName, - basePath % "/publicRooms", - queryToQueryPublicRooms(server)) - , d(new Private) + basePath % "/publicRooms", queryToQueryPublicRooms(server)), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("limit"), limit); addParam(_data, QStringLiteral("since"), since); addParam(_data, QStringLiteral("filter"), filter); - addParam(_data, QStringLiteral("include_all_networks"), includeAllNetworks); - addParam(_data, QStringLiteral("third_party_instance_id"), thirdPartyInstanceId); + addParam(_data, QStringLiteral("include_all_networks"), + includeAllNetworks); + addParam(_data, QStringLiteral("third_party_instance_id"), + thirdPartyInstanceId); setRequestData(_data); } QueryPublicRoomsJob::~QueryPublicRoomsJob() = default; -const PublicRoomsResponse& QueryPublicRoomsJob::data() const -{ - return d->data; -} +const PublicRoomsResponse& QueryPublicRoomsJob::data() const { return d->data; } BaseJob::Status QueryPublicRoomsJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index a6498745..1a0af880 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -6,169 +6,178 @@ #include "jobs/basejob.h" -#include "csapi/definitions/public_rooms_response.h" #include "converters.h" +#include "csapi/definitions/public_rooms_response.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets the visibility of a room in the directory /// - /// Gets the visibility of a given room on the server's public room directory. + /// Gets the visibility of a given room on the server's public room + /// directory. class GetRoomVisibilityOnDirectoryJob : public BaseJob { public: - /*! Gets the visibility of a room in the directory - * \param roomId - * The room ID. - */ - explicit GetRoomVisibilityOnDirectoryJob(const QString& roomId); + /*! Gets the visibility of a room in the directory + * \param roomId + * The room ID. + */ + explicit GetRoomVisibilityOnDirectoryJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomVisibilityOnDirectoryJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomVisibilityOnDirectoryJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetRoomVisibilityOnDirectoryJob() override; + ~GetRoomVisibilityOnDirectoryJob() override; - // Result properties + // Result properties - /// The visibility of the room in the directory. - const QString& visibility() const; + /// The visibility of the room in the directory. + const QString& visibility() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Sets the visibility of a room in the room directory /// /// Sets the visibility of a given room in the server's public room /// directory. - /// + /// /// Servers may choose to implement additional access control checks - /// here, for instance that room visibility can only be changed by + /// here, for instance that room visibility can only be changed by /// the room creator or a server administrator. class SetRoomVisibilityOnDirectoryJob : public BaseJob { public: - /*! Sets the visibility of a room in the room directory - * \param roomId - * The room ID. - * \param visibility - * The new visibility setting for the room. - * Defaults to 'public'. - */ - explicit SetRoomVisibilityOnDirectoryJob(const QString& roomId, const QString& visibility = {}); + /*! Sets the visibility of a room in the room directory + * \param roomId + * The room ID. + * \param visibility + * The new visibility setting for the room. + * Defaults to 'public'. + */ + explicit SetRoomVisibilityOnDirectoryJob( + const QString& roomId, const QString& visibility = {}); }; /// Lists the public rooms on the server. /// /// Lists the public rooms on the server. - /// - /// This API returns paginated responses. The rooms are ordered by the number - /// of joined members, with the largest rooms first. + /// + /// This API returns paginated responses. The rooms are ordered by the + /// number of joined members, with the largest rooms first. class GetPublicRoomsJob : public BaseJob { public: - /*! Lists the public rooms on the server. - * \param limit - * Limit the number of results returned. - * \param since - * A pagination token from a previous request, allowing clients to - * get the next (or previous) batch of rooms. - * The direction of pagination is specified solely by which token - * is supplied, rather than via an explicit flag. - * \param server - * The server to fetch the public room lists from. Defaults to the - * local server. - */ - explicit GetPublicRoomsJob(Omittable limit = none, const QString& since = {}, const QString& server = {}); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPublicRoomsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, Omittable limit = none, const QString& since = {}, const QString& server = {}); - - ~GetPublicRoomsJob() override; - - // Result properties - - /// A list of the rooms on the server. - const PublicRoomsResponse& data() const; + /*! Lists the public rooms on the server. + * \param limit + * Limit the number of results returned. + * \param since + * A pagination token from a previous request, allowing clients to + * get the next (or previous) batch of rooms. + * The direction of pagination is specified solely by which token + * is supplied, rather than via an explicit flag. + * \param server + * The server to fetch the public room lists from. Defaults to the + * local server. + */ + explicit GetPublicRoomsJob(Omittable limit = none, + const QString& since = {}, + const QString& server = {}); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPublicRoomsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, Omittable limit = none, + const QString& since = {}, + const QString& server = {}); + + ~GetPublicRoomsJob() override; + + // Result properties + + /// A list of the rooms on the server. + const PublicRoomsResponse& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Lists the public rooms on the server with optional filter. /// /// Lists the public rooms on the server, with optional filter. - /// - /// This API returns paginated responses. The rooms are ordered by the number - /// of joined members, with the largest rooms first. + /// + /// This API returns paginated responses. The rooms are ordered by the + /// number of joined members, with the largest rooms first. class QueryPublicRoomsJob : public BaseJob { public: - // Inner data structures - - /// Filter to apply to the results. - struct Filter - { - /// A string to search for in the room metadata, e.g. name, - /// topic, canonical alias etc. (Optional). - QString genericSearchTerm; - }; - - // Construction/destruction - - /*! Lists the public rooms on the server with optional filter. - * \param server - * The server to fetch the public room lists from. Defaults to the - * local server. - * \param limit - * Limit the number of results returned. - * \param since - * A pagination token from a previous request, allowing clients - * to get the next (or previous) batch of rooms. The direction - * of pagination is specified solely by which token is supplied, - * rather than via an explicit flag. - * \param filter - * Filter to apply to the results. - * \param includeAllNetworks - * Whether or not to include all known networks/protocols from - * application services on the homeserver. Defaults to false. - * \param thirdPartyInstanceId - * The specific third party network/protocol to request from the - * homeserver. Can only be used if ``include_all_networks`` is false. - */ - explicit QueryPublicRoomsJob(const QString& server = {}, Omittable limit = none, const QString& since = {}, const Omittable& filter = none, Omittable includeAllNetworks = none, const QString& thirdPartyInstanceId = {}); - ~QueryPublicRoomsJob() override; - - // Result properties - - /// A list of the rooms on the server. - const PublicRoomsResponse& data() const; + // Inner data structures + + /// Filter to apply to the results. + struct Filter { + /// A string to search for in the room metadata, e.g. name, + /// topic, canonical alias etc. (Optional). + QString genericSearchTerm; + }; + + // Construction/destruction + + /*! Lists the public rooms on the server with optional filter. + * \param server + * The server to fetch the public room lists from. Defaults to the + * local server. + * \param limit + * Limit the number of results returned. + * \param since + * A pagination token from a previous request, allowing clients + * to get the next (or previous) batch of rooms. The direction + * of pagination is specified solely by which token is supplied, + * rather than via an explicit flag. + * \param filter + * Filter to apply to the results. + * \param includeAllNetworks + * Whether or not to include all known networks/protocols from + * application services on the homeserver. Defaults to false. + * \param thirdPartyInstanceId + * The specific third party network/protocol to request from the + * homeserver. Can only be used if ``include_all_networks`` is false. + */ + explicit QueryPublicRoomsJob(const QString& server = {}, + Omittable limit = none, + const QString& since = {}, + const Omittable& filter = none, + Omittable includeAllNetworks = none, + const QString& thirdPartyInstanceId = {}); + ~QueryPublicRoomsJob() override; + + // Result properties + + /// A list of the rooms on the server. + const PublicRoomsResponse& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/login.cpp b/lib/csapi/login.cpp index 5e369b9a..77652e64 100644 --- a/lib/csapi/login.cpp +++ b/lib/csapi/login.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetLoginFlowsJob::LoginFlow& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetLoginFlowsJob::LoginFlow& result) { fromJson(jo.value("type"_ls), result.type); } @@ -28,21 +27,19 @@ namespace QMatrixClient class GetLoginFlowsJob::Private { public: - QVector flows; + QVector flows; }; QUrl GetLoginFlowsJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/login"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/login"); } static const auto GetLoginFlowsJobName = QStringLiteral("GetLoginFlowsJob"); GetLoginFlowsJob::GetLoginFlowsJob() - : BaseJob(HttpVerb::Get, GetLoginFlowsJobName, - basePath % "/login", false) - , d(new Private) + : BaseJob(HttpVerb::Get, GetLoginFlowsJobName, basePath % "/login", false), + d(new Private) { } @@ -63,19 +60,23 @@ BaseJob::Status GetLoginFlowsJob::parseJson(const QJsonDocument& data) class LoginJob::Private { public: - QString userId; - QString accessToken; - QString homeServer; - QString deviceId; - Omittable wellKnown; + QString userId; + QString accessToken; + QString homeServer; + QString deviceId; + Omittable wellKnown; }; static const auto LoginJobName = QStringLiteral("LoginJob"); -LoginJob::LoginJob(const QString& type, const Omittable& identifier, const QString& password, const QString& token, const QString& deviceId, const QString& initialDeviceDisplayName, const QString& user, const QString& medium, const QString& address) - : BaseJob(HttpVerb::Post, LoginJobName, - basePath % "/login", false) - , d(new Private) +LoginJob::LoginJob(const QString& type, + const Omittable& identifier, + const QString& password, const QString& token, + const QString& deviceId, + const QString& initialDeviceDisplayName, const QString& user, + const QString& medium, const QString& address) + : BaseJob(HttpVerb::Post, LoginJobName, basePath % "/login", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("type"), type); @@ -83,7 +84,8 @@ LoginJob::LoginJob(const QString& type, const Omittable& identif addParam(_data, QStringLiteral("password"), password); addParam(_data, QStringLiteral("token"), token); addParam(_data, QStringLiteral("device_id"), deviceId); - addParam(_data, QStringLiteral("initial_device_display_name"), initialDeviceDisplayName); + addParam(_data, QStringLiteral("initial_device_display_name"), + initialDeviceDisplayName); addParam(_data, QStringLiteral("user"), user); addParam(_data, QStringLiteral("medium"), medium); addParam(_data, QStringLiteral("address"), address); @@ -92,25 +94,13 @@ LoginJob::LoginJob(const QString& type, const Omittable& identif LoginJob::~LoginJob() = default; -const QString& LoginJob::userId() const -{ - return d->userId; -} +const QString& LoginJob::userId() const { return d->userId; } -const QString& LoginJob::accessToken() const -{ - return d->accessToken; -} +const QString& LoginJob::accessToken() const { return d->accessToken; } -const QString& LoginJob::homeServer() const -{ - return d->homeServer; -} +const QString& LoginJob::homeServer() const { return d->homeServer; } -const QString& LoginJob::deviceId() const -{ - return d->deviceId; -} +const QString& LoginJob::deviceId() const { return d->deviceId; } const Omittable& LoginJob::wellKnown() const { @@ -127,4 +117,3 @@ BaseJob::Status LoginJob::parseJson(const QJsonDocument& data) fromJson(json.value("well_known"_ls), d->wellKnown); return Success; } - diff --git a/lib/csapi/login.h b/lib/csapi/login.h index 648316df..89c8ca79 100644 --- a/lib/csapi/login.h +++ b/lib/csapi/login.h @@ -6,68 +6,68 @@ #include "jobs/basejob.h" -#include -#include "csapi/definitions/wellknown/full.h" -#include "csapi/definitions/user_identifier.h" #include "converters.h" +#include "csapi/definitions/user_identifier.h" +#include "csapi/definitions/wellknown/full.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Get the supported login types to authenticate users /// - /// Gets the homeserver's supported login types to authenticate users. Clients - /// should pick one of these and supply it as the ``type`` when logging in. + /// Gets the homeserver's supported login types to authenticate users. + /// Clients should pick one of these and supply it as the ``type`` when + /// logging in. class GetLoginFlowsJob : public BaseJob { public: - // Inner data structures + // Inner data structures - /// Gets the homeserver's supported login types to authenticate users. Clients - /// should pick one of these and supply it as the ``type`` when logging in. - struct LoginFlow - { - /// The login type. This is supplied as the ``type`` when - /// logging in. - QString type; - }; + /// Gets the homeserver's supported login types to authenticate users. + /// Clients should pick one of these and supply it as the ``type`` when + /// logging in. + struct LoginFlow { + /// The login type. This is supplied as the ``type`` when + /// logging in. + QString type; + }; - // Construction/destruction + // Construction/destruction - explicit GetLoginFlowsJob(); + explicit GetLoginFlowsJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetLoginFlowsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetLoginFlowsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetLoginFlowsJob() override; + ~GetLoginFlowsJob() override; - // Result properties + // Result properties - /// The homeserver's supported login types - const QVector& flows() const; + /// The homeserver's supported login types + const QVector& flows() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Authenticates the user. /// /// Authenticates the user, and issues an access token they can /// use to authorize themself in subsequent requests. - /// + /// /// If the client does not supply a ``device_id``, the server must /// auto-generate one. - /// + /// /// The returned access token must be associated with the ``device_id`` /// supplied by the client or generated by the server. The server may /// invalidate any access token previously associated with that device. See @@ -75,61 +75,69 @@ namespace QMatrixClient class LoginJob : public BaseJob { public: - /*! Authenticates the user. - * \param type - * The login type being used. - * \param identifier - * Identification information for the user. - * \param password - * Required when ``type`` is ``m.login.password``. The user's - * password. - * \param token - * Required when ``type`` is ``m.login.token``. Part of `Token-based`_ login. - * \param deviceId - * ID of the client device. If this does not correspond to a - * known client device, a new device will be created. The server - * will auto-generate a device_id if this is not specified. - * \param initialDeviceDisplayName - * A display name to assign to the newly-created device. Ignored - * if ``device_id`` corresponds to a known device. - * \param user - * The fully qualified user ID or just local part of the user ID, to log in. Deprecated in favour of ``identifier``. - * \param medium - * When logging in using a third party identifier, the medium of the identifier. Must be 'email'. Deprecated in favour of ``identifier``. - * \param address - * Third party identifier for the user. Deprecated in favour of ``identifier``. - */ - explicit LoginJob(const QString& type, const Omittable& identifier = none, const QString& password = {}, const QString& token = {}, const QString& deviceId = {}, const QString& initialDeviceDisplayName = {}, const QString& user = {}, const QString& medium = {}, const QString& address = {}); - ~LoginJob() override; - - // Result properties - - /// The fully-qualified Matrix ID that has been registered. - const QString& userId() const; - /// An access token for the account. - /// This access token can then be used to authorize other requests. - const QString& accessToken() const; - /// The server_name of the homeserver on which the account has - /// been registered. - /// - /// **Deprecated**. Clients should extract the server_name from - /// ``user_id`` (by splitting at the first colon) if they require - /// it. Note also that ``homeserver`` is not spelt this way. - const QString& homeServer() const; - /// ID of the logged-in device. Will be the same as the - /// corresponding parameter in the request, if one was specified. - const QString& deviceId() const; - /// Optional client configuration provided by the server. If present, - /// clients SHOULD use the provided object to reconfigure themselves, - /// optionally validating the URLs within. This object takes the same - /// form as the one returned from .well-known autodiscovery. - const Omittable& wellKnown() const; + /*! Authenticates the user. + * \param type + * The login type being used. + * \param identifier + * Identification information for the user. + * \param password + * Required when ``type`` is ``m.login.password``. The user's + * password. + * \param token + * Required when ``type`` is ``m.login.token``. Part of `Token-based`_ + * login. \param deviceId ID of the client device. If this does not + * correspond to a known client device, a new device will be created. + * The server will auto-generate a device_id if this is not specified. + * \param initialDeviceDisplayName + * A display name to assign to the newly-created device. Ignored + * if ``device_id`` corresponds to a known device. + * \param user + * The fully qualified user ID or just local part of the user ID, to + * log in. Deprecated in favour of ``identifier``. \param medium When + * logging in using a third party identifier, the medium of the + * identifier. Must be 'email'. Deprecated in favour of ``identifier``. + * \param address + * Third party identifier for the user. Deprecated in favour of + * ``identifier``. + */ + explicit LoginJob(const QString& type, + const Omittable& identifier = none, + const QString& password = {}, + const QString& token = {}, + const QString& deviceId = {}, + const QString& initialDeviceDisplayName = {}, + const QString& user = {}, const QString& medium = {}, + const QString& address = {}); + ~LoginJob() override; + + // Result properties + + /// The fully-qualified Matrix ID that has been registered. + const QString& userId() const; + /// An access token for the account. + /// This access token can then be used to authorize other requests. + const QString& accessToken() const; + /// The server_name of the homeserver on which the account has + /// been registered. + /// + /// **Deprecated**. Clients should extract the server_name from + /// ``user_id`` (by splitting at the first colon) if they require + /// it. Note also that ``homeserver`` is not spelt this way. + const QString& homeServer() const; + /// ID of the logged-in device. Will be the same as the + /// corresponding parameter in the request, if one was specified. + const QString& deviceId() const; + /// Optional client configuration provided by the server. If present, + /// clients SHOULD use the provided object to reconfigure themselves, + /// optionally validating the URLs within. This object takes the same + /// form as the one returned from .well-known autodiscovery. + const Omittable& wellKnown() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/logout.cpp b/lib/csapi/logout.cpp index 6e209e07..f2d2e130 100644 --- a/lib/csapi/logout.cpp +++ b/lib/csapi/logout.cpp @@ -14,29 +14,25 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); QUrl LogoutJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/logout"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/logout"); } static const auto LogoutJobName = QStringLiteral("LogoutJob"); LogoutJob::LogoutJob() - : BaseJob(HttpVerb::Post, LogoutJobName, - basePath % "/logout") + : BaseJob(HttpVerb::Post, LogoutJobName, basePath % "/logout") { } QUrl LogoutAllJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/logout/all"); + basePath % "/logout/all"); } static const auto LogoutAllJobName = QStringLiteral("LogoutAllJob"); LogoutAllJob::LogoutAllJob() - : BaseJob(HttpVerb::Post, LogoutAllJobName, - basePath % "/logout/all") + : BaseJob(HttpVerb::Post, LogoutAllJobName, basePath % "/logout/all") { } - diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h index 3ef3c656..4bbb8526 100644 --- a/lib/csapi/logout.h +++ b/lib/csapi/logout.h @@ -6,52 +6,50 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Invalidates a user access token /// - /// Invalidates an existing access token, so that it can no longer be used for - /// authorization. + /// Invalidates an existing access token, so that it can no longer be used + /// for authorization. class LogoutJob : public BaseJob { public: - explicit LogoutJob(); - - /*! Construct a URL without creating a full-fledged job object - * - * 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(); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * LogoutJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); }; /// Invalidates all access tokens for a user /// - /// Invalidates all access tokens for a user, so that they can no longer be used for - /// authorization. This includes the access token that made this request. - /// - /// This endpoint does not require UI authorization because UI authorization is - /// designed to protect against attacks where the someone gets hold of a single access - /// token then takes over the account. This endpoint invalidates all access tokens for - /// the user, including the token used in the request, and therefore the attacker is - /// unable to take over the account in this way. + /// Invalidates all access tokens for a user, so that they can no longer be + /// used for authorization. This includes the access token that made this + /// request. + /// + /// This endpoint does not require UI authorization because UI authorization + /// is designed to protect against attacks where the someone gets hold of a + /// single access token then takes over the account. This endpoint + /// invalidates all access tokens for the user, including the token used in + /// the request, and therefore the attacker is unable to take over the + /// account in this way. class LogoutAllJob : public BaseJob { public: - explicit LogoutAllJob(); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * LogoutAllJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); - + explicit LogoutAllJob(); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * LogoutAllJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); }; } // namespace QMatrixClient diff --git a/lib/csapi/message_pagination.cpp b/lib/csapi/message_pagination.cpp index 9aca7ec9..272c1c20 100644 --- a/lib/csapi/message_pagination.cpp +++ b/lib/csapi/message_pagination.cpp @@ -15,12 +15,14 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetRoomEventsJob::Private { public: - QString begin; - QString end; - RoomEvents chunk; + QString begin; + QString end; + RoomEvents chunk; }; -BaseJob::Query queryToGetRoomEvents(const QString& from, const QString& to, const QString& dir, Omittable limit, const QString& filter) +BaseJob::Query queryToGetRoomEvents(const QString& from, const QString& to, + const QString& dir, Omittable limit, + const QString& filter) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("from"), from); @@ -31,39 +33,35 @@ BaseJob::Query queryToGetRoomEvents(const QString& from, const QString& to, cons return _q; } -QUrl GetRoomEventsJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& from, const QString& dir, const QString& to, Omittable limit, const QString& filter) +QUrl GetRoomEventsJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& from, const QString& dir, + const QString& to, Omittable limit, + const QString& filter) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/messages", + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/rooms/" % roomId % "/messages", queryToGetRoomEvents(from, to, dir, limit, filter)); } static const auto GetRoomEventsJobName = QStringLiteral("GetRoomEventsJob"); -GetRoomEventsJob::GetRoomEventsJob(const QString& roomId, const QString& from, const QString& dir, const QString& to, Omittable limit, const QString& filter) +GetRoomEventsJob::GetRoomEventsJob(const QString& roomId, const QString& from, + const QString& dir, const QString& to, + Omittable limit, const QString& filter) : BaseJob(HttpVerb::Get, GetRoomEventsJobName, - basePath % "/rooms/" % roomId % "/messages", - queryToGetRoomEvents(from, to, dir, limit, filter)) - , d(new Private) + basePath % "/rooms/" % roomId % "/messages", + queryToGetRoomEvents(from, to, dir, limit, filter)), + d(new Private) { } GetRoomEventsJob::~GetRoomEventsJob() = default; -const QString& GetRoomEventsJob::begin() const -{ - return d->begin; -} +const QString& GetRoomEventsJob::begin() const { return d->begin; } -const QString& GetRoomEventsJob::end() const -{ - return d->end; -} +const QString& GetRoomEventsJob::end() const { return d->end; } -RoomEvents&& GetRoomEventsJob::chunk() -{ - return std::move(d->chunk); -} +RoomEvents&& GetRoomEventsJob::chunk() { return std::move(d->chunk); } BaseJob::Status GetRoomEventsJob::parseJson(const QJsonDocument& data) { @@ -73,4 +71,3 @@ BaseJob::Status GetRoomEventsJob::parseJson(const QJsonDocument& data) fromJson(json.value("chunk"_ls), d->chunk); return Success; } - diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index 12544f0c..8ce1962e 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include "events/eventloader.h" #include "converters.h" +#include "events/eventloader.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Get a list of events for this room @@ -20,54 +19,56 @@ namespace QMatrixClient class GetRoomEventsJob : public BaseJob { public: - /*! Get a list of events for this room - * \param roomId - * The room to get events from. - * \param from - * The token to start returning events from. This token can be obtained - * from a ``prev_batch`` token returned for each room by the sync API, - * or from a ``start`` or ``end`` token returned by a previous request - * to this endpoint. - * \param dir - * The direction to return events from. - * \param to - * The token to stop returning events at. This token can be obtained from - * a ``prev_batch`` token returned for each room by the sync endpoint, - * or from a ``start`` or ``end`` token returned by a previous request to - * this endpoint. - * \param limit - * The maximum number of events to return. Default: 10. - * \param filter - * A JSON RoomEventFilter to filter returned events with. - */ - explicit GetRoomEventsJob(const QString& roomId, const QString& from, const QString& dir, const QString& to = {}, Omittable limit = none, const QString& filter = {}); + /*! Get a list of events for this room + * \param roomId + * The room to get events from. + * \param from + * The token to start returning events from. This token can be + * obtained from a ``prev_batch`` token returned for each room by the + * sync API, or from a ``start`` or ``end`` token returned by a previous + * request to this endpoint. \param dir The direction to return events + * from. \param to The token to stop returning events at. This token can + * be obtained from a ``prev_batch`` token returned for each room by the + * sync endpoint, or from a ``start`` or ``end`` token returned by a + * previous request to this endpoint. \param limit The maximum number of + * events to return. Default: 10. \param filter A JSON RoomEventFilter + * to filter returned events with. + */ + explicit GetRoomEventsJob(const QString& roomId, const QString& from, + const QString& dir, const QString& to = {}, + Omittable limit = none, + const QString& filter = {}); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomEventsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& from, const QString& dir, const QString& to = {}, Omittable limit = none, const QString& filter = {}); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomEventsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& from, const QString& dir, + const QString& to = {}, + Omittable limit = none, + const QString& filter = {}); - ~GetRoomEventsJob() override; + ~GetRoomEventsJob() override; - // Result properties + // Result properties - /// The token the pagination starts from. If ``dir=b`` this will be - /// the token supplied in ``from``. - const QString& begin() const; - /// The token the pagination ends at. If ``dir=b`` this token should - /// be used again to request even earlier events. - const QString& end() const; - /// A list of room events. - RoomEvents&& chunk(); + /// The token the pagination starts from. If ``dir=b`` this will be + /// the token supplied in ``from``. + const QString& begin() const; + /// The token the pagination ends at. If ``dir=b`` this token should + /// be used again to request even earlier events. + const QString& end() const; + /// A list of room events. + RoomEvents&& chunk(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index c00b7cb0..b7f252ac 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetNotificationsJob::Notification& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetNotificationsJob::Notification& result) { fromJson(jo.value("actions"_ls), result.actions); fromJson(jo.value("event"_ls), result.event); @@ -33,11 +32,13 @@ namespace QMatrixClient class GetNotificationsJob::Private { public: - QString nextToken; - std::vector notifications; + QString nextToken; + std::vector notifications; }; -BaseJob::Query queryToGetNotifications(const QString& from, Omittable limit, const QString& only) +BaseJob::Query queryToGetNotifications(const QString& from, + Omittable limit, + const QString& only) { BaseJob::Query _q; addParam(_q, QStringLiteral("from"), from); @@ -46,31 +47,34 @@ BaseJob::Query queryToGetNotifications(const QString& from, Omittable limit return _q; } -QUrl GetNotificationsJob::makeRequestUrl(QUrl baseUrl, const QString& from, Omittable limit, const QString& only) +QUrl GetNotificationsJob::makeRequestUrl(QUrl baseUrl, const QString& from, + Omittable limit, + const QString& only) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/notifications", - queryToGetNotifications(from, limit, only)); + basePath % "/notifications", + queryToGetNotifications(from, limit, only)); } -static const auto GetNotificationsJobName = QStringLiteral("GetNotificationsJob"); +static const auto GetNotificationsJobName = + QStringLiteral("GetNotificationsJob"); -GetNotificationsJob::GetNotificationsJob(const QString& from, Omittable limit, const QString& only) +GetNotificationsJob::GetNotificationsJob(const QString& from, + Omittable limit, + const QString& only) : BaseJob(HttpVerb::Get, GetNotificationsJobName, - basePath % "/notifications", - queryToGetNotifications(from, limit, only)) - , d(new Private) + basePath % "/notifications", + queryToGetNotifications(from, limit, only)), + d(new Private) { } GetNotificationsJob::~GetNotificationsJob() = default; -const QString& GetNotificationsJob::nextToken() const -{ - return d->nextToken; -} +const QString& GetNotificationsJob::nextToken() const { return d->nextToken; } -std::vector&& GetNotificationsJob::notifications() +std::vector&& +GetNotificationsJob::notifications() { return std::move(d->notifications); } @@ -81,8 +85,7 @@ BaseJob::Status GetNotificationsJob::parseJson(const QJsonDocument& data) fromJson(json.value("next_token"_ls), d->nextToken); if (!json.contains("notifications"_ls)) return { JsonParseError, - "The key 'notifications' not found in the response" }; + "The key 'notifications' not found in the response" }; fromJson(json.value("notifications"_ls), d->notifications); return Success; } - diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index 898b5154..49bc33e9 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -6,14 +6,13 @@ #include "jobs/basejob.h" -#include "events/eventloader.h" #include "converters.h" -#include -#include +#include "events/eventloader.h" #include +#include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets a list of events that the user has been notified about @@ -23,67 +22,70 @@ namespace QMatrixClient class GetNotificationsJob : public BaseJob { public: - // Inner data structures + // Inner data structures - /// This API is used to paginate through the list of events that the - /// user has been, or would have been notified about. - struct Notification - { - /// The action(s) to perform when the conditions for this rule are met. - /// See `Push Rules: API`_. - QVector actions; - /// The Event object for the event that triggered the notification. - EventPtr event; - /// The profile tag of the rule that matched this event. - QString profileTag; - /// Indicates whether the user has sent a read receipt indicating - /// that they have read this message. - bool read; - /// The ID of the room in which the event was posted. - QString roomId; - /// The unix timestamp at which the event notification was sent, - /// in milliseconds. - int ts; - }; + /// This API is used to paginate through the list of events that the + /// user has been, or would have been notified about. + struct Notification { + /// The action(s) to perform when the conditions for this rule are + /// met. See `Push Rules: API`_. + QVector actions; + /// The Event object for the event that triggered the notification. + EventPtr event; + /// The profile tag of the rule that matched this event. + QString profileTag; + /// Indicates whether the user has sent a read receipt indicating + /// that they have read this message. + bool read; + /// The ID of the room in which the event was posted. + QString roomId; + /// The unix timestamp at which the event notification was sent, + /// in milliseconds. + int ts; + }; - // Construction/destruction + // Construction/destruction - /*! Gets a list of events that the user has been notified about - * \param from - * Pagination token given to retrieve the next set of events. - * \param limit - * Limit on the number of events to return in this request. - * \param only - * Allows basic filtering of events returned. Supply ``highlight`` - * to return only events where the notification had the highlight - * tweak set. - */ - explicit GetNotificationsJob(const QString& from = {}, Omittable limit = none, const QString& only = {}); + /*! Gets a list of events that the user has been notified about + * \param from + * Pagination token given to retrieve the next set of events. + * \param limit + * Limit on the number of events to return in this request. + * \param only + * Allows basic filtering of events returned. Supply ``highlight`` + * to return only events where the notification had the highlight + * tweak set. + */ + explicit GetNotificationsJob(const QString& from = {}, + Omittable limit = none, + const QString& only = {}); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetNotificationsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable limit = none, const QString& only = {}); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetNotificationsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, + Omittable limit = none, + const QString& only = {}); - ~GetNotificationsJob() override; + ~GetNotificationsJob() override; - // Result properties + // Result properties - /// The token to supply in the ``from`` param of the next - /// ``/notifications`` request in order to request more - /// events. If this is absent, there are no more results. - const QString& nextToken() const; - /// The list of events that triggered notifications. - std::vector&& notifications(); + /// The token to supply in the ``from`` param of the next + /// ``/notifications`` request in order to request more + /// events. If this is absent, there are no more results. + const QString& nextToken() const; + /// The list of events that triggered notifications. + std::vector&& notifications(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index b27fe0b8..82a3b055 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -15,18 +15,20 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class RequestOpenIdTokenJob::Private { public: - QString accessToken; - QString tokenType; - QString matrixServerName; - int expiresIn; + QString accessToken; + QString tokenType; + QString matrixServerName; + int expiresIn; }; -static const auto RequestOpenIdTokenJobName = QStringLiteral("RequestOpenIdTokenJob"); +static const auto RequestOpenIdTokenJobName = + QStringLiteral("RequestOpenIdTokenJob"); -RequestOpenIdTokenJob::RequestOpenIdTokenJob(const QString& userId, const QJsonObject& body) +RequestOpenIdTokenJob::RequestOpenIdTokenJob(const QString& userId, + const QJsonObject& body) : BaseJob(HttpVerb::Post, RequestOpenIdTokenJobName, - basePath % "/user/" % userId % "/openid/request_token") - , d(new Private) + basePath % "/user/" % userId % "/openid/request_token"), + d(new Private) { setRequestData(Data(toJson(body))); } @@ -38,40 +40,33 @@ const QString& RequestOpenIdTokenJob::accessToken() const return d->accessToken; } -const QString& RequestOpenIdTokenJob::tokenType() const -{ - return d->tokenType; -} +const QString& RequestOpenIdTokenJob::tokenType() const { return d->tokenType; } const QString& RequestOpenIdTokenJob::matrixServerName() const { return d->matrixServerName; } -int RequestOpenIdTokenJob::expiresIn() const -{ - return d->expiresIn; -} +int RequestOpenIdTokenJob::expiresIn() const { return d->expiresIn; } BaseJob::Status RequestOpenIdTokenJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("access_token"_ls)) return { JsonParseError, - "The key 'access_token' not found in the response" }; + "The key 'access_token' not found in the response" }; fromJson(json.value("access_token"_ls), d->accessToken); if (!json.contains("token_type"_ls)) return { JsonParseError, - "The key 'token_type' not found in the response" }; + "The key 'token_type' not found in the response" }; fromJson(json.value("token_type"_ls), d->tokenType); if (!json.contains("matrix_server_name"_ls)) return { JsonParseError, - "The key 'matrix_server_name' not found in the response" }; + "The key 'matrix_server_name' not found in the response" }; fromJson(json.value("matrix_server_name"_ls), d->matrixServerName); if (!json.contains("expires_in"_ls)) return { JsonParseError, - "The key 'expires_in' not found in the response" }; + "The key 'expires_in' not found in the response" }; fromJson(json.value("expires_in"_ls), d->expiresIn); return Success; } - diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h index 807801fb..6e3c744d 100644 --- a/lib/csapi/openid.h +++ b/lib/csapi/openid.h @@ -9,8 +9,7 @@ #include "converters.h" #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Get an OpenID token object to verify the requester's identity. @@ -19,43 +18,44 @@ namespace QMatrixClient /// service to verify their identity in Matrix. The generated token is only /// valid for exchanging for user information from the federation API for /// OpenID. - /// + /// /// The access token generated is only valid for the OpenID API. It cannot /// be used to request another OpenID access token or call ``/sync``, for /// example. class RequestOpenIdTokenJob : public BaseJob { public: - /*! Get an OpenID token object to verify the requester's identity. - * \param userId - * The user to request and OpenID token for. Should be the user who - * is authenticated for the request. - * \param body - * An empty object. Reserved for future expansion. - */ - explicit RequestOpenIdTokenJob(const QString& userId, const QJsonObject& body = {}); - ~RequestOpenIdTokenJob() override; - - // Result properties - - /// An access token the consumer may use to verify the identity of - /// the person who generated the token. This is given to the federation - /// API ``GET /openid/userinfo``. - const QString& accessToken() const; - /// The string ``Bearer``. - const QString& tokenType() const; - /// The homeserver domain the consumer should use when attempting to - /// verify the user's identity. - const QString& matrixServerName() const; - /// The number of seconds before this token expires and a new one must - /// be generated. - int expiresIn() const; + /*! Get an OpenID token object to verify the requester's identity. + * \param userId + * The user to request and OpenID token for. Should be the user who + * is authenticated for the request. + * \param body + * An empty object. Reserved for future expansion. + */ + explicit RequestOpenIdTokenJob(const QString& userId, + const QJsonObject& body = {}); + ~RequestOpenIdTokenJob() override; + + // Result properties + + /// An access token the consumer may use to verify the identity of + /// the person who generated the token. This is given to the federation + /// API ``GET /openid/userinfo``. + const QString& accessToken() const; + /// The string ``Bearer``. + const QString& tokenType() const; + /// The homeserver domain the consumer should use when attempting to + /// verify the user's identity. + const QString& matrixServerName() const; + /// The number of seconds before this token expires and a new one must + /// be generated. + int expiresIn() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/peeking_events.cpp b/lib/csapi/peeking_events.cpp index 3208d48d..d48eca3f 100644 --- a/lib/csapi/peeking_events.cpp +++ b/lib/csapi/peeking_events.cpp @@ -15,12 +15,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class PeekEventsJob::Private { public: - QString begin; - QString end; - RoomEvents chunk; + QString begin; + QString end; + RoomEvents chunk; }; -BaseJob::Query queryToPeekEvents(const QString& from, Omittable timeout, const QString& roomId) +BaseJob::Query queryToPeekEvents(const QString& from, Omittable timeout, + const QString& roomId) { BaseJob::Query _q; addParam(_q, QStringLiteral("from"), from); @@ -29,39 +30,31 @@ BaseJob::Query queryToPeekEvents(const QString& from, Omittable timeout, co return _q; } -QUrl PeekEventsJob::makeRequestUrl(QUrl baseUrl, const QString& from, Omittable timeout, const QString& roomId) +QUrl PeekEventsJob::makeRequestUrl(QUrl baseUrl, const QString& from, + Omittable timeout, + const QString& roomId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/events", - queryToPeekEvents(from, timeout, roomId)); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/events", + queryToPeekEvents(from, timeout, roomId)); } static const auto PeekEventsJobName = QStringLiteral("PeekEventsJob"); -PeekEventsJob::PeekEventsJob(const QString& from, Omittable timeout, const QString& roomId) - : BaseJob(HttpVerb::Get, PeekEventsJobName, - basePath % "/events", - queryToPeekEvents(from, timeout, roomId)) - , d(new Private) +PeekEventsJob::PeekEventsJob(const QString& from, Omittable timeout, + const QString& roomId) + : BaseJob(HttpVerb::Get, PeekEventsJobName, basePath % "/events", + queryToPeekEvents(from, timeout, roomId)), + d(new Private) { } PeekEventsJob::~PeekEventsJob() = default; -const QString& PeekEventsJob::begin() const -{ - return d->begin; -} +const QString& PeekEventsJob::begin() const { return d->begin; } -const QString& PeekEventsJob::end() const -{ - return d->end; -} +const QString& PeekEventsJob::end() const { return d->end; } -RoomEvents&& PeekEventsJob::chunk() -{ - return std::move(d->chunk); -} +RoomEvents&& PeekEventsJob::chunk() { return std::move(d->chunk); } BaseJob::Status PeekEventsJob::parseJson(const QJsonDocument& data) { @@ -71,4 +64,3 @@ BaseJob::Status PeekEventsJob::parseJson(const QJsonDocument& data) fromJson(json.value("chunk"_ls), d->chunk); return Success; } - diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index 5a6e513c..00552af0 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include "events/eventloader.h" #include "converters.h" +#include "events/eventloader.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Listen on the event stream. @@ -18,53 +17,57 @@ namespace QMatrixClient /// This will listen for new events related to a particular room and return /// them to the caller. This will block until an event is received, or until /// the ``timeout`` is reached. - /// + /// /// This API is the same as the normal ``/events`` endpoint, but can be /// called by users who have not joined the room. - /// + /// /// Note that the normal ``/events`` endpoint has been deprecated. This /// API will also be deprecated at some point, but its replacement is not /// yet known. class PeekEventsJob : public BaseJob { public: - /*! Listen on the event stream. - * \param from - * The token to stream from. This token is either from a previous - * request to this API or from the initial sync API. - * \param timeout - * The maximum time in milliseconds to wait for an event. - * \param roomId - * The room ID for which events should be returned. - */ - explicit PeekEventsJob(const QString& from = {}, Omittable timeout = none, const QString& roomId = {}); + /*! Listen on the event stream. + * \param from + * The token to stream from. This token is either from a previous + * request to this API or from the initial sync API. + * \param timeout + * The maximum time in milliseconds to wait for an event. + * \param roomId + * The room ID for which events should be returned. + */ + explicit PeekEventsJob(const QString& from = {}, + Omittable timeout = none, + const QString& roomId = {}); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * PeekEventsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable timeout = none, const QString& roomId = {}); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * PeekEventsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, + Omittable timeout = none, + const QString& roomId = {}); - ~PeekEventsJob() override; + ~PeekEventsJob() override; - // Result properties + // Result properties - /// A token which correlates to the first value in ``chunk``. This - /// is usually the same token supplied to ``from=``. - const QString& begin() const; - /// A token which correlates to the last value in ``chunk``. This - /// token should be used in the next request to ``/events``. - const QString& end() const; - /// An array of events. - RoomEvents&& chunk(); + /// A token which correlates to the first value in ``chunk``. This + /// is usually the same token supplied to ``from=``. + const QString& begin() const; + /// A token which correlates to the last value in ``chunk``. This + /// token should be used in the next request to ``/events``. + const QString& end() const; + /// An array of events. + RoomEvents&& chunk(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 024d7a34..4f9b9c78 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -14,9 +14,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SetPresenceJobName = QStringLiteral("SetPresenceJob"); -SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, const QString& statusMsg) +SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, + const QString& statusMsg) : BaseJob(HttpVerb::Put, SetPresenceJobName, - basePath % "/presence/" % userId % "/status") + basePath % "/presence/" % userId % "/status") { QJsonObject _data; addParam<>(_data, QStringLiteral("presence"), presence); @@ -27,43 +28,37 @@ SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, c class GetPresenceJob::Private { public: - QString presence; - Omittable lastActiveAgo; - QString statusMsg; - Omittable currentlyActive; + QString presence; + Omittable lastActiveAgo; + QString statusMsg; + Omittable currentlyActive; }; QUrl GetPresenceJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/presence/" % userId % "/status"); + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/presence/" % userId % "/status"); } static const auto GetPresenceJobName = QStringLiteral("GetPresenceJob"); GetPresenceJob::GetPresenceJob(const QString& userId) : BaseJob(HttpVerb::Get, GetPresenceJobName, - basePath % "/presence/" % userId % "/status") - , d(new Private) + basePath % "/presence/" % userId % "/status"), + d(new Private) { } GetPresenceJob::~GetPresenceJob() = default; -const QString& GetPresenceJob::presence() const -{ - return d->presence; -} +const QString& GetPresenceJob::presence() const { return d->presence; } Omittable GetPresenceJob::lastActiveAgo() const { return d->lastActiveAgo; } -const QString& GetPresenceJob::statusMsg() const -{ - return d->statusMsg; -} +const QString& GetPresenceJob::statusMsg() const { return d->statusMsg; } Omittable GetPresenceJob::currentlyActive() const { @@ -75,11 +70,10 @@ BaseJob::Status GetPresenceJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("presence"_ls)) return { JsonParseError, - "The key 'presence' not found in the response" }; + "The key 'presence' not found in the response" }; fromJson(json.value("presence"_ls), d->presence); fromJson(json.value("last_active_ago"_ls), d->lastActiveAgo); fromJson(json.value("status_msg"_ls), d->statusMsg); fromJson(json.value("currently_active"_ls), d->currentlyActive); return Success; } - diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index 5e132d24..881d9d5e 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -8,8 +8,7 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Update this user's presence state. @@ -21,15 +20,16 @@ namespace QMatrixClient class SetPresenceJob : public BaseJob { public: - /*! Update this user's presence state. - * \param userId - * The user whose presence state to update. - * \param presence - * The new presence state. - * \param statusMsg - * The status message to attach to this state. - */ - explicit SetPresenceJob(const QString& userId, const QString& presence, const QString& statusMsg = {}); + /*! Update this user's presence state. + * \param userId + * The user whose presence state to update. + * \param presence + * The new presence state. + * \param statusMsg + * The status message to attach to this state. + */ + explicit SetPresenceJob(const QString& userId, const QString& presence, + const QString& statusMsg = {}); }; /// Get this user's presence state. @@ -38,39 +38,39 @@ namespace QMatrixClient class GetPresenceJob : public BaseJob { public: - /*! Get this user's presence state. - * \param userId - * The user whose presence state to get. - */ - explicit GetPresenceJob(const QString& userId); + /*! Get this user's presence state. + * \param userId + * The user whose presence state to get. + */ + explicit GetPresenceJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPresenceJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPresenceJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetPresenceJob() override; + ~GetPresenceJob() override; - // Result properties + // Result properties - /// This user's presence. - const QString& presence() const; - /// The length of time in milliseconds since an action was performed - /// by this user. - Omittable lastActiveAgo() const; - /// The state message for this user if one was set. - const QString& statusMsg() const; - /// Whether the user is currently active - Omittable currentlyActive() const; + /// This user's presence. + const QString& presence() const; + /// The length of time in milliseconds since an action was performed + /// by this user. + Omittable lastActiveAgo() const; + /// The state message for this user if one was set. + const QString& statusMsg() const; + /// Whether the user is currently active + Omittable currentlyActive() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index 4ed3ad9b..fefc18f2 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -14,9 +14,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SetDisplayNameJobName = QStringLiteral("SetDisplayNameJob"); -SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displayname) +SetDisplayNameJob::SetDisplayNameJob(const QString& userId, + const QString& displayname) : BaseJob(HttpVerb::Put, SetDisplayNameJobName, - basePath % "/profile/" % userId % "/displayname") + basePath % "/profile/" % userId % "/displayname") { QJsonObject _data; addParam(_data, QStringLiteral("displayname"), displayname); @@ -26,30 +27,28 @@ SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displ class GetDisplayNameJob::Private { public: - QString displayname; + QString displayname; }; QUrl GetDisplayNameJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/profile/" % userId % "/displayname"); + basePath % "/profile/" % userId + % "/displayname"); } static const auto GetDisplayNameJobName = QStringLiteral("GetDisplayNameJob"); GetDisplayNameJob::GetDisplayNameJob(const QString& userId) : BaseJob(HttpVerb::Get, GetDisplayNameJobName, - basePath % "/profile/" % userId % "/displayname", false) - , d(new Private) + basePath % "/profile/" % userId % "/displayname", false), + d(new Private) { } GetDisplayNameJob::~GetDisplayNameJob() = default; -const QString& GetDisplayNameJob::displayname() const -{ - return d->displayname; -} +const QString& GetDisplayNameJob::displayname() const { return d->displayname; } BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data) { @@ -60,9 +59,10 @@ BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data) static const auto SetAvatarUrlJobName = QStringLiteral("SetAvatarUrlJob"); -SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl) +SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, + const QString& avatarUrl) : BaseJob(HttpVerb::Put, SetAvatarUrlJobName, - basePath % "/profile/" % userId % "/avatar_url") + basePath % "/profile/" % userId % "/avatar_url") { QJsonObject _data; addParam(_data, QStringLiteral("avatar_url"), avatarUrl); @@ -72,30 +72,28 @@ SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl class GetAvatarUrlJob::Private { public: - QString avatarUrl; + QString avatarUrl; }; QUrl GetAvatarUrlJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/profile/" % userId % "/avatar_url"); + basePath % "/profile/" % userId + % "/avatar_url"); } static const auto GetAvatarUrlJobName = QStringLiteral("GetAvatarUrlJob"); GetAvatarUrlJob::GetAvatarUrlJob(const QString& userId) : BaseJob(HttpVerb::Get, GetAvatarUrlJobName, - basePath % "/profile/" % userId % "/avatar_url", false) - , d(new Private) + basePath % "/profile/" % userId % "/avatar_url", false), + d(new Private) { } GetAvatarUrlJob::~GetAvatarUrlJob() = default; -const QString& GetAvatarUrlJob::avatarUrl() const -{ - return d->avatarUrl; -} +const QString& GetAvatarUrlJob::avatarUrl() const { return d->avatarUrl; } BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data) { @@ -107,36 +105,30 @@ BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data) class GetUserProfileJob::Private { public: - QString avatarUrl; - QString displayname; + QString avatarUrl; + QString displayname; }; QUrl GetUserProfileJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/profile/" % userId); + basePath % "/profile/" % userId); } static const auto GetUserProfileJobName = QStringLiteral("GetUserProfileJob"); GetUserProfileJob::GetUserProfileJob(const QString& userId) : BaseJob(HttpVerb::Get, GetUserProfileJobName, - basePath % "/profile/" % userId, false) - , d(new Private) + basePath % "/profile/" % userId, false), + d(new Private) { } GetUserProfileJob::~GetUserProfileJob() = default; -const QString& GetUserProfileJob::avatarUrl() const -{ - return d->avatarUrl; -} +const QString& GetUserProfileJob::avatarUrl() const { return d->avatarUrl; } -const QString& GetUserProfileJob::displayname() const -{ - return d->displayname; -} +const QString& GetUserProfileJob::displayname() const { return d->displayname; } BaseJob::Status GetUserProfileJob::parseJson(const QJsonDocument& data) { @@ -145,4 +137,3 @@ BaseJob::Status GetUserProfileJob::parseJson(const QJsonDocument& data) fromJson(json.value("displayname"_ls), d->displayname); return Success; } - diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 23094aff..9a6ab56b 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -6,25 +6,25 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Set the user's display name. /// /// This API sets the given user's display name. You must have permission to - /// set this user's display name, e.g. you need to have their ``access_token``. + /// set this user's display name, e.g. you need to have their + /// ``access_token``. class SetDisplayNameJob : public BaseJob { public: - /*! Set the user's display name. - * \param userId - * The user whose display name to set. - * \param displayname - * The new display name for this user. - */ - explicit SetDisplayNameJob(const QString& userId, const QString& displayname = {}); + /*! Set the user's display name. + * \param userId + * The user whose display name to set. + * \param displayname + * The new display name for this user. + */ + explicit SetDisplayNameJob(const QString& userId, + const QString& displayname = {}); }; /// Get the user's display name. @@ -35,49 +35,51 @@ namespace QMatrixClient class GetDisplayNameJob : public BaseJob { public: - /*! Get the user's display name. - * \param userId - * The user whose display name to get. - */ - explicit GetDisplayNameJob(const QString& userId); + /*! Get the user's display name. + * \param userId + * The user whose display name to get. + */ + explicit GetDisplayNameJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Construct a URL without creating a full-fledged job object + * + * 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); - ~GetDisplayNameJob() override; + ~GetDisplayNameJob() override; - // Result properties + // Result properties - /// The user's display name if they have set one, otherwise not present. - const QString& displayname() const; + /// The user's display name if they have set one, otherwise not present. + const QString& displayname() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Set the user's avatar URL. /// /// This API sets the given user's avatar URL. You must have permission to - /// set this user's avatar URL, e.g. you need to have their ``access_token``. + /// set this user's avatar URL, e.g. you need to have their + /// ``access_token``. class SetAvatarUrlJob : public BaseJob { public: - /*! Set the user's avatar URL. - * \param userId - * The user whose avatar URL to set. - * \param avatarUrl - * The new avatar URL for this user. - */ - explicit SetAvatarUrlJob(const QString& userId, const QString& avatarUrl = {}); + /*! Set the user's avatar URL. + * \param userId + * The user whose avatar URL to set. + * \param avatarUrl + * The new avatar URL for this user. + */ + explicit SetAvatarUrlJob(const QString& userId, + const QString& avatarUrl = {}); }; /// Get the user's avatar URL. @@ -88,33 +90,33 @@ namespace QMatrixClient class GetAvatarUrlJob : public BaseJob { public: - /*! Get the user's avatar URL. - * \param userId - * The user whose avatar URL to get. - */ - explicit GetAvatarUrlJob(const QString& userId); + /*! Get the user's avatar URL. + * \param userId + * The user whose avatar URL to get. + */ + explicit GetAvatarUrlJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Construct a URL without creating a full-fledged job object + * + * 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); - ~GetAvatarUrlJob() override; + ~GetAvatarUrlJob() override; - // Result properties + // Result properties - /// The user's avatar URL if they have set one, otherwise not present. - const QString& avatarUrl() const; + /// The user's avatar URL if they have set one, otherwise not present. + const QString& avatarUrl() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get this user's profile information. @@ -126,34 +128,34 @@ namespace QMatrixClient class GetUserProfileJob : public BaseJob { public: - /*! Get this user's profile information. - * \param userId - * The user whose profile information to get. - */ - explicit GetUserProfileJob(const QString& userId); + /*! Get this user's profile information. + * \param userId + * The user whose profile information to get. + */ + explicit GetUserProfileJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object - * - * 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); + /*! Construct a URL without creating a full-fledged job object + * + * 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); - ~GetUserProfileJob() override; + ~GetUserProfileJob() override; - // Result properties + // Result properties - /// The user's avatar URL if they have set one, otherwise not present. - const QString& avatarUrl() const; - /// The user's display name if they have set one, otherwise not present. - const QString& displayname() const; + /// The user's avatar URL if they have set one, otherwise not present. + const QString& avatarUrl() const; + /// The user's display name if they have set one, otherwise not present. + const QString& displayname() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index 664959f4..86b36c6d 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -12,28 +12,28 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetPushersJob::PusherData& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetPushersJob::PusherData& result) { fromJson(jo.value("url"_ls), result.url); fromJson(jo.value("format"_ls), result.format); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetPushersJob::Pusher& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetPushersJob::Pusher& result) { fromJson(jo.value("pushkey"_ls), result.pushkey); fromJson(jo.value("kind"_ls), result.kind); fromJson(jo.value("app_id"_ls), result.appId); fromJson(jo.value("app_display_name"_ls), result.appDisplayName); - fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); + fromJson(jo.value("device_display_name"_ls), + result.deviceDisplayName); fromJson(jo.value("profile_tag"_ls), result.profileTag); fromJson(jo.value("lang"_ls), result.lang); fromJson(jo.value("data"_ls), result.data); @@ -44,21 +44,19 @@ namespace QMatrixClient class GetPushersJob::Private { public: - QVector pushers; + QVector pushers; }; QUrl GetPushersJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushers"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushers"); } static const auto GetPushersJobName = QStringLiteral("GetPushersJob"); GetPushersJob::GetPushersJob() - : BaseJob(HttpVerb::Get, GetPushersJobName, - basePath % "/pushers") - , d(new Private) + : BaseJob(HttpVerb::Get, GetPushersJobName, basePath % "/pushers"), + d(new Private) { } @@ -76,13 +74,12 @@ BaseJob::Status GetPushersJob::parseJson(const QJsonDocument& data) return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const PostPusherJob::PusherData& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const PostPusherJob::PusherData& pod) { addParam(jo, QStringLiteral("url"), pod.url); addParam(jo, QStringLiteral("format"), pod.format); @@ -92,9 +89,13 @@ namespace QMatrixClient static const auto PostPusherJobName = QStringLiteral("PostPusherJob"); -PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag, Omittable append) - : BaseJob(HttpVerb::Post, PostPusherJobName, - basePath % "/pushers/set") +PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, + const QString& appId, + const QString& appDisplayName, + const QString& deviceDisplayName, + const QString& lang, const PusherData& data, + const QString& profileTag, Omittable append) + : BaseJob(HttpVerb::Post, PostPusherJobName, basePath % "/pushers/set") { QJsonObject _data; addParam<>(_data, QStringLiteral("pushkey"), pushkey); @@ -108,4 +109,3 @@ PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const addParam(_data, QStringLiteral("append"), append); setRequestData(_data); } - diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index da3303fe..947d7fc8 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include #include "converters.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets the current pushers for the authenticated user @@ -19,151 +18,154 @@ namespace QMatrixClient class GetPushersJob : public BaseJob { public: - // Inner data structures - + // Inner data structures + + /// A dictionary of information for the pusher implementation + /// itself. + struct PusherData { + /// Required if ``kind`` is ``http``. The URL to use to send + /// notifications to. + QString url; + /// The format to use when sending notifications to the Push + /// Gateway. + QString format; + }; + + /// Gets all currently active pushers for the authenticated user. + struct Pusher { + /// This is a unique identifier for this pusher. See ``/set`` for + /// more detail. + /// Max length, 512 bytes. + QString pushkey; + /// The kind of pusher. ``"http"`` is a pusher that + /// sends HTTP pokes. + QString kind; + /// This is a reverse-DNS style identifier for the application. + /// Max length, 64 chars. + QString appId; + /// A string that will allow the user to identify what application + /// owns this pusher. + QString appDisplayName; + /// A string that will allow the user to identify what device owns + /// this pusher. + QString deviceDisplayName; + /// This string determines which set of device specific rules this + /// pusher executes. + QString profileTag; + /// The preferred language for receiving notifications (e.g. 'en' + /// or 'en-US') + QString lang; /// A dictionary of information for the pusher implementation /// itself. - struct PusherData - { - /// Required if ``kind`` is ``http``. The URL to use to send - /// notifications to. - QString url; - /// The format to use when sending notifications to the Push - /// Gateway. - QString format; - }; - - /// Gets all currently active pushers for the authenticated user. - struct Pusher - { - /// This is a unique identifier for this pusher. See ``/set`` for - /// more detail. - /// Max length, 512 bytes. - QString pushkey; - /// The kind of pusher. ``"http"`` is a pusher that - /// sends HTTP pokes. - QString kind; - /// This is a reverse-DNS style identifier for the application. - /// Max length, 64 chars. - QString appId; - /// A string that will allow the user to identify what application - /// owns this pusher. - QString appDisplayName; - /// A string that will allow the user to identify what device owns - /// this pusher. - QString deviceDisplayName; - /// This string determines which set of device specific rules this - /// pusher executes. - QString profileTag; - /// The preferred language for receiving notifications (e.g. 'en' - /// or 'en-US') - QString lang; - /// A dictionary of information for the pusher implementation - /// itself. - PusherData data; - }; - - // Construction/destruction - - explicit GetPushersJob(); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPushersJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); - - ~GetPushersJob() override; - - // Result properties - - /// An array containing the current pushers for the user - const QVector& pushers() const; + PusherData data; + }; + + // Construction/destruction + + explicit GetPushersJob(); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPushersJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); + + ~GetPushersJob() override; + + // Result properties + + /// An array containing the current pushers for the user + const QVector& pushers() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Modify a pusher for this user on the homeserver. /// - /// This endpoint allows the creation, modification and deletion of `pushers`_ - /// for this user ID. The behaviour of this endpoint varies depending on the - /// values in the JSON body. + /// This endpoint allows the creation, modification and deletion of + /// `pushers`_ for this user ID. The behaviour of this endpoint varies + /// depending on the values in the JSON body. class PostPusherJob : public BaseJob { public: - // Inner data structures - - /// A dictionary of information for the pusher implementation - /// itself. If ``kind`` is ``http``, this should contain ``url`` - /// which is the URL to use to send notifications to. - struct PusherData - { - /// Required if ``kind`` is ``http``. The URL to use to send - /// notifications to. MUST be an HTTPS URL with a path of - /// ``/_matrix/push/v1/notify``. - QString url; - /// The format to send notifications in to Push Gateways if the - /// ``kind`` is ``http``. The details about what fields the - /// homeserver should send to the push gateway are defined in the - /// `Push Gateway Specification`_. Currently the only format - /// available is 'event_id_only'. - QString format; - }; - - // Construction/destruction - - /*! Modify a pusher for this user on the homeserver. - * \param pushkey - * This is a unique identifier for this pusher. The value you - * should use for this is the routing or destination address - * information for the notification, for example, the APNS token - * for APNS or the Registration ID for GCM. If your notification - * client has no such concept, use any unique identifier. - * Max length, 512 bytes. - * - * If the ``kind`` is ``"email"``, this is the email address to - * send notifications to. - * \param kind - * The kind of pusher to configure. ``"http"`` makes a pusher that - * sends HTTP pokes. ``"email"`` makes a pusher that emails the - * user with unread notifications. ``null`` deletes the pusher. - * \param appId - * This is a reverse-DNS style identifier for the application. - * It is recommended that this end with the platform, such that - * different platform versions get different app identifiers. - * Max length, 64 chars. - * - * If the ``kind`` is ``"email"``, this is ``"m.email"``. - * \param appDisplayName - * A string that will allow the user to identify what application - * owns this pusher. - * \param deviceDisplayName - * A string that will allow the user to identify what device owns - * this pusher. - * \param lang - * The preferred language for receiving notifications (e.g. 'en' - * or 'en-US'). - * \param data - * A dictionary of information for the pusher implementation - * itself. If ``kind`` is ``http``, this should contain ``url`` - * which is the URL to use to send notifications to. - * \param profileTag - * This string determines which set of device specific rules this - * pusher executes. - * \param append - * If true, the homeserver should add another pusher with the - * given pushkey and App ID in addition to any others with - * different user IDs. Otherwise, the homeserver must remove any - * other pushers with the same App ID and pushkey for different - * users. The default is ``false``. - */ - explicit PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag = {}, Omittable append = none); + // Inner data structures + + /// A dictionary of information for the pusher implementation + /// itself. If ``kind`` is ``http``, this should contain ``url`` + /// which is the URL to use to send notifications to. + struct PusherData { + /// Required if ``kind`` is ``http``. The URL to use to send + /// notifications to. MUST be an HTTPS URL with a path of + /// ``/_matrix/push/v1/notify``. + QString url; + /// The format to send notifications in to Push Gateways if the + /// ``kind`` is ``http``. The details about what fields the + /// homeserver should send to the push gateway are defined in the + /// `Push Gateway Specification`_. Currently the only format + /// available is 'event_id_only'. + QString format; + }; + + // Construction/destruction + + /*! Modify a pusher for this user on the homeserver. + * \param pushkey + * This is a unique identifier for this pusher. The value you + * should use for this is the routing or destination address + * information for the notification, for example, the APNS token + * for APNS or the Registration ID for GCM. If your notification + * client has no such concept, use any unique identifier. + * Max length, 512 bytes. + * + * If the ``kind`` is ``"email"``, this is the email address to + * send notifications to. + * \param kind + * The kind of pusher to configure. ``"http"`` makes a pusher that + * sends HTTP pokes. ``"email"`` makes a pusher that emails the + * user with unread notifications. ``null`` deletes the pusher. + * \param appId + * This is a reverse-DNS style identifier for the application. + * It is recommended that this end with the platform, such that + * different platform versions get different app identifiers. + * Max length, 64 chars. + * + * If the ``kind`` is ``"email"``, this is ``"m.email"``. + * \param appDisplayName + * A string that will allow the user to identify what application + * owns this pusher. + * \param deviceDisplayName + * A string that will allow the user to identify what device owns + * this pusher. + * \param lang + * The preferred language for receiving notifications (e.g. 'en' + * or 'en-US'). + * \param data + * A dictionary of information for the pusher implementation + * itself. If ``kind`` is ``http``, this should contain ``url`` + * which is the URL to use to send notifications to. + * \param profileTag + * This string determines which set of device specific rules this + * pusher executes. + * \param append + * If true, the homeserver should add another pusher with the + * given pushkey and App ID in addition to any others with + * different user IDs. Otherwise, the homeserver must remove any + * other pushers with the same App ID and pushkey for different + * users. The default is ``false``. + */ + explicit PostPusherJob(const QString& pushkey, const QString& kind, + const QString& appId, + const QString& appDisplayName, + const QString& deviceDisplayName, + const QString& lang, const PusherData& data, + const QString& profileTag = {}, + Omittable append = none); }; } // namespace QMatrixClient diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index b91d18f7..e70cbbde 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -15,37 +15,31 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetPushRulesJob::Private { public: - PushRuleset global; + PushRuleset global; }; QUrl GetPushRulesJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushrules"); } static const auto GetPushRulesJobName = QStringLiteral("GetPushRulesJob"); GetPushRulesJob::GetPushRulesJob() - : BaseJob(HttpVerb::Get, GetPushRulesJobName, - basePath % "/pushrules") - , d(new Private) + : BaseJob(HttpVerb::Get, GetPushRulesJobName, basePath % "/pushrules"), + d(new Private) { } GetPushRulesJob::~GetPushRulesJob() = default; -const PushRuleset& GetPushRulesJob::global() const -{ - return d->global; -} +const PushRuleset& GetPushRulesJob::global() const { return d->global; } BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("global"_ls)) - return { JsonParseError, - "The key 'global' not found in the response" }; + return { JsonParseError, "The key 'global' not found in the response" }; fromJson(json.value("global"_ls), d->global); return Success; } @@ -53,30 +47,30 @@ BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) class GetPushRuleJob::Private { public: - PushRule data; + PushRule data; }; -QUrl GetPushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) +QUrl GetPushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId); + basePath % "/pushrules/" % scope % "/" % kind + % "/" % ruleId); } static const auto GetPushRuleJobName = QStringLiteral("GetPushRuleJob"); -GetPushRuleJob::GetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) +GetPushRuleJob::GetPushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId) : BaseJob(HttpVerb::Get, GetPushRuleJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) - , d(new Private) + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId), + d(new Private) { } GetPushRuleJob::~GetPushRuleJob() = default; -const PushRule& GetPushRuleJob::data() const -{ - return d->data; -} +const PushRule& GetPushRuleJob::data() const { return d->data; } BaseJob::Status GetPushRuleJob::parseJson(const QJsonDocument& data) { @@ -84,17 +78,21 @@ BaseJob::Status GetPushRuleJob::parseJson(const QJsonDocument& data) return Success; } -QUrl DeletePushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) +QUrl DeletePushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, + const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId); + basePath % "/pushrules/" % scope % "/" % kind + % "/" % ruleId); } static const auto DeletePushRuleJobName = QStringLiteral("DeletePushRuleJob"); -DeletePushRuleJob::DeletePushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) +DeletePushRuleJob::DeletePushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId) : BaseJob(HttpVerb::Delete, DeletePushRuleJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) { } @@ -108,10 +106,15 @@ BaseJob::Query queryToSetPushRule(const QString& before, const QString& after) static const auto SetPushRuleJobName = QStringLiteral("SetPushRuleJob"); -SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions, const QString& before, const QString& after, const QVector& conditions, const QString& pattern) +SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId, + const QStringList& actions, + const QString& before, const QString& after, + const QVector& conditions, + const QString& pattern) : BaseJob(HttpVerb::Put, SetPushRuleJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId, - queryToSetPushRule(before, after)) + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId, + queryToSetPushRule(before, after)) { QJsonObject _data; addParam<>(_data, QStringLiteral("actions"), actions); @@ -123,46 +126,55 @@ SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, const class IsPushRuleEnabledJob::Private { public: - bool enabled; + bool enabled; }; -QUrl IsPushRuleEnabledJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) +QUrl IsPushRuleEnabledJob::makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, + const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled"); + basePath % "/pushrules/" % scope % "/" % kind + % "/" % ruleId % "/enabled"); } -static const auto IsPushRuleEnabledJobName = QStringLiteral("IsPushRuleEnabledJob"); +static const auto IsPushRuleEnabledJobName = + QStringLiteral("IsPushRuleEnabledJob"); -IsPushRuleEnabledJob::IsPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId) +IsPushRuleEnabledJob::IsPushRuleEnabledJob(const QString& scope, + const QString& kind, + const QString& ruleId) : BaseJob(HttpVerb::Get, IsPushRuleEnabledJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled") - , d(new Private) + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId + % "/enabled"), + d(new Private) { } IsPushRuleEnabledJob::~IsPushRuleEnabledJob() = default; -bool IsPushRuleEnabledJob::enabled() const -{ - return d->enabled; -} +bool IsPushRuleEnabledJob::enabled() const { return d->enabled; } BaseJob::Status IsPushRuleEnabledJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("enabled"_ls)) return { JsonParseError, - "The key 'enabled' not found in the response" }; + "The key 'enabled' not found in the response" }; fromJson(json.value("enabled"_ls), d->enabled); return Success; } -static const auto SetPushRuleEnabledJobName = QStringLiteral("SetPushRuleEnabledJob"); +static const auto SetPushRuleEnabledJobName = + QStringLiteral("SetPushRuleEnabledJob"); -SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId, bool enabled) +SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, + const QString& kind, + const QString& ruleId, + bool enabled) : BaseJob(HttpVerb::Put, SetPushRuleEnabledJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled") + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId + % "/enabled") { QJsonObject _data; addParam<>(_data, QStringLiteral("enabled"), enabled); @@ -172,49 +184,57 @@ SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, const QString class GetPushRuleActionsJob::Private { public: - QStringList actions; + QStringList actions; }; -QUrl GetPushRuleActionsJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) +QUrl GetPushRuleActionsJob::makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, + const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions"); + basePath % "/pushrules/" % scope % "/" % kind + % "/" % ruleId % "/actions"); } -static const auto GetPushRuleActionsJobName = QStringLiteral("GetPushRuleActionsJob"); +static const auto GetPushRuleActionsJobName = + QStringLiteral("GetPushRuleActionsJob"); -GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId) +GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, + const QString& kind, + const QString& ruleId) : BaseJob(HttpVerb::Get, GetPushRuleActionsJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions") - , d(new Private) + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId + % "/actions"), + d(new Private) { } GetPushRuleActionsJob::~GetPushRuleActionsJob() = default; -const QStringList& GetPushRuleActionsJob::actions() const -{ - return d->actions; -} +const QStringList& GetPushRuleActionsJob::actions() const { return d->actions; } BaseJob::Status GetPushRuleActionsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("actions"_ls)) return { JsonParseError, - "The key 'actions' not found in the response" }; + "The key 'actions' not found in the response" }; fromJson(json.value("actions"_ls), d->actions); return Success; } -static const auto SetPushRuleActionsJobName = QStringLiteral("SetPushRuleActionsJob"); +static const auto SetPushRuleActionsJobName = + QStringLiteral("SetPushRuleActionsJob"); -SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions) +SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, + const QString& kind, + const QString& ruleId, + const QStringList& actions) : BaseJob(HttpVerb::Put, SetPushRuleActionsJobName, - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions") + basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId + % "/actions") { QJsonObject _data; addParam<>(_data, QStringLiteral("actions"), actions); setRequestData(_data); } - diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index c038401c..5191d129 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -6,14 +6,13 @@ #include "jobs/basejob.h" -#include "csapi/definitions/push_ruleset.h" #include "converters.h" +#include "csapi/definitions/push_condition.h" #include "csapi/definitions/push_rule.h" +#include "csapi/definitions/push_ruleset.h" #include -#include "csapi/definitions/push_condition.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Retrieve all push rulesets. @@ -25,29 +24,29 @@ namespace QMatrixClient class GetPushRulesJob : public BaseJob { public: - explicit GetPushRulesJob(); + explicit GetPushRulesJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPushRulesJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPushRulesJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetPushRulesJob() override; + ~GetPushRulesJob() override; - // Result properties + // Result properties - /// The global ruleset. - const PushRuleset& global() const; + /// The global ruleset. + const PushRuleset& global() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Retrieve a push rule. @@ -56,37 +55,39 @@ namespace QMatrixClient class GetPushRuleJob : public BaseJob { public: - /*! Retrieve a push rule. - * \param scope - * ``global`` to specify global rules. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - */ - explicit GetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPushRuleJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - - ~GetPushRuleJob() override; - - // Result properties - - /// The push rule. - const PushRule& data() const; + /*! Retrieve a push rule. + * \param scope + * ``global`` to specify global rules. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + */ + explicit GetPushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPushRuleJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, const QString& ruleId); + + ~GetPushRuleJob() override; + + // Result properties + + /// The push rule. + const PushRule& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Delete a push rule. @@ -95,24 +96,25 @@ namespace QMatrixClient class DeletePushRuleJob : public BaseJob { public: - /*! Delete a push rule. - * \param scope - * ``global`` to specify global rules. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - */ - explicit DeletePushRuleJob(const QString& scope, const QString& kind, const QString& ruleId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * DeletePushRuleJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - + /*! Delete a push rule. + * \param scope + * ``global`` to specify global rules. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + */ + explicit DeletePushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * DeletePushRuleJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, const QString& ruleId); }; /// Add or change a push rule. @@ -120,36 +122,40 @@ namespace QMatrixClient /// This endpoint allows the creation, modification and deletion of pushers /// for this user ID. The behaviour of this endpoint varies depending on the /// values in the JSON body. - /// + /// /// When creating push rules, they MUST be enabled by default. class SetPushRuleJob : public BaseJob { public: - /*! Add or change a push rule. - * \param scope - * ``global`` to specify global rules. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - * \param actions - * The action(s) to perform when the conditions for this rule are met. - * \param before - * Use 'before' with a ``rule_id`` as its value to make the new rule the - * next-most important rule with respect to the given user defined rule. - * It is not possible to add a rule relative to a predefined server rule. - * \param after - * This makes the new rule the next-less important rule relative to the - * given user defined rule. It is not possible to add a rule relative - * to a predefined server rule. - * \param conditions - * The conditions that must hold true for an event in order for a - * rule to be applied to an event. A rule with no conditions - * always matches. Only applicable to ``underride`` and ``override`` rules. - * \param pattern - * Only applicable to ``content`` rules. The glob-style pattern to match against. - */ - explicit SetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions, const QString& before = {}, const QString& after = {}, const QVector& conditions = {}, const QString& pattern = {}); + /*! Add or change a push rule. + * \param scope + * ``global`` to specify global rules. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + * \param actions + * The action(s) to perform when the conditions for this rule are met. + * \param before + * Use 'before' with a ``rule_id`` as its value to make the new rule + * the next-most important rule with respect to the given user defined + * rule. It is not possible to add a rule relative to a predefined + * server rule. \param after This makes the new rule the next-less + * important rule relative to the given user defined rule. It is not + * possible to add a rule relative to a predefined server rule. \param + * conditions The conditions that must hold true for an event in order + * for a rule to be applied to an event. A rule with no conditions + * always matches. Only applicable to ``underride`` and ``override`` + * rules. \param pattern Only applicable to ``content`` rules. The + * glob-style pattern to match against. + */ + explicit SetPushRuleJob(const QString& scope, const QString& kind, + const QString& ruleId, + const QStringList& actions, + const QString& before = {}, + const QString& after = {}, + const QVector& conditions = {}, + const QString& pattern = {}); }; /// Get whether a push rule is enabled @@ -158,57 +164,62 @@ namespace QMatrixClient class IsPushRuleEnabledJob : public BaseJob { public: - /*! Get whether a push rule is enabled - * \param scope - * Either ``global`` or ``device/`` to specify global - * rules or device rules for the given ``profile_tag``. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - */ - explicit IsPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * IsPushRuleEnabledJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - - ~IsPushRuleEnabledJob() override; - - // Result properties - - /// Whether the push rule is enabled or not. - bool enabled() const; + /*! Get whether a push rule is enabled + * \param scope + * Either ``global`` or ``device/`` to specify global + * rules or device rules for the given ``profile_tag``. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + */ + explicit IsPushRuleEnabledJob(const QString& scope, const QString& kind, + const QString& ruleId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * IsPushRuleEnabledJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, const QString& ruleId); + + ~IsPushRuleEnabledJob() override; + + // Result properties + + /// Whether the push rule is enabled or not. + bool enabled() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Enable or disable a push rule. /// - /// This endpoint allows clients to enable or disable the specified push rule. + /// This endpoint allows clients to enable or disable the specified push + /// rule. class SetPushRuleEnabledJob : public BaseJob { public: - /*! Enable or disable a push rule. - * \param scope - * ``global`` to specify global rules. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - * \param enabled - * Whether the push rule is enabled or not. - */ - explicit SetPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId, bool enabled); + /*! Enable or disable a push rule. + * \param scope + * ``global`` to specify global rules. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + * \param enabled + * Whether the push rule is enabled or not. + */ + explicit SetPushRuleEnabledJob(const QString& scope, + const QString& kind, + const QString& ruleId, bool enabled); }; /// The actions for a push rule @@ -217,38 +228,41 @@ namespace QMatrixClient class GetPushRuleActionsJob : public BaseJob { public: - /*! The actions for a push rule - * \param scope - * Either ``global`` or ``device/`` to specify global - * rules or device rules for the given ``profile_tag``. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - */ - explicit GetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetPushRuleActionsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - - ~GetPushRuleActionsJob() override; - - // Result properties - - /// The action(s) to perform for this rule. - const QStringList& actions() const; + /*! The actions for a push rule + * \param scope + * Either ``global`` or ``device/`` to specify global + * rules or device rules for the given ``profile_tag``. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + */ + explicit GetPushRuleActionsJob(const QString& scope, + const QString& kind, + const QString& ruleId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetPushRuleActionsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, + const QString& kind, const QString& ruleId); + + ~GetPushRuleActionsJob() override; + + // Result properties + + /// The action(s) to perform for this rule. + const QStringList& actions() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Set the actions for a push rule. @@ -258,16 +272,19 @@ namespace QMatrixClient class SetPushRuleActionsJob : public BaseJob { public: - /*! Set the actions for a push rule. - * \param scope - * ``global`` to specify global rules. - * \param kind - * The kind of rule - * \param ruleId - * The identifier for the rule. - * \param actions - * The action(s) to perform for this rule. - */ - explicit SetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions); + /*! Set the actions for a push rule. + * \param scope + * ``global`` to specify global rules. + * \param kind + * The kind of rule + * \param ruleId + * The identifier for the rule. + * \param actions + * The action(s) to perform for this rule. + */ + explicit SetPushRuleActionsJob(const QString& scope, + const QString& kind, + const QString& ruleId, + const QStringList& actions); }; } // namespace QMatrixClient diff --git a/lib/csapi/read_markers.cpp b/lib/csapi/read_markers.cpp index 1bc67ba0..5ea45f88 100644 --- a/lib/csapi/read_markers.cpp +++ b/lib/csapi/read_markers.cpp @@ -14,13 +14,14 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SetReadMarkerJobName = QStringLiteral("SetReadMarkerJob"); -SetReadMarkerJob::SetReadMarkerJob(const QString& roomId, const QString& mFullyRead, const QString& mRead) +SetReadMarkerJob::SetReadMarkerJob(const QString& roomId, + const QString& mFullyRead, + const QString& mRead) : BaseJob(HttpVerb::Post, SetReadMarkerJobName, - basePath % "/rooms/" % roomId % "/read_markers") + basePath % "/rooms/" % roomId % "/read_markers") { QJsonObject _data; addParam<>(_data, QStringLiteral("m.fully_read"), mFullyRead); addParam(_data, QStringLiteral("m.read"), mRead); setRequestData(_data); } - diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h index d982b477..6afa5572 100644 --- a/lib/csapi/read_markers.h +++ b/lib/csapi/read_markers.h @@ -6,9 +6,7 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Set the position of the read marker for a room. @@ -18,17 +16,19 @@ namespace QMatrixClient class SetReadMarkerJob : public BaseJob { public: - /*! Set the position of the read marker for a room. - * \param roomId - * The room ID to set the read marker in for the user. - * \param mFullyRead - * The event ID the read marker should be located at. The - * event MUST belong to the room. - * \param mRead - * The event ID to set the read receipt location at. This is - * equivalent to calling ``/receipt/m.read/$elsewhere:example.org`` - * and is provided here to save that extra call. - */ - explicit SetReadMarkerJob(const QString& roomId, const QString& mFullyRead, const QString& mRead = {}); + /*! Set the position of the read marker for a room. + * \param roomId + * The room ID to set the read marker in for the user. + * \param mFullyRead + * The event ID the read marker should be located at. The + * event MUST belong to the room. + * \param mRead + * The event ID to set the read receipt location at. This is + * equivalent to calling ``/receipt/m.read/$elsewhere:example.org`` + * and is provided here to save that extra call. + */ + explicit SetReadMarkerJob(const QString& roomId, + const QString& mFullyRead, + const QString& mRead = {}); }; } // namespace QMatrixClient diff --git a/lib/csapi/receipts.cpp b/lib/csapi/receipts.cpp index b78ba533..25d0dd8e 100644 --- a/lib/csapi/receipts.cpp +++ b/lib/csapi/receipts.cpp @@ -14,10 +14,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto PostReceiptJobName = QStringLiteral("PostReceiptJob"); -PostReceiptJob::PostReceiptJob(const QString& roomId, const QString& receiptType, const QString& eventId, const QJsonObject& receipt) +PostReceiptJob::PostReceiptJob(const QString& roomId, + const QString& receiptType, + const QString& eventId, + const QJsonObject& receipt) : BaseJob(HttpVerb::Post, PostReceiptJobName, - basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" % eventId) + basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" + % eventId) { setRequestData(Data(toJson(receipt))); } - diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h index 47e2f3c7..4e40188a 100644 --- a/lib/csapi/receipts.h +++ b/lib/csapi/receipts.h @@ -8,8 +8,7 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Send a receipt for the given event ID. @@ -19,17 +18,20 @@ namespace QMatrixClient class PostReceiptJob : public BaseJob { public: - /*! Send a receipt for the given event ID. - * \param roomId - * The room in which to send the event. - * \param receiptType - * The type of receipt to send. - * \param eventId - * The event ID to acknowledge up to. - * \param receipt - * Extra receipt information to attach to ``content`` if any. The - * server will automatically set the ``ts`` field. - */ - explicit PostReceiptJob(const QString& roomId, const QString& receiptType, const QString& eventId, const QJsonObject& receipt = {}); + /*! Send a receipt for the given event ID. + * \param roomId + * The room in which to send the event. + * \param receiptType + * The type of receipt to send. + * \param eventId + * The event ID to acknowledge up to. + * \param receipt + * Extra receipt information to attach to ``content`` if any. The + * server will automatically set the ``ts`` field. + */ + explicit PostReceiptJob(const QString& roomId, + const QString& receiptType, + const QString& eventId, + const QJsonObject& receipt = {}); }; } // namespace QMatrixClient diff --git a/lib/csapi/redaction.cpp b/lib/csapi/redaction.cpp index 1d54e36d..713c5102 100644 --- a/lib/csapi/redaction.cpp +++ b/lib/csapi/redaction.cpp @@ -15,15 +15,17 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class RedactEventJob::Private { public: - QString eventId; + QString eventId; }; static const auto RedactEventJobName = QStringLiteral("RedactEventJob"); -RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason) +RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, + const QString& txnId, const QString& reason) : BaseJob(HttpVerb::Put, RedactEventJobName, - basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId) - , d(new Private) + basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" + % txnId), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("reason"), reason); @@ -32,10 +34,7 @@ RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, co RedactEventJob::~RedactEventJob() = default; -const QString& RedactEventJob::eventId() const -{ - return d->eventId; -} +const QString& RedactEventJob::eventId() const { return d->eventId; } BaseJob::Status RedactEventJob::parseJson(const QJsonDocument& data) { @@ -43,4 +42,3 @@ BaseJob::Status RedactEventJob::parseJson(const QJsonDocument& data) fromJson(json.value("event_id"_ls), d->eventId); return Success; } - diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h index d02abfd0..ab235b31 100644 --- a/lib/csapi/redaction.h +++ b/lib/csapi/redaction.h @@ -6,48 +6,47 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Strips all non-integrity-critical information out of an event. /// /// Strips all information out of an event which isn't critical to the /// integrity of the server-side representation of the room. - /// + /// /// This cannot be undone. - /// + /// /// Users may redact their own events, and any user with a power level /// greater than or equal to the `redact` power level of the room may /// redact events there. class RedactEventJob : public BaseJob { public: - /*! Strips all non-integrity-critical information out of an event. - * \param roomId - * The room from which to redact the event. - * \param eventId - * The ID of the event to redact - * \param txnId - * The transaction ID for this event. Clients should generate a - * unique ID; it will be used by the server to ensure idempotency of requests. - * \param reason - * The reason for the event being redacted. - */ - explicit RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason = {}); - ~RedactEventJob() override; - - // Result properties - - /// A unique identifier for the event. - const QString& eventId() const; + /*! Strips all non-integrity-critical information out of an event. + * \param roomId + * The room from which to redact the event. + * \param eventId + * The ID of the event to redact + * \param txnId + * The transaction ID for this event. Clients should generate a + * unique ID; it will be used by the server to ensure idempotency of + * requests. \param reason The reason for the event being redacted. + */ + explicit RedactEventJob(const QString& roomId, const QString& eventId, + const QString& txnId, + const QString& reason = {}); + ~RedactEventJob() override; + + // Result properties + + /// A unique identifier for the event. + const QString& eventId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 5dc9c1e5..b5e7d985 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -15,10 +15,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class RegisterJob::Private { public: - QString userId; - QString accessToken; - QString homeServer; - QString deviceId; + QString userId; + QString accessToken; + QString homeServer; + QString deviceId; }; BaseJob::Query queryToRegister(const QString& kind) @@ -30,12 +30,15 @@ BaseJob::Query queryToRegister(const QString& kind) static const auto RegisterJobName = QStringLiteral("RegisterJob"); -RegisterJob::RegisterJob(const QString& kind, const Omittable& auth, Omittable bindEmail, const QString& username, const QString& password, const QString& deviceId, const QString& initialDeviceDisplayName, Omittable inhibitLogin) - : BaseJob(HttpVerb::Post, RegisterJobName, - basePath % "/register", - queryToRegister(kind), - {}, false) - , d(new Private) +RegisterJob::RegisterJob(const QString& kind, + const Omittable& auth, + Omittable bindEmail, const QString& username, + const QString& password, const QString& deviceId, + const QString& initialDeviceDisplayName, + Omittable inhibitLogin) + : BaseJob(HttpVerb::Post, RegisterJobName, basePath % "/register", + queryToRegister(kind), {}, false), + d(new Private) { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); @@ -43,39 +46,28 @@ RegisterJob::RegisterJob(const QString& kind, const Omittable(_data, QStringLiteral("username"), username); addParam(_data, QStringLiteral("password"), password); addParam(_data, QStringLiteral("device_id"), deviceId); - addParam(_data, QStringLiteral("initial_device_display_name"), initialDeviceDisplayName); + addParam(_data, QStringLiteral("initial_device_display_name"), + initialDeviceDisplayName); addParam(_data, QStringLiteral("inhibit_login"), inhibitLogin); setRequestData(_data); } RegisterJob::~RegisterJob() = default; -const QString& RegisterJob::userId() const -{ - return d->userId; -} +const QString& RegisterJob::userId() const { return d->userId; } -const QString& RegisterJob::accessToken() const -{ - return d->accessToken; -} +const QString& RegisterJob::accessToken() const { return d->accessToken; } -const QString& RegisterJob::homeServer() const -{ - return d->homeServer; -} +const QString& RegisterJob::homeServer() const { return d->homeServer; } -const QString& RegisterJob::deviceId() const -{ - return d->deviceId; -} +const QString& RegisterJob::deviceId() const { return d->deviceId; } BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("user_id"_ls)) return { JsonParseError, - "The key 'user_id' not found in the response" }; + "The key 'user_id' not found in the response" }; fromJson(json.value("user_id"_ls), d->userId); fromJson(json.value("access_token"_ls), d->accessToken); fromJson(json.value("home_server"_ls), d->homeServer); @@ -86,15 +78,18 @@ BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) class RequestTokenToRegisterEmailJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenToRegisterEmailJobName = QStringLiteral("RequestTokenToRegisterEmailJob"); +static const auto RequestTokenToRegisterEmailJobName = + QStringLiteral("RequestTokenToRegisterEmailJob"); -RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob( + const QString& clientSecret, const QString& email, int sendAttempt, + const QString& idServer, const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenToRegisterEmailJobName, - basePath % "/register/email/requestToken", false) - , d(new Private) + basePath % "/register/email/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -107,12 +102,10 @@ RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob(const QString& cl RequestTokenToRegisterEmailJob::~RequestTokenToRegisterEmailJob() = default; -const Sid& RequestTokenToRegisterEmailJob::data() const -{ - return d->data; -} +const Sid& RequestTokenToRegisterEmailJob::data() const { return d->data; } -BaseJob::Status RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& data) +BaseJob::Status +RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; @@ -121,15 +114,19 @@ BaseJob::Status RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& d class RequestTokenToRegisterMSISDNJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenToRegisterMSISDNJobName = QStringLiteral("RequestTokenToRegisterMSISDNJob"); +static const auto RequestTokenToRegisterMSISDNJobName = + QStringLiteral("RequestTokenToRegisterMSISDNJob"); -RequestTokenToRegisterMSISDNJob::RequestTokenToRegisterMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenToRegisterMSISDNJob::RequestTokenToRegisterMSISDNJob( + const QString& clientSecret, const QString& country, + const QString& phoneNumber, int sendAttempt, const QString& idServer, + const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenToRegisterMSISDNJobName, - basePath % "/register/msisdn/requestToken", false) - , d(new Private) + basePath % "/register/msisdn/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -143,12 +140,10 @@ RequestTokenToRegisterMSISDNJob::RequestTokenToRegisterMSISDNJob(const QString& RequestTokenToRegisterMSISDNJob::~RequestTokenToRegisterMSISDNJob() = default; -const Sid& RequestTokenToRegisterMSISDNJob::data() const -{ - return d->data; -} +const Sid& RequestTokenToRegisterMSISDNJob::data() const { return d->data; } -BaseJob::Status RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& data) +BaseJob::Status +RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; @@ -156,9 +151,10 @@ BaseJob::Status RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& static const auto ChangePasswordJobName = QStringLiteral("ChangePasswordJob"); -ChangePasswordJob::ChangePasswordJob(const QString& newPassword, const Omittable& auth) +ChangePasswordJob::ChangePasswordJob(const QString& newPassword, + const Omittable& auth) : BaseJob(HttpVerb::Post, ChangePasswordJobName, - basePath % "/account/password") + basePath % "/account/password") { QJsonObject _data; addParam<>(_data, QStringLiteral("new_password"), newPassword); @@ -169,15 +165,18 @@ ChangePasswordJob::ChangePasswordJob(const QString& newPassword, const Omittable class RequestTokenToResetPasswordEmailJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenToResetPasswordEmailJobName = QStringLiteral("RequestTokenToResetPasswordEmailJob"); +static const auto RequestTokenToResetPasswordEmailJobName = + QStringLiteral("RequestTokenToResetPasswordEmailJob"); -RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob( + const QString& clientSecret, const QString& email, int sendAttempt, + const QString& idServer, const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenToResetPasswordEmailJobName, - basePath % "/account/password/email/requestToken", false) - , d(new Private) + basePath % "/account/password/email/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -188,14 +187,13 @@ RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob(const Q setRequestData(_data); } -RequestTokenToResetPasswordEmailJob::~RequestTokenToResetPasswordEmailJob() = default; +RequestTokenToResetPasswordEmailJob::~RequestTokenToResetPasswordEmailJob() = + default; -const Sid& RequestTokenToResetPasswordEmailJob::data() const -{ - return d->data; -} +const Sid& RequestTokenToResetPasswordEmailJob::data() const { return d->data; } -BaseJob::Status RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocument& data) +BaseJob::Status +RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; @@ -204,15 +202,19 @@ BaseJob::Status RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocume class RequestTokenToResetPasswordMSISDNJob::Private { public: - Sid data; + Sid data; }; -static const auto RequestTokenToResetPasswordMSISDNJobName = QStringLiteral("RequestTokenToResetPasswordMSISDNJob"); +static const auto RequestTokenToResetPasswordMSISDNJobName = + QStringLiteral("RequestTokenToResetPasswordMSISDNJob"); -RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) +RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob( + const QString& clientSecret, const QString& country, + const QString& phoneNumber, int sendAttempt, const QString& idServer, + const QString& nextLink) : BaseJob(HttpVerb::Post, RequestTokenToResetPasswordMSISDNJobName, - basePath % "/account/password/msisdn/requestToken", false) - , d(new Private) + basePath % "/account/password/msisdn/requestToken", false), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("client_secret"), clientSecret); @@ -224,24 +226,28 @@ RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob(const setRequestData(_data); } -RequestTokenToResetPasswordMSISDNJob::~RequestTokenToResetPasswordMSISDNJob() = default; +RequestTokenToResetPasswordMSISDNJob::~RequestTokenToResetPasswordMSISDNJob() = + default; const Sid& RequestTokenToResetPasswordMSISDNJob::data() const { return d->data; } -BaseJob::Status RequestTokenToResetPasswordMSISDNJob::parseJson(const QJsonDocument& data) +BaseJob::Status +RequestTokenToResetPasswordMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } -static const auto DeactivateAccountJobName = QStringLiteral("DeactivateAccountJob"); +static const auto DeactivateAccountJobName = + QStringLiteral("DeactivateAccountJob"); -DeactivateAccountJob::DeactivateAccountJob(const Omittable& auth) +DeactivateAccountJob::DeactivateAccountJob( + const Omittable& auth) : BaseJob(HttpVerb::Post, DeactivateAccountJobName, - basePath % "/account/deactivate") + basePath % "/account/deactivate") { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); @@ -251,7 +257,7 @@ DeactivateAccountJob::DeactivateAccountJob(const Omittable& class CheckUsernameAvailabilityJob::Private { public: - Omittable available; + Omittable available; }; BaseJob::Query queryToCheckUsernameAvailability(const QString& username) @@ -261,21 +267,23 @@ BaseJob::Query queryToCheckUsernameAvailability(const QString& username) return _q; } -QUrl CheckUsernameAvailabilityJob::makeRequestUrl(QUrl baseUrl, const QString& username) +QUrl CheckUsernameAvailabilityJob::makeRequestUrl(QUrl baseUrl, + const QString& username) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/register/available", - queryToCheckUsernameAvailability(username)); + basePath % "/register/available", + queryToCheckUsernameAvailability(username)); } -static const auto CheckUsernameAvailabilityJobName = QStringLiteral("CheckUsernameAvailabilityJob"); +static const auto CheckUsernameAvailabilityJobName = + QStringLiteral("CheckUsernameAvailabilityJob"); -CheckUsernameAvailabilityJob::CheckUsernameAvailabilityJob(const QString& username) +CheckUsernameAvailabilityJob::CheckUsernameAvailabilityJob( + const QString& username) : BaseJob(HttpVerb::Get, CheckUsernameAvailabilityJobName, - basePath % "/register/available", - queryToCheckUsernameAvailability(username), - {}, false) - , d(new Private) + basePath % "/register/available", + queryToCheckUsernameAvailability(username), {}, false), + d(new Private) { } @@ -286,10 +294,10 @@ Omittable CheckUsernameAvailabilityJob::available() const return d->available; } -BaseJob::Status CheckUsernameAvailabilityJob::parseJson(const QJsonDocument& data) +BaseJob::Status +CheckUsernameAvailabilityJob::parseJson(const QJsonDocument& data) { auto json = data.object(); fromJson(json.value("available"_ls), d->available); return Success; } - diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index ca1a1c21..02f4ddaf 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -6,42 +6,43 @@ #include "jobs/basejob.h" -#include "csapi/../identity/definitions/sid.h" #include "converters.h" +#include "csapi/../identity/definitions/sid.h" #include "csapi/definitions/auth_data.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Register for an account on this homeserver. /// /// This API endpoint uses the `User-Interactive Authentication API`_. - /// + /// /// Register for an account on this homeserver. - /// + /// /// There are two kinds of user account: - /// - /// - `user` accounts. These accounts may use the full API described in this specification. - /// - /// - `guest` accounts. These accounts may have limited permissions and may not be supported by all servers. - /// + /// + /// - `user` accounts. These accounts may use the full API described in this + /// specification. + /// + /// - `guest` accounts. These accounts may have limited permissions and may + /// not be supported by all servers. + /// /// If registration is successful, this endpoint will issue an access token /// the client can use to authorize itself in subsequent requests. - /// + /// /// If the client does not supply a ``device_id``, the server must /// auto-generate one. - /// + /// /// The server SHOULD register an account with a User ID based on the /// ``username`` provided, if any. Note that the grammar of Matrix User ID /// localparts is restricted, so the server MUST either map the provided /// ``username`` onto a ``user_id`` in a logical manner, or reject /// ``username``\s which do not comply to the grammar, with /// ``M_INVALID_USERNAME``. - /// + /// /// Matrix clients MUST NOT assume that localpart of the registered /// ``user_id`` matches the provided ``username``. - /// + /// /// The returned access token must be associated with the ``device_id`` /// supplied by the client or generated by the server. The server may /// invalidate any access token previously associated with that device. See @@ -49,72 +50,81 @@ namespace QMatrixClient class RegisterJob : public BaseJob { public: - /*! Register for an account on this homeserver. - * \param kind - * The kind of account to register. Defaults to `user`. - * \param auth - * Additional authentication information for the - * user-interactive authentication API. Note that this - * information is *not* used to define how the registered user - * should be authenticated, but is instead used to - * authenticate the ``register`` call itself. It should be - * left empty, or omitted, unless an earlier call returned an - * response with status code 401. - * \param bindEmail - * If true, the server binds the email used for authentication to - * the Matrix ID with the identity server. - * \param username - * The basis for the localpart of the desired Matrix ID. If omitted, - * the homeserver MUST generate a Matrix ID local part. - * \param password - * The desired password for the account. - * \param deviceId - * ID of the client device. If this does not correspond to a - * known client device, a new device will be created. The server - * will auto-generate a device_id if this is not specified. - * \param initialDeviceDisplayName - * A display name to assign to the newly-created device. Ignored - * if ``device_id`` corresponds to a known device. - * \param inhibitLogin - * If true, an ``access_token`` and ``device_id`` should not be - * returned from this call, therefore preventing an automatic - * login. Defaults to false. - */ - explicit RegisterJob(const QString& kind = QStringLiteral("user"), const Omittable& auth = none, Omittable bindEmail = none, const QString& username = {}, const QString& password = {}, const QString& deviceId = {}, const QString& initialDeviceDisplayName = {}, Omittable inhibitLogin = none); - ~RegisterJob() override; - - // Result properties - - /// The fully-qualified Matrix user ID (MXID) that has been registered. - /// - /// Any user ID returned by this API must conform to the grammar given in the - /// `Matrix specification `_. - const QString& userId() const; - /// An access token for the account. - /// This access token can then be used to authorize other requests. - /// Required if the ``inhibit_login`` option is false. - const QString& accessToken() const; - /// The server_name of the homeserver on which the account has - /// been registered. - /// - /// **Deprecated**. Clients should extract the server_name from - /// ``user_id`` (by splitting at the first colon) if they require - /// it. Note also that ``homeserver`` is not spelt this way. - const QString& homeServer() const; - /// ID of the registered device. Will be the same as the - /// corresponding parameter in the request, if one was specified. - /// Required if the ``inhibit_login`` option is false. - const QString& deviceId() const; + /*! Register for an account on this homeserver. + * \param kind + * The kind of account to register. Defaults to `user`. + * \param auth + * Additional authentication information for the + * user-interactive authentication API. Note that this + * information is *not* used to define how the registered user + * should be authenticated, but is instead used to + * authenticate the ``register`` call itself. It should be + * left empty, or omitted, unless an earlier call returned an + * response with status code 401. + * \param bindEmail + * If true, the server binds the email used for authentication to + * the Matrix ID with the identity server. + * \param username + * The basis for the localpart of the desired Matrix ID. If omitted, + * the homeserver MUST generate a Matrix ID local part. + * \param password + * The desired password for the account. + * \param deviceId + * ID of the client device. If this does not correspond to a + * known client device, a new device will be created. The server + * will auto-generate a device_id if this is not specified. + * \param initialDeviceDisplayName + * A display name to assign to the newly-created device. Ignored + * if ``device_id`` corresponds to a known device. + * \param inhibitLogin + * If true, an ``access_token`` and ``device_id`` should not be + * returned from this call, therefore preventing an automatic + * login. Defaults to false. + */ + explicit RegisterJob(const QString& kind = QStringLiteral("user"), + const Omittable& auth = none, + Omittable bindEmail = none, + const QString& username = {}, + const QString& password = {}, + const QString& deviceId = {}, + const QString& initialDeviceDisplayName = {}, + Omittable inhibitLogin = none); + ~RegisterJob() override; + + // Result properties + + /// The fully-qualified Matrix user ID (MXID) that has been registered. + /// + /// Any user ID returned by this API must conform to the grammar given + /// in the `Matrix specification + /// `_. + const QString& userId() const; + /// An access token for the account. + /// This access token can then be used to authorize other requests. + /// Required if the ``inhibit_login`` option is false. + const QString& accessToken() const; + /// The server_name of the homeserver on which the account has + /// been registered. + /// + /// **Deprecated**. Clients should extract the server_name from + /// ``user_id`` (by splitting at the first colon) if they require + /// it. Note also that ``homeserver`` is not spelt this way. + const QString& homeServer() const; + /// ID of the registered device. Will be the same as the + /// corresponding parameter in the request, if one was specified. + /// Required if the ``inhibit_login`` option is false. + const QString& deviceId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; - /// Begins the validation process for an email to be used during registration. + /// Begins the validation process for an email to be used during + /// registration. /// /// Proxies the Identity Service API ``validate/email/requestToken``, but /// first checks that the given email address is not already associated @@ -123,48 +133,52 @@ namespace QMatrixClient class RequestTokenToRegisterEmailJob : public BaseJob { public: - /*! Begins the validation process for an email to be used during registration. - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenToRegisterEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenToRegisterEmailJob() override; - - // Result properties - - /// An email has been sent to the specified address. - /// Note that this may be an email containing the validation token or it may be informing - /// the user of an error. - const Sid& data() const; + /*! Begins the validation process for an email to be used during registration. + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param email The email address to validate. + * \param sendAttempt + * The server will only send an email if the ``send_attempt`` + * is a number greater than the most recent one which it has seen, + * scoped to that ``email`` + ``client_secret`` pair. This is to + * avoid repeatedly sending the same email in the case of request + * retries between the POSTing user and the identity server. + * The client should increment this value if they desire a new + * email (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenToRegisterEmailJob(const QString& clientSecret, + const QString& email, + int sendAttempt, + const QString& idServer, + const QString& nextLink = {}); + ~RequestTokenToRegisterEmailJob() override; + + // Result properties + + /// An email has been sent to the specified address. + /// Note that this may be an email containing the validation token or it + /// may be informing the user of an error. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; - /// Requests a validation token be sent to the given phone number for the purpose of registering an account + /// Requests a validation token be sent to the given phone number for the + /// purpose of registering an account /// /// Proxies the Identity Service API ``validate/msisdn/requestToken``, but /// first checks that the given phone number is not already associated @@ -173,258 +187,280 @@ namespace QMatrixClient class RequestTokenToRegisterMSISDNJob : public BaseJob { public: - /*! Requests a validation token be sent to the given phone number for the purpose of registering an account - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenToRegisterMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenToRegisterMSISDNJob() override; - - // Result properties - - /// An SMS message has been sent to the specified phone number. - /// Note that this may be an SMS message containing the validation token or it may be informing - /// the user of an error. - const Sid& data() const; + /*! Requests a validation token be sent to the given phone number for the purpose of registering an account + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param country The two-letter uppercase ISO + * country code that the number in + * ``phone_number`` should be parsed as if it were dialled from. + * \param phoneNumber + * The phone number to validate. + * \param sendAttempt + * The server will only send an SMS if the ``send_attempt`` is a + * number greater than the most recent one which it has seen, + * scoped to that ``country`` + ``phone_number`` + ``client_secret`` + * triple. This is to avoid repeatedly sending the same SMS in + * the case of request retries between the POSTing user and the + * identity server. The client should increment this value if + * they desire a new SMS (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenToRegisterMSISDNJob(const QString& clientSecret, + const QString& country, + const QString& phoneNumber, + int sendAttempt, + const QString& idServer, + const QString& nextLink = {}); + ~RequestTokenToRegisterMSISDNJob() override; + + // Result properties + + /// An SMS message has been sent to the specified phone number. + /// Note that this may be an SMS message containing the validation token + /// or it may be informing the user of an error. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Changes a user's password. /// /// Changes the password for an account on this homeserver. - /// + /// /// This API endpoint uses the `User-Interactive Authentication API`_. - /// + /// /// An access token should be submitted to this endpoint if the client has /// an active session. - /// + /// /// The homeserver may change the flows available depending on whether a /// valid access token is provided. class ChangePasswordJob : public BaseJob { public: - /*! Changes a user's password. - * \param newPassword - * The new password for the account. - * \param auth - * Additional authentication information for the user-interactive authentication API. - */ - explicit ChangePasswordJob(const QString& newPassword, const Omittable& auth = none); + /*! Changes a user's password. + * \param newPassword + * The new password for the account. + * \param auth + * Additional authentication information for the user-interactive + * authentication API. + */ + explicit ChangePasswordJob( + const QString& newPassword, + const Omittable& auth = none); }; - /// Requests a validation token be sent to the given email address for the purpose of resetting a user's password + /// Requests a validation token be sent to the given email address for the + /// purpose of resetting a user's password /// /// Proxies the Identity Service API ``validate/email/requestToken``, but - /// first checks that the given email address **is** associated with an account - /// on this homeserver. This API should be used to request + /// first checks that the given email address **is** associated with an + /// account on this homeserver. This API should be used to request /// validation tokens when authenticating for the /// `account/password` endpoint. This API's parameters and response are - /// identical to that of the HS API |/register/email/requestToken|_ except that - /// `M_THREEPID_NOT_FOUND` may be returned if no account matching the + /// identical to that of the HS API |/register/email/requestToken|_ except + /// that `M_THREEPID_NOT_FOUND` may be returned if no account matching the /// given email address could be found. The server may instead send an /// email to the given address prompting the user to create an account. /// `M_THREEPID_IN_USE` may not be returned. - /// - /// .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` - /// - /// .. _/register/email/requestToken: #post-matrix-client-r0-register-email-requesttoken + /// + /// .. |/register/email/requestToken| replace:: + /// ``/register/email/requestToken`` + /// + /// .. _/register/email/requestToken: + /// #post-matrix-client-r0-register-email-requesttoken class RequestTokenToResetPasswordEmailJob : public BaseJob { public: - /*! Requests a validation token be sent to the given email address for the purpose of resetting a user's password - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenToResetPasswordEmailJob(const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenToResetPasswordEmailJob() override; - - // Result properties - - /// An email was sent to the given address. - const Sid& data() const; + /*! Requests a validation token be sent to the given email address for the purpose of resetting a user's password + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param email The email address to validate. + * \param sendAttempt + * The server will only send an email if the ``send_attempt`` + * is a number greater than the most recent one which it has seen, + * scoped to that ``email`` + ``client_secret`` pair. This is to + * avoid repeatedly sending the same email in the case of request + * retries between the POSTing user and the identity server. + * The client should increment this value if they desire a new + * email (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenToResetPasswordEmailJob( + const QString& clientSecret, const QString& email, + int sendAttempt, const QString& idServer, + const QString& nextLink = {}); + ~RequestTokenToResetPasswordEmailJob() override; + + // Result properties + + /// An email was sent to the given address. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; - /// Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. + /// Requests a validation token be sent to the given phone number for the + /// purpose of resetting a user's password. /// /// Proxies the Identity Service API ``validate/msisdn/requestToken``, but - /// first checks that the given phone number **is** associated with an account - /// on this homeserver. This API should be used to request + /// first checks that the given phone number **is** associated with an + /// account on this homeserver. This API should be used to request /// validation tokens when authenticating for the /// `account/password` endpoint. This API's parameters and response are - /// identical to that of the HS API |/register/msisdn/requestToken|_ except that - /// `M_THREEPID_NOT_FOUND` may be returned if no account matching the + /// identical to that of the HS API |/register/msisdn/requestToken|_ except + /// that `M_THREEPID_NOT_FOUND` may be returned if no account matching the /// given phone number could be found. The server may instead send an - /// SMS message to the given address prompting the user to create an account. - /// `M_THREEPID_IN_USE` may not be returned. - /// - /// .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` - /// - /// .. _/register/msisdn/requestToken: #post-matrix-client-r0-register-email-requesttoken + /// SMS message to the given address prompting the user to create an + /// account. `M_THREEPID_IN_USE` may not be returned. + /// + /// .. |/register/msisdn/requestToken| replace:: + /// ``/register/msisdn/requestToken`` + /// + /// .. _/register/msisdn/requestToken: + /// #post-matrix-client-r0-register-email-requesttoken class RequestTokenToResetPasswordMSISDNJob : public BaseJob { public: - /*! Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. - */ - explicit RequestTokenToResetPasswordMSISDNJob(const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink = {}); - ~RequestTokenToResetPasswordMSISDNJob() override; - - // Result properties - - /// An SMS message was sent to the given phone number. - const Sid& data() const; + /*! Requests a validation token be sent to the given phone number for the purpose of resetting a user's password. + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the + * characters + * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + * it must not be empty. \param country The two-letter uppercase ISO + * country code that the number in + * ``phone_number`` should be parsed as if it were dialled from. + * \param phoneNumber + * The phone number to validate. + * \param sendAttempt + * The server will only send an SMS if the ``send_attempt`` is a + * number greater than the most recent one which it has seen, + * scoped to that ``country`` + ``phone_number`` + ``client_secret`` + * triple. This is to avoid repeatedly sending the same SMS in + * the case of request retries between the POSTing user and the + * identity server. The client should increment this value if + * they desire a new SMS (e.g. a reminder) to be sent. + * \param idServer + * The hostname of the identity server to communicate with. May + * optionally include a port. + * \param nextLink + * Optional. When the validation is completed, the identity + * server will redirect the user to this URL. + */ + explicit RequestTokenToResetPasswordMSISDNJob( + const QString& clientSecret, const QString& country, + const QString& phoneNumber, int sendAttempt, + const QString& idServer, const QString& nextLink = {}); + ~RequestTokenToResetPasswordMSISDNJob() override; + + // Result properties + + /// An SMS message was sent to the given phone number. + const Sid& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Deactivate a user's account. /// /// Deactivate the user's account, removing all ability for the user to /// login again. - /// + /// /// This API endpoint uses the `User-Interactive Authentication API`_. - /// + /// /// An access token should be submitted to this endpoint if the client has /// an active session. - /// + /// /// The homeserver may change the flows available depending on whether a /// valid access token is provided. class DeactivateAccountJob : public BaseJob { public: - /*! Deactivate a user's account. - * \param auth - * Additional authentication information for the user-interactive authentication API. - */ - explicit DeactivateAccountJob(const Omittable& auth = none); + /*! Deactivate a user's account. + * \param auth + * Additional authentication information for the user-interactive + * authentication API. + */ + explicit DeactivateAccountJob( + const Omittable& auth = none); }; /// Checks to see if a username is available on the server. /// /// Checks to see if a username is available, and valid, for the server. - /// + /// /// The server should check to ensure that, at the time of the request, the /// username requested is available for use. This includes verifying that an /// application service has not claimed the username and that the username - /// fits the server's desired requirements (for example, a server could dictate - /// that it does not permit usernames with underscores). - /// - /// Matrix clients may wish to use this API prior to attempting registration, - /// however the clients must also be aware that using this API does not normally - /// reserve the username. This can mean that the username becomes unavailable - /// between checking its availability and attempting to register it. + /// fits the server's desired requirements (for example, a server could + /// dictate that it does not permit usernames with underscores). + /// + /// Matrix clients may wish to use this API prior to attempting + /// registration, however the clients must also be aware that using this API + /// does not normally reserve the username. This can mean that the username + /// becomes unavailable between checking its availability and attempting to + /// register it. class CheckUsernameAvailabilityJob : public BaseJob { public: - /*! Checks to see if a username is available on the server. - * \param username - * The username to check the availability of. - */ - explicit CheckUsernameAvailabilityJob(const QString& username); + /*! Checks to see if a username is available on the server. + * \param username + * The username to check the availability of. + */ + explicit CheckUsernameAvailabilityJob(const QString& username); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * CheckUsernameAvailabilityJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& username); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * CheckUsernameAvailabilityJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& username); - ~CheckUsernameAvailabilityJob() override; + ~CheckUsernameAvailabilityJob() override; - // Result properties + // Result properties - /// A flag to indicate that the username is available. This should always - /// be ``true`` when the server replies with 200 OK. - Omittable available() const; + /// A flag to indicate that the username is available. This should + /// always be ``true`` when the server replies with 200 OK. + Omittable available() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/report_content.cpp b/lib/csapi/report_content.cpp index a79d4dad..f7b74834 100644 --- a/lib/csapi/report_content.cpp +++ b/lib/csapi/report_content.cpp @@ -14,13 +14,14 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto ReportContentJobName = QStringLiteral("ReportContentJob"); -ReportContentJob::ReportContentJob(const QString& roomId, const QString& eventId, int score, const QString& reason) +ReportContentJob::ReportContentJob(const QString& roomId, + const QString& eventId, int score, + const QString& reason) : BaseJob(HttpVerb::Post, ReportContentJobName, - basePath % "/rooms/" % roomId % "/report/" % eventId) + basePath % "/rooms/" % roomId % "/report/" % eventId) { QJsonObject _data; addParam<>(_data, QStringLiteral("score"), score); addParam<>(_data, QStringLiteral("reason"), reason); setRequestData(_data); } - diff --git a/lib/csapi/report_content.h b/lib/csapi/report_content.h index a20c6838..aca5b30b 100644 --- a/lib/csapi/report_content.h +++ b/lib/csapi/report_content.h @@ -8,8 +8,7 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Reports an event as inappropriate. @@ -19,17 +18,18 @@ namespace QMatrixClient class ReportContentJob : public BaseJob { public: - /*! Reports an event as inappropriate. - * \param roomId - * The room in which the event being reported is located. - * \param eventId - * The event to report. - * \param score - * The score to rate this content as where -100 is most offensive - * and 0 is inoffensive. - * \param reason - * The reason the content is being reported. May be blank. - */ - explicit ReportContentJob(const QString& roomId, const QString& eventId, int score, const QString& reason); + /*! Reports an event as inappropriate. + * \param roomId + * The room in which the event being reported is located. + * \param eventId + * The event to report. + * \param score + * The score to rate this content as where -100 is most offensive + * and 0 is inoffensive. + * \param reason + * The reason the content is being reported. May be blank. + */ + explicit ReportContentJob(const QString& roomId, const QString& eventId, + int score, const QString& reason); }; } // namespace QMatrixClient diff --git a/lib/csapi/room_send.cpp b/lib/csapi/room_send.cpp index 0d25eb69..2bf42522 100644 --- a/lib/csapi/room_send.cpp +++ b/lib/csapi/room_send.cpp @@ -15,25 +15,24 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class SendMessageJob::Private { public: - QString eventId; + QString eventId; }; static const auto SendMessageJobName = QStringLiteral("SendMessageJob"); -SendMessageJob::SendMessageJob(const QString& roomId, const QString& eventType, const QString& txnId, const QJsonObject& body) +SendMessageJob::SendMessageJob(const QString& roomId, const QString& eventType, + const QString& txnId, const QJsonObject& body) : BaseJob(HttpVerb::Put, SendMessageJobName, - basePath % "/rooms/" % roomId % "/send/" % eventType % "/" % txnId) - , d(new Private) + basePath % "/rooms/" % roomId % "/send/" % eventType % "/" + % txnId), + d(new Private) { setRequestData(Data(toJson(body))); } SendMessageJob::~SendMessageJob() = default; -const QString& SendMessageJob::eventId() const -{ - return d->eventId; -} +const QString& SendMessageJob::eventId() const { return d->eventId; } BaseJob::Status SendMessageJob::parseJson(const QJsonDocument& data) { @@ -41,4 +40,3 @@ BaseJob::Status SendMessageJob::parseJson(const QJsonDocument& data) fromJson(json.value("event_id"_ls), d->eventId); return Success; } - diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h index 85c298e0..38e246e1 100644 --- a/lib/csapi/room_send.h +++ b/lib/csapi/room_send.h @@ -8,8 +8,7 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Send a message event to the given room. @@ -17,44 +16,46 @@ namespace QMatrixClient /// This endpoint is used to send a message event to a room. Message events /// allow access to historical events and pagination, making them suited /// for "once-off" activity in a room. - /// + /// /// The body of the request should be the content object of the event; the /// fields in this object will vary depending on the type of event. See /// `Room Events`_ for the m. event specification. class SendMessageJob : public BaseJob { public: - /*! Send a message event to the given room. - * \param roomId - * The room to send the event to. - * \param eventType - * The type of event to send. - * \param txnId - * The transaction ID for this event. Clients should generate an - * ID unique across requests with the same access token; it will be - * used by the server to ensure idempotency of requests. - * \param body - * This endpoint is used to send a message event to a room. Message events - * allow access to historical events and pagination, making them suited - * for "once-off" activity in a room. - * - * The body of the request should be the content object of the event; the - * fields in this object will vary depending on the type of event. See - * `Room Events`_ for the m. event specification. - */ - explicit SendMessageJob(const QString& roomId, const QString& eventType, const QString& txnId, const QJsonObject& body = {}); - ~SendMessageJob() override; - - // Result properties - - /// A unique identifier for the event. - const QString& eventId() const; + /*! Send a message event to the given room. + * \param roomId + * The room to send the event to. + * \param eventType + * The type of event to send. + * \param txnId + * The transaction ID for this event. Clients should generate an + * ID unique across requests with the same access token; it will be + * used by the server to ensure idempotency of requests. + * \param body + * This endpoint is used to send a message event to a room. Message + * events allow access to historical events and pagination, making them + * suited for "once-off" activity in a room. + * + * The body of the request should be the content object of the event; + * the fields in this object will vary depending on the type of event. + * See `Room Events`_ for the m. event specification. + */ + explicit SendMessageJob(const QString& roomId, const QString& eventType, + const QString& txnId, + const QJsonObject& body = {}); + ~SendMessageJob() override; + + // Result properties + + /// A unique identifier for the event. + const QString& eventId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/room_state.cpp b/lib/csapi/room_state.cpp index 3aa7d736..668a2931 100644 --- a/lib/csapi/room_state.cpp +++ b/lib/csapi/room_state.cpp @@ -15,25 +15,27 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class SetRoomStateWithKeyJob::Private { public: - QString eventId; + QString eventId; }; -static const auto SetRoomStateWithKeyJobName = QStringLiteral("SetRoomStateWithKeyJob"); +static const auto SetRoomStateWithKeyJobName = + QStringLiteral("SetRoomStateWithKeyJob"); -SetRoomStateWithKeyJob::SetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey, const QJsonObject& body) +SetRoomStateWithKeyJob::SetRoomStateWithKeyJob(const QString& roomId, + const QString& eventType, + const QString& stateKey, + const QJsonObject& body) : BaseJob(HttpVerb::Put, SetRoomStateWithKeyJobName, - basePath % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey) - , d(new Private) + basePath % "/rooms/" % roomId % "/state/" % eventType % "/" + % stateKey), + d(new Private) { setRequestData(Data(toJson(body))); } SetRoomStateWithKeyJob::~SetRoomStateWithKeyJob() = default; -const QString& SetRoomStateWithKeyJob::eventId() const -{ - return d->eventId; -} +const QString& SetRoomStateWithKeyJob::eventId() const { return d->eventId; } BaseJob::Status SetRoomStateWithKeyJob::parseJson(const QJsonDocument& data) { @@ -45,25 +47,24 @@ BaseJob::Status SetRoomStateWithKeyJob::parseJson(const QJsonDocument& data) class SetRoomStateJob::Private { public: - QString eventId; + QString eventId; }; static const auto SetRoomStateJobName = QStringLiteral("SetRoomStateJob"); -SetRoomStateJob::SetRoomStateJob(const QString& roomId, const QString& eventType, const QJsonObject& body) +SetRoomStateJob::SetRoomStateJob(const QString& roomId, + const QString& eventType, + const QJsonObject& body) : BaseJob(HttpVerb::Put, SetRoomStateJobName, - basePath % "/rooms/" % roomId % "/state/" % eventType) - , d(new Private) + basePath % "/rooms/" % roomId % "/state/" % eventType), + d(new Private) { setRequestData(Data(toJson(body))); } SetRoomStateJob::~SetRoomStateJob() = default; -const QString& SetRoomStateJob::eventId() const -{ - return d->eventId; -} +const QString& SetRoomStateJob::eventId() const { return d->eventId; } BaseJob::Status SetRoomStateJob::parseJson(const QJsonDocument& data) { @@ -71,4 +72,3 @@ BaseJob::Status SetRoomStateJob::parseJson(const QJsonDocument& data) fromJson(json.value("event_id"_ls), d->eventId); return Success; } - diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h index 67420545..07407af2 100644 --- a/lib/csapi/room_state.h +++ b/lib/csapi/room_state.h @@ -8,8 +8,7 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Send a state event to the given room. @@ -17,51 +16,55 @@ namespace QMatrixClient /// State events can be sent using this endpoint. These events will be /// overwritten if ````, ```` and ```` all /// match. - /// + /// /// Requests to this endpoint **cannot use transaction IDs** /// like other ``PUT`` paths because they cannot be differentiated from the /// ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - /// + /// /// The body of the request should be the content object of the event; the /// fields in this object will vary depending on the type of event. See /// `Room Events`_ for the ``m.`` event specification. class SetRoomStateWithKeyJob : public BaseJob { public: - /*! Send a state event to the given room. - * \param roomId - * The room to set the state in - * \param eventType - * The type of event to send. - * \param stateKey - * The state_key for the state to send. Defaults to the empty string. - * \param body - * State events can be sent using this endpoint. These events will be - * overwritten if ````, ```` and ```` all - * match. - * - * Requests to this endpoint **cannot use transaction IDs** - * like other ``PUT`` paths because they cannot be differentiated from the - * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - * - * The body of the request should be the content object of the event; the - * fields in this object will vary depending on the type of event. See - * `Room Events`_ for the ``m.`` event specification. - */ - explicit SetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey, const QJsonObject& body = {}); - ~SetRoomStateWithKeyJob() override; + /*! Send a state event to the given room. + * \param roomId + * The room to set the state in + * \param eventType + * The type of event to send. + * \param stateKey + * The state_key for the state to send. Defaults to the empty string. + * \param body + * State events can be sent using this endpoint. These events will be + * overwritten if ````, ```` and ```` + * all match. + * + * Requests to this endpoint **cannot use transaction IDs** + * like other ``PUT`` paths because they cannot be differentiated from + * the + * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. + * + * The body of the request should be the content object of the event; + * the fields in this object will vary depending on the type of event. + * See `Room Events`_ for the ``m.`` event specification. + */ + explicit SetRoomStateWithKeyJob(const QString& roomId, + const QString& eventType, + const QString& stateKey, + const QJsonObject& body = {}); + ~SetRoomStateWithKeyJob() override; - // Result properties + // Result properties - /// A unique identifier for the event. - const QString& eventId() const; + /// A unique identifier for the event. + const QString& eventId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Send a state event to the given room. @@ -69,50 +72,56 @@ namespace QMatrixClient /// State events can be sent using this endpoint. This endpoint is /// equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` /// with an empty `stateKey`. Previous state events with matching - /// `` and ``, and empty ``, will be overwritten. - /// + /// `` and ``, and empty ``, will be + /// overwritten. + /// /// Requests to this endpoint **cannot use transaction IDs** /// like other ``PUT`` paths because they cannot be differentiated from the /// ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - /// + /// /// The body of the request should be the content object of the event; the /// fields in this object will vary depending on the type of event. See /// `Room Events`_ for the ``m.`` event specification. class SetRoomStateJob : public BaseJob { public: - /*! Send a state event to the given room. - * \param roomId - * The room to set the state in - * \param eventType - * The type of event to send. - * \param body - * State events can be sent using this endpoint. This endpoint is - * equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` - * with an empty `stateKey`. Previous state events with matching - * `` and ``, and empty ``, will be overwritten. - * - * Requests to this endpoint **cannot use transaction IDs** - * like other ``PUT`` paths because they cannot be differentiated from the - * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - * - * The body of the request should be the content object of the event; the - * fields in this object will vary depending on the type of event. See - * `Room Events`_ for the ``m.`` event specification. - */ - explicit SetRoomStateJob(const QString& roomId, const QString& eventType, const QJsonObject& body = {}); - ~SetRoomStateJob() override; + /*! Send a state event to the given room. + * \param roomId + * The room to set the state in + * \param eventType + * The type of event to send. + * \param body + * State events can be sent using this endpoint. This endpoint is + * equivalent to calling + * `/rooms/{roomId}/state/{eventType}/{stateKey}` with an empty + * `stateKey`. Previous state events with matching + * `` and ``, and empty ``, will be + * overwritten. + * + * Requests to this endpoint **cannot use transaction IDs** + * like other ``PUT`` paths because they cannot be differentiated from + * the + * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. + * + * The body of the request should be the content object of the event; + * the fields in this object will vary depending on the type of event. + * See `Room Events`_ for the ``m.`` event specification. + */ + explicit SetRoomStateJob(const QString& roomId, + const QString& eventType, + const QJsonObject& body = {}); + ~SetRoomStateJob() override; - // Result properties + // Result properties - /// A unique identifier for the event. - const QString& eventId() const; + /// A unique identifier for the event. + const QString& eventId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/room_upgrades.cpp b/lib/csapi/room_upgrades.cpp index f58fd675..b9f285e8 100644 --- a/lib/csapi/room_upgrades.cpp +++ b/lib/csapi/room_upgrades.cpp @@ -15,15 +15,15 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class UpgradeRoomJob::Private { public: - QString replacementRoom; + QString replacementRoom; }; static const auto UpgradeRoomJobName = QStringLiteral("UpgradeRoomJob"); UpgradeRoomJob::UpgradeRoomJob(const QString& roomId, const QString& newVersion) : BaseJob(HttpVerb::Post, UpgradeRoomJobName, - basePath % "/rooms/" % roomId % "/upgrade") - , d(new Private) + basePath % "/rooms/" % roomId % "/upgrade"), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("new_version"), newVersion); @@ -42,8 +42,7 @@ BaseJob::Status UpgradeRoomJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("replacement_room"_ls)) return { JsonParseError, - "The key 'replacement_room' not found in the response" }; + "The key 'replacement_room' not found in the response" }; fromJson(json.value("replacement_room"_ls), d->replacementRoom); return Success; } - diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index 6f712f10..df30a562 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -6,38 +6,37 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Upgrades a room to a new room version. /// /// Upgrades the given room to a particular room version, migrating as much - /// data as possible over to the new room. See the `room_upgrades <#room-upgrades>`_ - /// module for more information on what this entails. + /// data as possible over to the new room. See the `room_upgrades + /// <#room-upgrades>`_ module for more information on what this entails. class UpgradeRoomJob : public BaseJob { public: - /*! Upgrades a room to a new room version. - * \param roomId - * The ID of the room to upgrade. - * \param newVersion - * The new version for the room. - */ - explicit UpgradeRoomJob(const QString& roomId, const QString& newVersion); - ~UpgradeRoomJob() override; + /*! Upgrades a room to a new room version. + * \param roomId + * The ID of the room to upgrade. + * \param newVersion + * The new version for the room. + */ + explicit UpgradeRoomJob(const QString& roomId, + const QString& newVersion); + ~UpgradeRoomJob() override; - // Result properties + // Result properties - /// The ID of the new room. - const QString& replacementRoom() const; + /// The ID of the new room. + const QString& replacementRoom() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/rooms.cpp b/lib/csapi/rooms.cpp index 0b08ccec..b9de276c 100644 --- a/lib/csapi/rooms.cpp +++ b/lib/csapi/rooms.cpp @@ -15,30 +15,30 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetOneRoomEventJob::Private { public: - EventPtr data; + EventPtr data; }; -QUrl GetOneRoomEventJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId) +QUrl GetOneRoomEventJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/event/" % eventId); + basePath % "/rooms/" % roomId % "/event/" + % eventId); } static const auto GetOneRoomEventJobName = QStringLiteral("GetOneRoomEventJob"); -GetOneRoomEventJob::GetOneRoomEventJob(const QString& roomId, const QString& eventId) +GetOneRoomEventJob::GetOneRoomEventJob(const QString& roomId, + const QString& eventId) : BaseJob(HttpVerb::Get, GetOneRoomEventJobName, - basePath % "/rooms/" % roomId % "/event/" % eventId) - , d(new Private) + basePath % "/rooms/" % roomId % "/event/" % eventId), + d(new Private) { } GetOneRoomEventJob::~GetOneRoomEventJob() = default; -EventPtr&& GetOneRoomEventJob::data() -{ - return std::move(d->data); -} +EventPtr&& GetOneRoomEventJob::data() { return std::move(d->data); } BaseJob::Status GetOneRoomEventJob::parseJson(const QJsonDocument& data) { @@ -46,61 +46,69 @@ BaseJob::Status GetOneRoomEventJob::parseJson(const QJsonDocument& data) return Success; } -QUrl GetRoomStateWithKeyJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType, const QString& stateKey) +QUrl GetRoomStateWithKeyJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventType, + const QString& stateKey) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey); + basePath % "/rooms/" % roomId % "/state/" + % eventType % "/" % stateKey); } -static const auto GetRoomStateWithKeyJobName = QStringLiteral("GetRoomStateWithKeyJob"); +static const auto GetRoomStateWithKeyJobName = + QStringLiteral("GetRoomStateWithKeyJob"); -GetRoomStateWithKeyJob::GetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey) +GetRoomStateWithKeyJob::GetRoomStateWithKeyJob(const QString& roomId, + const QString& eventType, + const QString& stateKey) : BaseJob(HttpVerb::Get, GetRoomStateWithKeyJobName, - basePath % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey) + basePath % "/rooms/" % roomId % "/state/" % eventType % "/" + % stateKey) { } -QUrl GetRoomStateByTypeJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType) +QUrl GetRoomStateByTypeJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventType) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/state/" % eventType); + basePath % "/rooms/" % roomId % "/state/" + % eventType); } -static const auto GetRoomStateByTypeJobName = QStringLiteral("GetRoomStateByTypeJob"); +static const auto GetRoomStateByTypeJobName = + QStringLiteral("GetRoomStateByTypeJob"); -GetRoomStateByTypeJob::GetRoomStateByTypeJob(const QString& roomId, const QString& eventType) +GetRoomStateByTypeJob::GetRoomStateByTypeJob(const QString& roomId, + const QString& eventType) : BaseJob(HttpVerb::Get, GetRoomStateByTypeJobName, - basePath % "/rooms/" % roomId % "/state/" % eventType) + basePath % "/rooms/" % roomId % "/state/" % eventType) { } class GetRoomStateJob::Private { public: - StateEvents data; + StateEvents data; }; QUrl GetRoomStateJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/state"); + basePath % "/rooms/" % roomId % "/state"); } static const auto GetRoomStateJobName = QStringLiteral("GetRoomStateJob"); GetRoomStateJob::GetRoomStateJob(const QString& roomId) : BaseJob(HttpVerb::Get, GetRoomStateJobName, - basePath % "/rooms/" % roomId % "/state") - , d(new Private) + basePath % "/rooms/" % roomId % "/state"), + d(new Private) { } GetRoomStateJob::~GetRoomStateJob() = default; -StateEvents&& GetRoomStateJob::data() -{ - return std::move(d->data); -} +StateEvents&& GetRoomStateJob::data() { return std::move(d->data); } BaseJob::Status GetRoomStateJob::parseJson(const QJsonDocument& data) { @@ -111,10 +119,12 @@ BaseJob::Status GetRoomStateJob::parseJson(const QJsonDocument& data) class GetMembersByRoomJob::Private { public: - EventsArray chunk; + EventsArray chunk; }; -BaseJob::Query queryToGetMembersByRoom(const QString& at, const QString& membership, const QString& notMembership) +BaseJob::Query queryToGetMembersByRoom(const QString& at, + const QString& membership, + const QString& notMembership) { BaseJob::Query _q; addParam(_q, QStringLiteral("at"), at); @@ -123,20 +133,27 @@ BaseJob::Query queryToGetMembersByRoom(const QString& at, const QString& members return _q; } -QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& at, const QString& membership, const QString& notMembership) +QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& at, + const QString& membership, + const QString& notMembership) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/members", + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/rooms/" % roomId % "/members", queryToGetMembersByRoom(at, membership, notMembership)); } -static const auto GetMembersByRoomJobName = QStringLiteral("GetMembersByRoomJob"); +static const auto GetMembersByRoomJobName = + QStringLiteral("GetMembersByRoomJob"); -GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, const QString& at, const QString& membership, const QString& notMembership) +GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, + const QString& at, + const QString& membership, + const QString& notMembership) : BaseJob(HttpVerb::Get, GetMembersByRoomJobName, - basePath % "/rooms/" % roomId % "/members", - queryToGetMembersByRoom(at, membership, notMembership)) - , d(new Private) + basePath % "/rooms/" % roomId % "/members", + queryToGetMembersByRoom(at, membership, notMembership)), + d(new Private) { } @@ -154,13 +171,13 @@ BaseJob::Status GetMembersByRoomJob::parseJson(const QJsonDocument& data) return Success; } -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, GetJoinedMembersByRoomJob::RoomMember& result) + template <> + struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetJoinedMembersByRoomJob::RoomMember& result) { fromJson(jo.value("display_name"_ls), result.displayName); fromJson(jo.value("avatar_url"_ls), result.avatarUrl); @@ -171,27 +188,31 @@ namespace QMatrixClient class GetJoinedMembersByRoomJob::Private { public: - QHash joined; + QHash joined; }; -QUrl GetJoinedMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) +QUrl GetJoinedMembersByRoomJob::makeRequestUrl(QUrl baseUrl, + const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/joined_members"); + basePath % "/rooms/" % roomId + % "/joined_members"); } -static const auto GetJoinedMembersByRoomJobName = QStringLiteral("GetJoinedMembersByRoomJob"); +static const auto GetJoinedMembersByRoomJobName = + QStringLiteral("GetJoinedMembersByRoomJob"); GetJoinedMembersByRoomJob::GetJoinedMembersByRoomJob(const QString& roomId) : BaseJob(HttpVerb::Get, GetJoinedMembersByRoomJobName, - basePath % "/rooms/" % roomId % "/joined_members") - , d(new Private) + basePath % "/rooms/" % roomId % "/joined_members"), + d(new Private) { } GetJoinedMembersByRoomJob::~GetJoinedMembersByRoomJob() = default; -const QHash& GetJoinedMembersByRoomJob::joined() const +const QHash& +GetJoinedMembersByRoomJob::joined() const { return d->joined; } @@ -202,4 +223,3 @@ BaseJob::Status GetJoinedMembersByRoomJob::parseJson(const QJsonDocument& data) fromJson(json.value("joined"_ls), d->joined); return Success; } - diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index b4d3d9b6..20bdd20e 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -6,51 +6,53 @@ #include "jobs/basejob.h" -#include "events/roommemberevent.h" +#include "converters.h" #include "events/eventloader.h" +#include "events/roommemberevent.h" #include -#include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Get a single event by event ID. /// - /// Get a single event based on ``roomId/eventId``. You must have permission to - /// retrieve this event e.g. by being a member in the room for this event. + /// Get a single event based on ``roomId/eventId``. You must have permission + /// to retrieve this event e.g. by being a member in the room for this + /// event. class GetOneRoomEventJob : public BaseJob { public: - /*! Get a single event by event ID. - * \param roomId - * The ID of the room the event is in. - * \param eventId - * The event ID to get. - */ - explicit GetOneRoomEventJob(const QString& roomId, const QString& eventId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetOneRoomEventJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId); - - ~GetOneRoomEventJob() override; - - // Result properties - - /// The full event. - EventPtr&& data(); + /*! Get a single event by event ID. + * \param roomId + * The ID of the room the event is in. + * \param eventId + * The event ID to get. + */ + explicit GetOneRoomEventJob(const QString& roomId, + const QString& eventId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetOneRoomEventJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventId); + + ~GetOneRoomEventJob() override; + + // Result properties + + /// The full event. + EventPtr&& data(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get the state identified by the type and key. @@ -62,24 +64,27 @@ namespace QMatrixClient class GetRoomStateWithKeyJob : public BaseJob { public: - /*! Get the state identified by the type and key. - * \param roomId - * The room to look up the state in. - * \param eventType - * The type of state to look up. - * \param stateKey - * The key of the state to look up. - */ - explicit GetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomStateWithKeyJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType, const QString& stateKey); - + /*! Get the state identified by the type and key. + * \param roomId + * The room to look up the state in. + * \param eventType + * The type of state to look up. + * \param stateKey + * The key of the state to look up. + */ + explicit GetRoomStateWithKeyJob(const QString& roomId, + const QString& eventType, + const QString& stateKey); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomStateWithKeyJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventType, + const QString& stateKey); }; /// Get the state identified by the type, with the empty state key. @@ -88,27 +93,28 @@ namespace QMatrixClient /// joined to the room then the state is taken from the current /// state of the room. If the user has left the room then the state is /// taken from the state of the room when they left. - /// + /// /// This looks up the state event with the empty state key. class GetRoomStateByTypeJob : public BaseJob { public: - /*! Get the state identified by the type, with the empty state key. - * \param roomId - * The room to look up the state in. - * \param eventType - * The type of state to look up. - */ - explicit GetRoomStateByTypeJob(const QString& roomId, const QString& eventType); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomStateByTypeJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType); - + /*! Get the state identified by the type, with the empty state key. + * \param roomId + * The room to look up the state in. + * \param eventType + * The type of state to look up. + */ + explicit GetRoomStateByTypeJob(const QString& roomId, + const QString& eventType); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomStateByTypeJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& eventType); }; /// Get all state events in the current state of a room. @@ -117,36 +123,36 @@ namespace QMatrixClient class GetRoomStateJob : public BaseJob { public: - /*! Get all state events in the current state of a room. - * \param roomId - * The room to look up the state for. - */ - explicit GetRoomStateJob(const QString& roomId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomStateJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - - ~GetRoomStateJob() override; - - // Result properties - - /// If the user is a member of the room this will be the - /// current state of the room as a list of events. If the user - /// has left the room then this will be the state of the room - /// when they left as a list of events. - StateEvents&& data(); + /*! Get all state events in the current state of a room. + * \param roomId + * The room to look up the state for. + */ + explicit GetRoomStateJob(const QString& roomId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomStateJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); + + ~GetRoomStateJob() override; + + // Result properties + + /// If the user is a member of the room this will be the + /// current state of the room as a list of events. If the user + /// has left the room then this will be the state of the room + /// when they left as a list of events. + StateEvents&& data(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Get the m.room.member events for the room. @@ -155,89 +161,104 @@ namespace QMatrixClient class GetMembersByRoomJob : public BaseJob { public: - /*! Get the m.room.member events for the room. - * \param roomId - * The room to get the member events for. - * \param at - * The token defining the timeline position as-of which to return - * the list of members. This token can be obtained from a batch token - * returned for each room by the sync API, or from - * a ``start``/``end`` token returned by a ``/messages`` request. - * \param membership - * Only return users with the specified membership - * \param notMembership - * Only return users with membership state other than specified - */ - explicit GetMembersByRoomJob(const QString& roomId, const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetMembersByRoomJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); - - ~GetMembersByRoomJob() override; - - // Result properties - - /// Get the list of members for this room. - EventsArray&& chunk(); + /*! Get the m.room.member events for the room. + * \param roomId + * The room to get the member events for. + * \param at + * The token defining the timeline position as-of which to return + * the list of members. This token can be obtained from a batch token + * returned for each room by the sync API, or from + * a ``start``/``end`` token returned by a ``/messages`` request. + * \param membership + * Only return users with the specified membership + * \param notMembership + * Only return users with membership state other than specified + */ + explicit GetMembersByRoomJob(const QString& roomId, + const QString& at = {}, + const QString& membership = {}, + const QString& notMembership = {}); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetMembersByRoomJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, + const QString& at = {}, + const QString& membership = {}, + const QString& notMembership = {}); + + ~GetMembersByRoomJob() override; + + // Result properties + + /// Get the list of members for this room. + EventsArray&& chunk(); protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Gets the list of currently joined users and their profile data. /// - /// This API returns a map of MXIDs to member info objects for members of the room. The current user must be in the room for it to work, unless it is an Application Service in which case any of the AS's users must be in the room. This API is primarily for Application Services and should be faster to respond than ``/members`` as it can be implemented more efficiently on the server. + /// This API returns a map of MXIDs to member info objects for members of + /// the room. The current user must be in the room for it to work, unless it + /// is an Application Service in which case any of the AS's users must be in + /// the room. This API is primarily for Application Services and should be + /// faster to respond than ``/members`` as it can be implemented more + /// efficiently on the server. class GetJoinedMembersByRoomJob : public BaseJob { public: - // Inner data structures - - /// This API returns a map of MXIDs to member info objects for members of the room. The current user must be in the room for it to work, unless it is an Application Service in which case any of the AS's users must be in the room. This API is primarily for Application Services and should be faster to respond than ``/members`` as it can be implemented more efficiently on the server. - struct RoomMember - { - /// The display name of the user this object is representing. - QString displayName; - /// The mxc avatar url of the user this object is representing. - QString avatarUrl; - }; - - // Construction/destruction - - /*! Gets the list of currently joined users and their profile data. - * \param roomId - * The room to get the members of. - */ - explicit GetJoinedMembersByRoomJob(const QString& roomId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetJoinedMembersByRoomJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - - ~GetJoinedMembersByRoomJob() override; - - // Result properties - - /// A map from user ID to a RoomMember object. - const QHash& joined() const; + // Inner data structures + + /// This API returns a map of MXIDs to member info objects for members + /// of the room. The current user must be in the room for it to work, + /// unless it is an Application Service in which case any of the AS's + /// users must be in the room. This API is primarily for Application + /// Services and should be faster to respond than ``/members`` as it can + /// be implemented more efficiently on the server. + struct RoomMember { + /// The display name of the user this object is representing. + QString displayName; + /// The mxc avatar url of the user this object is representing. + QString avatarUrl; + }; + + // Construction/destruction + + /*! Gets the list of currently joined users and their profile data. + * \param roomId + * The room to get the members of. + */ + explicit GetJoinedMembersByRoomJob(const QString& roomId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetJoinedMembersByRoomJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); + + ~GetJoinedMembersByRoomJob() override; + + // Result properties + + /// A map from user ID to a RoomMember object. + const QHash& joined() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/search.cpp b/lib/csapi/search.cpp index a5f83c79..22aaf616 100644 --- a/lib/csapi/search.cpp +++ b/lib/csapi/search.cpp @@ -12,70 +12,73 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const SearchJob::IncludeEventContext& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const SearchJob::IncludeEventContext& pod) { - addParam(jo, QStringLiteral("before_limit"), pod.beforeLimit); - addParam(jo, QStringLiteral("after_limit"), pod.afterLimit); - addParam(jo, QStringLiteral("include_profile"), pod.includeProfile); + addParam(jo, QStringLiteral("before_limit"), + pod.beforeLimit); + addParam(jo, QStringLiteral("after_limit"), + pod.afterLimit); + addParam(jo, QStringLiteral("include_profile"), + pod.includeProfile); } }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const SearchJob::Group& pod) { addParam(jo, QStringLiteral("key"), pod.key); } }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const SearchJob::Groupings& pod) { addParam(jo, QStringLiteral("group_by"), pod.groupBy); } }; - template <> struct JsonObjectConverter - { - static void dumpTo(QJsonObject& jo, const SearchJob::RoomEventsCriteria& pod) + template <> struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const SearchJob::RoomEventsCriteria& pod) { addParam<>(jo, QStringLiteral("search_term"), pod.searchTerm); addParam(jo, QStringLiteral("keys"), pod.keys); addParam(jo, QStringLiteral("filter"), pod.filter); addParam(jo, QStringLiteral("order_by"), pod.orderBy); - addParam(jo, QStringLiteral("event_context"), pod.eventContext); - addParam(jo, QStringLiteral("include_state"), pod.includeState); - addParam(jo, QStringLiteral("groupings"), pod.groupings); + addParam(jo, QStringLiteral("event_context"), + pod.eventContext); + addParam(jo, QStringLiteral("include_state"), + pod.includeState); + addParam(jo, QStringLiteral("groupings"), + pod.groupings); } }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const SearchJob::Categories& pod) { - addParam(jo, QStringLiteral("room_events"), pod.roomEvents); + addParam(jo, QStringLiteral("room_events"), + pod.roomEvents); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchJob::UserProfile& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchJob::UserProfile& result) { fromJson(jo.value("displayname"_ls), result.displayname); fromJson(jo.value("avatar_url"_ls), result.avatarUrl); } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchJob::EventContext& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchJob::EventContext& result) { fromJson(jo.value("start"_ls), result.begin); fromJson(jo.value("end"_ls), result.end); @@ -85,8 +88,7 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, SearchJob::Result& result) { fromJson(jo.value("rank"_ls), result.rank); @@ -95,9 +97,9 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchJob::GroupValue& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchJob::GroupValue& result) { fromJson(jo.value("next_batch"_ls), result.nextBatch); fromJson(jo.value("order"_ls), result.order); @@ -105,9 +107,9 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchJob::ResultRoomEvents& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchJob::ResultRoomEvents& result) { fromJson(jo.value("count"_ls), result.count); fromJson(jo.value("highlights"_ls), result.highlights); @@ -118,9 +120,9 @@ namespace QMatrixClient } }; - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchJob::ResultCategories& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchJob::ResultCategories& result) { fromJson(jo.value("room_events"_ls), result.roomEvents); } @@ -130,7 +132,7 @@ namespace QMatrixClient class SearchJob::Private { public: - ResultCategories searchCategories; + ResultCategories searchCategories; }; BaseJob::Query queryToSearch(const QString& nextBatch) @@ -142,11 +144,11 @@ BaseJob::Query queryToSearch(const QString& nextBatch) static const auto SearchJobName = QStringLiteral("SearchJob"); -SearchJob::SearchJob(const Categories& searchCategories, const QString& nextBatch) - : BaseJob(HttpVerb::Post, SearchJobName, - basePath % "/search", - queryToSearch(nextBatch)) - , d(new Private) +SearchJob::SearchJob(const Categories& searchCategories, + const QString& nextBatch) + : BaseJob(HttpVerb::Post, SearchJobName, basePath % "/search", + queryToSearch(nextBatch)), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("search_categories"), searchCategories); @@ -165,8 +167,7 @@ BaseJob::Status SearchJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("search_categories"_ls)) return { JsonParseError, - "The key 'search_categories' not found in the response" }; + "The key 'search_categories' not found in the response" }; fromJson(json.value("search_categories"_ls), d->searchCategories); return Success; } - diff --git a/lib/csapi/search.h b/lib/csapi/search.h index 86a0ee92..41761030 100644 --- a/lib/csapi/search.h +++ b/lib/csapi/search.h @@ -6,15 +6,14 @@ #include "jobs/basejob.h" -#include "csapi/definitions/room_event_filter.h" #include "converters.h" -#include +#include "csapi/definitions/room_event_filter.h" #include "events/eventloader.h" -#include #include +#include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Perform a server-side search. @@ -23,183 +22,175 @@ namespace QMatrixClient class SearchJob : public BaseJob { public: - // Inner data structures - + // Inner data structures + + /// Configures whether any context for the events + /// returned are included in the response. + struct IncludeEventContext { + /// How many events before the result are + /// returned. By default, this is ``5``. + Omittable beforeLimit; + /// How many events after the result are + /// returned. By default, this is ``5``. + Omittable afterLimit; + /// Requests that the server returns the + /// historic profile information for the users + /// that sent the events that were returned. + /// By default, this is ``false``. + Omittable includeProfile; + }; + + /// Configuration for group. + struct Group { + /// Key that defines the group. + QString key; + }; + + /// Requests that the server partitions the result set + /// based on the provided list of keys. + struct Groupings { + /// List of groups to request. + QVector groupBy; + }; + + /// Mapping of category name to search criteria. + struct RoomEventsCriteria { + /// The string to search events for + QString searchTerm; + /// The keys to search. Defaults to all. + QStringList keys; + /// This takes a `filter`_. + Omittable filter; + /// The order in which to search for results. + /// By default, this is ``"rank"``. + QString orderBy; /// Configures whether any context for the events /// returned are included in the response. - struct IncludeEventContext - { - /// How many events before the result are - /// returned. By default, this is ``5``. - Omittable beforeLimit; - /// How many events after the result are - /// returned. By default, this is ``5``. - Omittable afterLimit; - /// Requests that the server returns the - /// historic profile information for the users - /// that sent the events that were returned. - /// By default, this is ``false``. - Omittable includeProfile; - }; - - /// Configuration for group. - struct Group - { - /// Key that defines the group. - QString key; - }; - + Omittable eventContext; + /// Requests the server return the current state for + /// each room returned. + Omittable includeState; /// Requests that the server partitions the result set /// based on the provided list of keys. - struct Groupings - { - /// List of groups to request. - QVector groupBy; - }; + Omittable groupings; + }; + /// Describes which categories to search in and their criteria. + struct Categories { /// Mapping of category name to search criteria. - struct RoomEventsCriteria - { - /// The string to search events for - QString searchTerm; - /// The keys to search. Defaults to all. - QStringList keys; - /// This takes a `filter`_. - Omittable filter; - /// The order in which to search for results. - /// By default, this is ``"rank"``. - QString orderBy; - /// Configures whether any context for the events - /// returned are included in the response. - Omittable eventContext; - /// Requests the server return the current state for - /// each room returned. - Omittable includeState; - /// Requests that the server partitions the result set - /// based on the provided list of keys. - Omittable groupings; - }; - - /// Describes which categories to search in and their criteria. - struct Categories - { - /// Mapping of category name to search criteria. - Omittable roomEvents; - }; + Omittable roomEvents; + }; + /// Performs a full text search across different categories. + struct UserProfile { /// Performs a full text search across different categories. - struct UserProfile - { - /// Performs a full text search across different categories. - QString displayname; - /// Performs a full text search across different categories. - QString avatarUrl; - }; - + QString displayname; + /// Performs a full text search across different categories. + QString avatarUrl; + }; + + /// Context for result, if requested. + struct EventContext { + /// Pagination token for the start of the chunk + QString begin; + /// Pagination token for the end of the chunk + QString end; + /// The historic profile information of the + /// users that sent the events returned. + /// + /// The ``string`` key is the user ID for which + /// the profile belongs to. + QHash profileInfo; + /// Events just before the result. + RoomEvents eventsBefore; + /// Events just after the result. + RoomEvents eventsAfter; + }; + + /// The result object. + struct Result { + /// A number that describes how closely this result matches the + /// search. Higher is closer. + Omittable rank; + /// The event that matched. + RoomEventPtr result; /// Context for result, if requested. - struct EventContext - { - /// Pagination token for the start of the chunk - QString begin; - /// Pagination token for the end of the chunk - QString end; - /// The historic profile information of the - /// users that sent the events returned. - /// - /// The ``string`` key is the user ID for which - /// the profile belongs to. - QHash profileInfo; - /// Events just before the result. - RoomEvents eventsBefore; - /// Events just after the result. - RoomEvents eventsAfter; - }; - - /// The result object. - struct Result - { - /// A number that describes how closely this result matches the search. Higher is closer. - Omittable rank; - /// The event that matched. - RoomEventPtr result; - /// Context for result, if requested. - Omittable context; - }; - - /// The results for a particular group value. - struct GroupValue - { - /// Token that can be used to get the next batch - /// of results in the group, by passing as the - /// `next_batch` parameter to the next call. If - /// this field is absent, there are no more - /// results in this group. - QString nextBatch; - /// Key that can be used to order different - /// groups. - Omittable order; - /// Which results are in this group. - QStringList results; - }; - + Omittable context; + }; + + /// The results for a particular group value. + struct GroupValue { + /// Token that can be used to get the next batch + /// of results in the group, by passing as the + /// `next_batch` parameter to the next call. If + /// this field is absent, there are no more + /// results in this group. + QString nextBatch; + /// Key that can be used to order different + /// groups. + Omittable order; + /// Which results are in this group. + QStringList results; + }; + + /// Mapping of category name to search criteria. + struct ResultRoomEvents { + /// An approximate count of the total number of results found. + Omittable count; + /// List of words which should be highlighted, useful for stemming + /// which may change the query terms. + QStringList highlights; + /// List of results in the requested order. + std::vector results; + /// The current state for every room in the results. + /// This is included if the request had the + /// ``include_state`` key set with a value of ``true``. + /// + /// The ``string`` key is the room ID for which the ``State + /// Event`` array belongs to. + std::unordered_map state; + /// Any groups that were requested. + /// + /// The outer ``string`` key is the group key requested (eg: + /// ``room_id`` or ``sender``). The inner ``string`` key is the + /// grouped value (eg: a room's ID or a user's ID). + QHash> groups; + /// Token that can be used to get the next batch of + /// results, by passing as the `next_batch` parameter to + /// the next call. If this field is absent, there are no + /// more results. + QString nextBatch; + }; + + /// Describes which categories to search in and their criteria. + struct ResultCategories { /// Mapping of category name to search criteria. - struct ResultRoomEvents - { - /// An approximate count of the total number of results found. - Omittable count; - /// List of words which should be highlighted, useful for stemming which may change the query terms. - QStringList highlights; - /// List of results in the requested order. - std::vector results; - /// The current state for every room in the results. - /// This is included if the request had the - /// ``include_state`` key set with a value of ``true``. - /// - /// The ``string`` key is the room ID for which the ``State - /// Event`` array belongs to. - std::unordered_map state; - /// Any groups that were requested. - /// - /// The outer ``string`` key is the group key requested (eg: ``room_id`` - /// or ``sender``). The inner ``string`` key is the grouped value (eg: - /// a room's ID or a user's ID). - QHash> groups; - /// Token that can be used to get the next batch of - /// results, by passing as the `next_batch` parameter to - /// the next call. If this field is absent, there are no - /// more results. - QString nextBatch; - }; - - /// Describes which categories to search in and their criteria. - struct ResultCategories - { - /// Mapping of category name to search criteria. - Omittable roomEvents; - }; - - // Construction/destruction - - /*! Perform a server-side search. - * \param searchCategories - * Describes which categories to search in and their criteria. - * \param nextBatch - * The point to return events from. If given, this should be a - * ``next_batch`` result from a previous call to this endpoint. - */ - explicit SearchJob(const Categories& searchCategories, const QString& nextBatch = {}); - ~SearchJob() override; - - // Result properties - - /// Describes which categories to search in and their criteria. - const ResultCategories& searchCategories() const; + Omittable roomEvents; + }; + + // Construction/destruction + + /*! Perform a server-side search. + * \param searchCategories + * Describes which categories to search in and their criteria. + * \param nextBatch + * The point to return events from. If given, this should be a + * ``next_batch`` result from a previous call to this endpoint. + */ + explicit SearchJob(const Categories& searchCategories, + const QString& nextBatch = {}); + ~SearchJob() override; + + // Result properties + + /// Describes which categories to search in and their criteria. + const ResultCategories& searchCategories() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/sso_login_redirect.cpp b/lib/csapi/sso_login_redirect.cpp index 7323951c..92084d52 100644 --- a/lib/csapi/sso_login_redirect.cpp +++ b/lib/csapi/sso_login_redirect.cpp @@ -22,17 +22,15 @@ BaseJob::Query queryToRedirectToSSO(const QString& redirectUrl) QUrl RedirectToSSOJob::makeRequestUrl(QUrl baseUrl, const QString& redirectUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/login/sso/redirect", - queryToRedirectToSSO(redirectUrl)); + basePath % "/login/sso/redirect", + queryToRedirectToSSO(redirectUrl)); } static const auto RedirectToSSOJobName = QStringLiteral("RedirectToSSOJob"); RedirectToSSOJob::RedirectToSSOJob(const QString& redirectUrl) : BaseJob(HttpVerb::Get, RedirectToSSOJobName, - basePath % "/login/sso/redirect", - queryToRedirectToSSO(redirectUrl), - {}, false) + basePath % "/login/sso/redirect", + queryToRedirectToSSO(redirectUrl), {}, false) { } - diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h index c09365b0..2b0d3f65 100644 --- a/lib/csapi/sso_login_redirect.h +++ b/lib/csapi/sso_login_redirect.h @@ -6,34 +6,31 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Redirect the user's browser to the SSO interface. /// /// A web-based Matrix client should instruct the user's browser to /// navigate to this endpoint in order to log in via SSO. - /// + /// /// The server MUST respond with an HTTP redirect to the SSO interface. class RedirectToSSOJob : public BaseJob { public: - /*! Redirect the user's browser to the SSO interface. - * \param redirectUrl - * URI to which the user will be redirected after the homeserver has - * authenticated the user with SSO. - */ - explicit RedirectToSSOJob(const QString& redirectUrl); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * RedirectToSSOJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& redirectUrl); + /*! Redirect the user's browser to the SSO interface. + * \param redirectUrl + * URI to which the user will be redirected after the homeserver has + * authenticated the user with SSO. + */ + explicit RedirectToSSOJob(const QString& redirectUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * RedirectToSSOJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& redirectUrl); }; } // namespace QMatrixClient diff --git a/lib/csapi/tags.cpp b/lib/csapi/tags.cpp index 94026bb9..9f591447 100644 --- a/lib/csapi/tags.cpp +++ b/lib/csapi/tags.cpp @@ -12,12 +12,10 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void fillFrom(QJsonObject jo, GetRoomTagsJob::Tag& result) { fromJson(jo.take("order"_ls), result.order); @@ -29,21 +27,23 @@ namespace QMatrixClient class GetRoomTagsJob::Private { public: - QHash tags; + QHash tags; }; -QUrl GetRoomTagsJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId) +QUrl GetRoomTagsJob::makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags"); + basePath % "/user/" % userId % "/rooms/" + % roomId % "/tags"); } static const auto GetRoomTagsJobName = QStringLiteral("GetRoomTagsJob"); GetRoomTagsJob::GetRoomTagsJob(const QString& userId, const QString& roomId) : BaseJob(HttpVerb::Get, GetRoomTagsJobName, - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags") - , d(new Private) + basePath % "/user/" % userId % "/rooms/" % roomId % "/tags"), + d(new Private) { } @@ -63,26 +63,31 @@ BaseJob::Status GetRoomTagsJob::parseJson(const QJsonDocument& data) static const auto SetRoomTagJobName = QStringLiteral("SetRoomTagJob"); -SetRoomTagJob::SetRoomTagJob(const QString& userId, const QString& roomId, const QString& tag, Omittable order) +SetRoomTagJob::SetRoomTagJob(const QString& userId, const QString& roomId, + const QString& tag, Omittable order) : BaseJob(HttpVerb::Put, SetRoomTagJobName, - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) + basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" + % tag) { QJsonObject _data; addParam(_data, QStringLiteral("order"), order); setRequestData(_data); } -QUrl DeleteRoomTagJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& tag) +QUrl DeleteRoomTagJob::makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& roomId, const QString& tag) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag); + basePath % "/user/" % userId % "/rooms/" + % roomId % "/tags/" % tag); } static const auto DeleteRoomTagJobName = QStringLiteral("DeleteRoomTagJob"); -DeleteRoomTagJob::DeleteRoomTagJob(const QString& userId, const QString& roomId, const QString& tag) +DeleteRoomTagJob::DeleteRoomTagJob(const QString& userId, const QString& roomId, + const QString& tag) : BaseJob(HttpVerb::Delete, DeleteRoomTagJobName, - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) + basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" + % tag) { } - diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h index 2c20c2a2..dc683ef7 100644 --- a/lib/csapi/tags.h +++ b/lib/csapi/tags.h @@ -6,12 +6,11 @@ #include "jobs/basejob.h" -#include -#include #include "converters.h" +#include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// List the tags for a room. @@ -20,50 +19,50 @@ namespace QMatrixClient class GetRoomTagsJob : public BaseJob { public: - // Inner data structures + // Inner data structures + /// List the tags set by a user on a room. + struct Tag { + /// A number in a range ``[0,1]`` describing a relative + /// position of the room under the given tag. + Omittable order; /// List the tags set by a user on a room. - struct Tag - { - /// A number in a range ``[0,1]`` describing a relative - /// position of the room under the given tag. - Omittable order; - /// List the tags set by a user on a room. - QVariantHash additionalProperties; - }; - - // Construction/destruction - - /*! List the tags for a room. - * \param userId - * The id of the user to get tags for. The access token must be - * authorized to make requests for this user ID. - * \param roomId - * The ID of the room to get tags for. - */ - explicit GetRoomTagsJob(const QString& userId, const QString& roomId); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetRoomTagsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId); - - ~GetRoomTagsJob() override; - - // Result properties + QVariantHash additionalProperties; + }; - /// List the tags set by a user on a room. - const QHash& tags() const; + // Construction/destruction + + /*! List the tags for a room. + * \param userId + * The id of the user to get tags for. The access token must be + * authorized to make requests for this user ID. + * \param roomId + * The ID of the room to get tags for. + */ + explicit GetRoomTagsJob(const QString& userId, const QString& roomId); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetRoomTagsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& roomId); + + ~GetRoomTagsJob() override; + + // Result properties + + /// List the tags set by a user on a room. + const QHash& tags() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Add a tag to a room. @@ -72,19 +71,21 @@ namespace QMatrixClient class SetRoomTagJob : public BaseJob { public: - /*! Add a tag to a room. - * \param userId - * The id of the user to add a tag for. The access token must be - * authorized to make requests for this user ID. - * \param roomId - * The ID of the room to add a tag to. - * \param tag - * The tag to add. - * \param order - * A number in a range ``[0,1]`` describing a relative - * position of the room under the given tag. - */ - explicit SetRoomTagJob(const QString& userId, const QString& roomId, const QString& tag, Omittable order = none); + /*! Add a tag to a room. + * \param userId + * The id of the user to add a tag for. The access token must be + * authorized to make requests for this user ID. + * \param roomId + * The ID of the room to add a tag to. + * \param tag + * The tag to add. + * \param order + * A number in a range ``[0,1]`` describing a relative + * position of the room under the given tag. + */ + explicit SetRoomTagJob(const QString& userId, const QString& roomId, + const QString& tag, + Omittable order = none); }; /// Remove a tag from the room. @@ -93,24 +94,25 @@ namespace QMatrixClient class DeleteRoomTagJob : public BaseJob { public: - /*! Remove a tag from the room. - * \param userId - * The id of the user to remove a tag for. The access token must be - * authorized to make requests for this user ID. - * \param roomId - * The ID of the room to remove a tag from. - * \param tag - * The tag to remove. - */ - explicit DeleteRoomTagJob(const QString& userId, const QString& roomId, const QString& tag); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * DeleteRoomTagJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& tag); - + /*! Remove a tag from the room. + * \param userId + * The id of the user to remove a tag for. The access token must be + * authorized to make requests for this user ID. + * \param roomId + * The ID of the room to remove a tag from. + * \param tag + * The tag to remove. + */ + explicit DeleteRoomTagJob(const QString& userId, const QString& roomId, + const QString& tag); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * DeleteRoomTagJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, + const QString& roomId, const QString& tag); }; } // namespace QMatrixClient diff --git a/lib/csapi/third_party_lookup.cpp b/lib/csapi/third_party_lookup.cpp index 12cb7c59..339a0d90 100644 --- a/lib/csapi/third_party_lookup.cpp +++ b/lib/csapi/third_party_lookup.cpp @@ -15,21 +15,21 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetProtocolsJob::Private { public: - QHash data; + QHash data; }; QUrl GetProtocolsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/protocols"); + basePath % "/thirdparty/protocols"); } static const auto GetProtocolsJobName = QStringLiteral("GetProtocolsJob"); GetProtocolsJob::GetProtocolsJob() : BaseJob(HttpVerb::Get, GetProtocolsJobName, - basePath % "/thirdparty/protocols") - , d(new Private) + basePath % "/thirdparty/protocols"), + d(new Private) { } @@ -49,21 +49,23 @@ BaseJob::Status GetProtocolsJob::parseJson(const QJsonDocument& data) class GetProtocolMetadataJob::Private { public: - ThirdPartyProtocol data; + ThirdPartyProtocol data; }; -QUrl GetProtocolMetadataJob::makeRequestUrl(QUrl baseUrl, const QString& protocol) +QUrl GetProtocolMetadataJob::makeRequestUrl(QUrl baseUrl, + const QString& protocol) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/protocol/" % protocol); + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/thirdparty/protocol/" % protocol); } -static const auto GetProtocolMetadataJobName = QStringLiteral("GetProtocolMetadataJob"); +static const auto GetProtocolMetadataJobName = + QStringLiteral("GetProtocolMetadataJob"); GetProtocolMetadataJob::GetProtocolMetadataJob(const QString& protocol) : BaseJob(HttpVerb::Get, GetProtocolMetadataJobName, - basePath % "/thirdparty/protocol/" % protocol) - , d(new Private) + basePath % "/thirdparty/protocol/" % protocol), + d(new Private) { } @@ -83,7 +85,7 @@ BaseJob::Status GetProtocolMetadataJob::parseJson(const QJsonDocument& data) class QueryLocationByProtocolJob::Private { public: - QVector data; + QVector data; }; BaseJob::Query queryToQueryLocationByProtocol(const QString& searchFields) @@ -93,20 +95,24 @@ BaseJob::Query queryToQueryLocationByProtocol(const QString& searchFields) return _q; } -QUrl QueryLocationByProtocolJob::makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& searchFields) +QUrl QueryLocationByProtocolJob::makeRequestUrl(QUrl baseUrl, + const QString& protocol, + const QString& searchFields) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/location/" % protocol, + return BaseJob::makeRequestUrl( + std::move(baseUrl), basePath % "/thirdparty/location/" % protocol, queryToQueryLocationByProtocol(searchFields)); } -static const auto QueryLocationByProtocolJobName = QStringLiteral("QueryLocationByProtocolJob"); +static const auto QueryLocationByProtocolJobName = + QStringLiteral("QueryLocationByProtocolJob"); -QueryLocationByProtocolJob::QueryLocationByProtocolJob(const QString& protocol, const QString& searchFields) +QueryLocationByProtocolJob::QueryLocationByProtocolJob( + const QString& protocol, const QString& searchFields) : BaseJob(HttpVerb::Get, QueryLocationByProtocolJobName, - basePath % "/thirdparty/location/" % protocol, - queryToQueryLocationByProtocol(searchFields)) - , d(new Private) + basePath % "/thirdparty/location/" % protocol, + queryToQueryLocationByProtocol(searchFields)), + d(new Private) { } @@ -126,7 +132,7 @@ BaseJob::Status QueryLocationByProtocolJob::parseJson(const QJsonDocument& data) class QueryUserByProtocolJob::Private { public: - QVector data; + QVector data; }; BaseJob::Query queryToQueryUserByProtocol(const QString& fields) @@ -136,20 +142,24 @@ BaseJob::Query queryToQueryUserByProtocol(const QString& fields) return _q; } -QUrl QueryUserByProtocolJob::makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& fields) +QUrl QueryUserByProtocolJob::makeRequestUrl(QUrl baseUrl, + const QString& protocol, + const QString& fields) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/user/" % protocol, - queryToQueryUserByProtocol(fields)); + basePath % "/thirdparty/user/" % protocol, + queryToQueryUserByProtocol(fields)); } -static const auto QueryUserByProtocolJobName = QStringLiteral("QueryUserByProtocolJob"); +static const auto QueryUserByProtocolJobName = + QStringLiteral("QueryUserByProtocolJob"); -QueryUserByProtocolJob::QueryUserByProtocolJob(const QString& protocol, const QString& fields) +QueryUserByProtocolJob::QueryUserByProtocolJob(const QString& protocol, + const QString& fields) : BaseJob(HttpVerb::Get, QueryUserByProtocolJobName, - basePath % "/thirdparty/user/" % protocol, - queryToQueryUserByProtocol(fields)) - , d(new Private) + basePath % "/thirdparty/user/" % protocol, + queryToQueryUserByProtocol(fields)), + d(new Private) { } @@ -169,7 +179,7 @@ BaseJob::Status QueryUserByProtocolJob::parseJson(const QJsonDocument& data) class QueryLocationByAliasJob::Private { public: - QVector data; + QVector data; }; BaseJob::Query queryToQueryLocationByAlias(const QString& alias) @@ -182,17 +192,18 @@ BaseJob::Query queryToQueryLocationByAlias(const QString& alias) QUrl QueryLocationByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& alias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/location", - queryToQueryLocationByAlias(alias)); + basePath % "/thirdparty/location", + queryToQueryLocationByAlias(alias)); } -static const auto QueryLocationByAliasJobName = QStringLiteral("QueryLocationByAliasJob"); +static const auto QueryLocationByAliasJobName = + QStringLiteral("QueryLocationByAliasJob"); QueryLocationByAliasJob::QueryLocationByAliasJob(const QString& alias) : BaseJob(HttpVerb::Get, QueryLocationByAliasJobName, - basePath % "/thirdparty/location", - queryToQueryLocationByAlias(alias)) - , d(new Private) + basePath % "/thirdparty/location", + queryToQueryLocationByAlias(alias)), + d(new Private) { } @@ -212,7 +223,7 @@ BaseJob::Status QueryLocationByAliasJob::parseJson(const QJsonDocument& data) class QueryUserByIDJob::Private { public: - QVector data; + QVector data; }; BaseJob::Query queryToQueryUserByID(const QString& userid) @@ -225,17 +236,16 @@ BaseJob::Query queryToQueryUserByID(const QString& userid) QUrl QueryUserByIDJob::makeRequestUrl(QUrl baseUrl, const QString& userid) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/user", - queryToQueryUserByID(userid)); + basePath % "/thirdparty/user", + queryToQueryUserByID(userid)); } static const auto QueryUserByIDJobName = QStringLiteral("QueryUserByIDJob"); QueryUserByIDJob::QueryUserByIDJob(const QString& userid) : BaseJob(HttpVerb::Get, QueryUserByIDJobName, - basePath % "/thirdparty/user", - queryToQueryUserByID(userid)) - , d(new Private) + basePath % "/thirdparty/user", queryToQueryUserByID(userid)), + d(new Private) { } @@ -251,4 +261,3 @@ BaseJob::Status QueryUserByIDJob::parseJson(const QJsonDocument& data) fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/third_party_lookup.h b/lib/csapi/third_party_lookup.h index 3a60432b..91bc79e5 100644 --- a/lib/csapi/third_party_lookup.h +++ b/lib/csapi/third_party_lookup.h @@ -6,15 +6,14 @@ #include "jobs/basejob.h" -#include "csapi/../application-service/definitions/user.h" +#include "converters.h" #include "csapi/../application-service/definitions/location.h" +#include "csapi/../application-service/definitions/protocol.h" +#include "csapi/../application-service/definitions/user.h" #include #include -#include "converters.h" -#include "csapi/../application-service/definitions/protocol.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Retrieve metadata about all protocols that a homeserver supports. @@ -25,64 +24,66 @@ namespace QMatrixClient class GetProtocolsJob : public BaseJob { public: - explicit GetProtocolsJob(); + explicit GetProtocolsJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetProtocolsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetProtocolsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetProtocolsJob() override; + ~GetProtocolsJob() override; - // Result properties + // Result properties - /// The protocols supported by the homeserver. - const QHash& data() const; + /// The protocols supported by the homeserver. + const QHash& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; - /// Retrieve metadata about a specific protocol that the homeserver supports. + /// Retrieve metadata about a specific protocol that the homeserver + /// supports. /// - /// Fetches the metadata from the homeserver about a particular third party protocol. + /// Fetches the metadata from the homeserver about a particular third party + /// protocol. class GetProtocolMetadataJob : public BaseJob { public: - /*! Retrieve metadata about a specific protocol that the homeserver supports. - * \param protocol - * The name of the protocol. - */ - explicit GetProtocolMetadataJob(const QString& protocol); + /*! Retrieve metadata about a specific protocol that the homeserver supports. + * \param protocol + * The name of the protocol. + */ + explicit GetProtocolMetadataJob(const QString& protocol); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetProtocolMetadataJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetProtocolMetadataJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol); - ~GetProtocolMetadataJob() override; + ~GetProtocolMetadataJob() override; - // Result properties + // Result properties - /// The protocol was found and metadata returned. - const ThirdPartyProtocol& data() const; + /// The protocol was found and metadata returned. + const ThirdPartyProtocol& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Retrieve Matrix-side portals rooms leading to a third party location. @@ -98,74 +99,79 @@ namespace QMatrixClient class QueryLocationByProtocolJob : public BaseJob { public: - /*! Retrieve Matrix-side portals rooms leading to a third party location. - * \param protocol - * The protocol used to communicate to the third party network. - * \param searchFields - * One or more custom fields to help identify the third party - * location. - */ - explicit QueryLocationByProtocolJob(const QString& protocol, const QString& searchFields = {}); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * QueryLocationByProtocolJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& searchFields = {}); - - ~QueryLocationByProtocolJob() override; - - // Result properties - - /// At least one portal room was found. - const QVector& data() const; + /*! Retrieve Matrix-side portals rooms leading to a third party location. + * \param protocol + * The protocol used to communicate to the third party network. + * \param searchFields + * One or more custom fields to help identify the third party + * location. + */ + explicit QueryLocationByProtocolJob(const QString& protocol, + const QString& searchFields = {}); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * QueryLocationByProtocolJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, + const QString& searchFields = {}); + + ~QueryLocationByProtocolJob() override; + + // Result properties + + /// At least one portal room was found. + const QVector& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Retrieve the Matrix User ID of a corresponding third party user. /// - /// Retrieve a Matrix User ID linked to a user on the third party service, given - /// a set of user parameters. + /// Retrieve a Matrix User ID linked to a user on the third party service, + /// given a set of user parameters. class QueryUserByProtocolJob : public BaseJob { public: - /*! Retrieve the Matrix User ID of a corresponding third party user. - * \param protocol - * The name of the protocol. - * \param fields - * One or more custom fields that are passed to the AS to help identify the user. - */ - explicit QueryUserByProtocolJob(const QString& protocol, const QString& fields = {}); - - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * QueryUserByProtocolJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& fields = {}); - - ~QueryUserByProtocolJob() override; - - // Result properties - - /// The Matrix User IDs found with the given parameters. - const QVector& data() const; + /*! Retrieve the Matrix User ID of a corresponding third party user. + * \param protocol + * The name of the protocol. + * \param fields + * One or more custom fields that are passed to the AS to help + * identify the user. + */ + explicit QueryUserByProtocolJob(const QString& protocol, + const QString& fields = {}); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * QueryUserByProtocolJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, + const QString& fields = {}); + + ~QueryUserByProtocolJob() override; + + // Result properties + + /// The Matrix User IDs found with the given parameters. + const QVector& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Reverse-lookup third party locations given a Matrix room alias. @@ -175,33 +181,33 @@ namespace QMatrixClient class QueryLocationByAliasJob : public BaseJob { public: - /*! Reverse-lookup third party locations given a Matrix room alias. - * \param alias - * The Matrix room alias to look up. - */ - explicit QueryLocationByAliasJob(const QString& alias); + /*! Reverse-lookup third party locations given a Matrix room alias. + * \param alias + * The Matrix room alias to look up. + */ + explicit QueryLocationByAliasJob(const QString& alias); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * QueryLocationByAliasJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& alias); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * QueryLocationByAliasJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& alias); - ~QueryLocationByAliasJob() override; + ~QueryLocationByAliasJob() override; - // Result properties + // Result properties - /// All found third party locations. - const QVector& data() const; + /// All found third party locations. + const QVector& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; /// Reverse-lookup third party users given a Matrix User ID. @@ -210,32 +216,32 @@ namespace QMatrixClient class QueryUserByIDJob : public BaseJob { public: - /*! Reverse-lookup third party users given a Matrix User ID. - * \param userid - * The Matrix User ID to look up. - */ - explicit QueryUserByIDJob(const QString& userid); + /*! Reverse-lookup third party users given a Matrix User ID. + * \param userid + * The Matrix User ID to look up. + */ + explicit QueryUserByIDJob(const QString& userid); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * QueryUserByIDJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& userid); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * QueryUserByIDJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userid); - ~QueryUserByIDJob() override; + ~QueryUserByIDJob() override; - // Result properties + // Result properties - /// An array of third party users. - const QVector& data() const; + /// An array of third party users. + const QVector& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/third_party_membership.cpp b/lib/csapi/third_party_membership.cpp index c1683338..9fe702aa 100644 --- a/lib/csapi/third_party_membership.cpp +++ b/lib/csapi/third_party_membership.cpp @@ -14,9 +14,10 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto InviteBy3PIDJobName = QStringLiteral("InviteBy3PIDJob"); -InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, const QString& medium, const QString& address) +InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, + const QString& medium, const QString& address) : BaseJob(HttpVerb::Post, InviteBy3PIDJobName, - basePath % "/rooms/" % roomId % "/invite") + basePath % "/rooms/" % roomId % "/invite") { QJsonObject _data; addParam<>(_data, QStringLiteral("id_server"), idServer); @@ -24,4 +25,3 @@ InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, addParam<>(_data, QStringLiteral("address"), address); setRequestData(_data); } - diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h index d18fe554..d1261567 100644 --- a/lib/csapi/third_party_membership.h +++ b/lib/csapi/third_party_membership.h @@ -6,33 +6,31 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Invite a user to participate in a particular room. /// /// .. _invite-by-third-party-id-endpoint: - /// - /// *Note that there are two forms of this API, which are documented separately. - /// This version of the API does not require that the inviter know the Matrix - /// identifier of the invitee, and instead relies on third party identifiers. - /// The homeserver uses an identity server to perform the mapping from - /// third party identifier to a Matrix identifier. The other is documented in the* - /// `joining rooms section`_. - /// + /// + /// *Note that there are two forms of this API, which are documented + /// separately. This version of the API does not require that the inviter + /// know the Matrix identifier of the invitee, and instead relies on third + /// party identifiers. The homeserver uses an identity server to perform the + /// mapping from third party identifier to a Matrix identifier. The other is + /// documented in the* `joining rooms section`_. + /// /// This API invites a user to participate in a particular room. /// They do not start participating in the room until they actually join the /// room. - /// + /// /// Only users currently in a particular room can invite other users to /// join that room. - /// + /// /// If the identity server did know the Matrix user identifier for the /// third party identifier, the homeserver will append a ``m.room.member`` /// event to the room. - /// + /// /// If the identity server does not know a Matrix user identifier for the /// passed third party identifier, the homeserver will issue an invitation /// which can be accepted upon providing proof of ownership of the third @@ -40,36 +38,37 @@ namespace QMatrixClient /// token, which it gives to the inviting homeserver. The homeserver will /// add an ``m.room.third_party_invite`` event into the graph for the room, /// containing that token. - /// + /// /// When the invitee binds the invited third party identifier to a Matrix /// user ID, the identity server will give the user a list of pending /// invitations, each containing: - /// + /// /// - The room ID to which they were invited - /// + /// /// - The token given to the homeserver - /// - /// - A signature of the token, signed with the identity server's private key - /// + /// + /// - A signature of the token, signed with the identity server's private + /// key + /// /// - The matrix user ID who invited them to the room - /// + /// /// If a token is requested from the identity server, the homeserver will /// append a ``m.room.third_party_invite`` event to the room. - /// + /// /// .. _joining rooms section: `invite-by-user-id-endpoint`_ class InviteBy3PIDJob : public BaseJob { public: - /*! Invite a user to participate in a particular room. - * \param roomId - * The room identifier (not alias) to which to invite the user. - * \param idServer - * The hostname+port of the identity server which should be used for third party identifier lookups. - * \param medium - * The kind of address being passed in the address field, for example ``email``. - * \param address - * The invitee's third party identifier. - */ - explicit InviteBy3PIDJob(const QString& roomId, const QString& idServer, const QString& medium, const QString& address); + /*! Invite a user to participate in a particular room. + * \param roomId + * The room identifier (not alias) to which to invite the user. + * \param idServer + * The hostname+port of the identity server which should be used for + * third party identifier lookups. \param medium The kind of address + * being passed in the address field, for example ``email``. \param + * address The invitee's third party identifier. + */ + explicit InviteBy3PIDJob(const QString& roomId, const QString& idServer, + const QString& medium, const QString& address); }; } // namespace QMatrixClient diff --git a/lib/csapi/to_device.cpp b/lib/csapi/to_device.cpp index 7c7f495a..241869f3 100644 --- a/lib/csapi/to_device.cpp +++ b/lib/csapi/to_device.cpp @@ -14,12 +14,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SendToDeviceJobName = QStringLiteral("SendToDeviceJob"); -SendToDeviceJob::SendToDeviceJob(const QString& eventType, const QString& txnId, const QHash>& messages) +SendToDeviceJob::SendToDeviceJob( + const QString& eventType, const QString& txnId, + const QHash>& messages) : BaseJob(HttpVerb::Put, SendToDeviceJobName, - basePath % "/sendToDevice/" % eventType % "/" % txnId) + basePath % "/sendToDevice/" % eventType % "/" % txnId) { QJsonObject _data; addParam(_data, QStringLiteral("messages"), messages); setRequestData(_data); } - diff --git a/lib/csapi/to_device.h b/lib/csapi/to_device.h index 10f6b971..83df13b7 100644 --- a/lib/csapi/to_device.h +++ b/lib/csapi/to_device.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include #include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Send an event to a given set of devices. @@ -20,18 +19,21 @@ namespace QMatrixClient class SendToDeviceJob : public BaseJob { public: - /*! Send an event to a given set of devices. - * \param eventType - * The type of event to send. - * \param txnId - * The transaction ID for this event. Clients should generate an - * ID unique across requests with the same access token; it will be - * used by the server to ensure idempotency of requests. - * \param messages - * The messages to send. A map from user ID, to a map from - * device ID to message body. The device ID may also be `*`, - * meaning all known devices for the user. - */ - explicit SendToDeviceJob(const QString& eventType, const QString& txnId, const QHash>& messages = {}); + /*! Send an event to a given set of devices. + * \param eventType + * The type of event to send. + * \param txnId + * The transaction ID for this event. Clients should generate an + * ID unique across requests with the same access token; it will be + * used by the server to ensure idempotency of requests. + * \param messages + * The messages to send. A map from user ID, to a map from + * device ID to message body. The device ID may also be `*`, + * meaning all known devices for the user. + */ + explicit SendToDeviceJob( + const QString& eventType, const QString& txnId, + const QHash>& + messages = {}); }; } // namespace QMatrixClient diff --git a/lib/csapi/typing.cpp b/lib/csapi/typing.cpp index bf10912b..03499c76 100644 --- a/lib/csapi/typing.cpp +++ b/lib/csapi/typing.cpp @@ -14,13 +14,13 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); static const auto SetTypingJobName = QStringLiteral("SetTypingJob"); -SetTypingJob::SetTypingJob(const QString& userId, const QString& roomId, bool typing, Omittable timeout) +SetTypingJob::SetTypingJob(const QString& userId, const QString& roomId, + bool typing, Omittable timeout) : BaseJob(HttpVerb::Put, SetTypingJobName, - basePath % "/rooms/" % roomId % "/typing/" % userId) + basePath % "/rooms/" % roomId % "/typing/" % userId) { QJsonObject _data; addParam<>(_data, QStringLiteral("typing"), typing); addParam(_data, QStringLiteral("timeout"), timeout); setRequestData(_data); } - diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h index c6201440..1f32a8e5 100644 --- a/lib/csapi/typing.h +++ b/lib/csapi/typing.h @@ -8,8 +8,7 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Informs the server that the user has started or stopped typing. @@ -21,17 +20,18 @@ namespace QMatrixClient class SetTypingJob : public BaseJob { public: - /*! Informs the server that the user has started or stopped typing. - * \param userId - * The user who has started to type. - * \param roomId - * The room in which the user is typing. - * \param typing - * Whether the user is typing or not. If ``false``, the ``timeout`` - * key can be omitted. - * \param timeout - * The length of time in milliseconds to mark this user as typing. - */ - explicit SetTypingJob(const QString& userId, const QString& roomId, bool typing, Omittable timeout = none); + /*! Informs the server that the user has started or stopped typing. + * \param userId + * The user who has started to type. + * \param roomId + * The room in which the user is typing. + * \param typing + * Whether the user is typing or not. If ``false``, the ``timeout`` + * key can be omitted. + * \param timeout + * The length of time in milliseconds to mark this user as typing. + */ + explicit SetTypingJob(const QString& userId, const QString& roomId, + bool typing, Omittable timeout = none); }; } // namespace QMatrixClient diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index 97d8962d..6d005915 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -12,13 +12,12 @@ using namespace QMatrixClient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -namespace QMatrixClient -{ +namespace QMatrixClient { // Converters - template <> struct JsonObjectConverter - { - static void fillFrom(const QJsonObject& jo, SearchUserDirectoryJob::User& result) + template <> struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchUserDirectoryJob::User& result) { fromJson(jo.value("user_id"_ls), result.userId); fromJson(jo.value("display_name"_ls), result.displayName); @@ -30,16 +29,18 @@ namespace QMatrixClient class SearchUserDirectoryJob::Private { public: - QVector results; - bool limited; + QVector results; + bool limited; }; -static const auto SearchUserDirectoryJobName = QStringLiteral("SearchUserDirectoryJob"); +static const auto SearchUserDirectoryJobName = + QStringLiteral("SearchUserDirectoryJob"); -SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, Omittable limit) +SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, + Omittable limit) : BaseJob(HttpVerb::Post, SearchUserDirectoryJobName, - basePath % "/user_directory/search") - , d(new Private) + basePath % "/user_directory/search"), + d(new Private) { QJsonObject _data; addParam<>(_data, QStringLiteral("search_term"), searchTerm); @@ -49,27 +50,24 @@ SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, Omitta SearchUserDirectoryJob::~SearchUserDirectoryJob() = default; -const QVector& SearchUserDirectoryJob::results() const +const QVector& +SearchUserDirectoryJob::results() const { return d->results; } -bool SearchUserDirectoryJob::limited() const -{ - return d->limited; -} +bool SearchUserDirectoryJob::limited() const { return d->limited; } BaseJob::Status SearchUserDirectoryJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("results"_ls)) return { JsonParseError, - "The key 'results' not found in the response" }; + "The key 'results' not found in the response" }; fromJson(json.value("results"_ls), d->results); if (!json.contains("limited"_ls)) return { JsonParseError, - "The key 'limited' not found in the response" }; + "The key 'limited' not found in the response" }; fromJson(json.value("limited"_ls), d->limited); return Success; } - diff --git a/lib/csapi/users.h b/lib/csapi/users.h index 1e355b8f..7754b82a 100644 --- a/lib/csapi/users.h +++ b/lib/csapi/users.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include #include "converters.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Searches the user directory. @@ -18,61 +17,61 @@ namespace QMatrixClient /// Performs a search for users on the homeserver. The homeserver may /// determine which subset of users are searched, however the homeserver /// MUST at a minimum consider the users the requesting user shares a - /// room with and those who reside in public rooms (known to the homeserver). - /// The search MUST consider local users to the homeserver, and SHOULD - /// query remote users as part of the search. - /// + /// room with and those who reside in public rooms (known to the + /// homeserver). The search MUST consider local users to the homeserver, and + /// SHOULD query remote users as part of the search. + /// /// The search is performed case-insensitively on user IDs and display - /// names preferably using a collation determined based upon the + /// names preferably using a collation determined based upon the /// ``Accept-Language`` header provided in the request, if present. class SearchUserDirectoryJob : public BaseJob { public: - // Inner data structures + // Inner data structures - /// Performs a search for users on the homeserver. The homeserver may - /// determine which subset of users are searched, however the homeserver - /// MUST at a minimum consider the users the requesting user shares a - /// room with and those who reside in public rooms (known to the homeserver). - /// The search MUST consider local users to the homeserver, and SHOULD - /// query remote users as part of the search. - /// - /// The search is performed case-insensitively on user IDs and display - /// names preferably using a collation determined based upon the - /// ``Accept-Language`` header provided in the request, if present. - struct User - { - /// The user's matrix user ID. - QString userId; - /// The display name of the user, if one exists. - QString displayName; - /// The avatar url, as an MXC, if one exists. - QString avatarUrl; - }; + /// Performs a search for users on the homeserver. The homeserver may + /// determine which subset of users are searched, however the homeserver + /// MUST at a minimum consider the users the requesting user shares a + /// room with and those who reside in public rooms (known to the + /// homeserver). The search MUST consider local users to the homeserver, + /// and SHOULD query remote users as part of the search. + /// + /// The search is performed case-insensitively on user IDs and display + /// names preferably using a collation determined based upon the + /// ``Accept-Language`` header provided in the request, if present. + struct User { + /// The user's matrix user ID. + QString userId; + /// The display name of the user, if one exists. + QString displayName; + /// The avatar url, as an MXC, if one exists. + QString avatarUrl; + }; - // Construction/destruction + // Construction/destruction - /*! Searches the user directory. - * \param searchTerm - * The term to search for - * \param limit - * The maximum number of results to return. Defaults to 10. - */ - explicit SearchUserDirectoryJob(const QString& searchTerm, Omittable limit = none); - ~SearchUserDirectoryJob() override; + /*! Searches the user directory. + * \param searchTerm + * The term to search for + * \param limit + * The maximum number of results to return. Defaults to 10. + */ + explicit SearchUserDirectoryJob(const QString& searchTerm, + Omittable limit = none); + ~SearchUserDirectoryJob() override; - // Result properties + // Result properties - /// Ordered by rank and then whether or not profile info is available. - const QVector& results() const; - /// Indicates if the result list has been truncated by the limit. - bool limited() const; + /// Ordered by rank and then whether or not profile info is available. + const QVector& results() const; + /// Indicates if the result list has been truncated by the limit. + bool limited() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 6ee6725d..13e3be15 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -15,31 +15,26 @@ static const auto basePath = QStringLiteral("/_matrix/client"); class GetVersionsJob::Private { public: - QStringList versions; - QHash unstableFeatures; + QStringList versions; + QHash unstableFeatures; }; QUrl GetVersionsJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/versions"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/versions"); } static const auto GetVersionsJobName = QStringLiteral("GetVersionsJob"); GetVersionsJob::GetVersionsJob() - : BaseJob(HttpVerb::Get, GetVersionsJobName, - basePath % "/versions", false) - , d(new Private) + : BaseJob(HttpVerb::Get, GetVersionsJobName, basePath % "/versions", false), + d(new Private) { } GetVersionsJob::~GetVersionsJob() = default; -const QStringList& GetVersionsJob::versions() const -{ - return d->versions; -} +const QStringList& GetVersionsJob::versions() const { return d->versions; } const QHash& GetVersionsJob::unstableFeatures() const { @@ -51,9 +46,8 @@ BaseJob::Status GetVersionsJob::parseJson(const QJsonDocument& data) auto json = data.object(); if (!json.contains("versions"_ls)) return { JsonParseError, - "The key 'versions' not found in the response" }; + "The key 'versions' not found in the response" }; fromJson(json.value("versions"_ls), d->versions); fromJson(json.value("unstable_features"_ls), d->unstableFeatures); return Success; } - diff --git a/lib/csapi/versions.h b/lib/csapi/versions.h index b56f293f..d017708a 100644 --- a/lib/csapi/versions.h +++ b/lib/csapi/versions.h @@ -6,22 +6,22 @@ #include "jobs/basejob.h" -#include #include "converters.h" +#include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets the versions of the specification supported by the server. /// /// Gets the versions of the specification supported by the server. - /// + /// /// Values will take the form ``rX.Y.Z``. - /// - /// Only the latest ``Z`` value will be reported for each supported ``X.Y`` value. - /// i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``. - /// + /// + /// Only the latest ``Z`` value will be reported for each supported ``X.Y`` + /// value. i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and + /// ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``. + /// /// The server may additionally advertise experimental features it supports /// through ``unstable_features``. These features should be namespaced and /// may optionally include version information within their name if desired. @@ -37,32 +37,32 @@ namespace QMatrixClient class GetVersionsJob : public BaseJob { public: - explicit GetVersionsJob(); + explicit GetVersionsJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetVersionsJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetVersionsJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetVersionsJob() override; + ~GetVersionsJob() override; - // Result properties + // Result properties - /// The supported versions. - const QStringList& versions() const; - /// Experimental features the server supports. Features not listed here, - /// or the lack of this property all together, indicate that a feature is - /// not supported. - const QHash& unstableFeatures() const; + /// The supported versions. + const QStringList& versions() const; + /// Experimental features the server supports. Features not listed here, + /// or the lack of this property all together, indicate that a feature + /// is not supported. + const QHash& unstableFeatures() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/voip.cpp b/lib/csapi/voip.cpp index e8158723..ee511906 100644 --- a/lib/csapi/voip.cpp +++ b/lib/csapi/voip.cpp @@ -15,34 +15,30 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetTurnServerJob::Private { public: - QJsonObject data; + QJsonObject data; }; QUrl GetTurnServerJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/voip/turnServer"); + basePath % "/voip/turnServer"); } static const auto GetTurnServerJobName = QStringLiteral("GetTurnServerJob"); GetTurnServerJob::GetTurnServerJob() : BaseJob(HttpVerb::Get, GetTurnServerJobName, - basePath % "/voip/turnServer") - , d(new Private) + basePath % "/voip/turnServer"), + d(new Private) { } GetTurnServerJob::~GetTurnServerJob() = default; -const QJsonObject& GetTurnServerJob::data() const -{ - return d->data; -} +const QJsonObject& GetTurnServerJob::data() const { return d->data; } BaseJob::Status GetTurnServerJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/voip.h b/lib/csapi/voip.h index bb858499..3b011651 100644 --- a/lib/csapi/voip.h +++ b/lib/csapi/voip.h @@ -8,8 +8,7 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Obtain TURN server credentials. @@ -19,28 +18,28 @@ namespace QMatrixClient class GetTurnServerJob : public BaseJob { public: - explicit GetTurnServerJob(); + explicit GetTurnServerJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetTurnServerJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetTurnServerJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTurnServerJob() override; + ~GetTurnServerJob() override; - // Result properties + // Result properties - /// The TURN server credentials. - const QJsonObject& data() const; + /// The TURN server credentials. + const QJsonObject& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/wellknown.cpp b/lib/csapi/wellknown.cpp index a6107f86..8543c519 100644 --- a/lib/csapi/wellknown.cpp +++ b/lib/csapi/wellknown.cpp @@ -15,34 +15,30 @@ static const auto basePath = QStringLiteral("/.well-known"); class GetWellknownJob::Private { public: - DiscoveryInformation data; + DiscoveryInformation data; }; QUrl GetWellknownJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/matrix/client"); + basePath % "/matrix/client"); } static const auto GetWellknownJobName = QStringLiteral("GetWellknownJob"); GetWellknownJob::GetWellknownJob() - : BaseJob(HttpVerb::Get, GetWellknownJobName, - basePath % "/matrix/client", false) - , d(new Private) + : BaseJob(HttpVerb::Get, GetWellknownJobName, basePath % "/matrix/client", + false), + d(new Private) { } GetWellknownJob::~GetWellknownJob() = default; -const DiscoveryInformation& GetWellknownJob::data() const -{ - return d->data; -} +const DiscoveryInformation& GetWellknownJob::data() const { return d->data; } BaseJob::Status GetWellknownJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); return Success; } - diff --git a/lib/csapi/wellknown.h b/lib/csapi/wellknown.h index 8da9ce9f..1df524ce 100644 --- a/lib/csapi/wellknown.h +++ b/lib/csapi/wellknown.h @@ -6,11 +6,10 @@ #include "jobs/basejob.h" -#include "csapi/definitions/wellknown/full.h" #include "converters.h" +#include "csapi/definitions/wellknown/full.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets Matrix server discovery information about the domain. @@ -20,34 +19,34 @@ namespace QMatrixClient /// e.g. ``com.example.myapp.property``. This ensures property names are /// suitably namespaced for each application and reduces the risk of /// clashes. - /// + /// /// Note that this endpoint is not necessarily handled by the homeserver, /// but by another webserver, to be used for discovering the homeserver URL. class GetWellknownJob : public BaseJob { public: - explicit GetWellknownJob(); + explicit GetWellknownJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetWellknownJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetWellknownJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetWellknownJob() override; + ~GetWellknownJob() override; - // Result properties + // Result properties - /// Server discovery information. - const DiscoveryInformation& data() const; + /// Server discovery information. + const DiscoveryInformation& data() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index aebdf5d3..f0a77aee 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -15,38 +15,34 @@ static const auto basePath = QStringLiteral("/_matrix/client/r0"); class GetTokenOwnerJob::Private { public: - QString userId; + QString userId; }; QUrl GetTokenOwnerJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/account/whoami"); + basePath % "/account/whoami"); } static const auto GetTokenOwnerJobName = QStringLiteral("GetTokenOwnerJob"); GetTokenOwnerJob::GetTokenOwnerJob() : BaseJob(HttpVerb::Get, GetTokenOwnerJobName, - basePath % "/account/whoami") - , d(new Private) + basePath % "/account/whoami"), + d(new Private) { } GetTokenOwnerJob::~GetTokenOwnerJob() = default; -const QString& GetTokenOwnerJob::userId() const -{ - return d->userId; -} +const QString& GetTokenOwnerJob::userId() const { return d->userId; } BaseJob::Status GetTokenOwnerJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("user_id"_ls)) return { JsonParseError, - "The key 'user_id' not found in the response" }; + "The key 'user_id' not found in the response" }; fromJson(json.value("user_id"_ls), d->userId); return Success; } - diff --git a/lib/csapi/whoami.h b/lib/csapi/whoami.h index 71e9d532..8f191351 100644 --- a/lib/csapi/whoami.h +++ b/lib/csapi/whoami.h @@ -6,46 +6,44 @@ #include "jobs/basejob.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Operations /// Gets information about the owner of an access token. /// - /// Gets information about the owner of a given access token. - /// - /// Note that, as with the rest of the Client-Server API, - /// Application Services may masquerade as users within their - /// namespace by giving a ``user_id`` query parameter. In this + /// Gets information about the owner of a given access token. + /// + /// Note that, as with the rest of the Client-Server API, + /// Application Services may masquerade as users within their + /// namespace by giving a ``user_id`` query parameter. In this /// situation, the server should verify that the given ``user_id`` - /// is registered by the appservice, and return it in the response + /// is registered by the appservice, and return it in the response /// body. class GetTokenOwnerJob : public BaseJob { public: - explicit GetTokenOwnerJob(); + explicit GetTokenOwnerJob(); - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * GetTokenOwnerJob is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl); + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetTokenOwnerJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTokenOwnerJob() override; + ~GetTokenOwnerJob() override; - // Result properties + // Result properties - /// The user id that owns the access token. - const QString& userId() const; + /// The user id that owns the access token. + const QString& userId() const; protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } // namespace QMatrixClient diff --git a/lib/eventitem.cpp b/lib/eventitem.cpp index 8ec3fe48..f3f18854 100644 --- a/lib/eventitem.cpp +++ b/lib/eventitem.cpp @@ -13,13 +13,13 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "eventitem.h" -#include "events/roommessageevent.h" #include "events/roomavatarevent.h" +#include "events/roommessageevent.h" using namespace QMatrixClient; @@ -27,17 +27,15 @@ void PendingEventItem::setFileUploaded(const QUrl& remoteUrl) { // TODO: eventually we might introduce hasFileContent to RoomEvent, // and unify the code below. - if (auto* rme = getAs()) - { + if (auto* rme = getAs()) { Q_ASSERT(rme->hasFileContent()); - rme->editContent([remoteUrl] (EventContent::TypedBase& ec) { + rme->editContent([remoteUrl](EventContent::TypedBase& ec) { ec.fileInfo()->url = remoteUrl; }); } - if (auto* rae = getAs()) - { + if (auto* rae = getAs()) { Q_ASSERT(rae->content().fileInfo()); - rae->editContent([remoteUrl] (EventContent::FileInfo& fi) { + rae->editContent([remoteUrl](EventContent::FileInfo& fi) { fi.url = remoteUrl; }); } diff --git a/lib/eventitem.h b/lib/eventitem.h index 36ed2132..8b863d67 100644 --- a/lib/eventitem.h +++ b/lib/eventitem.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,87 +22,92 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { class StateEventBase; class EventStatus { - Q_GADGET + Q_GADGET public: - /** Special marks an event can assume - * - * This is used to hint at a special status of some events in UI. - * All values except Redacted and Hidden are mutually exclusive. - */ - enum Code { - Normal = 0x0, //< No special designation - Submitted = 0x01, //< The event has just been submitted for sending - FileUploaded = 0x02, //< The file attached to the event has been uploaded to the server - Departed = 0x03, //< The event has left the client - ReachedServer = 0x04, //< The server has received the event - SendingFailed = 0x05, //< The server could not receive the event - Redacted = 0x08, //< The event has been redacted - Hidden = 0x10, //< The event should not be shown in the timeline - }; - Q_DECLARE_FLAGS(Status, Code) - Q_FLAG(Status) + /** Special marks an event can assume + * + * This is used to hint at a special status of some events in UI. + * All values except Redacted and Hidden are mutually exclusive. + */ + enum Code { + Normal = 0x0, //< No special designation + Submitted = 0x01, //< The event has just been submitted for sending + FileUploaded = 0x02, //< The file attached to the event has been + //uploaded to the server + Departed = 0x03, //< The event has left the client + ReachedServer = 0x04, //< The server has received the event + SendingFailed = 0x05, //< The server could not receive the event + Redacted = 0x08, //< The event has been redacted + Hidden = 0x10, //< The event should not be shown in the timeline + }; + Q_DECLARE_FLAGS(Status, Code) + Q_FLAG(Status) }; class EventItemBase { public: - explicit EventItemBase(RoomEventPtr&& e) - : evt(std::move(e)) - { - Q_ASSERT(evt); - } - - const RoomEvent* event() const { return rawPtr(evt); } - const RoomEvent* get() const { return event(); } - template - const EventT* viewAs() const { return eventCast(evt); } - const RoomEventPtr& operator->() const { return evt; } - const RoomEvent& operator*() const { return *evt; } - - // Used for event redaction - RoomEventPtr replaceEvent(RoomEventPtr&& other) - { - return std::exchange(evt, move(other)); - } + explicit EventItemBase(RoomEventPtr&& e) : evt(std::move(e)) + { + Q_ASSERT(evt); + } + + const RoomEvent* event() const { return rawPtr(evt); } + const RoomEvent* get() const { return event(); } + template const EventT* viewAs() const + { + return eventCast(evt); + } + const RoomEventPtr& operator->() const { return evt; } + const RoomEvent& operator*() const { return *evt; } + + // Used for event redaction + RoomEventPtr replaceEvent(RoomEventPtr&& other) + { + return std::exchange(evt, move(other)); + } protected: - template - EventT* getAs() { return eventCast(evt); } + template EventT* getAs() + { + return eventCast(evt); + } + private: - RoomEventPtr evt; + RoomEventPtr evt; }; class TimelineItem : public EventItemBase { public: - // For compatibility with Qt containers, even though we use - // a std:: container now for the room timeline - using index_t = int; + // For compatibility with Qt containers, even though we use + // a std:: container now for the room timeline + using index_t = int; - TimelineItem(RoomEventPtr&& e, index_t number) - : EventItemBase(std::move(e)), idx(number) - { } + TimelineItem(RoomEventPtr&& e, index_t number) + : EventItemBase(std::move(e)), idx(number) + { + } - index_t index() const { return idx; } + index_t index() const { return idx; } private: - index_t idx; + index_t idx; }; - template<> + template <> inline const StateEventBase* EventItemBase::viewAs() const { return evt->isStateEvent() ? weakPtrCast(evt) : nullptr; } - template<> + template <> inline const CallEventBase* EventItemBase::viewAs() const { return evt->isCallEvent() ? weakPtrCast(evt) @@ -111,39 +116,39 @@ namespace QMatrixClient class PendingEventItem : public EventItemBase { - Q_GADGET + Q_GADGET public: - using EventItemBase::EventItemBase; - - EventStatus::Code deliveryStatus() const { return _status; } - QDateTime lastUpdated() const { return _lastUpdated; } - QString annotation() const { return _annotation; } - - void setDeparted() { setStatus(EventStatus::Departed); } - void setFileUploaded(const QUrl& remoteUrl); - void setReachedServer(const QString& eventId) - { - setStatus(EventStatus::ReachedServer); - (*this)->addId(eventId); - } - void setSendingFailed(QString errorText) - { - setStatus(EventStatus::SendingFailed); - _annotation = std::move(errorText); - } - void resetStatus() { setStatus(EventStatus::Submitted); } + using EventItemBase::EventItemBase; + + EventStatus::Code deliveryStatus() const { return _status; } + QDateTime lastUpdated() const { return _lastUpdated; } + QString annotation() const { return _annotation; } + + void setDeparted() { setStatus(EventStatus::Departed); } + void setFileUploaded(const QUrl& remoteUrl); + void setReachedServer(const QString& eventId) + { + setStatus(EventStatus::ReachedServer); + (*this)->addId(eventId); + } + void setSendingFailed(QString errorText) + { + setStatus(EventStatus::SendingFailed); + _annotation = std::move(errorText); + } + void resetStatus() { setStatus(EventStatus::Submitted); } private: - EventStatus::Code _status = EventStatus::Submitted; - QDateTime _lastUpdated = QDateTime::currentDateTimeUtc(); - QString _annotation; - - void setStatus(EventStatus::Code status) - { - _status = status; - _lastUpdated = QDateTime::currentDateTimeUtc(); - _annotation.clear(); - } + EventStatus::Code _status = EventStatus::Submitted; + QDateTime _lastUpdated = QDateTime::currentDateTimeUtc(); + QString _annotation; + + void setStatus(EventStatus::Code status) + { + _status = status; + _lastUpdated = QDateTime::currentDateTimeUtc(); + _annotation.clear(); + } }; inline QDebug& operator<<(QDebug& d, const TimelineItem& ti) diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index a99d85ac..0cf2dc60 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -15,38 +15,37 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once +#include "converters.h" #include "event.h" #include "eventcontent.h" -#include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { constexpr const char* FavouriteTag = "m.favourite"; constexpr const char* LowPriorityTag = "m.lowpriority"; - struct TagRecord - { + struct TagRecord { using order_type = Omittable; order_type order; - TagRecord (order_type order = none) : order(order) { } + TagRecord(order_type order = none) : order(order) {} bool operator<(const TagRecord& other) const { - // Per The Spec, rooms with no order should be after those with order - return !order.omitted() && - (other.order.omitted() || order.value() < other.order.value()); + // Per The Spec, rooms with no order should be after those with + // order + return !order.omitted() + && (other.order.omitted() + || order.value() < other.order.value()); } }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, TagRecord& rec) { // Parse a float both from JSON double and JSON string because @@ -54,8 +53,7 @@ namespace QMatrixClient const auto orderJv = jo.value("order"_ls); if (orderJv.isDouble()) rec.order = fromJson(orderJv); - if (orderJv.isString()) - { + if (orderJv.isString()) { bool ok; rec.order = orderJv.toString().toFloat(&ok); if (!ok) @@ -70,30 +68,31 @@ namespace QMatrixClient using TagsMap = QHash; -#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ - class _Name : public Event \ - { \ - public: \ - using content_type = _ContentType; \ - DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name(QJsonObject obj) \ - : Event(typeId(), std::move(obj)) \ - { } \ - explicit _Name(_ContentType content) \ - : Event(typeId(), matrixTypeId(), \ - QJsonObject { { QStringLiteral(#_ContentKey), \ - toJson(std::move(content)) } }) \ - { } \ - auto _ContentKey() const \ - { return fromJson(contentJson()[#_ContentKey##_ls]); } \ - }; \ - REGISTER_EVENT_TYPE(_Name) \ +#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ + class _Name : public Event \ + { \ + public: \ + using content_type = _ContentType; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ + explicit _Name(QJsonObject obj) : Event(typeId(), std::move(obj)) {} \ + explicit _Name(_ContentType content) \ + : Event(typeId(), matrixTypeId(), \ + QJsonObject { { QStringLiteral(#_ContentKey), \ + toJson(std::move(content)) } }) \ + { \ + } \ + auto _ContentKey() const \ + { \ + return fromJson(contentJson()[#_ContentKey##_ls]); \ + } \ + }; \ + REGISTER_EVENT_TYPE(_Name) \ // End of macro DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags) DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id) - DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", - QSet, ignored_users) + DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", QSet, + ignored_users) DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp index d2862241..91e164ad 100644 --- a/lib/events/callanswerevent.cpp +++ b/lib/events/callanswerevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callanswerevent.h" @@ -56,17 +56,19 @@ CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj) CallAnswerEvent::CallAnswerEvent(const QString& callId, const int lifetime, const QString& sdp) : CallEventBase(typeId(), matrixTypeId(), callId, 0, - { { QStringLiteral("lifetime"), lifetime } - , { QStringLiteral("answer"), QJsonObject { - { QStringLiteral("type"), QStringLiteral("answer") }, - { QStringLiteral("sdp"), sdp } } } - }) -{ } + { { QStringLiteral("lifetime"), lifetime }, + { QStringLiteral("answer"), + QJsonObject { { QStringLiteral("type"), + QStringLiteral("answer") }, + { QStringLiteral("sdp"), sdp } } } }) +{ +} CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp) : CallEventBase(typeId(), matrixTypeId(), callId, 0, - { { QStringLiteral("answer"), QJsonObject { - { QStringLiteral("type"), QStringLiteral("answer") }, - { QStringLiteral("sdp"), sdp } } } - }) -{ } + { { QStringLiteral("answer"), + QJsonObject { { QStringLiteral("type"), + QStringLiteral("answer") }, + { QStringLiteral("sdp"), sdp } } } }) +{ +} diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index 2d9e5bb0..f222803b 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -13,18 +13,17 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallAnswerEvent: public CallEventBase +namespace QMatrixClient { + class CallAnswerEvent : public CallEventBase { - public: + public: DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) explicit CallAnswerEvent(const QJsonObject& obj); @@ -33,10 +32,16 @@ namespace QMatrixClient const QString& sdp); explicit CallAnswerEvent(const QString& callId, const QString& sdp); - int lifetime() const { return content("lifetime"_ls); } // FIXME: Omittable<>? - QString sdp() const { - return contentJson()["answer"_ls].toObject() - .value("sdp"_ls).toString(); + int lifetime() const + { + return content("lifetime"_ls); + } // FIXME: Omittable<>? + QString sdp() const + { + return contentJson()["answer"_ls] + .toObject() + .value("sdp"_ls) + .toString(); } }; diff --git a/lib/events/callcandidatesevent.cpp b/lib/events/callcandidatesevent.cpp index 52cd1856..24f0dd46 100644 --- a/lib/events/callcandidatesevent.cpp +++ b/lib/events/callcandidatesevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callcandidatesevent.h" @@ -26,9 +26,8 @@ m.call.candidates "call_id": "12345", "candidates": [ { - "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0", - "sdpMLineIndex": 0, - "sdpMid": "audio" + "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 +43670 typ host generation 0", "sdpMLineIndex": 0, "sdpMid": "audio" } ], "version": 0 diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index 4618832c..e66e0c09 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -13,34 +13,35 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallCandidatesEvent: public CallEventBase +namespace QMatrixClient { + class CallCandidatesEvent : public CallEventBase { public: - DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) + DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) - explicit CallCandidatesEvent(const QJsonObject& obj) - : CallEventBase(typeId(), obj) - { } + explicit CallCandidatesEvent(const QJsonObject& obj) + : CallEventBase(typeId(), obj) + { + } - explicit CallCandidatesEvent(const QString& callId, - const QJsonArray& candidates) - : CallEventBase(typeId(), matrixTypeId(), callId, 0, - {{ QStringLiteral("candidates"), candidates }}) - { } + explicit CallCandidatesEvent(const QString& callId, + const QJsonArray& candidates) + : CallEventBase(typeId(), matrixTypeId(), callId, 0, + { { QStringLiteral("candidates"), candidates } }) + { + } - QJsonArray candidates() const - { - return content("candidates"_ls); - } + QJsonArray candidates() const + { + return content("candidates"_ls); + } }; REGISTER_EVENT_TYPE(CallCandidatesEvent) diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp index b1154806..80844f2d 100644 --- a/lib/events/callhangupevent.cpp +++ b/lib/events/callhangupevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callhangupevent.h" @@ -42,7 +42,6 @@ m.call.hangup using namespace QMatrixClient; - CallHangupEvent::CallHangupEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) { @@ -51,4 +50,5 @@ CallHangupEvent::CallHangupEvent(const QJsonObject& obj) CallHangupEvent::CallHangupEvent(const QString& callId) : CallEventBase(typeId(), matrixTypeId(), callId, 0) -{ } +{ +} diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index c74e20d5..3c3910be 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -13,20 +13,19 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallHangupEvent: public CallEventBase +namespace QMatrixClient { + class CallHangupEvent : public CallEventBase { - public: + public: DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) - + explicit CallHangupEvent(const QJsonObject& obj); explicit CallHangupEvent(const QString& callId); }; diff --git a/lib/events/callinviteevent.cpp b/lib/events/callinviteevent.cpp index bca3f296..2459c093 100644 --- a/lib/events/callinviteevent.cpp +++ b/lib/events/callinviteevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "callinviteevent.h" @@ -56,9 +56,10 @@ CallInviteEvent::CallInviteEvent(const QJsonObject& obj) CallInviteEvent::CallInviteEvent(const QString& callId, const int lifetime, const QString& sdp) : CallEventBase(typeId(), matrixTypeId(), callId, lifetime, - { { QStringLiteral("lifetime"), lifetime } - , { QStringLiteral("offer"), QJsonObject { - { QStringLiteral("type"), QStringLiteral("offer") }, - { QStringLiteral("sdp"), sdp } } - }}) -{ } + { { QStringLiteral("lifetime"), lifetime }, + { QStringLiteral("offer"), + QJsonObject { { QStringLiteral("type"), + QStringLiteral("offer") }, + { QStringLiteral("sdp"), sdp } } } }) +{ +} diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index d5315309..911ccf96 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -13,29 +13,34 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ - class CallInviteEvent: public CallEventBase +namespace QMatrixClient { + class CallInviteEvent : public CallEventBase { - public: + public: DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) - + explicit CallInviteEvent(const QJsonObject& obj); explicit CallInviteEvent(const QString& callId, const int lifetime, const QString& sdp); - int lifetime() const { return content("lifetime"_ls); } // FIXME: Omittable<>? - QString sdp() const { - return contentJson()["offer"_ls].toObject() - .value("sdp"_ls).toString(); + int lifetime() const + { + return content("lifetime"_ls); + } // FIXME: Omittable<>? + QString sdp() const + { + return contentJson()["offer"_ls] + .toObject() + .value("sdp"_ls) + .toString(); } }; diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 266d60d8..4ba098c2 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "directchatevent.h" @@ -26,13 +26,12 @@ QMultiHash DirectChatEvent::usersToDirectChats() const { QMultiHash result; const auto& json = contentJson(); - for (auto it = json.begin(); it != json.end(); ++it) - { + for (auto it = json.begin(); it != json.end(); ++it) { // Beware of range-for's over temporary returned from temporary // (see the bottom of // http://en.cppreference.com/w/cpp/language/range-for#Explanation) const auto roomIds = it.value().toArray(); - for (const auto& roomIdValue: roomIds) + for (const auto& roomIdValue : roomIds) result.insert(it.key(), roomIdValue.toString()); } return result; diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 7559796b..0d8b8f74 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -13,25 +13,24 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class DirectChatEvent : public Event { public: - DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) + DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) - explicit DirectChatEvent(const QJsonObject& obj) - : Event(typeId(), obj) - { } + explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) + { + } - QMultiHash usersToDirectChats() const; + QMultiHash usersToDirectChats() const; }; REGISTER_EVENT_TYPE(DirectChatEvent) DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) diff --git a/lib/events/event.cpp b/lib/events/event.cpp index c98dfbb6..f44b1e5d 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "event.h" @@ -31,8 +31,8 @@ event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId) if (strncmp(matrixTypeId, "", 1) == 0) qDebug(EVENTS) << "Initialized unknown event type with id" << id; else - qDebug(EVENTS) << "Initialized event type" << matrixTypeId - << "with id" << id; + qDebug(EVENTS) << "Initialized event type" << matrixTypeId << "with id" + << id; return id; } @@ -41,32 +41,26 @@ QString EventTypeRegistry::getMatrixType(event_type_t typeId) return typeId < get().eventTypes.size() ? get().eventTypes[typeId] : ""; } -Event::Event(Type type, const QJsonObject& json) - : _type(type), _json(json) +Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) { - if (!json.contains(ContentKeyL) && - !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) - { + if (!json.contains(ContentKeyL) + && !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) { qCWarning(EVENTS) << "Event without 'content' node"; qCWarning(EVENTS) << formatJson << json; } } -Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) +Event::Event(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson) : Event(type, basicEventJson(matrixType, contentJson)) -{ } +{ +} Event::~Event() = default; -QString Event::matrixType() const -{ - return fullJson()[TypeKeyL].toString(); -} +QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); } -QByteArray Event::originalJson() const -{ - return QJsonDocument(_json).toJson(); -} +QByteArray Event::originalJson() const { return QJsonDocument(_json).toJson(); } const QJsonObject Event::contentJson() const { diff --git a/lib/events/event.h b/lib/events/event.h index d7ac4292..a0f12b75 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -25,12 +25,10 @@ #define USE_EVENTTYPE_ALIAS 1 #endif -namespace QMatrixClient -{ +namespace QMatrixClient { // === event_ptr_tt<> and type casting facilities === - template - using event_ptr_tt = std::unique_ptr; + template using event_ptr_tt = std::unique_ptr; template inline EventT* rawPtr(const event_ptr_tt& ptr) // unwrap @@ -80,50 +78,46 @@ namespace QMatrixClient class EventTypeRegistry { public: - ~EventTypeRegistry() = default; + ~EventTypeRegistry() = default; - static event_type_t initializeTypeId(event_mtype_t matrixTypeId); + static event_type_t initializeTypeId(event_mtype_t matrixTypeId); - template - static inline event_type_t initializeTypeId() - { - return initializeTypeId(EventT::matrixTypeId()); - } + template static inline event_type_t initializeTypeId() + { + return initializeTypeId(EventT::matrixTypeId()); + } - static QString getMatrixType(event_type_t typeId); + static QString getMatrixType(event_type_t typeId); private: - EventTypeRegistry() = default; - Q_DISABLE_COPY(EventTypeRegistry) - DISABLE_MOVE(EventTypeRegistry) + EventTypeRegistry() = default; + Q_DISABLE_COPY(EventTypeRegistry) + DISABLE_MOVE(EventTypeRegistry) - static EventTypeRegistry& get() - { - static EventTypeRegistry etr; - return etr; - } + static EventTypeRegistry& get() + { + static EventTypeRegistry etr; + return etr; + } - std::vector eventTypes; + std::vector eventTypes; }; - template <> - inline event_type_t EventTypeRegistry::initializeTypeId() + template <> inline event_type_t EventTypeRegistry::initializeTypeId() { return initializeTypeId(""); } - template - struct EventTypeTraits - { + template struct EventTypeTraits { static event_type_t id() { - static const auto id = EventTypeRegistry::initializeTypeId(); + static const auto id = + EventTypeRegistry::initializeTypeId(); return id; } }; - template - inline event_type_t typeId() + template inline event_type_t typeId() { return EventTypeTraits>::id(); } @@ -139,49 +133,45 @@ namespace QMatrixClient return std::make_unique(std::forward(args)...); } - template - class EventFactory + template class EventFactory { public: - template - static auto addMethod(FnT&& method) - { - factories().emplace_back(std::forward(method)); - return 0; - } - - /** Chain two type factories - * Adds the factory class of EventT2 (EventT2::factory_t) to - * the list in factory class of EventT1 (EventT1::factory_t) so - * that when EventT1::factory_t::make() is invoked, types of - * EventT2 factory are looked through as well. This is used - * to include RoomEvent types into the more general Event factory, - * and state event types into the RoomEvent factory. - */ - template - static auto chainFactory() - { - return addMethod(&EventT::factory_t::make); - } - - static event_ptr_tt make(const QJsonObject& json, - const QString& matrixType) - { - for (const auto& f: factories()) - if (auto e = f(json, matrixType)) - return e; - return nullptr; - } + template static auto addMethod(FnT&& method) + { + factories().emplace_back(std::forward(method)); + return 0; + } + + /** Chain two type factories + * Adds the factory class of EventT2 (EventT2::factory_t) to + * the list in factory class of EventT1 (EventT1::factory_t) so + * that when EventT1::factory_t::make() is invoked, types of + * EventT2 factory are looked through as well. This is used + * to include RoomEvent types into the more general Event factory, + * and state event types into the RoomEvent factory. + */ + template static auto chainFactory() + { + return addMethod(&EventT::factory_t::make); + } + + static event_ptr_tt make(const QJsonObject& json, + const QString& matrixType) + { + for (const auto& f : factories()) + if (auto e = f(json, matrixType)) + return e; + return nullptr; + } private: - static auto& factories() - { - using inner_factory_tt = - std::function(const QJsonObject&, - const QString&)>; - static std::vector _factories {}; - return _factories; - } + static auto& factories() + { + using inner_factory_tt = std::function( + const QJsonObject&, const QString&)>; + static std::vector _factories {}; + return _factories; + } }; /** Add a type to its default factory @@ -193,20 +183,18 @@ namespace QMatrixClient * \return the registered type id * \sa loadEvent, Event::type */ - template - inline auto setupFactory() + template inline auto setupFactory() { qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId(); return EventT::factory_t::addMethod( - [] (const QJsonObject& json, const QString& jsonMatrixType) - { - return EventT::matrixTypeId() == jsonMatrixType - ? makeEvent(json) : nullptr; - }); + [](const QJsonObject& json, const QString& jsonMatrixType) { + return EventT::matrixTypeId() == jsonMatrixType + ? makeEvent(json) + : nullptr; + }); } - template - inline auto registerEventType() + template inline auto registerEventType() { // Initialise exactly once, even if this function is called twice for // the same type (for whatever reason - you never know the ways of @@ -219,66 +207,64 @@ namespace QMatrixClient class Event { - Q_GADGET - Q_PROPERTY(Type type READ type CONSTANT) - Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) + Q_GADGET + Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) public: - using Type = event_type_t; - using factory_t = EventFactory; - - explicit Event(Type type, const QJsonObject& json); - explicit Event(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson = {}); - Q_DISABLE_COPY(Event) - Event(Event&&) = default; - Event& operator=(Event&&) = delete; - virtual ~Event(); - - Type type() const { return _type; } - QString matrixType() const; - QByteArray originalJson() const; - QJsonObject originalJsonObject() const { return fullJson(); } - - const QJsonObject& fullJson() const { return _json; } - - // According to the CS API spec, every event also has - // a "content" object; but since its structure is different for - // different types, we're implementing it per-event type. - - const QJsonObject contentJson() const; - const QJsonObject unsignedJson() const; - - template - T content(const QString& key) const - { - return fromJson(contentJson()[key]); - } - - template - T content(const QLatin1String& key) const - { - return fromJson(contentJson()[key]); - } - - friend QDebug operator<<(QDebug dbg, const Event& e) - { - QDebugStateSaver _dss { dbg }; - dbg.noquote().nospace() - << e.matrixType() << '(' << e.type() << "): "; - e.dumpTo(dbg); - return dbg; - } - - virtual bool isStateEvent() const { return false; } - virtual bool isCallEvent() const { return false; } - virtual void dumpTo(QDebug dbg) const; + using Type = event_type_t; + using factory_t = EventFactory; + + explicit Event(Type type, const QJsonObject& json); + explicit Event(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); + Q_DISABLE_COPY(Event) + Event(Event&&) = default; + Event& operator=(Event&&) = delete; + virtual ~Event(); + + Type type() const { return _type; } + QString matrixType() const; + QByteArray originalJson() const; + QJsonObject originalJsonObject() const { return fullJson(); } + + const QJsonObject& fullJson() const { return _json; } + + // According to the CS API spec, every event also has + // a "content" object; but since its structure is different for + // different types, we're implementing it per-event type. + + const QJsonObject contentJson() const; + const QJsonObject unsignedJson() const; + + template T content(const QString& key) const + { + return fromJson(contentJson()[key]); + } + + template T content(const QLatin1String& key) const + { + return fromJson(contentJson()[key]); + } + + friend QDebug operator<<(QDebug dbg, const Event& e) + { + QDebugStateSaver _dss { dbg }; + dbg.noquote().nospace() + << e.matrixType() << '(' << e.type() << "): "; + e.dumpTo(dbg); + return dbg; + } + + virtual bool isStateEvent() const { return false; } + virtual bool isCallEvent() const { return false; } + virtual void dumpTo(QDebug dbg) const; protected: - QJsonObject& editJson() { return _json; } + QJsonObject& editJson() { return _json; } private: - Type _type; - QJsonObject _json; + Type _type; + QJsonObject _json; }; using EventPtr = event_ptr_tt; @@ -290,24 +276,23 @@ namespace QMatrixClient // This macro should be used in a public section of an event class to // provide matrixTypeId() and typeId(). -#define DEFINE_EVENT_TYPEID(_Id, _Type) \ - static constexpr event_mtype_t matrixTypeId() { return _Id; } \ - static auto typeId() { return QMatrixClient::typeId<_Type>(); } \ +#define DEFINE_EVENT_TYPEID(_Id, _Type) \ + static constexpr event_mtype_t matrixTypeId() { return _Id; } \ + static auto typeId() { return QMatrixClient::typeId<_Type>(); } \ // End of macro // This macro should be put after an event class definition (in .h or .cpp) // to enable its deserialisation from a /sync and other // polymorphic event arrays -#define REGISTER_EVENT_TYPE(_Type) \ - namespace { \ - [[gnu::unused]] \ - static const auto _factoryAdded##_Type = registerEventType<_Type>(); \ - } \ +#define REGISTER_EVENT_TYPE(_Type) \ + namespace { \ + [[gnu::unused]] static const auto _factoryAdded##_Type = \ + registerEventType<_Type>(); \ + } \ // End of macro #ifdef USE_EVENTTYPE_ALIAS - namespace EventType - { + namespace EventType { inline event_type_t logEventType(event_type_t id, const char* idName) { qDebug(EVENTS) << "Using id" << id << "for" << idName; @@ -317,12 +302,12 @@ namespace QMatrixClient // This macro provides constants in EventType:: namespace for // back-compatibility with libQMatrixClient 0.3 event type system. -#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ - namespace EventType \ - { \ - [[deprecated("Use is<>(), eventCast<>() or visit<>()")]] \ - static const auto _Id = logEventType(typeId<_Type>(), #_Id); \ - } \ +#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ + namespace EventType { \ + [[deprecated( \ + "Use is<>(), eventCast<>() or visit<>()")]] static const auto \ + _Id = logEventType(typeId<_Type>(), #_Id); \ + } \ // End of macro #else #define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing @@ -330,46 +315,48 @@ namespace QMatrixClient // === is<>(), eventCast<>() and visit<>() === - template - inline bool is(const Event& e) { return e.type() == typeId(); } + template inline bool is(const Event& e) + { + return e.type() == typeId(); + } - inline bool isUnknown(const Event& e) { return e.type() == unknownEventTypeId(); } + inline bool isUnknown(const Event& e) + { + return e.type() == unknownEventTypeId(); + } template inline auto eventCast(const BasePtrT& eptr) - -> decltype(static_cast(&*eptr)) + -> decltype(static_cast(&*eptr)) { Q_ASSERT(eptr); - return is>(*eptr) - ? static_cast(&*eptr) : nullptr; + return is>(*eptr) ? static_cast(&*eptr) + : nullptr; } // A single generic catch-all visitor template inline auto visit(const BaseEventT& event, FnT&& visitor) - -> decltype(visitor(event)) + -> decltype(visitor(event)) { return visitor(event); } - template - constexpr auto is_event() + template constexpr auto is_event() { return std::is_base_of>::value; } - template - constexpr auto needs_cast() + template constexpr auto needs_cast() { return !std::is_convertible>::value; } // A single type-specific void visitor template - inline - std::enable_if_t< - is_event() && needs_cast() && - std::is_void>::value> + inline std::enable_if_t() + && needs_cast() + && std::is_void>::value> visit(const BaseEventT& event, FnT&& visitor) { using event_type = fn_arg_t; @@ -379,10 +366,9 @@ namespace QMatrixClient // A single type-specific non-void visitor with an optional default value template - inline - std::enable_if_t< - is_event() && needs_cast(), - fn_return_t> // non-voidness is guarded by defaultValue type + inline std::enable_if_t< + is_event() && needs_cast(), + fn_return_t> // non-voidness is guarded by defaultValue type visit(const BaseEventT& event, FnT&& visitor, fn_return_t&& defaultValue = {}) { @@ -393,9 +379,9 @@ namespace QMatrixClient } // A chain of 2 or more visitors - template - inline - std::enable_if_t(), fn_return_t> + template + inline std::enable_if_t(), fn_return_t> visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, FnTs&&... visitors) { @@ -405,6 +391,6 @@ namespace QMatrixClient return visit(event, std::forward(visitor2), std::forward(visitors)...); } -} // namespace QMatrixClient +} // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Event*) Q_DECLARE_METATYPE(const QMatrixClient::Event*) diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 9a5e872c..cc31fea5 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "eventcontent.h" @@ -34,17 +34,21 @@ QJsonObject Base::toJson() const FileInfo::FileInfo(const QUrl& u, qint64 payloadSize, const QMimeType& mimeType, const QString& originalFilename) - : mimeType(mimeType), url(u), payloadSize(payloadSize) - , originalName(originalFilename) -{ } + : mimeType(mimeType), + url(u), + payloadSize(payloadSize), + originalName(originalFilename) +{ +} FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) - : originalInfoJson(infoJson) - , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString())) - , url(u) - , payloadSize(fromJson(infoJson["size"_ls])) - , originalName(originalFilename) + : originalInfoJson(infoJson), + mimeType(QMimeDatabase().mimeTypeForName( + infoJson["mimetype"_ls].toString())), + url(u), + payloadSize(fromJson(infoJson["size"_ls])), + originalName(originalFilename) { if (!mimeType.isValid()) mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); @@ -62,13 +66,15 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType, const QSize& imageSize, const QString& originalFilename) : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize) -{ } +{ +} ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) - : FileInfo(u, infoJson, originalFilename) - , imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) -{ } + : FileInfo(u, infoJson, originalFilename), + imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) +{ +} void ImageInfo::fillInfoJson(QJsonObject* infoJson) const { @@ -82,7 +88,8 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const Thumbnail::Thumbnail(const QJsonObject& infoJson) : ImageInfo(infoJson["thumbnail_url"_ls].toString(), infoJson["thumbnail_info"_ls].toObject()) -{ } +{ +} void Thumbnail::fillInfoJson(QJsonObject* infoJson) const { diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 0588c0e2..857e7369 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -23,13 +23,11 @@ #include #include -#include #include +#include -namespace QMatrixClient -{ - namespace EventContent - { +namespace QMatrixClient { + namespace EventContent { /** * A base class for all content types that can be stored * in a RoomMessageEvent @@ -43,17 +41,17 @@ namespace QMatrixClient class Base { public: - explicit Base (QJsonObject o = {}) : originalJson(std::move(o)) { } - virtual ~Base() = default; + explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {} + virtual ~Base() = default; - // FIXME: make toJson() from converters.* work on base classes - QJsonObject toJson() const; + // FIXME: make toJson() from converters.* work on base classes + QJsonObject toJson() const; public: - QJsonObject originalJson; + QJsonObject originalJson; protected: - virtual void fillJson(QJsonObject* o) const = 0; + virtual void fillJson(QJsonObject* o) const = 0; }; // The below structures fairly follow CS spec 11.2.1.6. The overall @@ -81,40 +79,40 @@ namespace QMatrixClient * JSON content, as well as contents of "url" (or a similar key) and * optionally "filename" node from the main JSON content. Assuming you * don't do unusual things, you should use \p UrlBasedContent<> instead - * of doing multiple inheritance and overriding Base::fillJson() by hand. + * of doing multiple inheritance and overriding Base::fillJson() by + * hand. * * This class is not polymorphic. */ class FileInfo { public: - explicit FileInfo(const QUrl& u, qint64 payloadSize = -1, - const QMimeType& mimeType = {}, - const QString& originalFilename = {}); - FileInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}); + explicit FileInfo(const QUrl& u, qint64 payloadSize = -1, + const QMimeType& mimeType = {}, + const QString& originalFilename = {}); + FileInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); - void fillInfoJson(QJsonObject* infoJson) const; + void fillInfoJson(QJsonObject* infoJson) const; - /** - * \brief Extract media id from the URL - * - * This can be used, e.g., to construct a QML-facing image:// - * URI as follows: - * \code "image://provider/" + info.mediaId() \endcode - */ - QString mediaId() const { return url.authority() + url.path(); } + /** + * \brief Extract media id from the URL + * + * This can be used, e.g., to construct a QML-facing image:// + * URI as follows: + * \code "image://provider/" + info.mediaId() \endcode + */ + QString mediaId() const { return url.authority() + url.path(); } public: - QJsonObject originalInfoJson; - QMimeType mimeType; - QUrl url; - qint64 payloadSize; - QString originalName; + QJsonObject originalInfoJson; + QMimeType mimeType; + QUrl url; + qint64 payloadSize; + QString originalName; }; - template - QJsonObject toInfoJson(const InfoT& info) + template QJsonObject toInfoJson(const InfoT& info) { QJsonObject infoJson; info.fillInfoJson(&infoJson); @@ -127,17 +125,17 @@ namespace QMatrixClient class ImageInfo : public FileInfo { public: - explicit ImageInfo(const QUrl& u, qint64 fileSize = -1, - QMimeType mimeType = {}, - const QSize& imageSize = {}, - const QString& originalFilename = {}); - ImageInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}); + explicit ImageInfo(const QUrl& u, qint64 fileSize = -1, + QMimeType mimeType = {}, + const QSize& imageSize = {}, + const QString& originalFilename = {}); + ImageInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); - void fillInfoJson(QJsonObject* infoJson) const; + void fillInfoJson(QJsonObject* infoJson) const; public: - QSize imageSize; + QSize imageSize; }; /** @@ -150,26 +148,26 @@ namespace QMatrixClient class Thumbnail : public ImageInfo { public: - Thumbnail() : ImageInfo(QUrl()) { } // To allow empty thumbnails - Thumbnail(const QJsonObject& infoJson); - Thumbnail(const ImageInfo& info) : ImageInfo(info) { } - using ImageInfo::ImageInfo; + Thumbnail() : ImageInfo(QUrl()) {} // To allow empty thumbnails + Thumbnail(const QJsonObject& infoJson); + Thumbnail(const ImageInfo& info) : ImageInfo(info) {} + using ImageInfo::ImageInfo; - /** - * Writes thumbnail information to "thumbnail_info" subobject - * and thumbnail URL to "thumbnail_url" node inside "info". - */ - void fillInfoJson(QJsonObject* infoJson) const; + /** + * Writes thumbnail information to "thumbnail_info" subobject + * and thumbnail URL to "thumbnail_url" node inside "info". + */ + void fillInfoJson(QJsonObject* infoJson) const; }; - class TypedBase: public Base + class TypedBase : public Base { public: - explicit TypedBase(const QJsonObject& o = {}) : Base(o) { } - virtual QMimeType type() const = 0; - virtual const FileInfo* fileInfo() const { return nullptr; } - virtual FileInfo* fileInfo() { return nullptr; } - virtual const Thumbnail* thumbnailInfo() const { return nullptr; } + explicit TypedBase(const QJsonObject& o = {}) : Base(o) {} + virtual QMimeType type() const = 0; + virtual const FileInfo* fileInfo() const { return nullptr; } + virtual FileInfo* fileInfo() { return nullptr; } + virtual const Thumbnail* thumbnailInfo() const { return nullptr; } }; /** @@ -186,59 +184,61 @@ namespace QMatrixClient class UrlBasedContent : public TypedBase, public InfoT { public: - using InfoT::InfoT; - explicit UrlBasedContent(const QJsonObject& json) - : TypedBase(json) - , InfoT(json["url"].toString(), json["info"].toObject(), - json["filename"].toString()) - { - // A small hack to facilitate links creation in QML. - originalJson.insert("mediaId", InfoT::mediaId()); - } + using InfoT::InfoT; + explicit UrlBasedContent(const QJsonObject& json) + : TypedBase(json), + InfoT(json["url"].toString(), json["info"].toObject(), + json["filename"].toString()) + { + // A small hack to facilitate links creation in QML. + originalJson.insert("mediaId", InfoT::mediaId()); + } - QMimeType type() const override { return InfoT::mimeType; } - const FileInfo* fileInfo() const override { return this; } - FileInfo* fileInfo() override { return this; } + QMimeType type() const override { return InfoT::mimeType; } + const FileInfo* fileInfo() const override { return this; } + FileInfo* fileInfo() override { return this; } protected: - void fillJson(QJsonObject* json) const override - { - Q_ASSERT(json); - json->insert("url", InfoT::url.toString()); - if (!InfoT::originalName.isEmpty()) - json->insert("filename", InfoT::originalName); - json->insert("info", toInfoJson(*this)); - } + void fillJson(QJsonObject* json) const override + { + Q_ASSERT(json); + json->insert("url", InfoT::url.toString()); + if (!InfoT::originalName.isEmpty()) + json->insert("filename", InfoT::originalName); + json->insert("info", toInfoJson(*this)); + } }; template class UrlWithThumbnailContent : public UrlBasedContent { public: - using UrlBasedContent::UrlBasedContent; - explicit UrlWithThumbnailContent(const QJsonObject& json) - : UrlBasedContent(json) - , thumbnail(InfoT::originalInfoJson) - { - // Another small hack, to simplify making a thumbnail link - UrlBasedContent::originalJson.insert( - "thumbnailMediaId", thumbnail.mediaId()); - } + using UrlBasedContent::UrlBasedContent; + explicit UrlWithThumbnailContent(const QJsonObject& json) + : UrlBasedContent(json), + thumbnail(InfoT::originalInfoJson) + { + // Another small hack, to simplify making a thumbnail link + UrlBasedContent::originalJson.insert( + "thumbnailMediaId", thumbnail.mediaId()); + } - const Thumbnail* thumbnailInfo() const override - { return &thumbnail; } + const Thumbnail* thumbnailInfo() const override + { + return &thumbnail; + } public: - Thumbnail thumbnail; + Thumbnail thumbnail; protected: - void fillJson(QJsonObject* json) const override - { - UrlBasedContent::fillJson(json); - auto infoJson = json->take("info").toObject(); - thumbnail.fillInfoJson(&infoJson); - json->insert("info", infoJson); - } + void fillJson(QJsonObject* json) const override + { + UrlBasedContent::fillJson(json); + auto infoJson = json->take("info").toObject(); + thumbnail.fillInfoJson(&infoJson); + json->insert("info", infoJson); + } }; /** @@ -278,5 +278,5 @@ namespace QMatrixClient * - thumbnail.imageSize (QSize for "h" and "w" in JSON) */ using FileContent = UrlWithThumbnailContent; - } // namespace EventContent -} // namespace QMatrixClient + } // namespace EventContent +} // namespace QMatrixClient diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index da663392..a19a83b6 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 @@ -57,8 +57,7 @@ namespace QMatrixClient { matrixType); } - template struct JsonConverter> - { + template struct JsonConverter> { static auto load(const QJsonValue& jv) { return loadEvent(jv.toObject()); diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 47e1398c..76c7b6e4 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* @@ -40,30 +40,27 @@ Example of a Receipt Event: using namespace QMatrixClient; -ReceiptEvent::ReceiptEvent(const QJsonObject& obj) - : Event(typeId(), obj) +ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& contents = contentJson(); _eventsWithReceipts.reserve(contents.size()); - for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt ) - { - if (eventIt.key().isEmpty()) - { - qCWarning(EPHEMERAL) << "ReceiptEvent has an empty event id, skipping"; + for (auto eventIt = contents.begin(); eventIt != contents.end(); + ++eventIt) { + if (eventIt.key().isEmpty()) { + qCWarning(EPHEMERAL) + << "ReceiptEvent has an empty event id, skipping"; qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents; continue; } - const QJsonObject reads = eventIt.value().toObject() - .value("m.read"_ls).toObject(); + const QJsonObject reads = + eventIt.value().toObject().value("m.read"_ls).toObject(); QVector receipts; receipts.reserve(reads.size()); - for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) - { + for (auto userIt = reads.begin(); userIt != reads.end(); ++userIt) { const QJsonObject user = userIt.value().toObject(); - receipts.push_back({userIt.key(), - fromJson(user["ts"_ls])}); + receipts.push_back( + { userIt.key(), fromJson(user["ts"_ls]) }); } - _eventsWithReceipts.push_back({eventIt.key(), std::move(receipts)}); + _eventsWithReceipts.push_back({ eventIt.key(), std::move(receipts) }); } } - diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index c15a01c2..fca38bba 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -13,42 +13,41 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -#include #include +#include -namespace QMatrixClient -{ - struct Receipt - { +namespace QMatrixClient { + struct Receipt { QString userId; QDateTime timestamp; }; - struct ReceiptsForEvent - { + struct ReceiptsForEvent { QString evtId; QVector receipts; }; using EventsWithReceipts = QVector; - class ReceiptEvent: public Event + class ReceiptEvent : public Event { public: - DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) - explicit ReceiptEvent(const QJsonObject& obj); + DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) + explicit ReceiptEvent(const QJsonObject& obj); - const EventsWithReceipts& eventsWithReceipts() const - { return _eventsWithReceipts; } + const EventsWithReceipts& eventsWithReceipts() const + { + return _eventsWithReceipts; + } private: - EventsWithReceipts _eventsWithReceipts; + EventsWithReceipts _eventsWithReceipts; }; REGISTER_EVENT_TYPE(ReceiptEvent) DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index a72a8ff9..4187291c 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -13,28 +13,29 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "roomevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class RedactionEvent : public RoomEvent { public: - DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) + DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) - explicit RedactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - { } + explicit RedactionEvent(const QJsonObject& obj) + : RoomEvent(typeId(), obj) + { + } - QString redactedEvent() const - { return fullJson()["redacts"_ls].toString(); } - QString reason() const - { return contentJson()["reason"_ls].toString(); } + QString redactedEvent() const + { + return fullJson()["redacts"_ls].toString(); + } + QString reason() const { return contentJson()["reason"_ls].toString(); } }; REGISTER_EVENT_TYPE(RedactionEvent) DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index a43d3a85..69cd1f9d 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -13,29 +13,29 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "stateevent.h" #include "eventcontent.h" +#include "stateevent.h" -namespace QMatrixClient -{ - class RoomAvatarEvent: public StateEvent +namespace QMatrixClient { + class RoomAvatarEvent : public StateEvent { - // It's a bit of an overkill to use a full-fledged ImageContent - // because in reality m.room.avatar usually only has a single URL, - // without a thumbnail. But The Spec says there be thumbnails, and - // we follow The Spec. + // It's a bit of an overkill to use a full-fledged ImageContent + // because in reality m.room.avatar usually only has a single URL, + // without a thumbnail. But The Spec says there be thumbnails, and + // we follow The Spec. public: - DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) - explicit RoomAvatarEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) - { } - QUrl url() const { return content().url; } + DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) + explicit RoomAvatarEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) + { + } + QUrl url() const { return content().url; } }; REGISTER_EVENT_TYPE(RoomAvatarEvent) DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/events/roomcreateevent.cpp b/lib/events/roomcreateevent.cpp index 8fd0f1de..cb575f24 100644 --- a/lib/events/roomcreateevent.cpp +++ b/lib/events/roomcreateevent.cpp @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2019 QMatrixClient project -* -* 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 -*/ + * Copyright (C) 2019 QMatrixClient project + * + * 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 "roomcreateevent.h" @@ -33,10 +33,8 @@ QString RoomCreateEvent::version() const RoomCreateEvent::Predecessor RoomCreateEvent::predecessor() const { const auto predJson = contentJson()["predecessor"_ls].toObject(); - return { - fromJson(predJson["room_id"_ls]), - fromJson(predJson["event_id"_ls]) - }; + return { fromJson(predJson["room_id"_ls]), + fromJson(predJson["event_id"_ls]) }; } bool RoomCreateEvent::isUpgrade() const diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index 0a8f27cc..fbfb33ca 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -1,49 +1,46 @@ /****************************************************************************** -* Copyright (C) 2019 QMatrixClient project -* -* 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 -*/ + * Copyright (C) 2019 QMatrixClient project + * + * 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 "stateevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class RoomCreateEvent : public StateEventBase { public: - DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) + DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) - explicit RoomCreateEvent() - : StateEventBase(typeId(), matrixTypeId()) - { } - explicit RoomCreateEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { } + explicit RoomCreateEvent() : StateEventBase(typeId(), matrixTypeId()) {} + explicit RoomCreateEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + { + } - struct Predecessor - { - QString roomId; - QString eventId; - }; + struct Predecessor { + QString roomId; + QString eventId; + }; - bool isFederated() const; - QString version() const; - Predecessor predecessor() const; - bool isUpgrade() const; + bool isFederated() const; + QString version() const; + Predecessor predecessor() const; + bool isUpgrade() const; }; REGISTER_EVENT_TYPE(RoomCreateEvent) } diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 3d03509f..221a3a61 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -1,26 +1,26 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 "roomevent.h" -#include "redactionevent.h" #include "converters.h" #include "logging.h" +#include "redactionevent.h" using namespace QMatrixClient; @@ -30,15 +30,14 @@ using namespace QMatrixClient; RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) : Event(type, matrixType, contentJson) -{ } +{ +} -RoomEvent::RoomEvent(Type type, const QJsonObject& json) - : Event(type, json) +RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json) { const auto unsignedData = json[UnsignedKeyL].toObject(); const auto redaction = unsignedData[RedactedCauseKeyL]; - if (redaction.isObject()) - { + if (redaction.isObject()) { _redactedBecause = makeEvent(redaction.toObject()); return; } @@ -46,14 +45,12 @@ RoomEvent::RoomEvent(Type type, const QJsonObject& json) RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job -QString RoomEvent::id() const -{ - return fullJson()[EventIdKeyL].toString(); -} +QString RoomEvent::id() const { return fullJson()[EventIdKeyL].toString(); } QDateTime RoomEvent::timestamp() const { - return QMatrixClient::fromJson(fullJson()["origin_server_ts"_ls]); + return QMatrixClient::fromJson( + fullJson()["origin_server_ts"_ls]); } QString RoomEvent::roomId() const @@ -68,7 +65,7 @@ QString RoomEvent::senderId() const QString RoomEvent::redactionReason() const { - return isRedacted() ? _redactedBecause->reason() : QString{}; + return isRedacted() ? _redactedBecause->reason() : QString {}; } QString RoomEvent::transactionId() const @@ -91,7 +88,8 @@ void RoomEvent::setTransactionId(const QString& txnId) void RoomEvent::addId(const QString& newId) { - Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty()); + Q_ASSERT(id().isEmpty()); + Q_ASSERT(!newId.isEmpty()); editJson().insert(EventIdKey, newId); qCDebug(EVENTS) << "Event txnId -> id:" << transactionId() << "->" << id(); Q_ASSERT(id() == newId); @@ -110,7 +108,8 @@ CallEventBase::CallEventBase(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) : RoomEvent(type, matrixType, makeCallContentJson(callId, version, contentJson)) -{ } +{ +} CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json) : RoomEvent(type, json) diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index ce96174e..746e9173 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 @@ -22,84 +22,84 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { class RedactionEvent; /** This class corresponds to m.room.* events */ class RoomEvent : public Event { - Q_GADGET - Q_PROPERTY(QString id READ id) - Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) - Q_PROPERTY(QString roomId READ roomId CONSTANT) - Q_PROPERTY(QString senderId READ senderId CONSTANT) - Q_PROPERTY(QString redactionReason READ redactionReason) - Q_PROPERTY(bool isRedacted READ isRedacted) - Q_PROPERTY(QString transactionId READ transactionId WRITE setTransactionId) + Q_GADGET + Q_PROPERTY(QString id READ id) + Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) + Q_PROPERTY(QString roomId READ roomId CONSTANT) + Q_PROPERTY(QString senderId READ senderId CONSTANT) + Q_PROPERTY(QString redactionReason READ redactionReason) + Q_PROPERTY(bool isRedacted READ isRedacted) + Q_PROPERTY( + QString transactionId READ transactionId WRITE setTransactionId) public: - using factory_t = EventFactory; + using factory_t = EventFactory; - // RedactionEvent is an incomplete type here so we cannot inline - // constructors and destructors and we cannot use 'using'. - RoomEvent(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson = {}); - RoomEvent(Type type, const QJsonObject& json); - ~RoomEvent() override; + // RedactionEvent is an incomplete type here so we cannot inline + // constructors and destructors and we cannot use 'using'. + RoomEvent(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); + RoomEvent(Type type, const QJsonObject& json); + ~RoomEvent() override; - QString id() const; - QDateTime timestamp() const; - QString roomId() const; - QString senderId() const; - bool isRedacted() const { return bool(_redactedBecause); } - const event_ptr_tt& redactedBecause() const - { - return _redactedBecause; - } - QString redactionReason() const; - QString transactionId() const; - QString stateKey() const; + QString id() const; + QDateTime timestamp() const; + QString roomId() const; + QString senderId() const; + bool isRedacted() const { return bool(_redactedBecause); } + const event_ptr_tt& redactedBecause() const + { + return _redactedBecause; + } + QString redactionReason() const; + QString transactionId() const; + QString stateKey() const; - /** - * Sets the transaction id for locally created events. This should be - * done before the event is exposed to any code using the respective - * Q_PROPERTY. - * - * \param txnId - transaction id, normally obtained from - * Connection::generateTxnId() - */ - void setTransactionId(const QString& txnId); + /** + * Sets the transaction id for locally created events. This should be + * done before the event is exposed to any code using the respective + * Q_PROPERTY. + * + * \param txnId - transaction id, normally obtained from + * Connection::generateTxnId() + */ + void setTransactionId(const QString& txnId); - /** - * Sets event id for locally created events - * - * When a new event is created locally, it has no server id yet. - * This function allows to add the id once the confirmation from - * the server is received. There should be no id set previously - * in the event. It's the responsibility of the code calling addId() - * to notify clients that use Q_PROPERTY(id) about its change - */ - void addId(const QString& newId); + /** + * Sets event id for locally created events + * + * When a new event is created locally, it has no server id yet. + * This function allows to add the id once the confirmation from + * the server is received. There should be no id set previously + * in the event. It's the responsibility of the code calling addId() + * to notify clients that use Q_PROPERTY(id) about its change + */ + void addId(const QString& newId); private: - event_ptr_tt _redactedBecause; + event_ptr_tt _redactedBecause; }; using RoomEventPtr = event_ptr_tt; using RoomEvents = EventsArray; using RoomEventsRange = Range; - class CallEventBase: public RoomEvent + class CallEventBase : public RoomEvent { public: - CallEventBase(Type type, event_mtype_t matrixType, - const QString& callId, int version, - const QJsonObject& contentJson = {}); - CallEventBase(Type type, const QJsonObject& json); - ~CallEventBase() override = default; - bool isCallEvent() const override { return true; } + CallEventBase(Type type, event_mtype_t matrixType, + const QString& callId, int version, + const QJsonObject& contentJson = {}); + CallEventBase(Type type, const QJsonObject& json); + ~CallEventBase() override = default; + bool isCallEvent() const override { return true; } - QString callId() const { return content("call_id"_ls); } - int version() const { return content("version"_ls); } + QString callId() const { return content("call_id"_ls); } + int version() const { return content("version"_ls); } }; } // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*) diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index a5ac3c5f..53203873 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "roommemberevent.h" @@ -23,21 +23,18 @@ #include -static const std::array membershipStrings = { { - QStringLiteral("invite"), QStringLiteral("join"), - QStringLiteral("knock"), QStringLiteral("leave"), - QStringLiteral("ban") -} }; +static const std::array membershipStrings = { + { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"), + QStringLiteral("leave"), QStringLiteral("ban") } +}; namespace QMatrixClient { - template <> - struct JsonConverter - { + template <> struct JsonConverter { static MembershipType load(const QJsonValue& jv) { const auto& membershipString = jv.toString(); for (auto it = membershipStrings.begin(); - it != membershipStrings.end(); ++it) + it != membershipStrings.end(); ++it) if (membershipString == *it) return MembershipType(it - membershipStrings.begin()); @@ -50,17 +47,18 @@ namespace QMatrixClient { using namespace QMatrixClient; MemberEventContent::MemberEventContent(const QJsonObject& json) - : membership(fromJson(json["membership"_ls])) - , isDirect(json["is_direct"_ls].toBool()) - , displayName(json["displayname"_ls].toString()) - , avatarUrl(json["avatar_url"_ls].toString()) -{ } + : membership(fromJson(json["membership"_ls])), + isDirect(json["is_direct"_ls].toBool()), + displayName(json["displayname"_ls].toString()), + avatarUrl(json["avatar_url"_ls].toString()) +{ +} void MemberEventContent::fillJson(QJsonObject* o) const { Q_ASSERT(o); Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__, - "The key 'membership' must be explicit in MemberEventContent"); + "The key 'membership' must be explicit in MemberEventContent"); if (membership != MembershipType::Undefined) o->insert(QStringLiteral("membership"), membershipStrings[membership]); o->insert(QStringLiteral("displayname"), displayName); @@ -70,21 +68,21 @@ void MemberEventContent::fillJson(QJsonObject* o) const bool RoomMemberEvent::isInvite() const { - return membership() == MembershipType::Invite && - (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Invite + && (!prevContent() || prevContent()->membership != membership()); } bool RoomMemberEvent::isJoin() const { - return membership() == MembershipType::Join && - (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Join + && (!prevContent() || prevContent()->membership != membership()); } bool RoomMemberEvent::isLeave() const { - return membership() == MembershipType::Leave && - prevContent() && prevContent()->membership != membership() && - prevContent()->membership != MembershipType::Ban; + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership != membership() + && prevContent()->membership != MembershipType::Ban; } bool RoomMemberEvent::isRename() const diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index b8224033..55f419d8 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -13,94 +13,100 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "stateevent.h" #include "eventcontent.h" +#include "stateevent.h" -namespace QMatrixClient -{ - class MemberEventContent: public EventContent::Base +namespace QMatrixClient { + class MemberEventContent : public EventContent::Base { public: - enum MembershipType : size_t { Invite = 0, Join, Knock, Leave, Ban, - Undefined }; + enum MembershipType : size_t { + Invite = 0, + Join, + Knock, + Leave, + Ban, + Undefined + }; - explicit MemberEventContent(MembershipType mt = Join) - : membership(mt) - { } - explicit MemberEventContent(const QJsonObject& json); + explicit MemberEventContent(MembershipType mt = Join) : membership(mt) + { + } + explicit MemberEventContent(const QJsonObject& json); - MembershipType membership; - bool isDirect = false; - QString displayName; - QUrl avatarUrl; + MembershipType membership; + bool isDirect = false; + QString displayName; + QUrl avatarUrl; protected: - void fillJson(QJsonObject* o) const override; + void fillJson(QJsonObject* o) const override; }; using MembershipType = MemberEventContent::MembershipType; - class RoomMemberEvent: public StateEvent + class RoomMemberEvent : public StateEvent { - Q_GADGET + Q_GADGET public: - DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) + DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) - using MembershipType = MemberEventContent::MembershipType; + using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) - { } - RoomMemberEvent(MemberEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) - { } + explicit RoomMemberEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) + { + } + RoomMemberEvent(MemberEventContent&& c) + : StateEvent(typeId(), matrixTypeId(), c) + { + } - /// A special constructor to create unknown RoomMemberEvents - /** - * This is needed in order to use RoomMemberEvent as a "base event - * class" in cases like GetMembersByRoomJob when RoomMemberEvents - * (rather than RoomEvents or StateEvents) are resolved from JSON. - * For such cases loadEvent<> requires an underlying class to be - * constructible with unknownTypeId() instead of its genuine id. - * Don't use it directly. - * \sa GetMembersByRoomJob, loadEvent, unknownTypeId - */ - RoomMemberEvent(Type type, const QJsonObject& fullJson) - : StateEvent(type, fullJson) - { } + /// A special constructor to create unknown RoomMemberEvents + /** + * This is needed in order to use RoomMemberEvent as a "base event + * class" in cases like GetMembersByRoomJob when RoomMemberEvents + * (rather than RoomEvents or StateEvents) are resolved from JSON. + * For such cases loadEvent<> requires an underlying class to be + * constructible with unknownTypeId() instead of its genuine id. + * Don't use it directly. + * \sa GetMembersByRoomJob, loadEvent, unknownTypeId + */ + RoomMemberEvent(Type type, const QJsonObject& fullJson) + : StateEvent(type, fullJson) + { + } - MembershipType membership() const { return content().membership; } - QString userId() const - { return fullJson()["state_key"_ls].toString(); } - bool isDirect() const { return content().isDirect; } - QString displayName() const { return content().displayName; } - QUrl avatarUrl() const { return content().avatarUrl; } - bool isInvite() const; - bool isJoin() const; - bool isLeave() const; - bool isRename() const; - bool isAvatarUpdate() const; + MembershipType membership() const { return content().membership; } + QString userId() const { return fullJson()["state_key"_ls].toString(); } + bool isDirect() const { return content().isDirect; } + QString displayName() const { return content().displayName; } + QUrl avatarUrl() const { return content().avatarUrl; } + bool isInvite() const; + bool isJoin() const; + bool isLeave() const; + bool isRename() const; + bool isAvatarUpdate() const; private: - REGISTER_ENUM(MembershipType) + REGISTER_ENUM(MembershipType) }; - template <> - class EventFactory + template <> class EventFactory { public: - static event_ptr_tt make(const QJsonObject& json, - const QString&) - { - return makeEvent(json); - } + static event_ptr_tt make(const QJsonObject& json, + const QString&) + { + return makeEvent(json); + } }; REGISTER_EVENT_TYPE(RoomMemberEvent) DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 8f4e0ebc..0af02eb0 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -13,15 +13,15 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "roommessageevent.h" #include "logging.h" -#include #include +#include #include #include @@ -40,41 +40,40 @@ static const auto NoticeTypeKey = "m.notice"; static const auto HtmlContentTypeId = QStringLiteral("org.matrix.custom.html"); -template -TypedBase* make(const QJsonObject& json) +template TypedBase* make(const QJsonObject& json) { return new ContentT(json); } -template <> -TypedBase* make(const QJsonObject& json) +template <> TypedBase* make(const QJsonObject& json) { return json.contains(FormattedBodyKey) || json.contains(RelatesToKey) - ? new TextContent(json) : nullptr; + ? new TextContent(json) + : nullptr; } -struct MsgTypeDesc -{ +struct MsgTypeDesc { QString matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); }; -const std::vector msgTypes = - { { TextTypeKey, MsgType::Text, make } - , { QStringLiteral("m.emote"), MsgType::Emote, make } - , { NoticeTypeKey, MsgType::Notice, make } - , { QStringLiteral("m.image"), MsgType::Image, make } - , { QStringLiteral("m.file"), MsgType::File, make } - , { QStringLiteral("m.location"), MsgType::Location, make } - , { QStringLiteral("m.video"), MsgType::Video, make } - , { QStringLiteral("m.audio"), MsgType::Audio, make } - }; +const std::vector msgTypes = { + { TextTypeKey, MsgType::Text, make }, + { QStringLiteral("m.emote"), MsgType::Emote, make }, + { NoticeTypeKey, MsgType::Notice, make }, + { QStringLiteral("m.image"), MsgType::Image, make }, + { QStringLiteral("m.file"), MsgType::File, make }, + { QStringLiteral("m.location"), MsgType::Location, make }, + { QStringLiteral("m.video"), MsgType::Video, make }, + { QStringLiteral("m.audio"), MsgType::Audio, make } +}; QString msgTypeToJson(MsgType enumType) { - auto it = std::find_if(msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; }); + auto it = std::find_if( + msgTypes.begin(), msgTypes.end(), + [=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; }); if (it != msgTypes.end()) return it->matrixType; @@ -84,7 +83,9 @@ QString msgTypeToJson(MsgType enumType) MsgType jsonToMsgType(const QString& matrixType) { auto it = std::find_if(msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.matrixType == matrixType; }); + [=](const MsgTypeDesc& mtd) { + return mtd.matrixType == matrixType; + }); if (it != msgTypes.end()) return it->enumType; @@ -92,12 +93,12 @@ MsgType jsonToMsgType(const QString& matrixType) } QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, - const QString& jsonMsgType, TypedBase* content) + const QString& jsonMsgType, + TypedBase* content) { auto json = content ? content->toJson() : QJsonObject(); - if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey && - json.contains(RelatesToKey)) - { + if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey + && json.contains(RelatesToKey)) { json.remove(RelatesToKey); qCWarning(EVENTS) << RelatesToKey << "cannot be used in" << jsonMsgType << "messages; the relation has been stripped off"; @@ -108,24 +109,26 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - const QString& jsonMsgType, TypedBase* content) + const QString& jsonMsgType, + TypedBase* content) : RoomEvent(typeId(), matrixTypeId(), - assembleContentJson(plainBody, jsonMsgType, content)) - , _content(content) -{ } + assembleContentJson(plainBody, jsonMsgType, content)), + _content(content) +{ +} -RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - MsgType msgType, TypedBase* content) +RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType, + TypedBase* content) : RoomMessageEvent(plainBody, msgTypeToJson(msgType), content) -{ } +{ +} TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile) { auto filePath = file.absoluteFilePath(); auto localUrl = QUrl::fromLocalFile(filePath); auto mimeType = QMimeDatabase().mimeTypeForFile(file); - if (!asGenericFile) - { + if (!asGenericFile) { auto mimeTypeName = mimeType.name(); if (mimeTypeName.startsWith("image/")) return new ImageContent(localUrl, file.size(), mimeType, @@ -147,11 +150,13 @@ TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile) } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - const QFileInfo& file, bool asGenericFile) + const QFileInfo& file, bool asGenericFile) : RoomMessageEvent(plainBody, - asGenericFile ? QStringLiteral("m.file") : rawMsgTypeForFile(file), - contentFromFile(file, asGenericFile)) -{ } + asGenericFile ? QStringLiteral("m.file") + : rawMsgTypeForFile(file), + contentFromFile(file, asGenericFile)) +{ +} RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj), _content(nullptr) @@ -159,26 +164,21 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) if (isRedacted()) return; const QJsonObject content = contentJson(); - if ( content.contains(MsgTypeKey) && content.contains(BodyKey) ) - { + if (content.contains(MsgTypeKey) && content.contains(BodyKey)) { auto msgtype = content[MsgTypeKey].toString(); bool msgTypeFound = false; - for (const auto& mt: msgTypes) - if (mt.matrixType == msgtype) - { + for (const auto& mt : msgTypes) + if (mt.matrixType == msgtype) { _content.reset(mt.maker(content)); msgTypeFound = true; } - if (!msgTypeFound) - { + if (!msgTypeFound) { qCWarning(EVENTS) << "RoomMessageEvent: unknown msg_type," << " full content dump follows"; qCWarning(EVENTS) << formatJson << content; } - } - else - { + } else { qCWarning(EVENTS) << "No body or msgtype in room message event"; qCWarning(EVENTS) << formatJson << obj; } @@ -208,9 +208,9 @@ QMimeType RoomMessageEvent::mimeType() const bool RoomMessageEvent::hasTextContent() const { - return !content() || - (msgtype() == MsgType::Text || msgtype() == MsgType::Emote || - msgtype() == MsgType::Notice); + return !content() + || (msgtype() == MsgType::Text || msgtype() == MsgType::Emote + || msgtype() == MsgType::Notice); } bool RoomMessageEvent::hasFileContent() const @@ -226,10 +226,11 @@ bool RoomMessageEvent::hasThumbnail() const QString rawMsgTypeForMimeType(const QMimeType& mimeType) { auto name = mimeType.name(); - return name.startsWith("image/") ? QStringLiteral("m.image") : - name.startsWith("video/") ? QStringLiteral("m.video") : - name.startsWith("audio/") ? QStringLiteral("m.audio") : - QStringLiteral("m.file"); + return name.startsWith("image/") ? QStringLiteral("m.image") + : name.startsWith("video/") + ? QStringLiteral("m.video") + : name.startsWith("audio/") ? QStringLiteral("m.audio") + : QStringLiteral("m.file"); } QString RoomMessageEvent::rawMsgTypeForUrl(const QUrl& url) @@ -244,8 +245,9 @@ QString RoomMessageEvent::rawMsgTypeForFile(const QFileInfo& fi) TextContent::TextContent(const QString& text, const QString& contentType, Omittable relatesTo) - : mimeType(QMimeDatabase().mimeTypeForName(contentType)), body(text) - , relatesTo(std::move(relatesTo)) + : mimeType(QMimeDatabase().mimeTypeForName(contentType)), + body(text), + relatesTo(std::move(relatesTo)) { if (contentType == HtmlContentTypeId) mimeType = QMimeDatabase().mimeTypeForName("text/html"); @@ -259,8 +261,7 @@ TextContent::TextContent(const QJsonObject& json) // Special-casing the custom matrix.org's (actually, Riot's) way // of sending HTML messages. - if (json["format"_ls].toString() == HtmlContentTypeId) - { + if (json["format"_ls].toString() == HtmlContentTypeId) { mimeType = HtmlMimeType; body = json[FormattedBodyKey].toString(); } else { @@ -269,8 +270,10 @@ TextContent::TextContent(const QJsonObject& json) mimeType = PlainTextMimeType; body = json[BodyKey].toString(); } - const auto replyJson = json[RelatesToKey].toObject() - .value(RelatesTo::ReplyTypeId()).toObject(); + const auto replyJson = json[RelatesToKey] + .toObject() + .value(RelatesTo::ReplyTypeId()) + .toObject(); if (!replyJson.isEmpty()) relatesTo = replyTo(fromJson(replyJson[EventIdKeyL])); } @@ -278,26 +281,27 @@ TextContent::TextContent(const QJsonObject& json) void TextContent::fillJson(QJsonObject* json) const { Q_ASSERT(json); - if (mimeType.inherits("text/html")) - { + if (mimeType.inherits("text/html")) { json->insert(QStringLiteral("format"), HtmlContentTypeId); json->insert(QStringLiteral("formatted_body"), body); } if (!relatesTo.omitted()) json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { relatesTo->type, relatesTo->eventId } }); + QJsonObject { { relatesTo->type, relatesTo->eventId } }); } LocationContent::LocationContent(const QString& geoUri, const Thumbnail& thumbnail) : geoUri(geoUri), thumbnail(thumbnail) -{ } +{ +} LocationContent::LocationContent(const QJsonObject& json) - : TypedBase(json) - , geoUri(json["geo_uri"_ls].toString()) - , thumbnail(json["info"_ls].toObject()) -{ } + : TypedBase(json), + geoUri(json["geo_uri"_ls].toString()), + thumbnail(json["info"_ls].toObject()) +{ +} QMimeType LocationContent::type() const { diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index c2e075eb..bd1b7c83 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -13,88 +13,94 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "roomevent.h" #include "eventcontent.h" +#include "roomevent.h" class QFileInfo; -namespace QMatrixClient -{ +namespace QMatrixClient { namespace MessageEventContent = EventContent; // Back-compatibility /** * The event class corresponding to m.room.message events */ - class RoomMessageEvent: public RoomEvent + class RoomMessageEvent : public RoomEvent { - Q_GADGET - Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) - Q_PROPERTY(QString plainBody READ plainBody CONSTANT) - Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) - Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT) + Q_GADGET + Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) + Q_PROPERTY(QString plainBody READ plainBody CONSTANT) + Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) + Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT) public: - DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) + DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) + + enum class MsgType { + Text, + Emote, + Notice, + Image, + File, + Location, + Video, + Audio, + Unknown + }; - enum class MsgType - { - Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown - }; - - RoomMessageEvent(const QString& plainBody, - const QString& jsonMsgType, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - MsgType msgType = MsgType::Text, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - const QFileInfo& file, - bool asGenericFile = false); - explicit RoomMessageEvent(const QJsonObject& obj); - - MsgType msgtype() const; - QString rawMsgtype() const; - QString plainBody() const; - EventContent::TypedBase* content() const - { return _content.data(); } - template - void editContent(VisitorT visitor) - { - visitor(*_content); - editJson()[ContentKeyL] = + RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, + MsgType msgType = MsgType::Text, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, + const QFileInfo& file, + bool asGenericFile = false); + explicit RoomMessageEvent(const QJsonObject& obj); + + MsgType msgtype() const; + QString rawMsgtype() const; + QString plainBody() const; + EventContent::TypedBase* content() const { return _content.data(); } + template void editContent(VisitorT visitor) + { + visitor(*_content); + editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(), content()); - } - QMimeType mimeType() const; - bool hasTextContent() const; - bool hasFileContent() const; - bool hasThumbnail() const; + } + QMimeType mimeType() const; + bool hasTextContent() const; + bool hasFileContent() const; + bool hasThumbnail() const; - static QString rawMsgTypeForUrl(const QUrl& url); - static QString rawMsgTypeForFile(const QFileInfo& fi); + static QString rawMsgTypeForUrl(const QUrl& url); + static QString rawMsgTypeForFile(const QFileInfo& fi); private: - QScopedPointer _content; + QScopedPointer _content; - static QJsonObject assembleContentJson(const QString& plainBody, - const QString& jsonMsgType, EventContent::TypedBase* content); + static QJsonObject + assembleContentJson(const QString& plainBody, + const QString& jsonMsgType, + EventContent::TypedBase* content); - REGISTER_ENUM(MsgType) + REGISTER_ENUM(MsgType) }; REGISTER_EVENT_TYPE(RoomMessageEvent) DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; - namespace EventContent - { + namespace EventContent { // Additional event content types - struct RelatesTo - { - static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; } + struct RelatesTo { + static constexpr const char* ReplyTypeId() + { + return "m.in_reply_to"; + } QString type; // The only supported relation so far QString eventId; }; @@ -109,21 +115,21 @@ namespace QMatrixClient * Available fields: mimeType, body. The body can be either rich text * or plain text, depending on what mimeType specifies. */ - class TextContent: public TypedBase + class TextContent : public TypedBase { public: - TextContent(const QString& text, const QString& contentType, - Omittable relatesTo = none); - explicit TextContent(const QJsonObject& json); + TextContent(const QString& text, const QString& contentType, + Omittable relatesTo = none); + explicit TextContent(const QJsonObject& json); - QMimeType type() const override { return mimeType; } + QMimeType type() const override { return mimeType; } - QMimeType mimeType; - QString body; - Omittable relatesTo; + QMimeType mimeType; + QString body; + Omittable relatesTo; protected: - void fillJson(QJsonObject* json) const override; + void fillJson(QJsonObject* json) const override; }; /** @@ -139,47 +145,47 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent: public TypedBase + class LocationContent : public TypedBase { public: - LocationContent(const QString& geoUri, - const Thumbnail& thumbnail = {}); - explicit LocationContent(const QJsonObject& json); + LocationContent(const QString& geoUri, + const Thumbnail& thumbnail = {}); + explicit LocationContent(const QJsonObject& json); - QMimeType type() const override; + QMimeType type() const override; public: - QString geoUri; - Thumbnail thumbnail; + QString geoUri; + Thumbnail thumbnail; protected: - void fillJson(QJsonObject* o) const override; + void fillJson(QJsonObject* o) const override; }; /** * A base class for info types that include duration: audio and video */ - template - class PlayableContent : public ContentT + template class PlayableContent : public ContentT { public: - using ContentT::ContentT; - PlayableContent(const QJsonObject& json) - : ContentT(json) - , duration(ContentT::originalInfoJson["duration"_ls].toInt()) - { } + using ContentT::ContentT; + PlayableContent(const QJsonObject& json) + : ContentT(json), + duration(ContentT::originalInfoJson["duration"_ls].toInt()) + { + } protected: - void fillJson(QJsonObject* json) const override - { - ContentT::fillJson(json); - auto infoJson = json->take("info"_ls).toObject(); - infoJson.insert(QStringLiteral("duration"), duration); - json->insert(QStringLiteral("info"), infoJson); - } + void fillJson(QJsonObject* json) const override + { + ContentT::fillJson(json); + auto infoJson = json->take("info"_ls).toObject(); + infoJson.insert(QStringLiteral("duration"), duration); + json->insert(QStringLiteral("info"), infoJson); + } public: - int duration; + int duration; }; /** @@ -201,7 +207,8 @@ namespace QMatrixClient * - mimeType * - imageSize */ - using VideoContent = PlayableContent>; + using VideoContent = + PlayableContent>; /** * Content class for m.audio @@ -216,5 +223,5 @@ namespace QMatrixClient * - duration */ using AudioContent = PlayableContent>; - } // namespace EventContent -} // namespace QMatrixClient + } // namespace EventContent +} // namespace QMatrixClient diff --git a/lib/events/roomtombstoneevent.cpp b/lib/events/roomtombstoneevent.cpp index 9c3bafd4..a74bb367 100644 --- a/lib/events/roomtombstoneevent.cpp +++ b/lib/events/roomtombstoneevent.cpp @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2019 QMatrixClient project -* -* 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 -*/ + * Copyright (C) 2019 QMatrixClient project + * + * 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 "roomtombstoneevent.h" diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index c7008ec4..5b7ade76 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -1,41 +1,41 @@ /****************************************************************************** -* Copyright (C) 2019 QMatrixClient project -* -* 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 -*/ + * Copyright (C) 2019 QMatrixClient project + * + * 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 "stateevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class RoomTombstoneEvent : public StateEventBase { public: - DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) + DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) - explicit RoomTombstoneEvent() - : StateEventBase(typeId(), matrixTypeId()) - { } - explicit RoomTombstoneEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { } + explicit RoomTombstoneEvent() : StateEventBase(typeId(), matrixTypeId()) + { + } + explicit RoomTombstoneEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + { + } - QString serverMessage() const; - QString successorRoomId() const; + QString serverMessage() const; + QString successorRoomId() const; }; REGISTER_EVENT_TYPE(RoomTombstoneEvent) } diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 2c23d9ca..430eacb0 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,71 +22,70 @@ #include "converters.h" -namespace QMatrixClient -{ - namespace EventContent - { - template - class SimpleContent +namespace QMatrixClient { + namespace EventContent { + template class SimpleContent { public: - using value_type = T; + using value_type = T; - // The constructor is templated to enable perfect forwarding - template - SimpleContent(QString keyName, TT&& value) - : value(std::forward(value)), key(std::move(keyName)) - { } - SimpleContent(const QJsonObject& json, QString keyName) - : value(fromJson(json[keyName])) - , key(std::move(keyName)) - { } - QJsonObject toJson() const - { - return { { key, QMatrixClient::toJson(value) } }; - } + // The constructor is templated to enable perfect forwarding + template + SimpleContent(QString keyName, TT&& value) + : value(std::forward(value)), key(std::move(keyName)) + { + } + SimpleContent(const QJsonObject& json, QString keyName) + : value(fromJson(json[keyName])), key(std::move(keyName)) + { + } + QJsonObject toJson() const + { + return { { key, QMatrixClient::toJson(value) } }; + } public: - T value; + T value; protected: - QString key; + QString key; }; } // namespace EventContent -#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \ - class _Name : public StateEvent> \ - { \ - public: \ - using value_type = content_type::value_type; \ - DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name() : _Name(value_type()) { } \ - template \ - explicit _Name(T&& value) \ - : StateEvent(typeId(), matrixTypeId(), \ - QStringLiteral(#_ContentKey), \ - std::forward(value)) \ - { } \ - explicit _Name(QJsonObject obj) \ - : StateEvent(typeId(), std::move(obj), \ - QStringLiteral(#_ContentKey)) \ - { } \ - auto _ContentKey() const { return content().value; } \ - }; \ - REGISTER_EVENT_TYPE(_Name) \ +#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \ + class _Name : public StateEvent> \ + { \ + public: \ + using value_type = content_type::value_type; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ + explicit _Name() : _Name(value_type()) {} \ + template \ + explicit _Name(T&& value) \ + : StateEvent(typeId(), matrixTypeId(), \ + QStringLiteral(#_ContentKey), std::forward(value)) \ + { \ + } \ + explicit _Name(QJsonObject obj) \ + : StateEvent(typeId(), std::move(obj), \ + QStringLiteral(#_ContentKey)) \ + { \ + } \ + auto _ContentKey() const { return content().value; } \ + }; \ + REGISTER_EVENT_TYPE(_Name) \ // End of macro DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - QStringList, aliases) + DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", QStringList, + aliases) DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", QString, alias) DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) - DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - QString, algorithm) + DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", QString, + algorithm) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) } // namespace QMatrixClient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index e96614d2..f172edb6 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -1,20 +1,20 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 "stateevent.h" @@ -24,9 +24,9 @@ using namespace QMatrixClient; // StateEventBase itself can be instantiated if there's a state_key JSON key // but the event type is unknown. [[gnu::unused]] static auto stateEventTypeInitialised = - RoomEvent::factory_t::addMethod( - [] (const QJsonObject& json, const QString& matrixType) -> StateEventPtr - { + RoomEvent::factory_t::addMethod([](const QJsonObject& json, + const QString& matrixType) + -> StateEventPtr { if (!json.contains("state_key")) return nullptr; @@ -53,6 +53,7 @@ void StateEventBase::dumpTo(QDebug dbg) const dbg << '<' << stateKey() << "> "; if (unsignedJson().contains(PrevContentKeyL)) dbg << QJsonDocument(unsignedJson()[PrevContentKeyL].toObject()) - .toJson(QJsonDocument::Compact) << " -> "; + .toJson(QJsonDocument::Compact) + << " -> "; RoomEvent::dumpTo(dbg); } diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 3f54f7bf..1a02646b 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -1,119 +1,127 @@ /****************************************************************************** -* Copyright (C) 2018 Kitsune Ral -* -* 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 -*/ + * Copyright (C) 2018 Kitsune Ral + * + * 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 "roomevent.h" namespace QMatrixClient { - class StateEventBase: public RoomEvent + class StateEventBase : public RoomEvent { public: - using factory_t = EventFactory; + using factory_t = EventFactory; - using RoomEvent::RoomEvent; - ~StateEventBase() override = default; + using RoomEvent::RoomEvent; + ~StateEventBase() override = default; - bool isStateEvent() const override { return true; } - QString replacedState() const; - void dumpTo(QDebug dbg) const override; + bool isStateEvent() const override { return true; } + QString replacedState() const; + void dumpTo(QDebug dbg) const override; - virtual bool repeatsState() const; + virtual bool repeatsState() const; }; using StateEventPtr = event_ptr_tt; using StateEvents = EventsArray; - template <> - inline bool is(const Event& e) { return e.isStateEvent(); } + template <> inline bool is(const Event& e) + { + return e.isStateEvent(); + } /** * A combination of event type and state key uniquely identifies a piece * of state in Matrix. - * \sa https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events + * \sa + * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events */ using StateEventKey = QPair; - template - struct Prev - { + template struct Prev { template explicit Prev(const QJsonObject& unsignedJson, ContentParamTs&&... contentParams) - : senderId(unsignedJson.value("prev_sender"_ls).toString()) - , content(unsignedJson.value(PrevContentKeyL).toObject(), - std::forward(contentParams)...) - { } + : senderId(unsignedJson.value("prev_sender"_ls).toString()), + content(unsignedJson.value(PrevContentKeyL).toObject(), + std::forward(contentParams)...) + { + } QString senderId; ContentT content; }; - template - class StateEvent: public StateEventBase + template class StateEvent : public StateEventBase { public: - using content_type = ContentT; + using content_type = ContentT; - template - explicit StateEvent(Type type, const QJsonObject& fullJson, - ContentParamTs&&... contentParams) - : StateEventBase(type, fullJson) - , _content(contentJson(), - std::forward(contentParams)...) - { - const auto& unsignedData = unsignedJson(); - if (unsignedData.contains(PrevContentKeyL)) - _prev = std::make_unique>(unsignedData, - std::forward(contentParams)...); - } - template - explicit StateEvent(Type type, event_mtype_t matrixType, - ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType) - , _content(std::forward(contentParams)...) - { - editJson().insert(ContentKey, _content.toJson()); - } + template + explicit StateEvent(Type type, const QJsonObject& fullJson, + ContentParamTs&&... contentParams) + : StateEventBase(type, fullJson), + _content(contentJson(), + std::forward(contentParams)...) + { + const auto& unsignedData = unsignedJson(); + if (unsignedData.contains(PrevContentKeyL)) + _prev = std::make_unique>( + unsignedData, + std::forward(contentParams)...); + } + template + explicit StateEvent(Type type, event_mtype_t matrixType, + ContentParamTs&&... contentParams) + : StateEventBase(type, matrixType), + _content(std::forward(contentParams)...) + { + editJson().insert(ContentKey, _content.toJson()); + } - const ContentT& content() const { return _content; } - template - void editContent(VisitorT&& visitor) - { - visitor(_content); - editJson()[ContentKeyL] = _content.toJson(); - } - [[deprecated("Use prevContent instead")]] - const ContentT* prev_content() const { return prevContent(); } - const ContentT* prevContent() const - { return _prev ? &_prev->content : nullptr; } - QString prevSenderId() const - { return _prev ? _prev->senderId : QString(); } + const ContentT& content() const { return _content; } + template void editContent(VisitorT&& visitor) + { + visitor(_content); + editJson()[ContentKeyL] = _content.toJson(); + } + [[deprecated("Use prevContent instead")]] const ContentT* + prev_content() const + { + return prevContent(); + } + const ContentT* prevContent() const + { + return _prev ? &_prev->content : nullptr; + } + QString prevSenderId() const + { + return _prev ? _prev->senderId : QString(); + } private: - ContentT _content; - std::unique_ptr> _prev; + ContentT _content; + std::unique_ptr> _prev; }; } // namespace QMatrixClient namespace std { - template <> struct hash - { - size_t operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT + template <> struct hash { + size_t + operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT { return qHash(k); } diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index 0d39d1be..ee3d6b67 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "typingevent.h" @@ -22,11 +22,9 @@ using namespace QMatrixClient; -TypingEvent::TypingEvent(const QJsonObject& obj) - : Event(typeId(), obj) +TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& array = contentJson()["user_ids"_ls].toArray(); - for(const auto& user: array ) + for (const auto& user : array) _users.push_back(user.toString()); } - diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index 27b668b4..f66a1fbc 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -13,27 +13,26 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "event.h" -namespace QMatrixClient -{ - class TypingEvent: public Event +namespace QMatrixClient { + class TypingEvent : public Event { public: - DEFINE_EVENT_TYPEID("m.typing", TypingEvent) + DEFINE_EVENT_TYPEID("m.typing", TypingEvent) - TypingEvent(const QJsonObject& obj); + TypingEvent(const QJsonObject& obj); - const QStringList& users() const { return _users; } + const QStringList& users() const { return _users; } private: - QStringList _users; + QStringList _users; }; REGISTER_EVENT_TYPE(TypingEvent) DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/identity/definitions/request_email_validation.cpp b/lib/identity/definitions/request_email_validation.cpp index 47463a8b..a2d0a707 100644 --- a/lib/identity/definitions/request_email_validation.cpp +++ b/lib/identity/definitions/request_email_validation.cpp @@ -16,11 +16,10 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RequestEmailValidation& result) + const QJsonObject& jo, RequestEmailValidation& result) { fromJson(jo.value("client_secret"_ls), result.clientSecret); fromJson(jo.value("email"_ls), result.email); fromJson(jo.value("send_attempt"_ls), result.sendAttempt); fromJson(jo.value("next_link"_ls), result.nextLink); } - diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h index eb7d8ed6..51369039 100644 --- a/lib/identity/definitions/request_email_validation.h +++ b/lib/identity/definitions/request_email_validation.h @@ -8,16 +8,14 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct RequestEmailValidation - { + struct RequestEmailValidation { /// A unique string generated by the client, and used to identify the /// validation attempt. It must be a string consisting of the characters - /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - /// must not be empty. + /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + /// it must not be empty. QString clientSecret; /// The email address to validate. QString email; @@ -33,10 +31,10 @@ namespace QMatrixClient /// server will redirect the user to this URL. QString nextLink; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RequestEmailValidation& pod); - static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod); + static void fillFrom(const QJsonObject& jo, + RequestEmailValidation& pod); }; } // namespace QMatrixClient diff --git a/lib/identity/definitions/request_msisdn_validation.cpp b/lib/identity/definitions/request_msisdn_validation.cpp index a123d326..9facbf7e 100644 --- a/lib/identity/definitions/request_msisdn_validation.cpp +++ b/lib/identity/definitions/request_msisdn_validation.cpp @@ -17,7 +17,7 @@ void JsonObjectConverter::dumpTo( } void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RequestMsisdnValidation& result) + const QJsonObject& jo, RequestMsisdnValidation& result) { fromJson(jo.value("client_secret"_ls), result.clientSecret); fromJson(jo.value("country"_ls), result.country); @@ -25,4 +25,3 @@ void JsonObjectConverter::fillFrom( fromJson(jo.value("send_attempt"_ls), result.sendAttempt); fromJson(jo.value("next_link"_ls), result.nextLink); } - diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h index b48ed6d5..c4fe479e 100644 --- a/lib/identity/definitions/request_msisdn_validation.h +++ b/lib/identity/definitions/request_msisdn_validation.h @@ -8,16 +8,14 @@ #include "converters.h" -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct RequestMsisdnValidation - { + struct RequestMsisdnValidation { /// A unique string generated by the client, and used to identify the /// validation attempt. It must be a string consisting of the characters - /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - /// must not be empty. + /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and + /// it must not be empty. QString clientSecret; /// The two-letter uppercase ISO country code that the number in /// ``phone_number`` should be parsed as if it were dialled from. @@ -36,10 +34,10 @@ namespace QMatrixClient /// server will redirect the user to this URL. QString nextLink; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RequestMsisdnValidation& pod); - static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod); + static void fillFrom(const QJsonObject& jo, + RequestMsisdnValidation& pod); }; } // namespace QMatrixClient diff --git a/lib/identity/definitions/sid.cpp b/lib/identity/definitions/sid.cpp index 1ba4b3b5..cc6973f2 100644 --- a/lib/identity/definitions/sid.cpp +++ b/lib/identity/definitions/sid.cpp @@ -6,15 +6,12 @@ using namespace QMatrixClient; -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const Sid& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, const Sid& pod) { addParam<>(jo, QStringLiteral("sid"), pod.sid); } -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, Sid& result) +void JsonObjectConverter::fillFrom(const QJsonObject& jo, Sid& result) { fromJson(jo.value("sid"_ls), result.sid); } - diff --git a/lib/identity/definitions/sid.h b/lib/identity/definitions/sid.h index ac8c4130..85462b46 100644 --- a/lib/identity/definitions/sid.h +++ b/lib/identity/definitions/sid.h @@ -6,21 +6,17 @@ #include "converters.h" - -namespace QMatrixClient -{ +namespace QMatrixClient { // Data structures - struct Sid - { - /// The session ID. Session IDs are opaque strings generated by the identity - /// server. They must consist entirely of the characters - /// ``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 characters and they - /// must not be empty. + struct Sid { + /// The session ID. Session IDs are opaque strings generated by the + /// identity server. They must consist entirely of the characters + /// ``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 characters and + /// they must not be empty. QString sid; }; - template <> struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Sid& pod); static void fillFrom(const QJsonObject& jo, Sid& pod); }; diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 8c3381ae..a023d4f7 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "basejob.h" @@ -21,19 +21,18 @@ #include "connectiondata.h" #include "util.h" +#include +#include +#include #include -#include #include -#include -#include -#include +#include #include using namespace QMatrixClient; -struct NetworkReplyDeleter : public QScopedPointerDeleteLater -{ +struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) { if (reply && reply->isRunning()) @@ -45,51 +44,58 @@ struct NetworkReplyDeleter : public QScopedPointerDeleteLater 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, const QUrlQuery& q, - Data&& data, bool nt) - : verb(v), apiEndpoint(std::move(endpoint)), requestQuery(q) - , requestData(std::move(data)), needsToken(nt) - { } - - void sendRequest(bool inBackground); - const JobTimeoutConfig& getCurrentTimeoutConfig() const; - - const ConnectionData* connection = nullptr; - - // Contents for the network request - HttpVerb verb; - QString apiEndpoint; - QHash 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 reply; - Status status = Pending; - QByteArray rawResponse; - QUrl errorUrl; //< May contain a URL to help with some errors - - QTimer timer; - QTimer retryTimer; - - QVector errorStrategy = - { { 90, 5 }, { 90, 10 }, { 120, 30 } }; - int maxRetries = errorStrategy.size(); - int retriesTaken = 0; - - LoggingCategory logCat = JOBS; + // Using an idiom from clang-tidy: + // http://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html + Private(HttpVerb v, QString endpoint, const QUrlQuery& q, Data&& data, + bool nt) + : verb(v), + apiEndpoint(std::move(endpoint)), + requestQuery(q), + requestData(std::move(data)), + needsToken(nt) + { + } + + void sendRequest(bool inBackground); + const JobTimeoutConfig& getCurrentTimeoutConfig() const; + + const ConnectionData* connection = nullptr; + + // Contents for the network request + HttpVerb verb; + QString apiEndpoint; + QHash 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 reply; + Status status = Pending; + QByteArray rawResponse; + QUrl errorUrl; //< May contain a URL to help with some errors + + QTimer timer; + QTimer retryTimer; + + QVector 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, + 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) @@ -98,7 +104,7 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, setObjectName(name); setExpectedContentTypes({ "application/json" }); d->timer.setSingleShot(true); - connect (&d->timer, &QTimer::timeout, this, &BaseJob::timeout); + connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout); } BaseJob::~BaseJob() @@ -114,21 +120,20 @@ QUrl BaseJob::requestUrl() const bool BaseJob::isBackground() const { - return d->reply && d->reply->request().attribute( - QNetworkRequest::BackgroundRequestAttribute).toBool(); + return d->reply + && d->reply->request() + .attribute(QNetworkRequest::BackgroundRequestAttribute) + .toBool(); } -const QString& BaseJob::apiEndpoint() const -{ - return d->apiEndpoint; -} +const QString& BaseJob::apiEndpoint() const { return d->apiEndpoint; } void BaseJob::setApiEndpoint(const QString& apiEndpoint) { d->apiEndpoint = apiEndpoint; } -const BaseJob::headers_t&BaseJob::requestHeaders() const +const BaseJob::headers_t& BaseJob::requestHeaders() const { return d->requestHeaders; } @@ -144,25 +149,16 @@ void BaseJob::setRequestHeaders(const BaseJob::headers_t& headers) d->requestHeaders = headers; } -const QUrlQuery& BaseJob::query() const -{ - return d->requestQuery; -} +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; -} +const BaseJob::Data& BaseJob::requestData() const { return d->requestData; } -void BaseJob::setRequestData(Data&& data) -{ - std::swap(d->requestData, data); -} +void BaseJob::setRequestData(Data&& data) { std::swap(d->requestData, data); } const QByteArrayList& BaseJob::expectedContentTypes() const { @@ -179,22 +175,22 @@ void BaseJob::setExpectedContentTypes(const QByteArrayList& contentTypes) d->expectedContentTypes = contentTypes; } -QUrl BaseJob::makeRequestUrl(QUrl baseUrl, - const QString& path, const QUrlQuery& query) +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.setPath(pathBase + path); baseUrl.setQuery(query); return baseUrl; } void BaseJob::Private::sendRequest(bool inBackground) { - QNetworkRequest req - { makeRequestUrl(connection->baseUrl(), apiEndpoint, requestQuery) }; + QNetworkRequest req { makeRequestUrl(connection->baseUrl(), apiEndpoint, + requestQuery) }; if (!requestHeaders.contains("Content-Type")) req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); req.setRawHeader("Authorization", @@ -211,38 +207,34 @@ void BaseJob::Private::sendRequest(bool inBackground) #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; + 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::beforeStart(const ConnectionData*) {} -void BaseJob::afterStart(const ConnectionData*, QNetworkReply*) -{ } +void BaseJob::afterStart(const ConnectionData*, QNetworkReply*) {} -void BaseJob::beforeAbandon(QNetworkReply*) -{ } +void BaseJob::beforeAbandon(QNetworkReply*) {} void BaseJob::start(const ConnectionData* connData, bool inBackground) { d->connection = connData; d->retryTimer.setSingleShot(true); - connect (&d->retryTimer, &QTimer::timeout, - this, [this,inBackground] { sendRequest(inBackground); }); + connect(&d->retryTimer, &QTimer::timeout, this, + [this, inBackground] { sendRequest(inBackground); }); beforeStart(connData); if (status().good()) @@ -261,27 +253,23 @@ void BaseJob::sendRequest(bool inBackground) if (!d->requestQuery.isEmpty()) qCDebug(d->logCat) << " query:" << d->requestQuery.toString(); d->sendRequest(inBackground); - connect( d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply ); - if (d->reply->isRunning()) - { - connect( d->reply.data(), &QNetworkReply::metaDataChanged, - this, &BaseJob::checkReply); - connect( d->reply.data(), &QNetworkReply::uploadProgress, - this, &BaseJob::uploadProgress); - connect( d->reply.data(), &QNetworkReply::downloadProgress, - this, &BaseJob::downloadProgress); + connect(d->reply.data(), &QNetworkReply::finished, this, + &BaseJob::gotReply); + if (d->reply->isRunning()) { + connect(d->reply.data(), &QNetworkReply::metaDataChanged, this, + &BaseJob::checkReply); + 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 + } else qCWarning(d->logCat) << this << "request could not start"; } -void BaseJob::checkReply() -{ - setStatus(doCheckReply(d->reply.data())); -} +void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } void BaseJob::gotReply() { @@ -293,20 +281,18 @@ void BaseJob::gotReply() d->rawResponse = d->reply->readAll(); const auto jsonBody = d->reply->rawHeader("Content-Type") == "application/json"; - qCDebug(d->logCat).noquote() - << "Error body (truncated if long):" << d->rawResponse.left(500); - if (jsonBody) - { + qCDebug(d->logCat).noquote() << "Error body (truncated if long):" + << d->rawResponse.left(500); + if (jsonBody) { auto json = QJsonDocument::fromJson(d->rawResponse).object(); const auto errCode = json.value("errcode"_ls).toString(); - if (error() == TooManyRequestsError || - errCode == "M_LIMIT_EXCEEDED") - { + if (error() == TooManyRequestsError + || errCode == "M_LIMIT_EXCEEDED") { QString msg = tr("Too many requests"); auto retryInterval = json.value("retry_after_ms"_ls).toInt(-1); if (retryInterval != -1) msg += tr(", next retry advised after %1 ms") - .arg(retryInterval); + .arg(retryInterval); else // We still have to figure some reasonable interval retryInterval = getNextRetryInterval(); @@ -320,19 +306,16 @@ void BaseJob::gotReply() emit retryScheduled(d->retriesTaken, retryInterval); return; } - if (errCode == "M_CONSENT_NOT_GIVEN") - { + if (errCode == "M_CONSENT_NOT_GIVEN") { d->status.code = UserConsentRequiredError; d->errorUrl = json.value("consent_uri"_ls).toString(); - } - else if (errCode == "M_UNSUPPORTED_ROOM_VERSION" || - errCode == "M_INCOMPATIBLE_ROOM_VERSION") - { + } else if (errCode == "M_UNSUPPORTED_ROOM_VERSION" + || errCode == "M_INCOMPATIBLE_ROOM_VERSION") { d->status.code = UnsupportedRoomVersionError; if (json.contains("room_version")) d->status.message = - tr("Requested room version: %1") - .arg(json.value("room_version").toString()); + tr("Requested room version: %1") + .arg(json.value("room_version").toString()); } else if (!json.isEmpty()) // Not localisable on the client side setStatus(IncorrectRequestError, json.value("error"_ls).toString()); @@ -350,18 +333,18 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) // ignore possible appendixes of the content type const auto ctype = type.split(';').front(); - for (const auto& pattern: patterns) - { + 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); + "BaseJob: Expected content type should have up to two" + " /-separated parts; violating pattern: " + + pattern); - if (ctype.split('/').front() == patternParts.front() && - patternParts.back() == "*") + if (ctype.split('/').front() == patternParts.front() + && patternParts.back() == "*") return true; // Exact match already went on fast lane } @@ -376,24 +359,26 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes const auto httpCodeHeader = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - if (!httpCodeHeader.isValid()) - { + if (!httpCodeHeader.isValid()) { qCWarning(d->logCat) << this << "didn't get valid HTTP headers"; return { NetworkError, reply->errorString() }; } - const QString replyState = reply->isRunning() ? - QStringLiteral("(tentative)") : QStringLiteral("(final)"); + const QString replyState = reply->isRunning() + ? QStringLiteral("(tentative)") + : QStringLiteral("(final)"); const auto urlString = '|' + d->reply->url().toDisplayString(); const auto httpCode = httpCodeHeader.toInt(); const auto reason = - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); + reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) + .toString(); if (httpCode / 100 == 2) // 2xx { qCDebug(d->logCat).noquote().nospace() << this << urlString; - qCDebug(d->logCat).noquote() << " " << httpCode << reason << replyState; + qCDebug(d->logCat).noquote() + << " " << httpCode << reason << replyState; if (!checkContentType(reply->rawHeader("Content-Type"), - d->expectedContentTypes)) + d->expectedContentTypes)) return { UnexpectedResponseTypeWarning, "Unexpected content type of the response" }; return NoError; @@ -402,29 +387,37 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const qCWarning(d->logCat).noquote().nospace() << this << urlString; qCWarning(d->logCat).noquote() << " " << httpCode << reason << replyState; return { [httpCode]() -> StatusCode { - if (httpCode / 10 == 41) - return httpCode == 410 ? IncorrectRequestError : NotFoundError; - switch (httpCode) - { - case 401: case 403: case 407: + if (httpCode / 10 == 41) + return httpCode == 410 ? IncorrectRequestError + : NotFoundError; + switch (httpCode) { + case 401: + case 403: + case 407: return ContentAccessError; case 404: return NotFoundError; - case 400: case 405: case 406: case 426: case 428: + case 400: + case 405: + case 406: + case 426: + case 428: case 505: case 494: // Unofficial nginx "Request header too large" case 497: // Unofficial nginx "HTTP request sent to HTTPS port" return IncorrectRequestError; case 429: return TooManyRequestsError; - case 501: case 510: + case 501: + case 510: return RequestNotImplementedError; case 511: return NetworkAuthRequiredError; default: return NetworkError; - } - }(), reply->errorString() }; + } + }(), + reply->errorString() }; } BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) @@ -432,30 +425,25 @@ BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) d->rawResponse = reply->readAll(); QJsonParseError error; const auto& json = QJsonDocument::fromJson(d->rawResponse, &error); - if( error.error == QJsonParseError::NoError ) + if (error.error == QJsonParseError::NoError) return parseJson(json); return { IncorrectResponseError, error.errorString() }; } -BaseJob::Status BaseJob::parseJson(const QJsonDocument&) -{ - return Success; -} +BaseJob::Status BaseJob::parseJson(const QJsonDocument&) { return Success; } void BaseJob::stop() { d->timer.stop(); - if (d->reply) - { + 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"; + if (d->reply->isRunning()) { + qCWarning(d->logCat) + << this << "stopped without ready network reply"; d->reply->abort(); } - } - else + } else qCWarning(d->logCat) << this << "stopped with empty network reply"; } @@ -463,16 +451,16 @@ void BaseJob::finishJob() { stop(); if ((error() == NetworkError || error() == TimeoutError) - && d->retriesTaken < d->maxRetries) - { + && 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).nospace() << this << ": retry #" << d->retriesTaken - << " in " << retryInterval/1000 << " s"; + qCWarning(d->logCat).nospace() + << this << ": retry #" << d->retriesTaken << " in " + << retryInterval / 1000 << " s"; d->retryTimer.start(retryInterval); emit retryScheduled(d->retriesTaken, retryInterval); return; @@ -510,93 +498,78 @@ BaseJob::duration_t BaseJob::millisToRetry() const return d->retryTimer.isActive() ? d->retryTimer.remainingTime() : 0; } -int BaseJob::maxRetries() const -{ - return d->maxRetries; -} +int BaseJob::maxRetries() const { return d->maxRetries; } void BaseJob::setMaxRetries(int newMaxRetries) { d->maxRetries = newMaxRetries; } -BaseJob::Status BaseJob::status() const -{ - return d->status; -} +BaseJob::Status BaseJob::status() const { return d->status; } QByteArray BaseJob::rawData(int bytesAtMost) const { return bytesAtMost > 0 && d->rawResponse.size() > bytesAtMost - ? d->rawResponse.left(bytesAtMost) : d->rawResponse; + ? d->rawResponse.left(bytesAtMost) + : d->rawResponse; } QString BaseJob::rawDataSample(int bytesAtMost) const { auto data = rawData(bytesAtMost); Q_ASSERT(data.size() <= d->rawResponse.size()); - return data.size() == d->rawResponse.size() - ? data : data + tr("...(truncated, %Ln bytes in total)", - "Comes after trimmed raw network response", - d->rawResponse.size()); - + return data.size() == d->rawResponse.size() ? data + : data + + tr("...(truncated, %Ln bytes in total)", + "Comes after trimmed raw network response", + d->rawResponse.size()); } QString BaseJob::statusCaption() const { - switch (d->status.code) - { - case Success: - return tr("Success"); - case Pending: - return tr("Request still pending response"); - case UnexpectedResponseTypeWarning: - return tr("Warning: Unexpected response type"); - case Abandoned: - return tr("Request was abandoned"); - case NetworkError: - return tr("Network problems"); - case JsonParseError: - return tr("Response could not be parsed"); - case TimeoutError: - return tr("Request timed out"); - case ContentAccessError: - return tr("Access error"); - case NotFoundError: - return tr("Not found"); - case IncorrectRequestError: - return tr("Invalid request"); - case IncorrectResponseError: - return tr("Response could not be parsed"); - case TooManyRequestsError: - return tr("Too many requests"); - case RequestNotImplementedError: - return tr("Function not implemented by the server"); - case NetworkAuthRequiredError: - return tr("Network authentication required"); - case UserConsentRequiredError: - return tr("User consent required"); - case UnsupportedRoomVersionError: - return tr("The server does not support the needed room version"); - default: - return tr("Request failed"); + switch (d->status.code) { + case Success: + return tr("Success"); + case Pending: + return tr("Request still pending response"); + case UnexpectedResponseTypeWarning: + return tr("Warning: Unexpected response type"); + case Abandoned: + return tr("Request was abandoned"); + case NetworkError: + return tr("Network problems"); + case JsonParseError: + return tr("Response could not be parsed"); + case TimeoutError: + return tr("Request timed out"); + case ContentAccessError: + return tr("Access error"); + case NotFoundError: + return tr("Not found"); + case IncorrectRequestError: + return tr("Invalid request"); + case IncorrectResponseError: + return tr("Response could not be parsed"); + case TooManyRequestsError: + return tr("Too many requests"); + case RequestNotImplementedError: + return tr("Function not implemented by the server"); + case NetworkAuthRequiredError: + return tr("Network authentication required"); + case UserConsentRequiredError: + return tr("User consent required"); + case UnsupportedRoomVersionError: + return tr("The server does not support the needed room version"); + default: + return tr("Request failed"); } } -int BaseJob::error() const -{ - return d->status.code; -} +int BaseJob::error() const { return d->status.code; } -QString BaseJob::errorString() const -{ - return d->status.message; -} +QString BaseJob::errorString() const { return d->status.message; } -QUrl BaseJob::errorUrl() const -{ - return d->errorUrl; -} +QUrl BaseJob::errorUrl() const { return d->errorUrl; } void BaseJob::setStatus(Status s) { @@ -629,11 +602,8 @@ void BaseJob::abandon() void BaseJob::timeout() { - setStatus( TimeoutError, "The job has timed out" ); + setStatus(TimeoutError, "The job has timed out"); finishJob(); } -void BaseJob::setLoggingCategory(LoggingCategory lcf) -{ - d->logCat = lcf; -} +void BaseJob::setLoggingCategory(LoggingCategory lcf) { d->logCat = lcf; } diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 4c1c7706..8ff25d42 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -21,332 +21,336 @@ #include "../logging.h" #include "requestdata.h" +#include #include #include -#include class QNetworkReply; class QSslError; -namespace QMatrixClient -{ +namespace QMatrixClient { class ConnectionData; enum class HttpVerb { Get, Put, Post, Delete }; - struct JobTimeoutConfig - { + struct JobTimeoutConfig { int jobTimeout; int nextRetryInterval; }; - class BaseJob: public QObject + class BaseJob : public QObject { - Q_OBJECT - Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) - Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries) + Q_OBJECT + Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) + 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 - , WarningLevel = 20 - , UnexpectedResponseTypeWarning = 21 - , Abandoned = 50 //< A very brief period between abandoning and object deletion - , ErrorLevel = 100 //< Errors have codes starting from this - , NetworkError = 100 - , JsonParseError // TODO: Merge into IncorrectResponseError - , TimeoutError - , ContentAccessError - , NotFoundError - , IncorrectRequestError - , IncorrectResponseError - , TooManyRequestsError - , RequestNotImplementedError - , UnsupportedRoomVersionError - , NetworkAuthRequiredError - , UserConsentRequiredError - , UserDefinedError = 200 - }; - - /** - * A simple wrapper around QUrlQuery that allows its creation from - * a list of string pairs - */ - class Query : public QUrlQuery + /* 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, + WarningLevel = 20, + UnexpectedResponseTypeWarning = 21, + Abandoned = 50 //< A very brief period between abandoning and object + //deletion + , + ErrorLevel = 100 //< Errors have codes starting from this + , + NetworkError = 100, + JsonParseError // TODO: Merge into IncorrectResponseError + , + TimeoutError, + ContentAccessError, + NotFoundError, + IncorrectRequestError, + IncorrectResponseError, + TooManyRequestsError, + RequestNotImplementedError, + UnsupportedRoomVersionError, + NetworkAuthRequiredError, + UserConsentRequiredError, + 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>& l) { - public: - using QUrlQuery::QUrlQuery; - Query() = default; - Query(const std::initializer_list< QPair >& 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 + 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) { - 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) - { - QDebugStateSaver _s(dbg); - return dbg.noquote().nospace() - << s.code << ": " << s.message; - } - - bool operator==(const Status& other) const - { - return code == other.code && message == other.message; - } - bool operator!=(const Status& other) const - { - return !operator==(other); - } - - int code; - QString message; - }; - - using duration_t = int; // milliseconds + QDebugStateSaver _s(dbg); + return dbg.noquote().nospace() << s.code << ": " << s.message; + } - 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); - - QUrl requestUrl() const; - bool isBackground() const; - - /** Current status of the job */ - Status status() const; - /** Short human-friendly message on the job status */ - QString statusCaption() const; - /** Get raw response body as received from the server - * \param bytesAtMost return this number of leftmost bytes, or -1 - * to return the entire response - */ - QByteArray rawData(int bytesAtMost = -1) const; - /** Get UI-friendly sample of raw data - * - * This is almost the same as rawData but appends the "truncated" - * suffix if not all data fit in bytesAtMost. This call is - * recommended to present a sample of raw data as "details" next to - * error messages. Note that the default \p bytesAtMost value is - * also tailored to UI cases. - */ - QString rawDataSample(int bytesAtMost = 65535) const; - - /** Error (more generally, status) code - * Equivalent to status().code - * \sa status - */ - int error() const; - /** Error-specific message, as returned by the server */ - virtual QString errorString() const; - /** A URL to help/clarify the error, if provided by the server */ - QUrl errorUrl() 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) + bool operator==(const Status& other) const { - return dbg << j->objectName(); + return code == other.code && message == other.message; } + bool operator!=(const Status& other) const + { + return !operator==(other); + } + + 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); + + QUrl requestUrl() const; + bool isBackground() const; + + /** Current status of the job */ + Status status() const; + /** Short human-friendly message on the job status */ + QString statusCaption() const; + /** Get raw response body as received from the server + * \param bytesAtMost return this number of leftmost bytes, or -1 + * to return the entire response + */ + QByteArray rawData(int bytesAtMost = -1) const; + /** Get UI-friendly sample of raw data + * + * This is almost the same as rawData but appends the "truncated" + * suffix if not all data fit in bytesAtMost. This call is + * recommended to present a sample of raw data as "details" next to + * error messages. Note that the default \p bytesAtMost value is + * also tailored to UI cases. + */ + QString rawDataSample(int bytesAtMost = 65535) const; + + /** Error (more generally, status) code + * Equivalent to status().code + * \sa status + */ + int error() const; + /** Error-specific message, as returned by the server */ + virtual QString errorString() const; + /** A URL to help/clarify the error, if provided by the server */ + QUrl errorUrl() 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, - bool inBackground = false); - - /** - * 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. No result signals - * (result, success, failure) are emitted. - */ - void abandon(); + void start(const ConnectionData* connData, bool inBackground = false); + + /** + * 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. No result signals + * (result, success, failure) are emitted. + */ + void abandon(); signals: - /** The job is about to send a network request */ - void aboutToStart(); - - /** The job has sent a network request */ - void started(); - - /** The job has changed its status */ - void statusChanged(Status newStatus); - - /** - * 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 result(), success(), or failure() - * rather than finished(). However if you need to track the job's - * lifecycle you should connect to this instead of result(); - * in particular, only this signal will be emitted on abandoning. - * - * @param job the job that emitted this signal - * - * @see result, success, failure - */ - void finished(BaseJob* job); - - /** - * Emitted when the job is finished (except when abandoned). - * - * 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() in case there's no error. - * - * @see result, failure - */ - void success(BaseJob*); - - /** - * Emitted together with result() if there's an error. - * Similar to result(), this won't be emitted in case of abandon(). - * - * @see result, success - */ - void failure(BaseJob*); - - void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); - void uploadProgress(qint64 bytesSent, qint64 bytesTotal); + /** The job is about to send a network request */ + void aboutToStart(); + + /** The job has sent a network request */ + void started(); + + /** The job has changed its status */ + void statusChanged(Status newStatus); + + /** + * 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 result(), success(), or failure() + * rather than finished(). However if you need to track the job's + * lifecycle you should connect to this instead of result(); + * in particular, only this signal will be emitted on abandoning. + * + * @param job the job that emitted this signal + * + * @see result, success, failure + */ + void finished(BaseJob* job); + + /** + * Emitted when the job is finished (except when abandoned). + * + * 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() in case there's no error. + * + * @see result, failure + */ + void success(BaseJob*); + + /** + * Emitted together with result() if there's an error. + * Similar to result(), this won't be emitted in case of abandon(). + * + * @see result, success + */ + void failure(BaseJob*); + + void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); + void uploadProgress(qint64 bytesSent, qint64 bytesTotal); protected: - using headers_t = QHash; - - 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 doCheckReply(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; + using headers_t = QHash; + + 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 doCheckReply(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(); + void timeout(); private slots: - void sendRequest(bool inBackground); - void checkReply(); - void gotReply(); + void sendRequest(bool inBackground); + void checkReply(); + void gotReply(); private: - void stop(); - void finishJob(); + void stop(); + void finishJob(); - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; inline bool isJobRunning(BaseJob* job) { return job && job->error() == BaseJob::Pending; } -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 2bf9dd8f..12aacb8b 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -1,23 +1,24 @@ #include "downloadfilejob.h" -#include #include #include +#include using namespace QMatrixClient; class DownloadFileJob::Private { public: - Private() : tempFile(new QTemporaryFile()) { } + Private() : tempFile(new QTemporaryFile()) {} - explicit Private(const QString& localFilename) - : targetFile(new QFile(localFilename)) - , tempFile(new QFile(targetFile->fileName() + ".qmcdownload")) - { } + explicit Private(const QString& localFilename) + : targetFile(new QFile(localFilename)), + tempFile(new QFile(targetFile->fileName() + ".qmcdownload")) + { + } - QScopedPointer targetFile; - QScopedPointer tempFile; + QScopedPointer targetFile; + QScopedPointer tempFile; }; QUrl DownloadFileJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri) @@ -28,8 +29,8 @@ QUrl DownloadFileJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri) DownloadFileJob::DownloadFileJob(const QString& serverName, const QString& mediaId, const QString& localFilename) - : GetContentJob(serverName, mediaId) - , d(localFilename.isEmpty() ? new Private : new Private(localFilename)) + : GetContentJob(serverName, mediaId), + d(localFilename.isEmpty() ? new Private : new Private(localFilename)) { setObjectName("DownloadFileJob"); } @@ -41,16 +42,15 @@ QString DownloadFileJob::targetFileName() const 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"; + 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)) - { + 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"); @@ -61,16 +61,14 @@ void DownloadFileJob::beforeStart(const ConnectionData*) void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply) { - connect(reply, &QNetworkReply::metaDataChanged, this, [this,reply] { + connect(reply, &QNetworkReply::metaDataChanged, this, [this, reply] { if (!status().good()) return; auto sizeHeader = reply->header(QNetworkRequest::ContentLengthHeader); - if (sizeHeader.isValid()) - { + if (sizeHeader.isValid()) { auto targetSize = sizeHeader.value(); if (targetSize != -1) - if (!d->tempFile->resize(targetSize)) - { + if (!d->tempFile->resize(targetSize)) { qCWarning(JOBS) << "Failed to allocate" << targetSize << "bytes for" << d->tempFile->fileName(); setStatus(FileError, @@ -78,16 +76,15 @@ void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply) } } }); - connect(reply, &QIODevice::readyRead, this, [this,reply] { + connect(reply, &QIODevice::readyRead, this, [this, reply] { if (!status().good()) return; auto bytes = reply->read(reply->bytesAvailable()); if (!bytes.isEmpty()) d->tempFile->write(bytes); else - qCWarning(JOBS) - << "Unexpected empty chunk when downloading from" - << reply->url() << "to" << d->tempFile->fileName(); + qCWarning(JOBS) << "Unexpected empty chunk when downloading from" + << reply->url() << "to" << d->tempFile->fileName(); }); } @@ -100,22 +97,18 @@ void DownloadFileJob::beforeAbandon(QNetworkReply*) BaseJob::Status DownloadFileJob::parseReply(QNetworkReply*) { - if (d->targetFile) - { + if (d->targetFile) { d->targetFile->close(); - if (!d->targetFile->remove()) - { + 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())) - { + 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 + } 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 index ce47ab1c..fd34ba5a 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -2,29 +2,27 @@ #include "csapi/content-repo.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class DownloadFileJob : public GetContentJob { public: - enum { FileError = BaseJob::UserDefinedError + 1 }; + enum { FileError = BaseJob::UserDefinedError + 1 }; - using GetContentJob::makeRequestUrl; - static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri); + using GetContentJob::makeRequestUrl; + static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri); - DownloadFileJob(const QString& serverName, const QString& mediaId, - const QString& localFilename = {}); + DownloadFileJob(const QString& serverName, const QString& mediaId, + const QString& localFilename = {}); - QString targetFileName() const; + QString targetFileName() const; private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; - void beforeStart(const ConnectionData*) override; - void afterStart(const ConnectionData*, - QNetworkReply* reply) override; - void beforeAbandon(QNetworkReply*) override; - Status parseReply(QNetworkReply*) override; + 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/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index aeb49839..d3370f1f 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -13,41 +13,42 @@ * * 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 + * 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) +QUrl MediaThumbnailJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri, + QSize requestedSize) { - return makeRequestUrl(std::move(baseUrl), - mxcUri.authority(), mxcUri.path().mid(1), - requestedSize.width(), requestedSize.height()); + return makeRequestUrl(std::move(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()) -{ } + const QString& mediaId, + QSize requestedSize) + : GetContentThumbnailJob(serverName, mediaId, requestedSize.width(), + requestedSize.height()) +{ +} MediaThumbnailJob::MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize) - : MediaThumbnailJob(mxcUri.authority(), mxcUri.path().mid(1), // sans leading '/' + : MediaThumbnailJob(mxcUri.authority(), + mxcUri.path().mid(1), // sans leading '/' requestedSize) -{ } - -QImage MediaThumbnailJob::thumbnail() const { - return _thumbnail; } +QImage MediaThumbnailJob::thumbnail() const { return _thumbnail; } + QImage MediaThumbnailJob::scaledThumbnail(QSize toSize) const { - return _thumbnail.scaled(toSize, - Qt::KeepAspectRatio, Qt::SmoothTransformation); + return _thumbnail.scaled(toSize, Qt::KeepAspectRatio, + Qt::SmoothTransformation); } BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply) @@ -56,7 +57,7 @@ BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply) if (!result.good()) return result; - if( _thumbnail.loadFromData(data()->readAll()) ) + if (_thumbnail.loadFromData(data()->readAll())) return Success; return { IncorrectResponseError, "Could not read image data" }; diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index 7963796e..1dcf8ccb 100644 --- a/lib/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,26 +22,25 @@ #include -namespace QMatrixClient -{ - class MediaThumbnailJob: public GetContentThumbnailJob +namespace QMatrixClient { + class MediaThumbnailJob : public GetContentThumbnailJob { public: - using GetContentThumbnailJob::makeRequestUrl; - static QUrl makeRequestUrl(QUrl baseUrl, - const QUrl& mxcUri, QSize requestedSize); + 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); + MediaThumbnailJob(const QString& serverName, const QString& mediaId, + QSize requestedSize); + MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize); - QImage thumbnail() const; - QImage scaledThumbnail(QSize toSize) const; + QImage thumbnail() const; + QImage scaledThumbnail(QSize toSize) const; protected: - Status parseReply(QNetworkReply* reply) override; + Status parseReply(QNetworkReply* reply) override; private: - QImage _thumbnail; + QImage _thumbnail; }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h index 63a8e1d0..3c5cac89 100644 --- a/lib/jobs/postreadmarkersjob.h +++ b/lib/jobs/postreadmarkersjob.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -27,13 +27,13 @@ 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 }}); - } + explicit PostReadMarkersJob(const QString& roomId, + const QString& readUpToEventId) + : BaseJob(HttpVerb::Post, "PostReadMarkersJob", + QStringLiteral("_matrix/client/r0/rooms/%1/read_markers") + .arg(roomId)) + { + setRequestData(QJsonObject { + { QStringLiteral("m.fully_read"), readUpToEventId } }); + } }; diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp index 5cb62221..477f49e7 100644 --- a/lib/jobs/requestdata.cpp +++ b/lib/jobs/requestdata.cpp @@ -1,10 +1,10 @@ #include "requestdata.h" +#include #include -#include #include #include -#include +#include using namespace QMatrixClient; @@ -17,22 +17,15 @@ auto fromData(const QByteArray& data) return source; } -template -inline auto fromJson(const JsonDataT& jdata) +template inline auto fromJson(const JsonDataT& jdata) { return fromData(QJsonDocument(jdata).toJson(QJsonDocument::Compact)); } -RequestData::RequestData(const QByteArray& a) - : _source(fromData(a)) -{ } +RequestData::RequestData(const QByteArray& a) : _source(fromData(a)) {} -RequestData::RequestData(const QJsonObject& jo) - : _source(fromJson(jo)) -{ } +RequestData::RequestData(const QJsonObject& jo) : _source(fromJson(jo)) {} -RequestData::RequestData(const QJsonArray& ja) - : _source(fromJson(ja)) -{ } +RequestData::RequestData(const QJsonArray& ja) : _source(fromJson(ja)) {} RequestData::~RequestData() = default; diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h index db011b61..207ff731 100644 --- a/lib/jobs/requestdata.h +++ b/lib/jobs/requestdata.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -26,8 +26,7 @@ class QJsonArray; class QJsonDocument; class QIODevice; -namespace QMatrixClient -{ +namespace QMatrixClient { /** * A simple wrapper that represents the request body. * Provides a unified interface to dump an unstructured byte stream @@ -37,25 +36,23 @@ namespace QMatrixClient class RequestData { public: - RequestData() = default; - RequestData(const QByteArray& a); - RequestData(const QJsonObject& jo); - RequestData(const QJsonArray& ja); - RequestData(QIODevice* source) - : _source(std::unique_ptr(source)) - { } - RequestData(const RequestData&) = delete; - RequestData& operator=(const RequestData&) = delete; - RequestData(RequestData&&) = default; - RequestData& operator=(RequestData&&) = default; - ~RequestData(); + RequestData() = default; + RequestData(const QByteArray& a); + RequestData(const QJsonObject& jo); + RequestData(const QJsonArray& ja); + RequestData(QIODevice* source) + : _source(std::unique_ptr(source)) + { + } + RequestData(const RequestData&) = delete; + RequestData& operator=(const RequestData&) = delete; + RequestData(RequestData&&) = default; + RequestData& operator=(RequestData&&) = default; + ~RequestData(); - QIODevice* source() const - { - return _source.get(); - } + QIODevice* source() const { return _source.get(); } private: - std::unique_ptr _source; + std::unique_ptr _source; }; } // namespace QMatrixClient diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index 84385b55..db11005a 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "syncjob.h" @@ -29,25 +29,26 @@ SyncJob::SyncJob(const QString& since, const QString& filter, int timeout, { setLoggingCategory(SYNCJOB); QUrlQuery query; - if( !filter.isEmpty() ) + if (!filter.isEmpty()) query.addQueryItem(QStringLiteral("filter"), filter); - if( !presence.isEmpty() ) + if (!presence.isEmpty()) query.addQueryItem(QStringLiteral("set_presence"), presence); - if( timeout >= 0 ) + if (timeout >= 0) query.addQueryItem(QStringLiteral("timeout"), QString::number(timeout)); - if( !since.isEmpty() ) + if (!since.isEmpty()) query.addQueryItem(QStringLiteral("since"), since); setRequestQuery(query); setMaxRetries(std::numeric_limits::max()); } -SyncJob::SyncJob(const QString& since, const Filter& filter, - int timeout, const QString& presence) +SyncJob::SyncJob(const QString& since, const Filter& filter, int timeout, + const QString& presence) : SyncJob(since, QJsonDocument(toJson(filter)).toJson(QJsonDocument::Compact), timeout, presence) -{ } +{ +} BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) { @@ -59,4 +60,3 @@ BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) << d.unresolvedRooms().join(','); return BaseJob::IncorrectResponseError; } - diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index 036b25d0..2afaf0f7 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -13,33 +13,31 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "basejob.h" -#include "../syncdata.h" #include "../csapi/definitions/sync_filter.h" +#include "../syncdata.h" -namespace QMatrixClient -{ - class SyncJob: public BaseJob +namespace QMatrixClient { + class SyncJob : public BaseJob { public: - explicit SyncJob(const QString& since = {}, - const QString& filter = {}, - int timeout = -1, const QString& presence = {}); - explicit SyncJob(const QString& since, const Filter& filter, - int timeout = -1, const QString& presence = {}); + explicit SyncJob(const QString& since = {}, const QString& filter = {}, + int timeout = -1, const QString& presence = {}); + explicit SyncJob(const QString& since, const Filter& filter, + int timeout = -1, const QString& presence = {}); - SyncData &&takeData() { return std::move(d); } + SyncData&& takeData() { return std::move(d); } protected: - Status parseJson(const QJsonDocument& data) override; + Status parseJson(const QJsonDocument& data) override; private: - SyncData d; + SyncData d; }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/joinstate.h b/lib/joinstate.h index 379183f6..ddaba9a5 100644 --- a/lib/joinstate.h +++ b/lib/joinstate.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,10 +22,8 @@ #include -namespace QMatrixClient -{ - enum class JoinState : unsigned int - { +namespace QMatrixClient { + enum class JoinState : unsigned int { Join = 0x1, Invite = 0x2, Leave = 0x4, @@ -35,14 +33,16 @@ namespace QMatrixClient // We cannot use REGISTER_ENUM outside of a Q_OBJECT and besides, we want // to use strings that match respective JSON keys. - static const std::array JoinStateStrings - { { "join", "invite", "leave" } }; + static const std::array JoinStateStrings { + { "join", "invite", "leave" } + }; inline const char* toCString(JoinState js) { size_t state = size_t(js), index = 0; - while (state >>= 1) ++index; + while (state >>= 1) + ++index; return JoinStateStrings[index]; } -} // namespace QMatrixClient +} // namespace QMatrixClient Q_DECLARE_OPERATORS_FOR_FLAGS(QMatrixClient::JoinStates) diff --git a/lib/logging.cpp b/lib/logging.cpp index 7476781f..5d70ba36 100644 --- a/lib/logging.cpp +++ b/lib/logging.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "logging.h" diff --git a/lib/logging.h b/lib/logging.h index a3a65887..06603322 100644 --- a/lib/logging.h +++ b/lib/logging.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -28,8 +28,7 @@ Q_DECLARE_LOGGING_CATEGORY(EPHEMERAL) Q_DECLARE_LOGGING_CATEGORY(JOBS) Q_DECLARE_LOGGING_CATEGORY(SYNCJOB) -namespace QMatrixClient -{ +namespace QMatrixClient { // QDebug manipulators using QDebugManip = QDebug (*)(QDebug); @@ -47,9 +46,9 @@ namespace QMatrixClient inline QDebug formatJson(QDebug debug_object) { #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) - return debug_object; + return debug_object; #else - return debug_object.noquote(); + return debug_object.noquote(); #endif } @@ -61,7 +60,7 @@ namespace QMatrixClient * @param qdm a QDebug manipulator * @return a copy of debug_object that has its mode altered by qdm */ - inline QDebug operator<< (QDebug debug_object, QDebugManip qdm) + inline QDebug operator<<(QDebug debug_object, QDebugManip qdm) { return qdm(debug_object); } @@ -70,15 +69,15 @@ namespace QMatrixClient { return #ifdef PROFILER_LOG_USECS - PROFILER_LOG_USECS + PROFILER_LOG_USECS #else - 200 + 200 #endif - * 1000; + * 1000; } } -inline QDebug operator<< (QDebug debug_object, const QElapsedTimer& et) +inline QDebug operator<<(QDebug debug_object, const QElapsedTimer& et) { auto val = et.nsecsElapsed() / 1000; if (val < 1000) diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 89967a8a..174ac16e 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -13,24 +13,26 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "networkaccessmanager.h" -#include #include +#include using namespace QMatrixClient; class NetworkAccessManager::Private { public: - QList ignoredSslErrors; + QList ignoredSslErrors; }; -NetworkAccessManager::NetworkAccessManager(QObject* parent) : d(std::make_unique()) -{ } +NetworkAccessManager::NetworkAccessManager(QObject* parent) + : d(std::make_unique()) +{ +} QList NetworkAccessManager::ignoredSslErrors() const { @@ -52,8 +54,9 @@ static NetworkAccessManager* createNam() auto nam = new NetworkAccessManager(QCoreApplication::instance()); // See #109. Once Qt bearer management gets better, this workaround // should become unnecessary. - nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, - [nam] { nam->setNetworkAccessible(QNetworkAccessManager::Accessible); }); + nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, [nam] { + nam->setNetworkAccessible(QNetworkAccessManager::Accessible); + }); return nam; } @@ -65,8 +68,8 @@ NetworkAccessManager* NetworkAccessManager::instance() NetworkAccessManager::~NetworkAccessManager() = default; -QNetworkReply* NetworkAccessManager::createRequest(Operation op, - const QNetworkRequest& request, QIODevice* outgoingData) +QNetworkReply* NetworkAccessManager::createRequest( + Operation op, const QNetworkRequest& request, QIODevice* outgoingData) { auto reply = QNetworkAccessManager::createRequest(op, request, outgoingData); diff --git a/lib/networkaccessmanager.h b/lib/networkaccessmanager.h index ae847582..ebaaa5b2 100644 --- a/lib/networkaccessmanager.h +++ b/lib/networkaccessmanager.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -22,28 +22,27 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { class NetworkAccessManager : public QNetworkAccessManager { - Q_OBJECT + Q_OBJECT public: - NetworkAccessManager(QObject* parent = nullptr); - ~NetworkAccessManager() override; + NetworkAccessManager(QObject* parent = nullptr); + ~NetworkAccessManager() override; - QList ignoredSslErrors() const; - void addIgnoredSslError(const QSslError& error); - void clearIgnoredSslErrors(); + QList ignoredSslErrors() const; + void addIgnoredSslError(const QSslError& error); + void clearIgnoredSslErrors(); - /** Get a pointer to the singleton */ - static NetworkAccessManager* instance(); + /** Get a pointer to the singleton */ + static NetworkAccessManager* instance(); private: - QNetworkReply * createRequest(Operation op, - const QNetworkRequest &request, - QIODevice *outgoingData = Q_NULLPTR) override; + QNetworkReply* + createRequest(Operation op, const QNetworkRequest& request, + QIODevice* outgoingData = Q_NULLPTR) override; - class Private; - std::unique_ptr d; + class Private; + std::unique_ptr d; }; } // namespace QMatrixClient diff --git a/lib/networksettings.cpp b/lib/networksettings.cpp index 48bd09f3..de333884 100644 --- a/lib/networksettings.cpp +++ b/lib/networksettings.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "networksettings.h" @@ -23,9 +23,12 @@ using namespace QMatrixClient; void NetworkSettings::setupApplicationProxy() const { QNetworkProxy::setApplicationProxy( - { proxyType(), proxyHostName(), proxyPort() }); + { proxyType(), proxyHostName(), proxyPort() }); } -QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) -QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", "", setProxyHostName) -QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, setProxyPort) +QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, + "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) +QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", + "", setProxyHostName) +QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, + setProxyPort) diff --git a/lib/networksettings.h b/lib/networksettings.h index 83613060..ca9c7dfc 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -25,20 +25,21 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) namespace QMatrixClient { - class NetworkSettings: public SettingsGroup + class NetworkSettings : public SettingsGroup { - Q_OBJECT - QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) - QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) - QMC_DECLARE_SETTING(quint16, proxyPort, setProxyPort) - Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) + Q_OBJECT + QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) + QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) + QMC_DECLARE_SETTING(quint16, proxyPort, setProxyPort) + Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) public: - template - explicit NetworkSettings(ArgTs... qsettingsArgs) - : SettingsGroup(QStringLiteral("Network"), qsettingsArgs...) - { } - ~NetworkSettings() override = default; + template + explicit NetworkSettings(ArgTs... qsettingsArgs) + : SettingsGroup(QStringLiteral("Network"), qsettingsArgs...) + { + } + ~NetworkSettings() override = default; - Q_INVOKABLE void setupApplicationProxy() const; + Q_INVOKABLE void setupApplicationProxy() const; }; } diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index c2bde8df..0f36424f 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -24,11 +24,12 @@ namespace QMatrixClient { namespace _impl { - template - inline QMetaObject::Connection connectUntil( - SenderT* sender, SignalT signal, ContextT* context, - std::function slot, Qt::ConnectionType connType) + template + inline QMetaObject::Connection + connectUntil(SenderT* sender, SignalT signal, ContextT* context, + std::function slot, + Qt::ConnectionType connType) { // See https://bugreports.qt.io/browse/QTBUG-60339 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) @@ -37,23 +38,27 @@ namespace QMatrixClient { auto pc = std::make_unique(); #endif auto& c = *pc; // Resolve a reference before pc is moved to lambda - c = QObject::connect(sender, signal, context, - [pc=std::move(pc),slot] (ArgTs... args) { - Q_ASSERT(*pc); // If it's been triggered, it should exist - if (slot(std::forward(args)...)) - QObject::disconnect(*pc); - }, connType); + c = QObject::connect( + sender, signal, context, + [pc = std::move(pc), slot](ArgTs... args) { + Q_ASSERT( + *pc); // If it's been triggered, it should exist + if (slot(std::forward(args)...)) + QObject::disconnect(*pc); + }, + connType); return c; } } - template + template inline auto connectUntil(SenderT* sender, SignalT signal, ContextT* context, const FunctorT& slot, Qt::ConnectionType connType = Qt::AutoConnection) { - return _impl::connectUntil(sender, signal, context, + return _impl::connectUntil( + sender, signal, context, typename function_traits::function_type(slot), connType); } @@ -63,8 +68,8 @@ namespace QMatrixClient { * * Only supports DirectConnection type. */ - template + template inline auto connectSingleShot(SenderT* sender, SignalT signal, ReceiverT* receiver, SlotT slot) { @@ -84,24 +89,24 @@ namespace QMatrixClient { * disconnected from signals of the underlying pointer upon the guard's * destruction. */ - template - class ConnectionsGuard : public QPointer + template class ConnectionsGuard : public QPointer { public: - ConnectionsGuard(T* publisher, QObject* subscriber) - : QPointer(publisher), subscriber(subscriber) - { } - ~ConnectionsGuard() - { - if (*this) - (*this)->disconnect(subscriber); - } - ConnectionsGuard(ConnectionsGuard&&) = default; - ConnectionsGuard& operator=(ConnectionsGuard&&) = default; - Q_DISABLE_COPY(ConnectionsGuard) - using QPointer::operator=; + ConnectionsGuard(T* publisher, QObject* subscriber) + : QPointer(publisher), subscriber(subscriber) + { + } + ~ConnectionsGuard() + { + if (*this) + (*this)->disconnect(subscriber); + } + ConnectionsGuard(ConnectionsGuard&&) = default; + ConnectionsGuard& operator=(ConnectionsGuard&&) = default; + Q_DISABLE_COPY(ConnectionsGuard) + using QPointer::operator=; private: - QObject* subscriber; + QObject* subscriber; }; } diff --git a/lib/room.cpp b/lib/room.cpp index 5da9373e..c7c94fe5 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -13,55 +13,55 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "room.h" -#include "csapi/kicking.h" -#include "csapi/inviting.h" +#include "avatar.h" +#include "connection.h" +#include "converters.h" +#include "csapi/account-data.h" #include "csapi/banning.h" +#include "csapi/inviting.h" +#include "csapi/kicking.h" #include "csapi/leaving.h" #include "csapi/receipts.h" #include "csapi/redaction.h" -#include "csapi/account-data.h" -#include "csapi/room_state.h" #include "csapi/room_send.h" +#include "csapi/room_state.h" +#include "csapi/room_upgrades.h" #include "csapi/rooms.h" #include "csapi/tags.h" -#include "csapi/room_upgrades.h" -#include "events/simplestateevents.h" -#include "events/roomcreateevent.h" -#include "events/roomtombstoneevent.h" -#include "events/roomavatarevent.h" -#include "events/roommemberevent.h" -#include "events/typingevent.h" -#include "events/receiptevent.h" -#include "events/callinviteevent.h" -#include "events/callcandidatesevent.h" #include "events/callanswerevent.h" +#include "events/callcandidatesevent.h" #include "events/callhangupevent.h" +#include "events/callinviteevent.h" +#include "events/receiptevent.h" #include "events/redactionevent.h" -#include "jobs/mediathumbnailjob.h" +#include "events/roomavatarevent.h" +#include "events/roomcreateevent.h" +#include "events/roommemberevent.h" +#include "events/roomtombstoneevent.h" +#include "events/simplestateevents.h" +#include "events/typingevent.h" #include "jobs/downloadfilejob.h" +#include "jobs/mediathumbnailjob.h" #include "jobs/postreadmarkersjob.h" -#include "avatar.h" -#include "connection.h" -#include "user.h" -#include "converters.h" #include "syncdata.h" +#include "user.h" +#include #include -#include // for efficient string concats (operator%) +#include #include -#include -#include #include -#include +#include // for efficient string concats (operator%) +#include #include -#include #include +#include using namespace QMatrixClient; using namespace std::placeholders; @@ -75,239 +75,235 @@ enum EventsPlacement : int { Older = -1, Newer = 1 }; class Room::Private { public: - /** Map of user names to users. User names potentially duplicate, hence a multi-hashmap. */ - using members_map_t = QMultiHash; - - Private(Connection* c, QString id_, JoinState initialJoinState) - : q(nullptr), connection(c), id(move(id_)) - , joinState(initialJoinState) - { } - - Room* q; - - Connection* connection; - QString id; - JoinState joinState; - RoomSummary summary = { none, 0, none }; - /// The state of the room at timeline position before-0 - /// \sa timelineBase - std::unordered_map baseState; - /// The state of the room at timeline position after-maxTimelineIndex() - /// \sa Room::syncEdge - QHash currentState; - Timeline timeline; - PendingEvents unsyncedEvents; - QHash eventsIndex; - QString displayname; - Avatar avatar; - int highlightCount = 0; - int notificationCount = 0; - members_map_t membersMap; - QList usersTyping; - QMultiHash eventIdReadUsers; - QList membersLeft; - int unreadMessages = 0; - bool displayed = false; - QString firstDisplayedEventId; - QString lastDisplayedEventId; - QHash lastReadEventIds; - QString serverReadMarker; - TagsMap tags; - std::unordered_map accountData; - QString prevBatch; - QPointer eventsHistoryJob; - QPointer allMembersJob; - - struct FileTransferPrivateInfo + /** Map of user names to users. User names potentially duplicate, hence a + * multi-hashmap. */ + using members_map_t = QMultiHash; + + Private(Connection* c, QString id_, JoinState initialJoinState) + : q(nullptr), connection(c), id(move(id_)), joinState(initialJoinState) + { + } + + Room* q; + + Connection* connection; + QString id; + JoinState joinState; + RoomSummary summary = { none, 0, none }; + /// The state of the room at timeline position before-0 + /// \sa timelineBase + std::unordered_map baseState; + /// The state of the room at timeline position after-maxTimelineIndex() + /// \sa Room::syncEdge + QHash currentState; + Timeline timeline; + PendingEvents unsyncedEvents; + QHash eventsIndex; + QString displayname; + Avatar avatar; + int highlightCount = 0; + int notificationCount = 0; + members_map_t membersMap; + QList usersTyping; + QMultiHash eventIdReadUsers; + QList membersLeft; + int unreadMessages = 0; + bool displayed = false; + QString firstDisplayedEventId; + QString lastDisplayedEventId; + QHash lastReadEventIds; + QString serverReadMarker; + TagsMap tags; + std::unordered_map accountData; + QString prevBatch; + QPointer eventsHistoryJob; + QPointer allMembersJob; + + struct FileTransferPrivateInfo { + FileTransferPrivateInfo() = default; + FileTransferPrivateInfo(BaseJob* j, const QString& fileName, + bool isUploading = false) + : status(FileTransferInfo::Started), + job(j), + localFileInfo(fileName), + isUpload(isUploading) { - FileTransferPrivateInfo() = default; - FileTransferPrivateInfo(BaseJob* j, const QString& fileName, - bool isUploading = false) - : status(FileTransferInfo::Started), job(j) - , localFileInfo(fileName), isUpload(isUploading) - { } - - FileTransferInfo::Status status = FileTransferInfo::None; - QPointer job = nullptr; - QFileInfo localFileInfo { }; - bool isUpload = false; - qint64 progress = 0; - qint64 total = -1; - - void update(qint64 p, qint64 t) - { - if (t == 0) - { - t = -1; - if (p == 0) - p = -1; - } - if (p != -1) - qCDebug(PROFILER) << "Transfer progress:" << p << "/" << t - << "=" << llround(double(p) / t * 100) << "%"; - progress = p; total = t; - } - }; - void failedTransfer(const QString& tid, const QString& errorMessage = {}) + } + + FileTransferInfo::Status status = FileTransferInfo::None; + QPointer job = nullptr; + QFileInfo localFileInfo {}; + bool isUpload = false; + qint64 progress = 0; + qint64 total = -1; + + void update(qint64 p, qint64 t) { - qCWarning(MAIN) << "File transfer failed for id" << tid; - if (!errorMessage.isEmpty()) - qCWarning(MAIN) << "Message:" << errorMessage; - fileTransfers[tid].status = FileTransferInfo::Failed; - emit q->fileTransferFailed(tid, errorMessage); + if (t == 0) { + t = -1; + if (p == 0) + p = -1; + } + if (p != -1) + qCDebug(PROFILER) << "Transfer progress:" << p << "/" << t + << "=" << llround(double(p) / t * 100) << "%"; + progress = p; + total = t; } - /// A map from event/txn ids to information about the long operation; - /// used for both download and upload operations - QHash fileTransfers; + }; + void failedTransfer(const QString& tid, const QString& errorMessage = {}) + { + qCWarning(MAIN) << "File transfer failed for id" << tid; + if (!errorMessage.isEmpty()) + qCWarning(MAIN) << "Message:" << errorMessage; + fileTransfers[tid].status = FileTransferInfo::Failed; + emit q->fileTransferFailed(tid, errorMessage); + } + /// A map from event/txn ids to information about the long operation; + /// used for both download and upload operations + QHash fileTransfers; - const RoomMessageEvent* getEventWithFile(const QString& eventId) const; - QString fileNameToDownload(const RoomMessageEvent* event) const; + const RoomMessageEvent* getEventWithFile(const QString& eventId) const; + QString fileNameToDownload(const RoomMessageEvent* event) const; - Changes setSummary(RoomSummary&& newSummary); + Changes setSummary(RoomSummary&& newSummary); - //void inviteUser(User* u); // We might get it at some point in time. - void insertMemberIntoMap(User* u); - void renameMember(User* u, QString oldName); - void removeMemberFromMap(const QString& username, User* u); + // void inviteUser(User* u); // We might get it at some point in time. + void insertMemberIntoMap(User* u); + void renameMember(User* u, QString oldName); + void removeMemberFromMap(const QString& username, User* u); - // This updates the room displayname field (which is the way a room - // should be shown in the room list); called whenever the list of - // members, the room name (m.room.name) or canonical alias change. - void updateDisplayname(); - // This is used by updateDisplayname() but only calculates the new name - // without any updates. - QString calculateDisplayname() const; + // This updates the room displayname field (which is the way a room + // should be shown in the room list); called whenever the list of + // members, the room name (m.room.name) or canonical alias change. + void updateDisplayname(); + // This is used by updateDisplayname() but only calculates the new name + // without any updates. + QString calculateDisplayname() const; - /// A point in the timeline corresponding to baseState - rev_iter_t timelineBase() const { return q->findInTimeline(-1); } + /// A point in the timeline corresponding to baseState + rev_iter_t timelineBase() const { return q->findInTimeline(-1); } - void getPreviousContent(int limit = 10); + void getPreviousContent(int limit = 10); - template - const EventT* getCurrentState(QString stateKey = {}) const - { - static const EventT empty; - const auto* evt = - currentState.value({EventT::matrixTypeId(), stateKey}, &empty); - Q_ASSERT(evt->type() == EventT::typeId() && - evt->matrixType() == EventT::matrixTypeId()); - return static_cast(evt); - } + template + const EventT* getCurrentState(QString stateKey = {}) const + { + static const EventT empty; + const auto* evt = currentState.value( + { EventT::matrixTypeId(), stateKey }, &empty); + Q_ASSERT(evt->type() == EventT::typeId() + && evt->matrixType() == EventT::matrixTypeId()); + return static_cast(evt); + } - bool isEventNotable(const TimelineItem& ti) const - { - return !ti->isRedacted() && - ti->senderId() != connection->userId() && - is(*ti); - } + bool isEventNotable(const TimelineItem& ti) const + { + return !ti->isRedacted() && ti->senderId() != connection->userId() + && is(*ti); + } - template - Changes updateStateFrom(EventArrayT&& events) - { - Changes changes = NoChange; - if (!events.empty()) - { - QElapsedTimer et; et.start(); - for (auto&& eptr: events) - { - const auto& evt = *eptr; - Q_ASSERT(evt.isStateEvent()); - // Update baseState afterwards to make sure that the old state - // is valid and usable inside processStateEvent - changes |= q->processStateEvent(evt); - baseState[{evt.matrixType(),evt.stateKey()}] = move(eptr); - } - if (events.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) - qCDebug(PROFILER) << "*** Room::Private::updateStateFrom():" - << events.size() << "event(s)," << et; + template + Changes updateStateFrom(EventArrayT&& events) + { + Changes changes = NoChange; + if (!events.empty()) { + QElapsedTimer et; + et.start(); + for (auto&& eptr : events) { + const auto& evt = *eptr; + Q_ASSERT(evt.isStateEvent()); + // Update baseState afterwards to make sure that the old state + // is valid and usable inside processStateEvent + changes |= q->processStateEvent(evt); + baseState[{ evt.matrixType(), evt.stateKey() }] = move(eptr); } - return changes; - } - Changes addNewMessageEvents(RoomEvents&& events); - void addHistoricalMessageEvents(RoomEvents&& events); - - /** Move events into the timeline - * - * Insert events into the timeline, either new or historical. - * Pointers in the original container become empty, the ownership - * is passed to the timeline container. - * @param events - the range of events to be inserted - * @param placement - position and direction of insertion: Older for - * historical messages, Newer for new ones - */ - Timeline::difference_type moveEventsToTimeline(RoomEventsRange events, - EventsPlacement placement); - - /** - * Remove events from the passed container that are already in the timeline - */ - void dropDuplicateEvents(RoomEvents& events) const; - - Changes setLastReadEvent(User* u, QString eventId); - void updateUnreadCount(rev_iter_t from, rev_iter_t to); - Changes promoteReadMarker(User* u, rev_iter_t newMarker, - bool force = false); - - Changes markMessagesAsRead(rev_iter_t upToMarker); - - void getAllMembers(); - - QString sendEvent(RoomEventPtr&& event); - - template - QString sendEvent(ArgTs&&... eventArgs) - { - return sendEvent(makeEvent(std::forward(eventArgs)...)); + if (events.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) + qCDebug(PROFILER) << "*** Room::Private::updateStateFrom():" + << events.size() << "event(s)," << et; } + return changes; + } + Changes addNewMessageEvents(RoomEvents&& events); + void addHistoricalMessageEvents(RoomEvents&& events); + + /** Move events into the timeline + * + * Insert events into the timeline, either new or historical. + * Pointers in the original container become empty, the ownership + * is passed to the timeline container. + * @param events - the range of events to be inserted + * @param placement - position and direction of insertion: Older for + * historical messages, Newer for new ones + */ + Timeline::difference_type moveEventsToTimeline(RoomEventsRange events, + EventsPlacement placement); + + /** + * Remove events from the passed container that are already in the timeline + */ + void dropDuplicateEvents(RoomEvents& events) const; + + Changes setLastReadEvent(User* u, QString eventId); + void updateUnreadCount(rev_iter_t from, rev_iter_t to); + Changes promoteReadMarker(User* u, rev_iter_t newMarker, + bool force = false); + + Changes markMessagesAsRead(rev_iter_t upToMarker); + + void getAllMembers(); + + QString sendEvent(RoomEventPtr&& event); + + template + QString sendEvent(ArgTs&&... eventArgs) + { + return sendEvent(makeEvent(std::forward(eventArgs)...)); + } - RoomEvent* addAsPending(RoomEventPtr&& event); + RoomEvent* addAsPending(RoomEventPtr&& event); - QString doSendEvent(const RoomEvent* pEvent); - void onEventSendingFailure(const QString& txnId, BaseJob* call = nullptr); + QString doSendEvent(const RoomEvent* pEvent); + void onEventSendingFailure(const QString& txnId, BaseJob* call = nullptr); - template - SetRoomStateWithKeyJob* requestSetState(const QString& stateKey, - const EvT& event) - { - if (q->successorId().isEmpty()) - { - // TODO: Queue up state events sending (see #133). - return connection->callApi( - id, EvT::matrixTypeId(), stateKey, event.contentJson()); - } - qCWarning(MAIN) << q << "has been upgraded, state won't be set"; - return nullptr; + template + SetRoomStateWithKeyJob* requestSetState(const QString& stateKey, + const EvT& event) + { + if (q->successorId().isEmpty()) { + // TODO: Queue up state events sending (see #133). + return connection->callApi( + id, EvT::matrixTypeId(), stateKey, event.contentJson()); } + qCWarning(MAIN) << q << "has been upgraded, state won't be set"; + return nullptr; + } - template - auto requestSetState(const EvT& event) - { - return connection->callApi( - id, EvT::matrixTypeId(), event.contentJson()); - } + template auto requestSetState(const EvT& event) + { + return connection->callApi(id, EvT::matrixTypeId(), + event.contentJson()); + } - /** - * @brief Apply redaction to the timeline - * - * Tries to find an event in the timeline and redact it; deletes the - * redaction event whether the redacted event was found or not. - */ - bool processRedaction(const RedactionEvent& redaction); + /** + * @brief Apply redaction to the timeline + * + * Tries to find an event in the timeline and redact it; deletes the + * redaction event whether the redacted event was found or not. + */ + bool processRedaction(const RedactionEvent& redaction); - void setTags(TagsMap newTags); + void setTags(TagsMap newTags); - QJsonObject toJson() const; + QJsonObject toJson() const; private: - using users_shortlist_t = std::array; - template - users_shortlist_t buildShortlist(const ContT& users) const; - users_shortlist_t buildShortlist(const QStringList& userIds) const; + using users_shortlist_t = std::array; + template + users_shortlist_t buildShortlist(const ContT& users) const; + users_shortlist_t buildShortlist(const QStringList& userIds) const; - bool isLocalUser(const User* u) const - { - return u == q->localUser(); - } + bool isLocalUser(const User* u) const { return u == q->localUser(); } }; Room::Room(Connection* connection, QString id, JoinState initialJoinState) @@ -318,24 +314,18 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState) // https://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl-%E2%80%94-reloaded/ d->q = this; d->displayname = d->calculateDisplayname(); // Set initial "Empty room" name - connectUntil(connection, &Connection::loadedRoomState, this, - [this] (Room* r) { - if (this == r) - emit baseStateLoaded(); - return this == r; // loadedRoomState fires only once per room - }); + connectUntil( + connection, &Connection::loadedRoomState, this, [this](Room* r) { + if (this == r) + emit baseStateLoaded(); + return this == r; // loadedRoomState fires only once per room + }); qCDebug(MAIN) << "New" << toCString(initialJoinState) << "Room:" << id; } -Room::~Room() -{ - delete d; -} +Room::~Room() { delete d; } -const QString& Room::id() const -{ - return d->id; -} +const QString& Room::id() const { return d->id; } QString Room::version() const { @@ -345,8 +335,8 @@ QString Room::version() const bool Room::isUnstable() const { - return !connection()->loadingCapabilities() && - !connection()->stableRoomVersions().contains(version()); + return !connection()->loadingCapabilities() + && !connection()->stableRoomVersions().contains(version()); } QString Room::predecessorId() const @@ -359,10 +349,7 @@ QString Room::successorId() const return d->getCurrentState()->successorRoomId(); } -const Room::Timeline& Room::messageEvents() const -{ - return d->timeline; -} +const Room::Timeline& Room::messageEvents() const { return d->timeline; } const Room::PendingEvents& Room::pendingEvents() const { @@ -384,35 +371,20 @@ QString Room::canonicalAlias() const return d->getCurrentState()->alias(); } -QString Room::displayName() const -{ - return d->displayname; -} +QString Room::displayName() const { return d->displayname; } QString Room::topic() const { return d->getCurrentState()->topic(); } -QString Room::avatarMediaId() const -{ - return d->avatar.mediaId(); -} +QString Room::avatarMediaId() const { return d->avatar.mediaId(); } -QUrl Room::avatarUrl() const -{ - return d->avatar.url(); -} +QUrl Room::avatarUrl() const { return d->avatar.url(); } -const Avatar& Room::avatarObject() const -{ - return d->avatar; -} +const Avatar& Room::avatarObject() const { return d->avatar; } -QImage Room::avatar(int dimension) -{ - return avatar(dimension, dimension); -} +QImage Room::avatar(int dimension) { return avatar(dimension, dimension); } QImage Room::avatar(int width, int height) { @@ -422,9 +394,10 @@ QImage Room::avatar(int width, int height) // Use the first (excluding self) user's avatar for direct chats const auto dcUsers = directChatUsers(); - for (auto* u: dcUsers) + for (auto* u : dcUsers) if (u != localUser()) - return u->avatar(width, height, this, [=] { emit avatarChanged(); }); + return u->avatar(width, height, this, + [=] { emit avatarChanged(); }); return {}; } @@ -436,24 +409,20 @@ User* Room::user(const QString& userId) const JoinState Room::memberJoinState(User* user) const { - return - d->membersMap.contains(user->name(this), user) ? JoinState::Join : - JoinState::Leave; + return d->membersMap.contains(user->name(this), user) ? JoinState::Join + : JoinState::Leave; } -JoinState Room::joinState() const -{ - return d->joinState; -} +JoinState Room::joinState() const { return d->joinState; } void Room::setJoinState(JoinState state) { JoinState oldState = d->joinState; - if( state == oldState ) + if (state == oldState) return; d->joinState = state; - qCDebug(MAIN) << "Room" << id() << "changed state: " - << int(oldState) << "->" << int(state); + qCDebug(MAIN) << "Room" << id() << "changed state: " << int(oldState) + << "->" << int(state); emit changed(Change::JoinStateChange); emit joinStateChanged(oldState, state); } @@ -468,8 +437,7 @@ Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId) swap(storedId, eventId); emit q->lastReadEventChanged(u); emit q->readMarkerForUserMoved(u, eventId, storedId); - if (isLocalUser(u)) - { + if (isLocalUser(u)) { if (storedId != serverReadMarker) connection->callApi(id, storedId); emit q->readMarkerMoved(eventId, storedId); @@ -488,32 +456,33 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) // unreadMessages and might need to promote the read marker further // over local-origin messages. const auto readMarker = q->readMarker(); - if (readMarker >= from && readMarker < to) - { + if (readMarker >= from && readMarker < to) { promoteReadMarker(q->localUser(), readMarker, true); return; } Q_ASSERT(to <= readMarker); - QElapsedTimer et; et.start(); - const auto newUnreadMessages = count_if(from, to, - std::bind(&Room::Private::isEventNotable, this, _1)); + QElapsedTimer et; + et.start(); + const auto newUnreadMessages = count_if( + from, to, std::bind(&Room::Private::isEventNotable, this, _1)); if (et.nsecsElapsed() > profilerMinNsecs() / 10) qCDebug(PROFILER) << "Counting gained unread messages took" << et; - if(newUnreadMessages > 0) - { - // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count + if (newUnreadMessages > 0) { + // See + // https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count if (unreadMessages < 0) unreadMessages = 0; unreadMessages += newUnreadMessages; qCDebug(MAIN) << "Room" << q->objectName() << "has gained" - << newUnreadMessages << "unread message(s)," - << (q->readMarker() == timeline.crend() ? - "in total at least" : "in total") - << unreadMessages << "unread message(s)"; + << newUnreadMessages << "unread message(s)," + << (q->readMarker() == timeline.crend() + ? "in total at least" + : "in total") + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); } } @@ -525,34 +494,36 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, Q_ASSERT(newMarker >= timeline.crbegin() && newMarker <= timeline.crend()); const auto prevMarker = q->readMarker(u); - if (!force && prevMarker <= newMarker) // Remember, we deal with reverse iterators + if (!force + && prevMarker <= newMarker) // Remember, we deal with reverse iterators return Change::NoChange; Q_ASSERT(newMarker < timeline.crend()); // Try to auto-promote the read marker over the user's own messages // (switch to direct iterators for that). - auto eagerMarker = find_if(newMarker.base(), timeline.cend(), - [=](const TimelineItem& ti) { return ti->senderId() != u->id(); }); + auto eagerMarker = find_if( + newMarker.base(), timeline.cend(), + [=](const TimelineItem& ti) { return ti->senderId() != u->id(); }); auto changes = setLastReadEvent(u, (*(eagerMarker - 1))->id()); - if (isLocalUser(u)) - { + if (isLocalUser(u)) { const auto oldUnreadCount = unreadMessages; - QElapsedTimer et; et.start(); - unreadMessages = count_if(eagerMarker, timeline.cend(), - std::bind(&Room::Private::isEventNotable, this, _1)); + QElapsedTimer et; + et.start(); + unreadMessages = + count_if(eagerMarker, timeline.cend(), + std::bind(&Room::Private::isEventNotable, this, _1)); if (et.nsecsElapsed() > profilerMinNsecs() / 10) qCDebug(PROFILER) << "Recounting unread messages took" << et; - // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count + // See + // https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count if (unreadMessages == 0) unreadMessages = -1; - if (force || unreadMessages != oldUnreadCount) - { - if (unreadMessages == -1) - { + if (force || unreadMessages != oldUnreadCount) { + if (unreadMessages == -1) { qCDebug(MAIN) << "Room" << displayname << "has no more unread messages"; } else @@ -575,10 +546,8 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) // We shouldn't send read receipts for the local user's own messages - so // search earlier messages for the latest message not from the local user // until the previous last-read message, whichever comes first. - for (; upToMarker < prevMarker; ++upToMarker) - { - if ((*upToMarker)->senderId() != q->localUser()->id()) - { + for (; upToMarker < prevMarker; ++upToMarker) { + if ((*upToMarker)->senderId() != q->localUser()->id()) { connection->callApi(id, "m.read", (*upToMarker)->id()); break; @@ -601,47 +570,36 @@ void Room::markAllMessagesAsRead() bool Room::canSwitchVersions() const { // TODO, #276: m.room.power_levels - const auto* plEvt = - d->currentState.value({"m.room.power_levels", ""}); + const auto* plEvt = d->currentState.value({ "m.room.power_levels", "" }); if (!plEvt) return true; const auto plJson = plEvt->contentJson(); const auto currentUserLevel = - plJson.value("users"_ls).toObject() - .value(localUser()->id()).toInt( - plJson.value("users_default"_ls).toInt()); + plJson.value("users"_ls) + .toObject() + .value(localUser()->id()) + .toInt(plJson.value("users_default"_ls).toInt()); const auto tombstonePowerLevel = - plJson.value("events").toObject() - .value("m.room.tombstone"_ls).toInt( - plJson.value("state_default"_ls).toInt()); + plJson.value("events") + .toObject() + .value("m.room.tombstone"_ls) + .toInt(plJson.value("state_default"_ls).toInt()); return currentUserLevel >= tombstonePowerLevel; } -bool Room::hasUnreadMessages() const -{ - return unreadCount() >= 0; -} +bool Room::hasUnreadMessages() const { return unreadCount() >= 0; } -int Room::unreadCount() const -{ - return d->unreadMessages; -} +int Room::unreadCount() const { return d->unreadMessages; } -Room::rev_iter_t Room::historyEdge() const -{ - return d->timeline.crend(); -} +Room::rev_iter_t Room::historyEdge() const { return d->timeline.crend(); } Room::Timeline::const_iterator Room::syncEdge() const { return d->timeline.cend(); } -Room::rev_iter_t Room::timelineEdge() const -{ - return historyEdge(); -} +Room::rev_iter_t Room::timelineEdge() const { return historyEdge(); } TimelineItem::index_t Room::minTimelineIndex() const { @@ -655,21 +613,19 @@ TimelineItem::index_t Room::maxTimelineIndex() const bool Room::isValidIndex(TimelineItem::index_t timelineIndex) const { - return !d->timeline.empty() && - timelineIndex >= minTimelineIndex() && - timelineIndex <= maxTimelineIndex(); + return !d->timeline.empty() && timelineIndex >= minTimelineIndex() + && timelineIndex <= maxTimelineIndex(); } Room::rev_iter_t Room::findInTimeline(TimelineItem::index_t index) const { - return timelineEdge() - - (isValidIndex(index) ? index - minTimelineIndex() + 1 : 0); + return timelineEdge() + - (isValidIndex(index) ? index - minTimelineIndex() + 1 : 0); } Room::rev_iter_t Room::findInTimeline(const QString& evtId) const { - if (!d->timeline.empty() && d->eventsIndex.contains(evtId)) - { + if (!d->timeline.empty() && d->eventsIndex.contains(evtId)) { auto it = findInTimeline(d->eventsIndex.value(evtId)); Q_ASSERT((*it)->id() == evtId); return it; @@ -680,14 +636,18 @@ Room::rev_iter_t Room::findInTimeline(const QString& evtId) const Room::PendingEvents::iterator Room::findPendingEvent(const QString& txnId) { return std::find_if(d->unsyncedEvents.begin(), d->unsyncedEvents.end(), - [txnId] (const auto& item) { return item->transactionId() == txnId; }); + [txnId](const auto& item) { + return item->transactionId() == txnId; + }); } Room::PendingEvents::const_iterator Room::findPendingEvent(const QString& txnId) const { return std::find_if(d->unsyncedEvents.cbegin(), d->unsyncedEvents.cend(), - [txnId] (const auto& item) { return item->transactionId() == txnId; }); + [txnId](const auto& item) { + return item->transactionId() == txnId; + }); } void Room::Private::getAllMembers() @@ -697,28 +657,25 @@ void Room::Private::getAllMembers() return; allMembersJob = connection->callApi( - id, connection->nextBatchToken(), "join"); + id, connection->nextBatchToken(), "join"); auto nextIndex = timeline.empty() ? 0 : timeline.back().index() + 1; - connect( allMembersJob, &BaseJob::success, q, [=] { + connect(allMembersJob, &BaseJob::success, q, [=] { Q_ASSERT(timeline.empty() || nextIndex <= q->maxTimelineIndex() + 1); auto roomChanges = updateStateFrom(allMembersJob->chunk()); // Replay member events that arrived after the point for which // the full members list was requested. - if (!timeline.empty() ) + if (!timeline.empty()) for (auto it = q->findInTimeline(nextIndex).base(); it != timeline.cend(); ++it) if (is(**it)) roomChanges |= q->processStateEvent(**it); - if (roomChanges&MembersChange) + if (roomChanges & MembersChange) emit q->memberListChanged(); emit q->allMembersLoaded(); }); } -bool Room::displayed() const -{ - return d->displayed; -} +bool Room::displayed() const { return d->displayed; } void Room::setDisplayed(bool displayed) { @@ -727,18 +684,14 @@ void Room::setDisplayed(bool displayed) d->displayed = displayed; emit displayedChanged(displayed); - if( displayed ) - { + if (displayed) { resetHighlightCount(); resetNotificationCount(); d->getAllMembers(); } } -QString Room::firstDisplayedEventId() const -{ - return d->firstDisplayedEventId; -} +QString Room::firstDisplayedEventId() const { return d->firstDisplayedEventId; } Room::rev_iter_t Room::firstDisplayedMarker() const { @@ -760,10 +713,7 @@ void Room::setFirstDisplayedEvent(TimelineItem::index_t index) setFirstDisplayedEventId(findInTimeline(index)->event()->id()); } -QString Room::lastDisplayedEventId() const -{ - return d->lastDisplayedEventId; -} +QString Room::lastDisplayedEventId() const { return d->lastDisplayedEventId; } Room::rev_iter_t Room::lastDisplayedMarker() const { @@ -791,41 +741,33 @@ Room::rev_iter_t Room::readMarker(const User* user) const return findInTimeline(d->lastReadEventIds.value(user)); } -Room::rev_iter_t Room::readMarker() const -{ - return readMarker(localUser()); -} +Room::rev_iter_t Room::readMarker() const { return readMarker(localUser()); } QString Room::readMarkerEventId() const { return d->lastReadEventIds.value(localUser()); } -QList Room::usersAtEventId(const QString& eventId) { +QList Room::usersAtEventId(const QString& eventId) +{ return d->eventIdReadUsers.values(eventId); } -int Room::notificationCount() const -{ - return d->notificationCount; -} +int Room::notificationCount() const { return d->notificationCount; } void Room::resetNotificationCount() { - if( d->notificationCount == 0 ) + if (d->notificationCount == 0) return; d->notificationCount = 0; emit notificationCountChanged(this); } -int Room::highlightCount() const -{ - return d->highlightCount; -} +int Room::highlightCount() const { return d->highlightCount; } void Room::resetHighlightCount() { - if( d->highlightCount == 0 ) + if (d->highlightCount == 0) return; d->highlightCount = 0; emit highlightCountChanged(this); @@ -834,9 +776,8 @@ void Room::resetHighlightCount() void Room::switchVersion(QString newVersion) { auto* job = connection()->callApi(id(), newVersion); - connect(job, &BaseJob::failure, this, [this,job] { - emit upgradeFailed(job->errorString()); - }); + connect(job, &BaseJob::failure, this, + [this, job] { emit upgradeFailed(job->errorString()); }); } bool Room::hasAccountData(const QString& type) const @@ -851,20 +792,11 @@ const EventPtr& Room::accountData(const QString& type) const return it != d->accountData.end() ? it->second : NoEventPtr; } -QStringList Room::tagNames() const -{ - return d->tags.keys(); -} +QStringList Room::tagNames() const { return d->tags.keys(); } -TagsMap Room::tags() const -{ - return d->tags; -} +TagsMap Room::tags() const { return d->tags; } -TagRecord Room::tag(const QString& name) const -{ - return d->tags.value(name); -} +TagRecord Room::tag(const QString& name) const { return d->tags.value(name); } std::pair validatedTag(QString name) { @@ -882,8 +814,8 @@ std::pair validatedTag(QString name) void Room::addTag(const QString& name, const TagRecord& record) { const auto& checkRes = validatedTag(name); - if (d->tags.contains(name) || - (checkRes.first && d->tags.contains(checkRes.second))) + if (d->tags.contains(name) + || (checkRes.first && d->tags.contains(checkRes.second))) return; emit tagsAboutToChange(); @@ -895,13 +827,12 @@ void Room::addTag(const QString& name, const TagRecord& record) void Room::addTag(const QString& name, float order) { - addTag(name, TagRecord{order}); + addTag(name, TagRecord { order }); } void Room::removeTag(const QString& name) { - if (d->tags.contains(name)) - { + if (d->tags.contains(name)) { emit tagsAboutToChange(); d->tags.remove(name); emit tagsChanged(); @@ -918,18 +849,16 @@ void Room::setTags(TagsMap newTags) d->setTags(move(newTags)); connection()->callApi( localUser()->id(), id(), TagEvent::matrixTypeId(), - TagEvent(d->tags).contentJson()); + TagEvent(d->tags).contentJson()); } void Room::Private::setTags(TagsMap newTags) { emit q->tagsAboutToChange(); const auto keys = newTags.keys(); - for (const auto& k: keys) - { + for (const auto& k : keys) { const auto& checkRes = validatedTag(k); - if (checkRes.first) - { + if (checkRes.first) { if (newTags.contains(checkRes.second)) newTags.remove(k); else @@ -942,20 +871,11 @@ void Room::Private::setTags(TagsMap newTags) emit q->tagsChanged(); } -bool Room::isFavourite() const -{ - return d->tags.contains(FavouriteTag); -} +bool Room::isFavourite() const { return d->tags.contains(FavouriteTag); } -bool Room::isLowPriority() const -{ - return d->tags.contains(LowPriorityTag); -} +bool Room::isLowPriority() const { return d->tags.contains(LowPriorityTag); } -bool Room::isDirectChat() const -{ - return connection()->isDirectChat(id()); -} +bool Room::isDirectChat() const { return connection()->isDirectChat(id()); } QList Room::directChatUsers() const { @@ -966,8 +886,7 @@ const RoomMessageEvent* Room::Private::getEventWithFile(const QString& eventId) const { auto evtIt = q->findInTimeline(eventId); - if (evtIt != timeline.rend() && is(**evtIt)) - { + if (evtIt != timeline.rend() && is(**evtIt)) { auto* event = evtIt->viewAs(); if (event->hasFileContent()) return event; @@ -981,12 +900,9 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const Q_ASSERT(event->hasFileContent()); const auto* fileInfo = event->content()->fileInfo(); QString fileName; - if (!fileInfo->originalName.isEmpty()) - { + if (!fileInfo->originalName.isEmpty()) { fileName = QFileInfo(fileInfo->originalName).fileName(); - } - else if (!event->plainBody().isEmpty()) - { + } else if (!event->plainBody().isEmpty()) { // Having no better options, assume that the body has // the original file URL or at least the file name. QUrl u { event->plainBody() }; @@ -997,13 +913,13 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const if (fileName.isEmpty() || !QTemporaryFile(fileName).open()) return "file." % fileInfo->mimeType.preferredSuffix(); - if (QSysInfo::productType() == "windows") - { + if (QSysInfo::productType() == "windows") { const auto& suffixes = fileInfo->mimeType.suffixes(); - if (!suffixes.isEmpty() && - std::none_of(suffixes.begin(), suffixes.end(), - [&fileName] (const QString& s) { - return fileName.endsWith(s); })) + if (!suffixes.isEmpty() + && std::none_of(suffixes.begin(), suffixes.end(), + [&fileName](const QString& s) { + return fileName.endsWith(s); + })) return fileName % '.' % fileInfo->mimeType.preferredSuffix(); } return fileName; @@ -1012,12 +928,12 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const QUrl Room::urlToThumbnail(const QString& eventId) const { if (auto* event = d->getEventWithFile(eventId)) - if (event->hasThumbnail()) - { + if (event->hasThumbnail()) { auto* thumbnail = event->content()->thumbnailInfo(); Q_ASSERT(thumbnail != nullptr); return MediaThumbnailJob::makeRequestUrl(connection()->homeserver(), - thumbnail->url, thumbnail->imageSize); + thumbnail->url, + thumbnail->imageSize); } qDebug() << "Event" << eventId << "has no thumbnail"; return {}; @@ -1025,8 +941,7 @@ QUrl Room::urlToThumbnail(const QString& eventId) const QUrl Room::urlToDownload(const QString& eventId) const { - if (auto* event = d->getEventWithFile(eventId)) - { + if (auto* event = d->getEventWithFile(eventId)) { auto* fileInfo = event->content()->fileInfo(); Q_ASSERT(fileInfo != nullptr); return DownloadFileJob::makeRequestUrl(connection()->homeserver(), @@ -1053,8 +968,7 @@ FileTransferInfo Room::fileTransferInfo(const QString& id) const qint64 progress = infoIt->progress; qint64 total = infoIt->total; - if (total > INT_MAX) - { + if (total > INT_MAX) { // JavaScript doesn't deal with 64-bit integers; scale down if necessary progress = llround(double(progress) / total * INT_MAX); total = INT_MAX; @@ -1066,13 +980,16 @@ FileTransferInfo Room::fileTransferInfo(const QString& id) const fti.progress = int(progress); fti.total = int(total); fti.localDir = QUrl::fromLocalFile(infoIt->localFileInfo.absolutePath()); - fti.localPath = QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()); + fti.localPath = + QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()); return fti; #else - return { infoIt->status, infoIt->isUpload, int(progress), int(total), - QUrl::fromLocalFile(infoIt->localFileInfo.absolutePath()), - QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()) - }; + return { infoIt->status, + infoIt->isUpload, + int(progress), + int(total), + QUrl::fromLocalFile(infoIt->localFileInfo.absolutePath()), + QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()) }; #endif } @@ -1096,39 +1013,24 @@ QString Room::prettyPrint(const QString& plainText) const return QMatrixClient::prettyPrint(plainText); } -QList< User* > Room::usersTyping() const -{ - return d->usersTyping; -} +QList Room::usersTyping() const { return d->usersTyping; } -QList< User* > Room::membersLeft() const -{ - return d->membersLeft; -} +QList Room::membersLeft() const { return d->membersLeft; } -QList< User* > Room::users() const -{ - return d->membersMap.values(); -} +QList Room::users() const { return d->membersMap.values(); } QStringList Room::memberNames() const { QStringList res; for (auto u : qAsConst(d->membersMap)) - res.append( roomMembername(u) ); + res.append(roomMembername(u)); return res; } -int Room::memberCount() const -{ - return d->membersMap.size(); -} +int Room::memberCount() const { return d->membersMap.size(); } -int Room::timelineSize() const -{ - return int(d->timeline.size()); -} +int Room::timelineSize() const { return int(d->timeline.size()); } bool Room::usesEncryption() const { @@ -1149,31 +1051,25 @@ int Room::invitedCount() const return d->summary.invitedMemberCount.value(); } -int Room::totalMemberCount() const -{ - return joinedCount() + invitedCount(); -} +int Room::totalMemberCount() const { return joinedCount() + invitedCount(); } -GetRoomEventsJob* Room::eventsHistoryJob() const -{ - return d->eventsHistoryJob; -} +GetRoomEventsJob* Room::eventsHistoryJob() const { return d->eventsHistoryJob; } Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) { if (!summary.merge(newSummary)) return Change::NoChange; - qCDebug(MAIN).nospace().noquote() - << "Updated room summary for " << q->objectName() << ": " << summary; + qCDebug(MAIN).nospace().noquote() << "Updated room summary for " + << q->objectName() << ": " << summary; emit q->memberListChanged(); return Change::SummaryChange; } -void Room::Private::insertMemberIntoMap(User *u) +void Room::Private::insertMemberIntoMap(User* u) { const auto userName = u->name(q); - // If there is exactly one namesake of the added user, signal member renaming - // for that other one because the two should be disambiguated now. + // If there is exactly one namesake of the added user, signal member + // renaming for that other one because the two should be disambiguated now. auto namesakes = membersMap.values(userName); if (namesakes.size() == 1) emit q->memberAboutToRename(namesakes.front(), @@ -1185,14 +1081,11 @@ void Room::Private::insertMemberIntoMap(User *u) void Room::Private::renameMember(User* u, QString oldName) { - if (u->name(q) == oldName) - { + if (u->name(q) == oldName) { qCWarning(MAIN) << "Room::Private::renameMember(): the user " << u->fullName(q) << "is already known in the room under a new name."; - } - else if (membersMap.contains(oldName, u)) - { + } else if (membersMap.contains(oldName, u)) { removeMemberFromMap(oldName, u); insertMemberIntoMap(u); } @@ -1203,15 +1096,15 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) { User* namesake = nullptr; auto namesakes = membersMap.values(username); - if (namesakes.size() == 2) - { - namesake = namesakes.front() == u ? namesakes.back() : namesakes.front(); + if (namesakes.size() == 2) { + namesake = + namesakes.front() == u ? namesakes.back() : namesakes.front(); Q_ASSERT_X(namesake != u, __FUNCTION__, "Room members list is broken"); emit q->memberAboutToRename(namesake, username); } membersMap.remove(username, u); - // If there was one namesake besides the removed user, signal member renaming - // for it because it doesn't need to be disambiguated anymore. + // If there was one namesake besides the removed user, signal member + // renaming for it because it doesn't need to be disambiguated anymore. // TODO: Think about left users. if (namesake) emit q->memberRenamed(namesake); @@ -1222,27 +1115,31 @@ inline auto makeErrorStr(const Event& e, QByteArray msg) return msg.append("; event dump follows:\n").append(e.originalJson()); } -Room::Timeline::difference_type Room::Private::moveEventsToTimeline( - RoomEventsRange events, EventsPlacement placement) +Room::Timeline::difference_type +Room::Private::moveEventsToTimeline(RoomEventsRange events, + EventsPlacement placement) { Q_ASSERT(!events.empty()); // Historical messages arrive in newest-to-oldest order, so the process for // them is almost symmetric to the one for new messages. New messages get // appended from index 0; old messages go backwards from index -1. - auto index = timeline.empty() ? -((placement+1)/2) /* 1 -> -1; -1 -> 0 */ : - placement == Older ? timeline.front().index() : - timeline.back().index(); + auto index = timeline.empty() + ? -((placement + 1) / 2) /* 1 -> -1; -1 -> 0 */ + : placement == Older ? timeline.front().index() + : timeline.back().index(); auto baseIndex = index; - for (auto&& e: events) - { + for (auto&& e : events) { const auto eId = e->id(); Q_ASSERT_X(e, __FUNCTION__, "Attempt to add nullptr to timeline"); - Q_ASSERT_X(!eId.isEmpty(), __FUNCTION__, - makeErrorStr(*e, - "Event with empty id cannot be in the timeline")); - Q_ASSERT_X(!eventsIndex.contains(eId), __FUNCTION__, - makeErrorStr(*e, "Event is already in the timeline; " - "incoming events were not properly deduplicated")); + Q_ASSERT_X( + !eId.isEmpty(), __FUNCTION__, + makeErrorStr(*e, + "Event with empty id cannot be in the timeline")); + Q_ASSERT_X( + !eventsIndex.contains(eId), __FUNCTION__, + makeErrorStr(*e, + "Event is already in the timeline; " + "incoming events were not properly deduplicated")); if (placement == Older) timeline.emplace_front(move(e), --index); else @@ -1279,8 +1176,7 @@ QString Room::roomMembername(const User* u) const // (extension to the spec) QVector bridges; for (; namesakesIt != d->membersMap.cend() && namesakesIt.key() == username; - ++namesakesIt) - { + ++namesakesIt) { const auto bridgeName = (*namesakesIt)->bridged(); if (bridges.contains(bridgeName)) // Two accounts on the same bridge return u->fullName(this); // Disambiguate fully @@ -1298,60 +1194,56 @@ QString Room::roomMembername(const QString& userId) const void Room::updateData(SyncRoomData&& data, bool fromCache) { - if( d->prevBatch.isEmpty() ) + if (d->prevBatch.isEmpty()) d->prevBatch = data.timelinePrevBatch; setJoinState(data.joinState); Changes roomChanges = Change::NoChange; - QElapsedTimer et; et.start(); - for (auto&& event: data.accountData) + QElapsedTimer et; + et.start(); + for (auto&& event : data.accountData) roomChanges |= processAccountDataEvent(move(event)); roomChanges |= d->updateStateFrom(data.state); - if (!data.timeline.empty()) - { + if (!data.timeline.empty()) { et.restart(); roomChanges |= d->addNewMessageEvents(move(data.timeline)); if (data.timeline.size() > 9 || et.nsecsElapsed() >= profilerMinNsecs()) qCDebug(PROFILER) << "*** Room::addNewMessageEvents():" << data.timeline.size() << "event(s)," << et; } - if (roomChanges&TopicChange) + if (roomChanges & TopicChange) emit topicChanged(); - if (roomChanges&NameChange) + if (roomChanges & NameChange) emit namesChanged(this); - if (roomChanges&MembersChange) + if (roomChanges & MembersChange) emit memberListChanged(); roomChanges |= d->setSummary(move(data.summary)); d->updateDisplayname(); - for( auto&& ephemeralEvent: data.ephemeral ) + for (auto&& ephemeralEvent : data.ephemeral) roomChanges |= processEphemeralEvent(move(ephemeralEvent)); // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count - if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) - { + if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; d->unreadMessages = data.unreadCount; emit unreadMessagesChanged(this); } - if( data.highlightCount != d->highlightCount ) - { + if (data.highlightCount != d->highlightCount) { d->highlightCount = data.highlightCount; emit highlightCountChanged(this); } - if( data.notificationCount != d->notificationCount ) - { + if (data.notificationCount != d->notificationCount) { d->notificationCount = data.notificationCount; emit notificationCountChanged(this); } - if (roomChanges != Change::NoChange) - { + if (roomChanges != Change::NoChange) { emit changed(roomChanges); if (!fromCache) connection()->saveRoomState(this); @@ -1382,37 +1274,34 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) { const auto txnId = pEvent->transactionId(); // TODO, #133: Enqueue the job rather than immediately trigger it. - if (auto call = connection->callApi(BackgroundRequest, - id, pEvent->matrixType(), txnId, pEvent->contentJson())) - { - Room::connect(call, &BaseJob::started, q, - [this,txnId] { - auto it = q->findPendingEvent(txnId); - if (it == unsyncedEvents.end()) - { - qWarning(EVENTS) << "Pending event for transaction" << txnId - << "not found - got synced so soon?"; - return; - } - it->setDeparted(); - emit q->pendingEventChanged(it - unsyncedEvents.begin()); - }); + if (auto call = connection->callApi( + BackgroundRequest, id, pEvent->matrixType(), txnId, + pEvent->contentJson())) { + Room::connect(call, &BaseJob::started, q, [this, txnId] { + auto it = q->findPendingEvent(txnId); + if (it == unsyncedEvents.end()) { + qWarning(EVENTS) << "Pending event for transaction" << txnId + << "not found - got synced so soon?"; + return; + } + it->setDeparted(); + emit q->pendingEventChanged(it - unsyncedEvents.begin()); + }); Room::connect(call, &BaseJob::failure, q, - std::bind(&Room::Private::onEventSendingFailure, this, txnId, call)); - Room::connect(call, &BaseJob::success, q, - [this,call,txnId] { - emit q->messageSent(txnId, call->eventId()); - auto it = q->findPendingEvent(txnId); - if (it == unsyncedEvents.end()) - { - qDebug(EVENTS) << "Pending event for transaction" << txnId - << "already merged"; - return; - } + std::bind(&Room::Private::onEventSendingFailure, this, + txnId, call)); + Room::connect(call, &BaseJob::success, q, [this, call, txnId] { + emit q->messageSent(txnId, call->eventId()); + auto it = q->findPendingEvent(txnId); + if (it == unsyncedEvents.end()) { + qDebug(EVENTS) << "Pending event for transaction" << txnId + << "already merged"; + return; + } - it->setReachedServer(call->eventId()); - emit q->pendingEventChanged(it - unsyncedEvents.begin()); - }); + it->setReachedServer(call->eventId()); + emit q->pendingEventChanged(it - unsyncedEvents.begin()); + }); } else onEventSendingFailure(txnId); return txnId; @@ -1421,15 +1310,14 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) void Room::Private::onEventSendingFailure(const QString& txnId, BaseJob* call) { auto it = q->findPendingEvent(txnId); - if (it == unsyncedEvents.end()) - { + if (it == unsyncedEvents.end()) { qCritical(EVENTS) << "Pending event for transaction" << txnId << "could not be sent"; return; } - it->setSendingFailed(call - ? call->statusCaption() % ": " % call->errorString() - : tr("The call could not be started")); + it->setSendingFailed(call ? call->statusCaption() % ": " + % call->errorString() + : tr("The call could not be started")); emit q->pendingEventChanged(it - unsyncedEvents.begin()); } @@ -1439,30 +1327,29 @@ QString Room::retryMessage(const QString& txnId) Q_ASSERT(it != d->unsyncedEvents.end()); qDebug(EVENTS) << "Retrying transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); - if (transferIt != d->fileTransfers.end()) - { + if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); - if (transferIt->status == FileTransferInfo::Completed) - { + if (transferIt->status == FileTransferInfo::Completed) { qCDebug(MAIN) << "File for transaction" << txnId << "has already been uploaded, bypassing re-upload"; } else { - if (isJobRunning(transferIt->job)) - { + if (isJobRunning(transferIt->job)) { qCDebug(MAIN) << "Abandoning the upload job for transaction" << txnId << "and starting again"; transferIt->job->abandon(); - emit fileTransferFailed(txnId, tr("File upload will be retried")); + emit fileTransferFailed(txnId, + tr("File upload will be retried")); } uploadFile(txnId, - QUrl::fromLocalFile(transferIt->localFileInfo.absoluteFilePath())); + QUrl::fromLocalFile( + transferIt->localFileInfo.absoluteFilePath())); // FIXME: Content type is no more passed here but it should } } - if (it->deliveryStatus() == EventStatus::ReachedServer) - { - qCWarning(MAIN) << "The previous attempt has reached the server; two" - " events are likely to be in the timeline after retry"; + if (it->deliveryStatus() == EventStatus::ReachedServer) { + qCWarning(MAIN) + << "The previous attempt has reached the server; two" + " events are likely to be in the timeline after retry"; } it->resetStatus(); return d->doSendEvent(it->event()); @@ -1470,23 +1357,22 @@ QString Room::retryMessage(const QString& txnId) void Room::discardMessage(const QString& txnId) { - auto it = std::find_if(d->unsyncedEvents.begin(), d->unsyncedEvents.end(), - [txnId] (const auto& evt) { return evt->transactionId() == txnId; }); + auto it = std::find_if( + d->unsyncedEvents.begin(), d->unsyncedEvents.end(), + [txnId](const auto& evt) { return evt->transactionId() == txnId; }); Q_ASSERT(it != d->unsyncedEvents.end()); qDebug(EVENTS) << "Discarding transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); - if (transferIt != d->fileTransfers.end()) - { + if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); - if (isJobRunning(transferIt->job)) - { + if (isJobRunning(transferIt->job)) { transferIt->status = FileTransferInfo::Cancelled; transferIt->job->abandon(); emit fileTransferFailed(txnId, tr("File upload cancelled")); - } else if (transferIt->status == FileTransferInfo::Completed) - { - qCWarning(MAIN) << "File for transaction" << txnId - << "has been uploaded but the message was discarded"; + } else if (transferIt->status == FileTransferInfo::Completed) { + qCWarning(MAIN) + << "File for transaction" << txnId + << "has been uploaded but the message was discarded"; } } emit pendingEventAboutToDiscard(int(it - d->unsyncedEvents.begin())); @@ -1507,8 +1393,9 @@ QString Room::postPlainText(const QString& plainText) QString Room::postHtmlMessage(const QString& plainText, const QString& html, MessageEventType type) { - return d->sendEvent(plainText, type, - new EventContent::TextContent(html, QStringLiteral("text/html"))); + return d->sendEvent( + plainText, type, + new EventContent::TextContent(html, QStringLiteral("text/html"))); } QString Room::postHtmlText(const QString& plainText, const QString& html) @@ -1523,58 +1410,58 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, Q_ASSERT(localFile.isFile()); // Remote URL will only be known after upload; fill in the local path // to enable the preview while the event is pending. - const auto txnId = d->addAsPending(makeEvent( - plainText, localFile, asGenericFile) - )->transactionId(); + const auto txnId = + d->addAsPending(makeEvent(plainText, localFile, + asGenericFile)) + ->transactionId(); uploadFile(txnId, localPath); auto* context = new QObject(this); connect(this, &Room::fileTransferCompleted, context, - [context,this,txnId] (const QString& id, QUrl, const QUrl& mxcUri) { - if (id == txnId) - { - auto it = findPendingEvent(txnId); - if (it != d->unsyncedEvents.end()) - { - it->setFileUploaded(mxcUri); - emit pendingEventChanged( + [context, this, txnId](const QString& id, QUrl, + const QUrl& mxcUri) { + if (id == txnId) { + auto it = findPendingEvent(txnId); + if (it != d->unsyncedEvents.end()) { + it->setFileUploaded(mxcUri); + emit pendingEventChanged( int(it - d->unsyncedEvents.begin())); - d->doSendEvent(it->get()); - } else { - // Normally in this situation we should instruct - // the media server to delete the file; alas, there's no - // API specced for that. - qCWarning(MAIN) << "File uploaded to" << mxcUri - << "but the event referring to it was cancelled"; + d->doSendEvent(it->get()); + } else { + // Normally in this situation we should instruct + // the media server to delete the file; alas, there's no + // API specced for that. + qCWarning(MAIN) << "File uploaded to" << mxcUri + << "but the event referring to it was " + "cancelled"; + } + context->deleteLater(); } - context->deleteLater(); - } - }); + }); connect(this, &Room::fileTransferCancelled, this, - [context,this,txnId] (const QString& id) { - if (id == txnId) - { - auto it = findPendingEvent(txnId); - if (it != d->unsyncedEvents.end()) - { - const auto idx = int(it - d->unsyncedEvents.begin()); - emit pendingEventAboutToDiscard(idx); - // See #286 on why iterator may not be valid here. - d->unsyncedEvents.erase(d->unsyncedEvents.begin() + idx); - emit pendingEventDiscarded(); + [context, this, txnId](const QString& id) { + if (id == txnId) { + auto it = findPendingEvent(txnId); + if (it != d->unsyncedEvents.end()) { + const auto idx = int(it - d->unsyncedEvents.begin()); + emit pendingEventAboutToDiscard(idx); + // See #286 on why iterator may not be valid here. + d->unsyncedEvents.erase(d->unsyncedEvents.begin() + + idx); + emit pendingEventDiscarded(); + } + context->deleteLater(); } - context->deleteLater(); - } - }); + }); return txnId; } QString Room::postEvent(RoomEvent* event) { - if (usesEncryption()) - { + if (usesEncryption()) { qCCritical(MAIN) << "Room" << displayName() - << "enforces encryption; sending encrypted messages is not supported yet"; + << "enforces encryption; sending encrypted messages " + "is not supported yet"; } return d->sendEvent(RoomEventPtr(event)); } @@ -1582,7 +1469,8 @@ QString Room::postEvent(RoomEvent* event) QString Room::postJson(const QString& matrixType, const QJsonObject& eventContent) { - return d->sendEvent(loadEvent(basicEventJson(matrixType, eventContent))); + return d->sendEvent( + loadEvent(basicEventJson(matrixType, eventContent))); } void Room::setName(const QString& newName) @@ -1625,10 +1513,7 @@ bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re) return le->contentJson() == re->contentJson(); } -bool Room::supportsCalls() const -{ - return joinedCount() == 2; -} +bool Room::supportsCalls() const { return joinedCount() == 2; } void Room::checkVersion() { @@ -1638,8 +1523,7 @@ void Room::checkVersion() // This method is only called after the base state has been loaded // or the server capabilities have been loaded. emit stabilityUpdated(defaultVersion, stableVersions); - if (!stableVersions.contains(version())) - { + if (!stableVersions.contains(version())) { qCDebug(MAIN) << this << "version is" << version() << "which the server doesn't count as stable"; if (canSwitchVersions()) @@ -1680,25 +1564,22 @@ void Room::hangupCall(const QString& callId) postEvent(new CallHangupEvent(callId)); } -void Room::getPreviousContent(int limit) -{ - d->getPreviousContent(limit); -} +void Room::getPreviousContent(int limit) { d->getPreviousContent(limit); } void Room::Private::getPreviousContent(int limit) { if (isJobRunning(eventsHistoryJob)) return; - eventsHistoryJob = - connection->callApi(id, prevBatch, "b", "", limit); + eventsHistoryJob = connection->callApi(id, prevBatch, "b", + "", limit); emit q->eventsHistoryJobChanged(); - connect( eventsHistoryJob, &BaseJob::success, q, [=] { + connect(eventsHistoryJob, &BaseJob::success, q, [=] { prevBatch = eventsHistoryJob->end(); addHistoricalMessageEvents(eventsHistoryJob->chunk()); }); - connect( eventsHistoryJob, &QObject::destroyed, - q, &Room::eventsHistoryJobChanged); + connect(eventsHistoryJob, &QObject::destroyed, q, + &Room::eventsHistoryJobChanged); } void Room::inviteToRoom(const QString& memberId) @@ -1712,7 +1593,8 @@ LeaveRoomJob* Room::leaveRoom() return connection()->leaveRoom(this); } -SetRoomStateWithKeyJob*Room::setMemberState(const QString& memberId, const RoomMemberEvent& event) const +SetRoomStateWithKeyJob* Room::setMemberState(const QString& memberId, + const RoomMemberEvent& event) const { return d->requestSetState(memberId, event); } @@ -1735,7 +1617,7 @@ void Room::unban(const QString& userId) void Room::redactEvent(const QString& eventId, const QString& reason) { connection()->callApi( - id(), eventId, connection()->generateTxnId(), reason); + id(), eventId, connection()->generateTxnId(), reason); } void Room::uploadFile(const QString& id, const QUrl& localFilename, @@ -1745,18 +1627,17 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, "localFilename should point at a local file"); auto fileName = localFilename.toLocalFile(); auto job = connection()->uploadFile(fileName, overrideContentType); - if (isJobRunning(job)) - { + if (isJobRunning(job)) { d->fileTransfers.insert(id, { job, fileName, true }); connect(job, &BaseJob::uploadProgress, this, - [this,id] (qint64 sent, qint64 total) { + [this, id](qint64 sent, qint64 total) { d->fileTransfers[id].update(sent, total); emit fileTransferProgress(id, sent, total); }); - connect(job, &BaseJob::success, this, [this,id,localFilename,job] { - d->fileTransfers[id].status = FileTransferInfo::Completed; - emit fileTransferCompleted(id, localFilename, job->contentUri()); - }); + connect(job, &BaseJob::success, this, [this, id, localFilename, job] { + d->fileTransfers[id].status = FileTransferInfo::Completed; + emit fileTransferCompleted(id, localFilename, job->contentUri()); + }); connect(job, &BaseJob::failure, this, std::bind(&Private::failedTransfer, d, id, job->errorString())); emit newFileTransfer(id, localFilename); @@ -1767,9 +1648,8 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, void Room::downloadFile(const QString& eventId, const QUrl& localFilename) { auto ongoingTransfer = d->fileTransfers.find(eventId); - if (ongoingTransfer != d->fileTransfers.end() && - ongoingTransfer->status == FileTransferInfo::Started) - { + if (ongoingTransfer != d->fileTransfers.end() + && ongoingTransfer->status == FileTransferInfo::Started) { qCWarning(MAIN) << "Transfer for" << eventId << "is ongoing; download won't start"; return; @@ -1778,42 +1658,41 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) Q_ASSERT_X(localFilename.isEmpty() || localFilename.isLocalFile(), __FUNCTION__, "localFilename should point at a local file"); const auto* event = d->getEventWithFile(eventId); - if (!event) - { + if (!event) { qCCritical(MAIN) - << eventId << "is not in the local timeline or has no file content"; + << eventId + << "is not in the local timeline or has no file content"; Q_ASSERT(false); return; } const auto fileUrl = event->content()->fileInfo()->url; auto filePath = localFilename.toLocalFile(); - if (filePath.isEmpty()) - { + if (filePath.isEmpty()) { // Build our own file path, starting with temp directory and eventId. filePath = eventId; - filePath = QDir::tempPath() % '/' % - filePath.replace(QRegularExpression("[/\\<>|\"*?:]"), "_") % - '#' % d->fileNameToDownload(event); + filePath = QDir::tempPath() % '/' + % filePath.replace(QRegularExpression("[/\\<>|\"*?:]"), "_") + % '#' % d->fileNameToDownload(event); } auto job = connection()->downloadFile(fileUrl, filePath); - if (isJobRunning(job)) - { + if (isJobRunning(job)) { // If there was a previous transfer (completed or failed), remove it. d->fileTransfers.remove(eventId); d->fileTransfers.insert(eventId, { job, job->targetFileName() }); connect(job, &BaseJob::downloadProgress, this, - [this,eventId] (qint64 received, qint64 total) { - d->fileTransfers[eventId].update(received, total); - emit fileTransferProgress(eventId, received, total); - }); - connect(job, &BaseJob::success, this, [this,eventId,fileUrl,job] { - d->fileTransfers[eventId].status = FileTransferInfo::Completed; - emit fileTransferCompleted(eventId, fileUrl, - QUrl::fromLocalFile(job->targetFileName())); - }); + [this, eventId](qint64 received, qint64 total) { + d->fileTransfers[eventId].update(received, total); + emit fileTransferProgress(eventId, received, total); + }); + connect(job, &BaseJob::success, this, [this, eventId, fileUrl, job] { + d->fileTransfers[eventId].status = FileTransferInfo::Completed; + emit fileTransferCompleted( + eventId, fileUrl, + QUrl::fromLocalFile(job->targetFileName())); + }); connect(job, &BaseJob::failure, this, - std::bind(&Private::failedTransfer, d, - eventId, job->errorString())); + std::bind(&Private::failedTransfer, d, eventId, + job->errorString())); } else d->failedTransfer(eventId); } @@ -1821,10 +1700,9 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) void Room::cancelFileTransfer(const QString& id) { auto it = d->fileTransfers.find(id); - if (it == d->fileTransfers.end()) - { - qCWarning(MAIN) << "No information on file transfer" << id - << "in room" << d->id; + if (it == d->fileTransfers.end()) { + qCWarning(MAIN) << "No information on file transfer" << id << "in room" + << d->id; return; } if (isJobRunning(it->job)) @@ -1840,15 +1718,16 @@ void Room::Private::dropDuplicateEvents(RoomEvents& events) const // Multiple-remove (by different criteria), single-erase // 1. Check for duplicates against the timeline. - auto dupsBegin = remove_if(events.begin(), events.end(), - [&] (const RoomEventPtr& e) - { return eventsIndex.contains(e->id()); }); + auto dupsBegin = + remove_if(events.begin(), events.end(), [&](const RoomEventPtr& e) { + return eventsIndex.contains(e->id()); + }); // 2. Check for duplicates within the batch if there are still events. for (auto eIt = events.begin(); distance(eIt, dupsBegin) > 1; ++eIt) - dupsBegin = remove_if(eIt + 1, dupsBegin, - [&] (const RoomEventPtr& e) - { return e->id() == (*eIt)->id(); }); + dupsBegin = remove_if(eIt + 1, dupsBegin, [&](const RoomEventPtr& e) { + return e->id() == (*eIt)->id(); + }); if (dupsBegin == events.end()) return; @@ -1867,48 +1746,54 @@ RoomEventPtr makeRedacted(const RoomEvent& target, const RedactionEvent& redaction) { auto originalJson = target.originalJsonObject(); - static const QStringList keepKeys { - EventIdKey, TypeKey, QStringLiteral("room_id"), - QStringLiteral("sender"), QStringLiteral("state_key"), - QStringLiteral("prev_content"), ContentKey, - QStringLiteral("hashes"), QStringLiteral("signatures"), - QStringLiteral("depth"), QStringLiteral("prev_events"), - QStringLiteral("prev_state"), QStringLiteral("auth_events"), - QStringLiteral("origin"), QStringLiteral("origin_server_ts"), - QStringLiteral("membership") + static const QStringList keepKeys { EventIdKey, + TypeKey, + QStringLiteral("room_id"), + QStringLiteral("sender"), + QStringLiteral("state_key"), + QStringLiteral("prev_content"), + ContentKey, + QStringLiteral("hashes"), + QStringLiteral("signatures"), + QStringLiteral("depth"), + QStringLiteral("prev_events"), + QStringLiteral("prev_state"), + QStringLiteral("auth_events"), + QStringLiteral("origin"), + QStringLiteral("origin_server_ts"), + QStringLiteral("membership") }; + + std::vector> keepContentKeysMap { + { RoomMemberEvent::typeId(), { QStringLiteral("membership") } }, + { RoomCreateEvent::typeId(), { QStringLiteral("creator") } } + // , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } } + // , { RoomPowerLevels::typeId(), + // { QStringLiteral("ban"), QStringLiteral("events"), + // QStringLiteral("events_default"), + // QStringLiteral("kick"), QStringLiteral("redact"), + // QStringLiteral("state_default"), + // QStringLiteral("users"), QStringLiteral("users_default") + // } } + , + { RoomAliasesEvent::typeId(), { QStringLiteral("aliases") } } + // , { RoomHistoryVisibility::typeId(), + // { QStringLiteral("history_visibility") } } }; - - std::vector> keepContentKeysMap - { { RoomMemberEvent::typeId(), { QStringLiteral("membership") } } - , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } } -// , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } } -// , { RoomPowerLevels::typeId(), -// { QStringLiteral("ban"), QStringLiteral("events"), -// QStringLiteral("events_default"), QStringLiteral("kick"), -// QStringLiteral("redact"), QStringLiteral("state_default"), -// QStringLiteral("users"), QStringLiteral("users_default") } } - , { RoomAliasesEvent::typeId(), { QStringLiteral("aliases") } } -// , { RoomHistoryVisibility::typeId(), -// { QStringLiteral("history_visibility") } } - }; - for (auto it = originalJson.begin(); it != originalJson.end();) - { + for (auto it = originalJson.begin(); it != originalJson.end();) { if (!keepKeys.contains(it.key())) it = originalJson.erase(it); // TODO: shred the value else ++it; } - auto keepContentKeys = - find_if(keepContentKeysMap.begin(), keepContentKeysMap.end(), - [&target](const auto& t) { return target.type() == t.first; } ); - if (keepContentKeys == keepContentKeysMap.end()) - { + auto keepContentKeys = find_if( + keepContentKeysMap.begin(), keepContentKeysMap.end(), + [&target](const auto& t) { return target.type() == t.first; }); + if (keepContentKeys == keepContentKeysMap.end()) { originalJson.remove(ContentKeyL); originalJson.remove(PrevContentKeyL); } else { auto content = originalJson.take(ContentKeyL).toObject(); - for (auto it = content.begin(); it != content.end(); ) - { + for (auto it = content.begin(); it != content.end();) { if (!keepContentKeys->second.contains(it.key())) it = content.erase(it); else @@ -1934,10 +1819,9 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) Q_ASSERT(q->isValidIndex(*pIdx)); auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; - if (ti->isRedacted() && ti->redactedBecause()->id() == redaction.id()) - { - qCDebug(MAIN) << "Redaction" << redaction.id() - << "of event" << ti->id() << "already done, skipping"; + if (ti->isRedacted() && ti->redactedBecause()->id() == redaction.id()) { + qCDebug(MAIN) << "Redaction" << redaction.id() << "of event" << ti->id() + << "already done, skipping"; return true; } @@ -1945,15 +1829,16 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeRedacted(*ti, redaction)); qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction.id(); - if (oldEvent->isStateEvent()) - { - const StateEventKey evtKey { oldEvent->matrixType(), oldEvent->stateKey() }; + if (oldEvent->isStateEvent()) { + const StateEventKey evtKey { oldEvent->matrixType(), + oldEvent->stateKey() }; Q_ASSERT(currentState.contains(evtKey)); - if (currentState[evtKey] == oldEvent.get()) - { - Q_ASSERT(ti.index() >= 0); // Historical states can't be in currentState - qCDebug(MAIN).nospace() << "Reverting state " - << oldEvent->matrixType() << "/" << oldEvent->stateKey(); + if (currentState[evtKey] == oldEvent.get()) { + Q_ASSERT(ti.index() + >= 0); // Historical states can't be in currentState + qCDebug(MAIN).nospace() + << "Reverting state " << oldEvent->matrixType() << "/" + << oldEvent->stateKey(); // Retarget the current state to the newly made event. if (q->processStateEvent(*ti)) emit q->namesChanged(q); @@ -1971,10 +1856,7 @@ Connection* Room::connection() const return d->connection; } -User* Room::localUser() const -{ - return connection()->user(); -} +User* Room::localUser() const { return connection()->user(); } inline bool isRedaction(const RoomEventPtr& ep) { @@ -1992,22 +1874,22 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // batch landed in the timeline already redacted. // NB: We have to store redaction events to the timeline too - see #220. auto redactionIt = std::find_if(events.begin(), events.end(), isRedaction); - for(const auto& eptr: RoomEventsRange(redactionIt, events.end())) - if (auto* r = eventCast(eptr)) - { + for (const auto& eptr : RoomEventsRange(redactionIt, events.end())) + if (auto* r = eventCast(eptr)) { // Try to find the target in the timeline, then in the batch. if (processRedaction(*r)) continue; - auto targetIt = std::find_if(events.begin(), redactionIt, - [id=r->redactedEvent()] (const RoomEventPtr& ep) { - return ep->id() == id; - }); + auto targetIt = std::find_if( + events.begin(), redactionIt, + [id = r->redactedEvent()](const RoomEventPtr& ep) { + return ep->id() == id; + }); if (targetIt != redactionIt) *targetIt = makeRedacted(**targetIt, *r); else - qCDebug(MAIN) << "Redaction" << r->id() - << "ignored: target event" << r->redactedEvent() - << "is not found"; + qCDebug(MAIN) + << "Redaction" << r->id() << "ignored: target event" + << r->redactedEvent() << "is not found"; // If the target event comes later, it comes already redacted. } @@ -2017,26 +1899,26 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // postulate that the current state is only current between syncs but not // within a sync. Changes roomChanges = Change::NoChange; - for (const auto& eptr: events) + for (const auto& eptr : events) roomChanges |= q->processStateEvent(*eptr); auto timelineSize = timeline.size(); auto totalInserted = 0; - for (auto it = events.begin(); it != events.end();) - { - auto nextPendingPair = findFirstOf(it, events.end(), - unsyncedEvents.begin(), unsyncedEvents.end(), isEchoEvent); + for (auto it = events.begin(); it != events.end();) { + auto nextPendingPair = + findFirstOf(it, events.end(), unsyncedEvents.begin(), + unsyncedEvents.end(), isEchoEvent); auto nextPending = nextPendingPair.first; - if (it != nextPending) - { + if (it != nextPending) { RoomEventsRange eventsSpan { it, nextPending }; emit q->aboutToAddNewMessages(eventsSpan); auto insertedSize = moveEventsToTimeline(eventsSpan, Newer); totalInserted += insertedSize; auto firstInserted = timeline.cend() - insertedSize; q->onAddNewTimelineEvents(firstInserted); - emit q->addedMessages(firstInserted->index(), timeline.back().index()); + emit q->addedMessages(firstInserted->index(), + timeline.back().index()); } if (nextPending == events.end()) break; @@ -2058,8 +1940,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // unsyncedEvents (see #286). Fortunately, unsyncedEvents only grows at // its back so we can rely on the index staying valid at least. unsyncedEvents.erase(unsyncedEvents.begin() + pendingEvtIdx); - if (auto insertedSize = moveEventsToTimeline({nextPending, it}, Newer)) - { + if (auto insertedSize = + moveEventsToTimeline({ nextPending, it }, Newer)) { totalInserted += insertedSize; q->onAddNewTimelineEvents(timeline.cend() - insertedSize); } @@ -2073,11 +1955,10 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (auto* evt = it->viewAs()) emit q->callEvent(q, evt); - if (totalInserted > 0) - { - qCDebug(MAIN) - << "Room" << q->objectName() << "received" << totalInserted - << "new events; the last event is now" << timeline.back(); + if (totalInserted > 0) { + qCDebug(MAIN) << "Room" << q->objectName() << "received" + << totalInserted << "new events; the last event is now" + << timeline.back(); // The first event in the just-added batch (referred to by `from`) // defines whose read marker can possibly be promoted any further over @@ -2086,11 +1967,11 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // markMessagesAsRead() invocation) to promote their read markers over // the new message events. auto firstWriter = q->user((*from)->senderId()); - if (q->readMarker(firstWriter) != timeline.crend()) - { + if (q->readMarker(firstWriter) != timeline.crend()) { roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() - << "to" << *q->readMarker(firstWriter); + qCDebug(MAIN) << "Auto-promoted read marker for" + << firstWriter->id() << "to" + << *q->readMarker(firstWriter); } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); @@ -2103,7 +1984,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) { - QElapsedTimer et; et.start(); + QElapsedTimer et; + et.start(); const auto timelineSize = timeline.size(); dropDuplicateEvents(events); @@ -2114,12 +1996,10 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) // messages. Also, the cache doesn't store events with empty content; // so when such events show up in the timeline they should be properly // incorporated. - for (const auto& eptr: events) - { + for (const auto& eptr : events) { const auto& e = *eptr; - if (e.isStateEvent() && - !currentState.contains({e.matrixType(), e.stateKey()})) - { + if (e.isStateEvent() + && !currentState.contains({ e.matrixType(), e.stateKey() })) { q->processStateEvent(e); } } @@ -2147,167 +2027,157 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!e.isStateEvent()) return Change::NoChange; - const auto* oldStateEvent = std::exchange( - d->currentState[{e.matrixType(),e.stateKey()}], - static_cast(&e)); - Q_ASSERT(!oldStateEvent || - (oldStateEvent->matrixType() == e.matrixType() && - oldStateEvent->stateKey() == e.stateKey())); + const auto* oldStateEvent = + std::exchange(d->currentState[{ e.matrixType(), e.stateKey() }], + static_cast(&e)); + Q_ASSERT(!oldStateEvent + || (oldStateEvent->matrixType() == e.matrixType() + && oldStateEvent->stateKey() == e.stateKey())); if (!is(e)) qCDebug(EVENTS) << "Room state event:" << e; - return visit(e - , [] (const RoomNameEvent&) { - return NameChange; - } - , [this,oldStateEvent] (const RoomAliasesEvent& ae) { - const auto previousAliases = oldStateEvent - ? static_cast(oldStateEvent)->aliases() - : QStringList(); - connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); - return OtherChange; - } - , [this] (const RoomCanonicalAliasEvent& evt) { - setObjectName(evt.alias().isEmpty() ? d->id : evt.alias()); - return CanonicalAliasChange; - } - , [] (const RoomTopicEvent&) { - return TopicChange; - } - , [this] (const RoomAvatarEvent& evt) { - if (d->avatar.updateUrl(evt.url())) - emit avatarChanged(); - return AvatarChange; - } - , [this] (const RoomMemberEvent& evt) { - auto* u = user(evt.userId()); - u->processEvent(evt, this); - if (u == localUser() && memberJoinState(u) == JoinState::Invite + return visit( + e, [](const RoomNameEvent&) { return NameChange; }, + [this, oldStateEvent](const RoomAliasesEvent& ae) { + const auto previousAliases = oldStateEvent + ? static_cast(oldStateEvent) + ->aliases() + : QStringList(); + connection()->updateRoomAliases(id(), previousAliases, + ae.aliases()); + return OtherChange; + }, + [this](const RoomCanonicalAliasEvent& evt) { + setObjectName(evt.alias().isEmpty() ? d->id : evt.alias()); + return CanonicalAliasChange; + }, + [](const RoomTopicEvent&) { return TopicChange; }, + [this](const RoomAvatarEvent& evt) { + if (d->avatar.updateUrl(evt.url())) + emit avatarChanged(); + return AvatarChange; + }, + [this](const RoomMemberEvent& evt) { + auto* u = user(evt.userId()); + u->processEvent(evt, this); + if (u == localUser() && memberJoinState(u) == JoinState::Invite && evt.isDirect()) - connection()->addToDirectChats(this, user(evt.senderId())); - - if( evt.membership() == MembershipType::Join ) - { - if (memberJoinState(u) != JoinState::Join) - { - d->insertMemberIntoMap(u); - connect(u, &User::nameAboutToChange, this, - [=] (QString newName, QString, const Room* context) { - if (context == this) - emit memberAboutToRename(u, newName); - }); - connect(u, &User::nameChanged, this, - [=] (QString, QString oldName, const Room* context) { - if (context == this) - d->renameMember(u, oldName); - }); - emit userAdded(u); - } - } - else if( evt.membership() != MembershipType::Join ) - { - if (memberJoinState(u) == JoinState::Join) - { - if (evt.membership() == MembershipType::Invite) - qCWarning(MAIN) << "Invalid membership change:" << evt; - if (!d->membersLeft.contains(u)) - d->membersLeft.append(u); - d->removeMemberFromMap(u->name(this), u); - emit userRemoved(u); + connection()->addToDirectChats(this, user(evt.senderId())); + + if (evt.membership() == MembershipType::Join) { + if (memberJoinState(u) != JoinState::Join) { + d->insertMemberIntoMap(u); + connect(u, &User::nameAboutToChange, this, + [=](QString newName, QString, + const Room* context) { + if (context == this) + emit memberAboutToRename(u, newName); + }); + connect(u, &User::nameChanged, this, + [=](QString, QString oldName, + const Room* context) { + if (context == this) + d->renameMember(u, oldName); + }); + emit userAdded(u); + } + } else if (evt.membership() != MembershipType::Join) { + if (memberJoinState(u) == JoinState::Join) { + if (evt.membership() == MembershipType::Invite) + qCWarning(MAIN) + << "Invalid membership change:" << evt; + if (!d->membersLeft.contains(u)) + d->membersLeft.append(u); + d->removeMemberFromMap(u->name(this), u); + emit userRemoved(u); + } } - } - return MembersChange; - } - , [this] (const EncryptionEvent&) { - emit encryption(); // It can only be done once, so emit it here. - return OtherChange; - } - , [this] (const RoomTombstoneEvent& evt) { - const auto successorId = evt.successorRoomId(); - if (auto* successor = connection()->room(successorId)) - emit upgraded(evt.serverMessage(), successor); - else - connectUntil(connection(), &Connection::loadedRoomState, this, - [this,successorId,serverMsg=evt.serverMessage()] - (Room* newRoom) { - if (newRoom->id() != successorId) - return false; - emit upgraded(serverMsg, newRoom); - return true; - }); - - return OtherChange; - } - ); + return MembersChange; + }, + [this](const EncryptionEvent&) { + emit encryption(); // It can only be done once, so emit it here. + return OtherChange; + }, + [this](const RoomTombstoneEvent& evt) { + const auto successorId = evt.successorRoomId(); + if (auto* successor = connection()->room(successorId)) + emit upgraded(evt.serverMessage(), successor); + else + connectUntil( + connection(), &Connection::loadedRoomState, this, + [this, successorId, + serverMsg = evt.serverMessage()](Room* newRoom) { + if (newRoom->id() != successorId) + return false; + emit upgraded(serverMsg, newRoom); + return true; + }); + + return OtherChange; + }); } Room::Changes Room::processEphemeralEvent(EventPtr&& event) { Changes changes = NoChange; - QElapsedTimer et; et.start(); - if (auto* evt = eventCast(event)) - { + QElapsedTimer et; + et.start(); + if (auto* evt = eventCast(event)) { d->usersTyping.clear(); - for( const QString& userId: qAsConst(evt->users()) ) - { + for (const QString& userId : qAsConst(evt->users())) { auto u = user(userId); if (memberJoinState(u) == JoinState::Join) d->usersTyping.append(u); } if (evt->users().size() > 3 || et.nsecsElapsed() >= profilerMinNsecs()) qCDebug(PROFILER) << "*** Room::processEphemeralEvent(typing):" - << evt->users().size() << "users," << et; + << evt->users().size() << "users," << et; emit typingChanged(); } - if (auto* evt = eventCast(event)) - { + if (auto* evt = eventCast(event)) { int totalReceipts = 0; - for( const auto &p: qAsConst(evt->eventsWithReceipts()) ) - { + for (const auto& p : qAsConst(evt->eventsWithReceipts())) { totalReceipts += p.receipts.size(); { if (p.receipts.size() == 1) - qCDebug(EPHEMERAL) << "Marking" << p.evtId - << "as read for" << p.receipts[0].userId; + qCDebug(EPHEMERAL) << "Marking" << p.evtId << "as read for" + << p.receipts[0].userId; else - qCDebug(EPHEMERAL) << "Marking" << p.evtId - << "as read for" << p.receipts.size() << "users"; + qCDebug(EPHEMERAL) << "Marking" << p.evtId << "as read for" + << p.receipts.size() << "users"; } const auto newMarker = findInTimeline(p.evtId); - if (newMarker != timelineEdge()) - { - for( const Receipt& r: p.receipts ) - { + if (newMarker != timelineEdge()) { + for (const Receipt& r : p.receipts) { if (r.userId == connection()->userId()) continue; // FIXME, #185 auto u = user(r.userId); if (memberJoinState(u) == JoinState::Join) changes |= d->promoteReadMarker(u, newMarker); } - } else - { + } else { qCDebug(EPHEMERAL) << "Event" << p.evtId - << "not found; saving read receipts anyway"; + << "not found; saving read receipts anyway"; // If the event is not found (most likely, because it's too old // and hasn't been fetched from the server yet), but there is // a previous marker for a user, keep the previous marker. // Otherwise, blindly store the event id for this user. - for( const Receipt& r: p.receipts ) - { + for (const Receipt& r : p.receipts) { if (r.userId == connection()->userId()) continue; // FIXME, #185 auto u = user(r.userId); - if (memberJoinState(u) == JoinState::Join && - readMarker(u) == timelineEdge()) + if (memberJoinState(u) == JoinState::Join + && readMarker(u) == timelineEdge()) changes |= d->setLastReadEvent(u, p.evtId); } } } - if (evt->eventsWithReceipts().size() > 3 || totalReceipts > 10 || - et.nsecsElapsed() >= profilerMinNsecs()) - qCDebug(PROFILER) << "*** Room::processEphemeralEvent(receipts):" - << evt->eventsWithReceipts().size() - << "event(s) with" << totalReceipts << "receipt(s)," << et; + if (evt->eventsWithReceipts().size() > 3 || totalReceipts > 10 + || et.nsecsElapsed() >= profilerMinNsecs()) + qCDebug(PROFILER) + << "*** Room::processEphemeralEvent(receipts):" + << evt->eventsWithReceipts().size() << "event(s) with" + << totalReceipts << "receipt(s)," << et; } return changes; } @@ -2315,28 +2185,25 @@ Room::Changes Room::processEphemeralEvent(EventPtr&& event) Room::Changes Room::processAccountDataEvent(EventPtr&& event) { Changes changes = NoChange; - if (auto* evt = eventCast(event)) - { + if (auto* evt = eventCast(event)) { d->setTags(evt->tags()); changes |= Change::TagsChange; } - if (auto* evt = eventCast(event)) - { + if (auto* evt = eventCast(event)) { auto readEventId = evt->event_id(); qCDebug(MAIN) << "Server-side read marker at" << readEventId; d->serverReadMarker = readEventId; const auto newMarker = findInTimeline(readEventId); changes |= newMarker != timelineEdge() - ? d->markMessagesAsRead(newMarker) - : d->setLastReadEvent(localUser(), readEventId); + ? d->markMessagesAsRead(newMarker) + : d->setLastReadEvent(localUser(), readEventId); } // For all account data events auto& currentData = d->accountData[event->matrixType()]; // A polymorphic event-specific comparison might be a bit more // efficient; maaybe do it another day - if (!currentData || currentData->contentJson() != event->contentJson()) - { + if (!currentData || currentData->contentJson() != event->contentJson()) { emit accountDataAboutToChange(event->matrixType()); currentData = move(event); qCDebug(MAIN) << "Updated account data of type" @@ -2356,15 +2223,14 @@ Room::Private::buildShortlist(const ContT& users) const // display names of two topmost users excluding the current one to render // the name of the room. The below code selects 3 topmost users, // slightly extending the spec. - users_shortlist_t shortlist { }; // Prefill with nullptrs + users_shortlist_t shortlist {}; // Prefill with nullptrs std::partial_sort_copy( - users.begin(), users.end(), - shortlist.begin(), shortlist.end(), - [this] (const User* u1, const User* u2) { - // localUser(), if it's in the list, is sorted below all others - return isLocalUser(u2) || (!isLocalUser(u1) && u1->id() < u2->id()); - } - ); + users.begin(), users.end(), shortlist.begin(), shortlist.end(), + [this](const User* u1, const User* u2) { + // localUser(), if it's in the list, is sorted below all others + return isLocalUser(u2) + || (!isLocalUser(u1) && u1->id() < u2->id()); + }); return shortlist; } @@ -2373,7 +2239,7 @@ Room::Private::buildShortlist(const QStringList& userIds) const { QList users; users.reserve(userIds.size()); - for (const auto& h: userIds) + for (const auto& h : userIds) users.push_back(q->user(h)); return buildShortlist(users); } @@ -2395,23 +2261,22 @@ QString Room::Private::calculateDisplayname() const return dispName; // Using m.room.aliases in naming is explicitly discouraged by the spec - //if (!q->aliases().empty() && !q->aliases().at(0).isEmpty()) + // if (!q->aliases().empty() && !q->aliases().at(0).isEmpty()) // return q->aliases().at(0); // Supplementary code for 3 and 4: build the shortlist of users whose names // will be used to construct the room name. Takes into account MSC688's // "heroes" if available. - const bool emptyRoom = membersMap.isEmpty() || - (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); - const auto shortlist = - !summary.heroes.omitted() ? buildShortlist(summary.heroes.value()) : - !emptyRoom ? buildShortlist(membersMap) : - buildShortlist(membersLeft); + const bool emptyRoom = membersMap.isEmpty() + || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); + const auto shortlist = !summary.heroes.omitted() + ? buildShortlist(summary.heroes.value()) + : !emptyRoom ? buildShortlist(membersMap) + : buildShortlist(membersLeft); QStringList names; - for (auto u: shortlist) - { + for (auto u : shortlist) { if (u == nullptr || isLocalUser(u)) break; names.push_back(q->roomMembername(u)); @@ -2421,10 +2286,10 @@ QString Room::Private::calculateDisplayname() const ? membersLeft.size() - int(joinState == JoinState::Leave) : q->joinedCount() - int(joinState == JoinState::Join); if (usersCountExceptLocal > int(shortlist.size())) - names << - tr("%Ln other(s)", - "Used to make a room name from user names: A, B and _N others_", - usersCountExceptLocal); + names << tr( + "%Ln other(s)", + "Used to make a room name from user names: A, B and _N others_", + usersCountExceptLocal); auto namesList = QLocale().createSeparatedList(names); // 3. Room members @@ -2442,8 +2307,7 @@ QString Room::Private::calculateDisplayname() const void Room::Private::updateDisplayname() { auto swappedName = calculateDisplayname(); - if (swappedName != displayname) - { + if (swappedName != displayname) { emit q->displaynameAboutToChange(q); swap(displayname, swappedName); qDebug(MAIN) << q->objectName() << "has changed display name from" @@ -2454,17 +2318,17 @@ void Room::Private::updateDisplayname() QJsonObject Room::Private::toJson() const { - QElapsedTimer et; et.start(); + QElapsedTimer et; + et.start(); QJsonObject result; addParam(result, QStringLiteral("summary"), summary); { QJsonArray stateEvents; - for (const auto* evt: currentState) - { + for (const auto* evt : currentState) { Q_ASSERT(evt->isStateEvent()); - if ((evt->isRedacted() && !is(*evt)) || - evt->contentJson().isEmpty()) + if ((evt->isRedacted() && !is(*evt)) + || evt->contentJson().isEmpty()) continue; auto json = evt->fullJson(); @@ -2474,56 +2338,54 @@ QJsonObject Room::Private::toJson() const stateEvents.append(json); } - const auto stateObjName = joinState == JoinState::Invite ? - QStringLiteral("invite_state") : QStringLiteral("state"); - result.insert(stateObjName, - QJsonObject {{ QStringLiteral("events"), stateEvents }}); + const auto stateObjName = joinState == JoinState::Invite + ? QStringLiteral("invite_state") + : QStringLiteral("state"); + result.insert( + stateObjName, + QJsonObject { { QStringLiteral("events"), stateEvents } }); } - if (!accountData.empty()) - { + if (!accountData.empty()) { QJsonArray accountDataEvents; - for (const auto& e: accountData) - { + for (const auto& e : accountData) { if (!e.second->contentJson().isEmpty()) accountDataEvents.append(e.second->fullJson()); } result.insert(QStringLiteral("account_data"), - QJsonObject {{ QStringLiteral("events"), accountDataEvents }}); + QJsonObject { { QStringLiteral("events"), + accountDataEvents } }); } - QJsonObject unreadNotifObj - { { SyncRoomData::UnreadCountKey, unreadMessages } }; + QJsonObject unreadNotifObj { { SyncRoomData::UnreadCountKey, + unreadMessages } }; if (highlightCount > 0) - unreadNotifObj.insert(QStringLiteral("highlight_count"), highlightCount); + unreadNotifObj.insert(QStringLiteral("highlight_count"), + highlightCount); if (notificationCount > 0) - unreadNotifObj.insert(QStringLiteral("notification_count"), notificationCount); + unreadNotifObj.insert(QStringLiteral("notification_count"), + notificationCount); result.insert(QStringLiteral("unread_notifications"), unreadNotifObj); if (et.elapsed() > 30) - qCDebug(PROFILER) << "Room::toJson() for" << displayname << "took" << et; + qCDebug(PROFILER) << "Room::toJson() for" << displayname << "took" + << et; return result; } -QJsonObject Room::toJson() const -{ - return d->toJson(); -} +QJsonObject Room::toJson() const { return d->toJson(); } -MemberSorter Room::memberSorter() const -{ - return MemberSorter(this); -} +MemberSorter Room::memberSorter() const { return MemberSorter(this); } -bool MemberSorter::operator()(User *u1, User *u2) const +bool MemberSorter::operator()(User* u1, User* u2) const { return operator()(u1, room->roomMembername(u2)); } -bool MemberSorter::operator ()(User* u1, const QString& u2name) const +bool MemberSorter::operator()(User* u1, const QString& u2name) const { auto n1 = room->roomMembername(u1); if (n1.startsWith('@')) diff --git a/lib/room.h b/lib/room.h index f4ecef42..4645d02a 100644 --- a/lib/room.h +++ b/lib/room.h @@ -13,25 +13,24 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include "csapi/message_pagination.h" -#include "events/roommessageevent.h" -#include "events/accountdataevents.h" #include "eventitem.h" +#include "events/accountdataevents.h" +#include "events/roommessageevent.h" #include "joinstate.h" #include -#include #include +#include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Event; class Avatar; class SyncRoomData; @@ -50,553 +49,571 @@ namespace QMatrixClient */ class FileTransferInfo { - Q_GADGET - Q_PROPERTY(bool isUpload MEMBER isUpload CONSTANT) - Q_PROPERTY(bool active READ active CONSTANT) - Q_PROPERTY(bool started READ started CONSTANT) - Q_PROPERTY(bool completed READ completed CONSTANT) - Q_PROPERTY(bool failed READ failed CONSTANT) - Q_PROPERTY(int progress MEMBER progress CONSTANT) - Q_PROPERTY(int total MEMBER total CONSTANT) - Q_PROPERTY(QUrl localDir MEMBER localDir CONSTANT) - Q_PROPERTY(QUrl localPath MEMBER localPath CONSTANT) + Q_GADGET + Q_PROPERTY(bool isUpload MEMBER isUpload CONSTANT) + Q_PROPERTY(bool active READ active CONSTANT) + Q_PROPERTY(bool started READ started CONSTANT) + Q_PROPERTY(bool completed READ completed CONSTANT) + Q_PROPERTY(bool failed READ failed CONSTANT) + Q_PROPERTY(int progress MEMBER progress CONSTANT) + Q_PROPERTY(int total MEMBER total CONSTANT) + Q_PROPERTY(QUrl localDir MEMBER localDir CONSTANT) + Q_PROPERTY(QUrl localPath MEMBER localPath CONSTANT) public: - enum Status { None, Started, Completed, Failed, Cancelled }; - Status status = None; - bool isUpload = false; - int progress = 0; - int total = -1; - QUrl localDir { }; - QUrl localPath { }; - - bool started() const { return status == Started; } - bool completed() const { return status == Completed; } - bool active() const { return started() || completed(); } - bool failed() const { return status == Failed; } + enum Status { None, Started, Completed, Failed, Cancelled }; + Status status = None; + bool isUpload = false; + int progress = 0; + int total = -1; + QUrl localDir {}; + QUrl localPath {}; + + bool started() const { return status == Started; } + bool completed() const { return status == Completed; } + bool active() const { return started() || completed(); } + bool failed() const { return status == Failed; } }; - class Room: public QObject + class Room : public QObject { - Q_OBJECT - Q_PROPERTY(Connection* connection READ connection CONSTANT) - Q_PROPERTY(User* localUser READ localUser CONSTANT) - Q_PROPERTY(QString id READ id CONSTANT) - Q_PROPERTY(QString version READ version NOTIFY baseStateLoaded) - Q_PROPERTY(bool isUnstable READ isUnstable NOTIFY stabilityUpdated) - Q_PROPERTY(QString predecessorId READ predecessorId NOTIFY baseStateLoaded) - Q_PROPERTY(QString successorId READ successorId NOTIFY upgraded) - Q_PROPERTY(QString name READ name NOTIFY namesChanged) - Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) - Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) - Q_PROPERTY(QString displayName READ displayName NOTIFY namesChanged) - Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) - Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) - Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) - Q_PROPERTY(bool usesEncryption READ usesEncryption NOTIFY encryption) - - Q_PROPERTY(int timelineSize READ timelineSize NOTIFY addedMessages) - Q_PROPERTY(QStringList memberNames READ memberNames NOTIFY memberListChanged) - Q_PROPERTY(int memberCount READ memberCount NOTIFY memberListChanged) - Q_PROPERTY(int joinedCount READ joinedCount NOTIFY memberListChanged) - Q_PROPERTY(int invitedCount READ invitedCount NOTIFY memberListChanged) - Q_PROPERTY(int totalMemberCount READ totalMemberCount NOTIFY memberListChanged) - - Q_PROPERTY(bool displayed READ displayed WRITE setDisplayed NOTIFY displayedChanged) - Q_PROPERTY(QString firstDisplayedEventId READ firstDisplayedEventId WRITE setFirstDisplayedEventId NOTIFY firstDisplayedEventChanged) - Q_PROPERTY(QString lastDisplayedEventId READ lastDisplayedEventId WRITE setLastDisplayedEventId NOTIFY lastDisplayedEventChanged) - - Q_PROPERTY(QString readMarkerEventId READ readMarkerEventId WRITE markMessagesAsRead NOTIFY readMarkerMoved) - Q_PROPERTY(bool hasUnreadMessages READ hasUnreadMessages NOTIFY unreadMessagesChanged) - Q_PROPERTY(int unreadCount READ unreadCount NOTIFY unreadMessagesChanged) - Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged) - Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY tagsChanged) - Q_PROPERTY(bool isLowPriority READ isLowPriority NOTIFY tagsChanged) - - Q_PROPERTY(GetRoomEventsJob* eventsHistoryJob READ eventsHistoryJob NOTIFY eventsHistoryJobChanged) + Q_OBJECT + Q_PROPERTY(Connection* connection READ connection CONSTANT) + Q_PROPERTY(User* localUser READ localUser CONSTANT) + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(QString version READ version NOTIFY baseStateLoaded) + Q_PROPERTY(bool isUnstable READ isUnstable NOTIFY stabilityUpdated) + Q_PROPERTY( + QString predecessorId READ predecessorId NOTIFY baseStateLoaded) + Q_PROPERTY(QString successorId READ successorId NOTIFY upgraded) + Q_PROPERTY(QString name READ name NOTIFY namesChanged) + Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) + Q_PROPERTY( + QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) + Q_PROPERTY(QString displayName READ displayName NOTIFY namesChanged) + Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) + Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged + STORED false) + Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) + Q_PROPERTY(bool usesEncryption READ usesEncryption NOTIFY encryption) + + Q_PROPERTY(int timelineSize READ timelineSize NOTIFY addedMessages) + Q_PROPERTY(QStringList memberNames READ memberNames NOTIFY + memberListChanged) + Q_PROPERTY(int memberCount READ memberCount NOTIFY memberListChanged) + Q_PROPERTY(int joinedCount READ joinedCount NOTIFY memberListChanged) + Q_PROPERTY(int invitedCount READ invitedCount NOTIFY memberListChanged) + Q_PROPERTY(int totalMemberCount READ totalMemberCount NOTIFY + memberListChanged) + + Q_PROPERTY(bool displayed READ displayed WRITE setDisplayed NOTIFY + displayedChanged) + Q_PROPERTY(QString firstDisplayedEventId READ firstDisplayedEventId + WRITE setFirstDisplayedEventId NOTIFY + firstDisplayedEventChanged) + Q_PROPERTY(QString lastDisplayedEventId READ lastDisplayedEventId WRITE + setLastDisplayedEventId NOTIFY + lastDisplayedEventChanged) + + Q_PROPERTY(QString readMarkerEventId READ readMarkerEventId WRITE + markMessagesAsRead NOTIFY readMarkerMoved) + Q_PROPERTY(bool hasUnreadMessages READ hasUnreadMessages NOTIFY + unreadMessagesChanged) + Q_PROPERTY( + int unreadCount READ unreadCount NOTIFY unreadMessagesChanged) + Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged) + Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY tagsChanged) + Q_PROPERTY(bool isLowPriority READ isLowPriority NOTIFY tagsChanged) + + Q_PROPERTY(GetRoomEventsJob* eventsHistoryJob READ eventsHistoryJob + NOTIFY eventsHistoryJobChanged) public: - using Timeline = std::deque; - using PendingEvents = std::vector; - using rev_iter_t = Timeline::const_reverse_iterator; - using timeline_iter_t = Timeline::const_iterator; - - enum Change : uint { - NoChange = 0x0, - NameChange = 0x1, - CanonicalAliasChange = 0x2, - TopicChange = 0x4, - UnreadNotifsChange = 0x8, - AvatarChange = 0x10, - JoinStateChange = 0x20, - TagsChange = 0x40, - MembersChange = 0x80, - /* = 0x100, */ - AccountDataChange = 0x200, - SummaryChange = 0x400, - ReadMarkerChange = 0x800, - OtherChange = 0x8000, - AnyChange = 0xFFFF - }; - Q_DECLARE_FLAGS(Changes, Change) - Q_FLAG(Changes) - - Room(Connection* connection, QString id, JoinState initialJoinState); - ~Room() override; - - // Property accessors - - Connection* connection() const; - User* localUser() const; - const QString& id() const; - QString version() const; - bool isUnstable() const; - QString predecessorId() const; - QString successorId() const; - QString name() const; - QStringList aliases() const; - QString canonicalAlias() const; - QString displayName() const; - QString topic() const; - QString avatarMediaId() const; - QUrl avatarUrl() const; - const Avatar& avatarObject() const; - Q_INVOKABLE JoinState joinState() const; - Q_INVOKABLE QList usersTyping() const; - QList membersLeft() const; - - Q_INVOKABLE QList users() const; - QStringList memberNames() const; - [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] - int memberCount() const; - int timelineSize() const; - bool usesEncryption() const; - int joinedCount() const; - int invitedCount() const; - int totalMemberCount() const; - - GetRoomEventsJob* eventsHistoryJob() const; - - /** - * Returns a square room avatar with the given size and requests it - * from the network if needed - * \return a pixmap with the avatar or a placeholder if there's none - * available yet - */ - Q_INVOKABLE QImage avatar(int dimension); - /** - * Returns a room avatar with the given dimensions and requests it - * from the network if needed - * \return a pixmap with the avatar or a placeholder if there's none - * available yet - */ - Q_INVOKABLE QImage avatar(int width, int height); - - /** - * \brief Get a user object for a given user id - * This is the recommended way to get a user object in a room - * context. The actual object type may be changed in further - * versions to provide room-specific user information (display name, - * avatar etc.). - * \note The method will return a valid user regardless of - * the membership. - */ - Q_INVOKABLE User* user(const QString& userId) const; - - /** - * \brief Check the join state of a given user in this room - * - * \note Banned and invited users are not tracked for now (Leave - * will be returned for them). - * - * \return either of Join, Leave, depending on the given - * user's state in the room - */ - Q_INVOKABLE JoinState memberJoinState(User* user) const; - - /** - * Get a disambiguated name for a given user in - * the context of the room - */ - Q_INVOKABLE QString roomMembername(const User* u) const; - /** - * Get a disambiguated name for a user with this id in - * the context of the room - */ - Q_INVOKABLE QString roomMembername(const QString& userId) const; - - const Timeline& messageEvents() const; - const PendingEvents& pendingEvents() const; - /** - * A convenience method returning the read marker to the position - * before the "oldest" event; same as messageEvents().crend() - */ - rev_iter_t historyEdge() const; - /** - * A convenience method returning the iterator beyond the latest - * arrived event; same as messageEvents().cend() - */ - Timeline::const_iterator syncEdge() const; - /// \deprecated Use historyEdge instead - rev_iter_t timelineEdge() const; - Q_INVOKABLE TimelineItem::index_t minTimelineIndex() const; - Q_INVOKABLE TimelineItem::index_t maxTimelineIndex() const; - Q_INVOKABLE bool isValidIndex(TimelineItem::index_t timelineIndex) const; - - rev_iter_t findInTimeline(TimelineItem::index_t index) const; - rev_iter_t findInTimeline(const QString& evtId) const; - PendingEvents::iterator findPendingEvent(const QString & txnId); - PendingEvents::const_iterator findPendingEvent(const QString & txnId) const; - - bool displayed() const; - /// Mark the room as currently displayed to the user - /** - * Marking the room displayed causes the room to obtain the full - * list of members if it's been lazy-loaded before; in the future - * it may do more things bound to "screen time" of the room, e.g. - * measure that "screen time". - */ - void setDisplayed(bool displayed = true); - QString firstDisplayedEventId() const; - rev_iter_t firstDisplayedMarker() const; - void setFirstDisplayedEventId(const QString& eventId); - void setFirstDisplayedEvent(TimelineItem::index_t index); - QString lastDisplayedEventId() const; - rev_iter_t lastDisplayedMarker() const; - void setLastDisplayedEventId(const QString& eventId); - void setLastDisplayedEvent(TimelineItem::index_t index); - - rev_iter_t readMarker(const User* user) const; - rev_iter_t readMarker() const; - QString readMarkerEventId() const; - QList usersAtEventId(const QString& eventId); - /** - * \brief Mark the event with uptoEventId as read - * - * Finds in the timeline and marks as read the event with - * the specified id; also posts a read receipt to the server either - * for this message or, if it's from the local user, for - * the nearest non-local message before. uptoEventId must be non-empty. - */ - void markMessagesAsRead(QString uptoEventId); - - /// Check whether there are unread messages in the room - bool hasUnreadMessages() const; - - /** Get the number of unread messages in the room - * Depending on the read marker state, this call may return either - * a precise or an estimate number of unread events. Only "notable" - * events (non-redacted message events from users other than local) - * are counted. - * - * In a case when readMarker() == timelineEdge() (the local read - * marker is beyond the local timeline) only the bottom limit of - * the unread messages number can be estimated (and even that may - * be slightly off due to, e.g., redactions of events not loaded - * to the local timeline). - * - * If all messages are read, this function will return -1 (_not_ 0, - * as zero may mean "zero or more unread messages" in a situation - * when the read marker is outside the local timeline. - */ - int unreadCount() const; - - Q_INVOKABLE int notificationCount() const; - Q_INVOKABLE void resetNotificationCount(); - Q_INVOKABLE int highlightCount() const; - Q_INVOKABLE void resetHighlightCount(); - - /** Check whether the room has account data of the given type - * Tags and read markers are not supported by this method _yet_. - */ - bool hasAccountData(const QString& type) const; - - /** Get a generic account data event of the given type - * This returns a generic hashmap for any room account data event - * stored on the server. Tags and read markers cannot be retrieved - * using this method _yet_. - */ - const EventPtr& accountData(const QString& type) const; - - QStringList tagNames() const; - TagsMap tags() const; - TagRecord tag(const QString& name) const; - - /** Add a new tag to this room - * If this room already has this tag, nothing happens. If it's a new - * tag for the room, the respective tag record is added to the set - * of tags and the new set is sent to the server to update other - * clients. - */ - void addTag(const QString& name, const TagRecord& record = {}); - Q_INVOKABLE void addTag(const QString& name, float order); - - /// Remove a tag from the room - Q_INVOKABLE void removeTag(const QString& name); - - /** Overwrite the room's tags - * This completely replaces the existing room's tags with a set - * of new ones and updates the new set on the server. Unlike - * most other methods in Room, this one sends a signal about changes - * immediately, not waiting for confirmation from the server - * (because tags are saved in account data rather than in shared - * room state). - */ - void setTags(TagsMap newTags); - - /// Check whether the list of tags has m.favourite - bool isFavourite() const; - /// Check whether the list of tags has m.lowpriority - bool isLowPriority() const; - - /// Check whether this room is a direct chat - Q_INVOKABLE bool isDirectChat() const; - - /// Get the list of users this room is a direct chat with - QList directChatUsers() const; - - Q_INVOKABLE QUrl urlToThumbnail(const QString& eventId) const; - Q_INVOKABLE QUrl urlToDownload(const QString& eventId) const; - Q_INVOKABLE QString fileNameToDownload(const QString& eventId) const; - Q_INVOKABLE FileTransferInfo fileTransferInfo(const QString& id) const; - Q_INVOKABLE QUrl fileSource(const QString& id) const; - - /** Pretty-prints plain text into HTML - * As of now, it's exactly the same as QMatrixClient::prettyPrint(); - * in the future, it will also linkify room aliases, mxids etc. - * using the room context. - */ - QString prettyPrint(const QString& plainText) const; - - MemberSorter memberSorter() const; - - Q_INVOKABLE void inviteCall(const QString& callId, - const int lifetime, const QString& sdp); - Q_INVOKABLE void sendCallCandidates(const QString& callId, + using Timeline = std::deque; + using PendingEvents = std::vector; + using rev_iter_t = Timeline::const_reverse_iterator; + using timeline_iter_t = Timeline::const_iterator; + + enum Change : uint { + NoChange = 0x0, + NameChange = 0x1, + CanonicalAliasChange = 0x2, + TopicChange = 0x4, + UnreadNotifsChange = 0x8, + AvatarChange = 0x10, + JoinStateChange = 0x20, + TagsChange = 0x40, + MembersChange = 0x80, + /* = 0x100, */ + AccountDataChange = 0x200, + SummaryChange = 0x400, + ReadMarkerChange = 0x800, + OtherChange = 0x8000, + AnyChange = 0xFFFF + }; + Q_DECLARE_FLAGS(Changes, Change) + Q_FLAG(Changes) + + Room(Connection* connection, QString id, JoinState initialJoinState); + ~Room() override; + + // Property accessors + + Connection* connection() const; + User* localUser() const; + const QString& id() const; + QString version() const; + bool isUnstable() const; + QString predecessorId() const; + QString successorId() const; + QString name() const; + QStringList aliases() const; + QString canonicalAlias() const; + QString displayName() const; + QString topic() const; + QString avatarMediaId() const; + QUrl avatarUrl() const; + const Avatar& avatarObject() const; + Q_INVOKABLE JoinState joinState() const; + Q_INVOKABLE QList usersTyping() const; + QList membersLeft() const; + + Q_INVOKABLE QList users() const; + QStringList memberNames() const; + [[deprecated( + "Use joinedCount(), invitedCount(), totalMemberCount()")]] int + memberCount() const; + int timelineSize() const; + bool usesEncryption() const; + int joinedCount() const; + int invitedCount() const; + int totalMemberCount() const; + + GetRoomEventsJob* eventsHistoryJob() const; + + /** + * Returns a square room avatar with the given size and requests it + * from the network if needed + * \return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QImage avatar(int dimension); + /** + * Returns a room avatar with the given dimensions and requests it + * from the network if needed + * \return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QImage avatar(int width, int height); + + /** + * \brief Get a user object for a given user id + * This is the recommended way to get a user object in a room + * context. The actual object type may be changed in further + * versions to provide room-specific user information (display name, + * avatar etc.). + * \note The method will return a valid user regardless of + * the membership. + */ + Q_INVOKABLE User* user(const QString& userId) const; + + /** + * \brief Check the join state of a given user in this room + * + * \note Banned and invited users are not tracked for now (Leave + * will be returned for them). + * + * \return either of Join, Leave, depending on the given + * user's state in the room + */ + Q_INVOKABLE JoinState memberJoinState(User* user) const; + + /** + * Get a disambiguated name for a given user in + * the context of the room + */ + Q_INVOKABLE QString roomMembername(const User* u) const; + /** + * Get a disambiguated name for a user with this id in + * the context of the room + */ + Q_INVOKABLE QString roomMembername(const QString& userId) const; + + const Timeline& messageEvents() const; + const PendingEvents& pendingEvents() const; + /** + * A convenience method returning the read marker to the position + * before the "oldest" event; same as messageEvents().crend() + */ + rev_iter_t historyEdge() const; + /** + * A convenience method returning the iterator beyond the latest + * arrived event; same as messageEvents().cend() + */ + Timeline::const_iterator syncEdge() const; + /// \deprecated Use historyEdge instead + rev_iter_t timelineEdge() const; + Q_INVOKABLE TimelineItem::index_t minTimelineIndex() const; + Q_INVOKABLE TimelineItem::index_t maxTimelineIndex() const; + Q_INVOKABLE bool + isValidIndex(TimelineItem::index_t timelineIndex) const; + + rev_iter_t findInTimeline(TimelineItem::index_t index) const; + rev_iter_t findInTimeline(const QString& evtId) const; + PendingEvents::iterator findPendingEvent(const QString& txnId); + PendingEvents::const_iterator + findPendingEvent(const QString& txnId) const; + + bool displayed() const; + /// Mark the room as currently displayed to the user + /** + * Marking the room displayed causes the room to obtain the full + * list of members if it's been lazy-loaded before; in the future + * it may do more things bound to "screen time" of the room, e.g. + * measure that "screen time". + */ + void setDisplayed(bool displayed = true); + QString firstDisplayedEventId() const; + rev_iter_t firstDisplayedMarker() const; + void setFirstDisplayedEventId(const QString& eventId); + void setFirstDisplayedEvent(TimelineItem::index_t index); + QString lastDisplayedEventId() const; + rev_iter_t lastDisplayedMarker() const; + void setLastDisplayedEventId(const QString& eventId); + void setLastDisplayedEvent(TimelineItem::index_t index); + + rev_iter_t readMarker(const User* user) const; + rev_iter_t readMarker() const; + QString readMarkerEventId() const; + QList usersAtEventId(const QString& eventId); + /** + * \brief Mark the event with uptoEventId as read + * + * Finds in the timeline and marks as read the event with + * the specified id; also posts a read receipt to the server either + * for this message or, if it's from the local user, for + * the nearest non-local message before. uptoEventId must be non-empty. + */ + void markMessagesAsRead(QString uptoEventId); + + /// Check whether there are unread messages in the room + bool hasUnreadMessages() const; + + /** Get the number of unread messages in the room + * Depending on the read marker state, this call may return either + * a precise or an estimate number of unread events. Only "notable" + * events (non-redacted message events from users other than local) + * are counted. + * + * In a case when readMarker() == timelineEdge() (the local read + * marker is beyond the local timeline) only the bottom limit of + * the unread messages number can be estimated (and even that may + * be slightly off due to, e.g., redactions of events not loaded + * to the local timeline). + * + * If all messages are read, this function will return -1 (_not_ 0, + * as zero may mean "zero or more unread messages" in a situation + * when the read marker is outside the local timeline. + */ + int unreadCount() const; + + Q_INVOKABLE int notificationCount() const; + Q_INVOKABLE void resetNotificationCount(); + Q_INVOKABLE int highlightCount() const; + Q_INVOKABLE void resetHighlightCount(); + + /** Check whether the room has account data of the given type + * Tags and read markers are not supported by this method _yet_. + */ + bool hasAccountData(const QString& type) const; + + /** Get a generic account data event of the given type + * This returns a generic hashmap for any room account data event + * stored on the server. Tags and read markers cannot be retrieved + * using this method _yet_. + */ + const EventPtr& accountData(const QString& type) const; + + QStringList tagNames() const; + TagsMap tags() const; + TagRecord tag(const QString& name) const; + + /** Add a new tag to this room + * If this room already has this tag, nothing happens. If it's a new + * tag for the room, the respective tag record is added to the set + * of tags and the new set is sent to the server to update other + * clients. + */ + void addTag(const QString& name, const TagRecord& record = {}); + Q_INVOKABLE void addTag(const QString& name, float order); + + /// Remove a tag from the room + Q_INVOKABLE void removeTag(const QString& name); + + /** Overwrite the room's tags + * This completely replaces the existing room's tags with a set + * of new ones and updates the new set on the server. Unlike + * most other methods in Room, this one sends a signal about changes + * immediately, not waiting for confirmation from the server + * (because tags are saved in account data rather than in shared + * room state). + */ + void setTags(TagsMap newTags); + + /// Check whether the list of tags has m.favourite + bool isFavourite() const; + /// Check whether the list of tags has m.lowpriority + bool isLowPriority() const; + + /// Check whether this room is a direct chat + Q_INVOKABLE bool isDirectChat() const; + + /// Get the list of users this room is a direct chat with + QList directChatUsers() const; + + Q_INVOKABLE QUrl urlToThumbnail(const QString& eventId) const; + Q_INVOKABLE QUrl urlToDownload(const QString& eventId) const; + Q_INVOKABLE QString fileNameToDownload(const QString& eventId) const; + Q_INVOKABLE FileTransferInfo fileTransferInfo(const QString& id) const; + Q_INVOKABLE QUrl fileSource(const QString& id) const; + + /** Pretty-prints plain text into HTML + * As of now, it's exactly the same as QMatrixClient::prettyPrint(); + * in the future, it will also linkify room aliases, mxids etc. + * using the room context. + */ + QString prettyPrint(const QString& plainText) const; + + MemberSorter memberSorter() const; + + Q_INVOKABLE void inviteCall(const QString& callId, const int lifetime, + const QString& sdp); + Q_INVOKABLE void sendCallCandidates(const QString& callId, const QJsonArray& candidates); - Q_INVOKABLE void answerCall(const QString& callId, const int lifetime, - const QString& sdp); - Q_INVOKABLE void answerCall(const QString& callId, - const QString& sdp); - Q_INVOKABLE void hangupCall(const QString& callId); - Q_INVOKABLE bool supportsCalls() const; + Q_INVOKABLE void answerCall(const QString& callId, const int lifetime, + const QString& sdp); + Q_INVOKABLE void answerCall(const QString& callId, const QString& sdp); + Q_INVOKABLE void hangupCall(const QString& callId); + Q_INVOKABLE bool supportsCalls() const; public slots: - /** Check whether the room should be upgraded */ - void checkVersion(); - - QString postMessage(const QString& plainText, MessageEventType type); - QString postPlainText(const QString& plainText); - QString postHtmlMessage(const QString& plainText, - const QString& html, - MessageEventType type = MessageEventType::Text); - QString postHtmlText(const QString& plainText, const QString& html); - QString postFile(const QString& plainText, const QUrl& localPath, - bool asGenericFile = false); - /** Post a pre-created room message event - * - * Takes ownership of the event, deleting it once the matching one - * arrives with the sync - * \return transaction id associated with the event. - */ - QString postEvent(RoomEvent* event); - QString postJson(const QString& matrixType, - const QJsonObject& eventContent); - QString retryMessage(const QString& txnId); - void discardMessage(const QString& txnId); - void setName(const QString& newName); - void setCanonicalAlias(const QString& newAlias); - void setAliases(const QStringList& aliases); - void setTopic(const QString& newTopic); - - void getPreviousContent(int limit = 10); - - void inviteToRoom(const QString& memberId); - LeaveRoomJob* leaveRoom(); - SetRoomStateWithKeyJob* setMemberState( - const QString& memberId, const RoomMemberEvent& event) const; - void kickMember(const QString& memberId, const QString& reason = {}); - void ban(const QString& userId, const QString& reason = {}); - void unban(const QString& userId); - void redactEvent(const QString& eventId, - const QString& reason = {}); - - void uploadFile(const QString& id, const QUrl& localFilename, - const QString& overrideContentType = {}); - // If localFilename is empty a temporary file is created - void downloadFile(const QString& eventId, - const QUrl& localFilename = {}); - void cancelFileTransfer(const QString& id); - - /// Mark all messages in the room as read - void markAllMessagesAsRead(); - - /// Whether the current user is allowed to upgrade the room - bool canSwitchVersions() const; - - /// Switch the room's version (aka upgrade) - void switchVersion(QString newVersion); + /** Check whether the room should be upgraded */ + void checkVersion(); + + QString postMessage(const QString& plainText, MessageEventType type); + QString postPlainText(const QString& plainText); + QString postHtmlMessage(const QString& plainText, const QString& html, + MessageEventType type = MessageEventType::Text); + QString postHtmlText(const QString& plainText, const QString& html); + QString postFile(const QString& plainText, const QUrl& localPath, + bool asGenericFile = false); + /** Post a pre-created room message event + * + * Takes ownership of the event, deleting it once the matching one + * arrives with the sync + * \return transaction id associated with the event. + */ + QString postEvent(RoomEvent* event); + QString postJson(const QString& matrixType, + const QJsonObject& eventContent); + QString retryMessage(const QString& txnId); + void discardMessage(const QString& txnId); + void setName(const QString& newName); + void setCanonicalAlias(const QString& newAlias); + void setAliases(const QStringList& aliases); + void setTopic(const QString& newTopic); + + void getPreviousContent(int limit = 10); + + void inviteToRoom(const QString& memberId); + LeaveRoomJob* leaveRoom(); + SetRoomStateWithKeyJob* + setMemberState(const QString& memberId, + const RoomMemberEvent& event) const; + void kickMember(const QString& memberId, const QString& reason = {}); + void ban(const QString& userId, const QString& reason = {}); + void unban(const QString& userId); + void redactEvent(const QString& eventId, const QString& reason = {}); + + void uploadFile(const QString& id, const QUrl& localFilename, + const QString& overrideContentType = {}); + // If localFilename is empty a temporary file is created + void downloadFile(const QString& eventId, + const QUrl& localFilename = {}); + void cancelFileTransfer(const QString& id); + + /// Mark all messages in the room as read + void markAllMessagesAsRead(); + + /// Whether the current user is allowed to upgrade the room + bool canSwitchVersions() const; + + /// Switch the room's version (aka upgrade) + void switchVersion(QString newVersion); signals: - /// Initial set of state events has been loaded - /** - * The initial set is what comes from the initial sync for the room. - * This includes all basic things like RoomCreateEvent, - * RoomNameEvent, a (lazy-loaded, not full) set of RoomMemberEvents - * etc. This is a per-room reflection of Connection::loadedRoomState - * \sa Connection::loadedRoomState - */ - void baseStateLoaded(); - void eventsHistoryJobChanged(); - void aboutToAddHistoricalMessages(RoomEventsRange events); - void aboutToAddNewMessages(RoomEventsRange events); - void addedMessages(int fromIndex, int toIndex); - /// The event is about to be appended to the list of pending events - void pendingEventAboutToAdd(RoomEvent* event); - /// An event has been appended to the list of pending events - void pendingEventAdded(); - /// The remote echo has arrived with the sync and will be merged - /// with its local counterpart - /** NB: Requires a sync loop to be emitted */ - void pendingEventAboutToMerge(RoomEvent* serverEvent, - int pendingEventIndex); - /// The remote and local copies of the event have been merged - /** NB: Requires a sync loop to be emitted */ - void pendingEventMerged(); - /// An event will be removed from the list of pending events - void pendingEventAboutToDiscard(int pendingEventIndex); - /// An event has just been removed from the list of pending events - void pendingEventDiscarded(); - /// The status of a pending event has changed - /** \sa PendingEventItem::deliveryStatus */ - void pendingEventChanged(int pendingEventIndex); - /// The server accepted the message - /** This is emitted when an event sending request has successfully - * completed. This does not mean that the event is already in the - * local timeline, only that the server has accepted it. - * \param txnId transaction id assigned by the client during sending - * \param eventId event id assigned by the server upon acceptance - * \sa postEvent, postPlainText, postMessage, postHtmlMessage - * \sa pendingEventMerged, aboutToAddNewMessages - */ - void messageSent(QString txnId, QString eventId); - - /** A common signal for various kinds of changes in the room - * Aside from all changes in the room state - * @param changes a set of flags describing what changes occured - * upon the last sync - * \sa StateChange - */ - void changed(Changes changes); - /** - * \brief The room name, the canonical alias or other aliases changed - * - * Not triggered when displayname changes. - */ - void namesChanged(Room* room); - void displaynameAboutToChange(Room* room); - void displaynameChanged(Room* room, QString oldName); - void topicChanged(); - void avatarChanged(); - void userAdded(User* user); - void userRemoved(User* user); - void memberAboutToRename(User* user, QString newName); - void memberRenamed(User* user); - /// The list of members has changed - /** Emitted no more than once per sync, this is a good signal to - * for cases when some action should be done upon any change in - * the member list. If you need per-item granularity you should use - * userAdded, userRemoved and memberAboutToRename / memberRenamed - * instead. - */ - void memberListChanged(); - /// The previously lazy-loaded members list is now loaded entirely - /// \sa setDisplayed - void allMembersLoaded(); - void encryption(); - - void joinStateChanged(JoinState oldState, JoinState newState); - void typingChanged(); - - void highlightCountChanged(Room* room); - void notificationCountChanged(Room* room); - - void displayedChanged(bool displayed); - void firstDisplayedEventChanged(); - void lastDisplayedEventChanged(); - void lastReadEventChanged(User* user); - void readMarkerMoved(QString fromEventId, QString toEventId); - void readMarkerForUserMoved(User* user, QString fromEventId, QString toEventId); - void unreadMessagesChanged(Room* room); - - void accountDataAboutToChange(QString type); - void accountDataChanged(QString type); - void tagsAboutToChange(); - void tagsChanged(); - - void replacedEvent(const RoomEvent* newEvent, - const RoomEvent* oldEvent); - - void newFileTransfer(QString id, QUrl localFile); - void fileTransferProgress(QString id, qint64 progress, qint64 total); - void fileTransferCompleted(QString id, QUrl localFile, QUrl mxcUrl); - void fileTransferFailed(QString id, QString errorMessage = {}); - void fileTransferCancelled(QString id); - - void callEvent(Room* room, const RoomEvent* event); - - /// The room's version stability may have changed - void stabilityUpdated(QString recommendedDefault, - QStringList stableVersions); - /// This room has been upgraded and won't receive updates anymore - void upgraded(QString serverMessage, Room* successor); - /// An attempted room upgrade has failed - void upgradeFailed(QString errorMessage); - - /// The room is about to be deleted - void beforeDestruction(Room*); + /// Initial set of state events has been loaded + /** + * The initial set is what comes from the initial sync for the room. + * This includes all basic things like RoomCreateEvent, + * RoomNameEvent, a (lazy-loaded, not full) set of RoomMemberEvents + * etc. This is a per-room reflection of Connection::loadedRoomState + * \sa Connection::loadedRoomState + */ + void baseStateLoaded(); + void eventsHistoryJobChanged(); + void aboutToAddHistoricalMessages(RoomEventsRange events); + void aboutToAddNewMessages(RoomEventsRange events); + void addedMessages(int fromIndex, int toIndex); + /// The event is about to be appended to the list of pending events + void pendingEventAboutToAdd(RoomEvent* event); + /// An event has been appended to the list of pending events + void pendingEventAdded(); + /// The remote echo has arrived with the sync and will be merged + /// with its local counterpart + /** NB: Requires a sync loop to be emitted */ + void pendingEventAboutToMerge(RoomEvent* serverEvent, + int pendingEventIndex); + /// The remote and local copies of the event have been merged + /** NB: Requires a sync loop to be emitted */ + void pendingEventMerged(); + /// An event will be removed from the list of pending events + void pendingEventAboutToDiscard(int pendingEventIndex); + /// An event has just been removed from the list of pending events + void pendingEventDiscarded(); + /// The status of a pending event has changed + /** \sa PendingEventItem::deliveryStatus */ + void pendingEventChanged(int pendingEventIndex); + /// The server accepted the message + /** This is emitted when an event sending request has successfully + * completed. This does not mean that the event is already in the + * local timeline, only that the server has accepted it. + * \param txnId transaction id assigned by the client during sending + * \param eventId event id assigned by the server upon acceptance + * \sa postEvent, postPlainText, postMessage, postHtmlMessage + * \sa pendingEventMerged, aboutToAddNewMessages + */ + void messageSent(QString txnId, QString eventId); + + /** A common signal for various kinds of changes in the room + * Aside from all changes in the room state + * @param changes a set of flags describing what changes occured + * upon the last sync + * \sa StateChange + */ + void changed(Changes changes); + /** + * \brief The room name, the canonical alias or other aliases changed + * + * Not triggered when displayname changes. + */ + void namesChanged(Room* room); + void displaynameAboutToChange(Room* room); + void displaynameChanged(Room* room, QString oldName); + void topicChanged(); + void avatarChanged(); + void userAdded(User* user); + void userRemoved(User* user); + void memberAboutToRename(User* user, QString newName); + void memberRenamed(User* user); + /// The list of members has changed + /** Emitted no more than once per sync, this is a good signal to + * for cases when some action should be done upon any change in + * the member list. If you need per-item granularity you should use + * userAdded, userRemoved and memberAboutToRename / memberRenamed + * instead. + */ + void memberListChanged(); + /// The previously lazy-loaded members list is now loaded entirely + /// \sa setDisplayed + void allMembersLoaded(); + void encryption(); + + void joinStateChanged(JoinState oldState, JoinState newState); + void typingChanged(); + + void highlightCountChanged(Room* room); + void notificationCountChanged(Room* room); + + void displayedChanged(bool displayed); + void firstDisplayedEventChanged(); + void lastDisplayedEventChanged(); + void lastReadEventChanged(User* user); + void readMarkerMoved(QString fromEventId, QString toEventId); + void readMarkerForUserMoved(User* user, QString fromEventId, + QString toEventId); + void unreadMessagesChanged(Room* room); + + void accountDataAboutToChange(QString type); + void accountDataChanged(QString type); + void tagsAboutToChange(); + void tagsChanged(); + + void replacedEvent(const RoomEvent* newEvent, + const RoomEvent* oldEvent); + + void newFileTransfer(QString id, QUrl localFile); + void fileTransferProgress(QString id, qint64 progress, qint64 total); + void fileTransferCompleted(QString id, QUrl localFile, QUrl mxcUrl); + void fileTransferFailed(QString id, QString errorMessage = {}); + void fileTransferCancelled(QString id); + + void callEvent(Room* room, const RoomEvent* event); + + /// The room's version stability may have changed + void stabilityUpdated(QString recommendedDefault, + QStringList stableVersions); + /// This room has been upgraded and won't receive updates anymore + void upgraded(QString serverMessage, Room* successor); + /// An attempted room upgrade has failed + void upgradeFailed(QString errorMessage); + + /// The room is about to be deleted + void beforeDestruction(Room*); protected: - /// Returns true if any of room names/aliases has changed - virtual Changes processStateEvent(const RoomEvent& e); - virtual Changes processEphemeralEvent(EventPtr&& event); - virtual Changes processAccountDataEvent(EventPtr&& event); - virtual void onAddNewTimelineEvents(timeline_iter_t /*from*/) { } - virtual void onAddHistoricalTimelineEvents(rev_iter_t /*from*/) { } - virtual void onRedaction(const RoomEvent& /*prevEvent*/, - const RoomEvent& /*after*/) { } - virtual QJsonObject toJson() const; - virtual void updateData(SyncRoomData&& data, bool fromCache = false); + /// Returns true if any of room names/aliases has changed + virtual Changes processStateEvent(const RoomEvent& e); + virtual Changes processEphemeralEvent(EventPtr&& event); + virtual Changes processAccountDataEvent(EventPtr&& event); + virtual void onAddNewTimelineEvents(timeline_iter_t /*from*/) {} + virtual void onAddHistoricalTimelineEvents(rev_iter_t /*from*/) {} + virtual void onRedaction(const RoomEvent& /*prevEvent*/, + const RoomEvent& /*after*/) + { + } + virtual QJsonObject toJson() const; + virtual void updateData(SyncRoomData&& data, bool fromCache = false); private: - friend class Connection; + friend class Connection; - class Private; - Private* d; + class Private; + Private* d; - // This is called from Connection, reflecting a state change that - // arrived from the server. Clients should use - // Connection::joinRoom() and Room::leaveRoom() to change the state. - void setJoinState(JoinState state); + // This is called from Connection, reflecting a state change that + // arrived from the server. Clients should use + // Connection::joinRoom() and Room::leaveRoom() to change the state. + void setJoinState(JoinState state); }; class MemberSorter { public: - explicit MemberSorter(const Room* r) : room(r) { } + explicit MemberSorter(const Room* r) : room(r) {} - bool operator()(User* u1, User* u2) const; - bool operator()(User* u1, const QString& u2name) const; + bool operator()(User* u1, User* u2) const; + bool operator()(User* u1, const QString& u2name) const; - template - typename ContT::size_type lowerBoundIndex(const ContT& c, - const ValT& v) const - { - return std::lower_bound(c.begin(), c.end(), v, *this) - c.begin(); - } + template + typename ContT::size_type lowerBoundIndex(const ContT& c, + const ValT& v) const + { + return std::lower_bound(c.begin(), c.end(), v, *this) - c.begin(); + } private: - const Room* room; + const Room* room; }; -} // namespace QMatrixClient +} // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::FileTransferInfo) Q_DECLARE_OPERATORS_FOR_FLAGS(QMatrixClient::Room::Changes) diff --git a/lib/settings.cpp b/lib/settings.cpp index 852e19cb..e0a1783f 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -18,7 +18,7 @@ void Settings::setLegacyNames(const QString& organizationName, void Settings::setValue(const QString& key, const QVariant& value) { -// qCDebug() << "Setting" << key << "to" << value; + // qCDebug() << "Setting" << key << "to" << value; QSettings::setValue(key, value); if (legacySettings.contains(key)) legacySettings.remove(key); @@ -32,7 +32,8 @@ QVariant Settings::value(const QString& key, const QVariant& defaultValue) const // (QVariant("false") == true in JavaScript). Since we have a mixed // environment where both QSettings and Qt.labs.Settings may potentially // work with same settings, better ensure compatibility. - return value.toString() == QStringLiteral("false") ? QVariant(false) : value; + return value.toString() == QStringLiteral("false") ? QVariant(false) + : value; } bool Settings::contains(const QString& key) const @@ -56,15 +57,13 @@ bool SettingsGroup::contains(const QString& key) const return Settings::contains(groupPath + '/' + key); } -QVariant SettingsGroup::value(const QString& key, const QVariant& defaultValue) const +QVariant SettingsGroup::value(const QString& key, + const QVariant& defaultValue) const { return Settings::value(groupPath + '/' + key, defaultValue); } -QString SettingsGroup::group() const -{ - return groupPath; -} +QString SettingsGroup::group() const { return groupPath; } QStringList SettingsGroup::childGroups() const { @@ -84,9 +83,12 @@ void SettingsGroup::remove(const QString& key) Settings::remove(fullKey); } -QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", "", setDeviceId) -QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", "", setDeviceName) -QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) +QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", "", + setDeviceId) +QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", "", + setDeviceName) +QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, + setKeepLoggedIn) QUrl AccountSettings::homeserver() const { @@ -98,10 +100,7 @@ void AccountSettings::setHomeserver(const QUrl& url) setValue("homeserver", url.toString()); } -QString AccountSettings::userId() const -{ - return group().section('/', -1); -} +QString AccountSettings::userId() const { return group().section('/', -1); } QString AccountSettings::accessToken() const { diff --git a/lib/settings.h b/lib/settings.h index 0b3ecaff..0cde47a3 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -13,140 +13,142 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once #include -#include #include +#include class QVariant; -namespace QMatrixClient -{ - class Settings: public QSettings +namespace QMatrixClient { + class Settings : public QSettings { - Q_OBJECT + Q_OBJECT public: - /** - * Use this function before creating any Settings objects in order - * to setup a read-only location where configuration has previously - * been stored. This will provide an additional fallback in case of - * renaming the organisation/application. - */ - static void setLegacyNames(const QString& organizationName, - const QString& applicationName = {}); + /** + * Use this function before creating any Settings objects in order + * to setup a read-only location where configuration has previously + * been stored. This will provide an additional fallback in case of + * renaming the organisation/application. + */ + static void setLegacyNames(const QString& organizationName, + const QString& applicationName = {}); #if defined(_MSC_VER) && _MSC_VER < 1900 - // VS 2013 (and probably older) aren't friends with 'using' statements - // that involve private constructors - explicit Settings(QObject* parent = 0) : QSettings(parent) { } + // VS 2013 (and probably older) aren't friends with 'using' statements + // that involve private constructors + explicit Settings(QObject* parent = 0) : QSettings(parent) {} #else - using QSettings::QSettings; + using QSettings::QSettings; #endif - Q_INVOKABLE void setValue(const QString &key, - const QVariant &value); - Q_INVOKABLE QVariant value(const QString &key, - const QVariant &defaultValue = {}) const; + Q_INVOKABLE void setValue(const QString& key, const QVariant& value); + Q_INVOKABLE QVariant value(const QString& key, + const QVariant& defaultValue = {}) const; - template - T get(const QString& key, const T& defaultValue = {}) const - { - const auto qv = value(key, QVariant()); - return qv.isValid() && qv.canConvert() ? qv.value() - : defaultValue; - } + template + T get(const QString& key, const T& defaultValue = {}) const + { + const auto qv = value(key, QVariant()); + return qv.isValid() && qv.canConvert() ? qv.value() + : defaultValue; + } - Q_INVOKABLE bool contains(const QString& key) const; - Q_INVOKABLE QStringList childGroups() const; + Q_INVOKABLE bool contains(const QString& key) const; + Q_INVOKABLE QStringList childGroups() const; private: - static QString legacyOrganizationName; - static QString legacyApplicationName; + static QString legacyOrganizationName; + static QString legacyApplicationName; protected: - QSettings legacySettings { legacyOrganizationName, - legacyApplicationName }; + QSettings legacySettings { legacyOrganizationName, + legacyApplicationName }; }; - class SettingsGroup: public Settings + class SettingsGroup : public Settings { public: - template - explicit SettingsGroup(QString path, ArgTs&&... qsettingsArgs) - : Settings(std::forward(qsettingsArgs)...) - , groupPath(std::move(path)) - { } - - Q_INVOKABLE bool contains(const QString& key) const; - Q_INVOKABLE QVariant value(const QString &key, - const QVariant &defaultValue = {}) const; - - template - T get(const QString& key, const T& defaultValue = {}) const - { - const auto qv = value(key, QVariant()); - return qv.isValid() && qv.canConvert() ? qv.value() - : defaultValue; - } - - Q_INVOKABLE QString group() const; - Q_INVOKABLE QStringList childGroups() const; - Q_INVOKABLE void setValue(const QString &key, - const QVariant &value); - - Q_INVOKABLE void remove(const QString& key); + template + explicit SettingsGroup(QString path, ArgTs&&... qsettingsArgs) + : Settings(std::forward(qsettingsArgs)...), + groupPath(std::move(path)) + { + } + + Q_INVOKABLE bool contains(const QString& key) const; + Q_INVOKABLE QVariant value(const QString& key, + const QVariant& defaultValue = {}) const; + + template + T get(const QString& key, const T& defaultValue = {}) const + { + const auto qv = value(key, QVariant()); + return qv.isValid() && qv.canConvert() ? qv.value() + : defaultValue; + } + + Q_INVOKABLE QString group() const; + Q_INVOKABLE QStringList childGroups() const; + Q_INVOKABLE void setValue(const QString& key, const QVariant& value); + + Q_INVOKABLE void remove(const QString& key); private: - QString groupPath; + QString groupPath; }; -#define QMC_DECLARE_SETTING(type, propname, setter) \ - Q_PROPERTY(type propname READ propname WRITE setter) \ - public: \ - type propname() const; \ - void setter(type newValue); \ +#define QMC_DECLARE_SETTING(type, propname, setter) \ + Q_PROPERTY(type propname READ propname WRITE setter) \ + public: \ + type propname() const; \ + void setter(type newValue); \ + \ private: -#define QMC_DEFINE_SETTING(classname, type, propname, qsettingname, defaultValue, setter) \ -type classname::propname() const \ -{ \ - return get(QStringLiteral(qsettingname), defaultValue); \ -} \ -\ -void classname::setter(type newValue) \ -{ \ - setValue(QStringLiteral(qsettingname), newValue); \ -} \ - - class AccountSettings: public SettingsGroup +#define QMC_DEFINE_SETTING(classname, type, propname, qsettingname, \ + defaultValue, setter) \ + type classname::propname() const \ + { \ + return get(QStringLiteral(qsettingname), defaultValue); \ + } \ + \ + void classname::setter(type newValue) \ + { \ + setValue(QStringLiteral(qsettingname), newValue); \ + } + + class AccountSettings : public SettingsGroup { - Q_OBJECT - Q_PROPERTY(QString userId READ userId CONSTANT) - QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) - QMC_DECLARE_SETTING(QString, deviceName, setDeviceName) - QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) - /** \deprecated \sa setAccessToken */ - Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) + Q_OBJECT + Q_PROPERTY(QString userId READ userId CONSTANT) + QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) + QMC_DECLARE_SETTING(QString, deviceName, setDeviceName) + QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) + /** \deprecated \sa setAccessToken */ + Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) public: - template - explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) - : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) - { } - - QString userId() const; - - QUrl homeserver() const; - void setHomeserver(const QUrl& url); - - /** \deprecated \sa setToken */ - QString accessToken() const; - /** \deprecated Storing accessToken in QSettings is unsafe, - * see QMatrixClient/Quaternion#181 */ - void setAccessToken(const QString& accessToken); - Q_INVOKABLE void clearAccessToken(); + template + explicit AccountSettings(const QString& accountId, + ArgTs... qsettingsArgs) + : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) + { + } + + QString userId() const; + + QUrl homeserver() const; + void setHomeserver(const QUrl& url); + + /** \deprecated \sa setToken */ + QString accessToken() const; + /** \deprecated Storing accessToken in QSettings is unsafe, + * see QMatrixClient/Quaternion#181 */ + void setAccessToken(const QString& accessToken); + Q_INVOKABLE void clearAccessToken(); }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index f55d4396..d8fb7bb6 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "syncdata.h" @@ -30,17 +30,16 @@ const QString SyncRoomData::UnreadCountKey = bool RoomSummary::isEmpty() const { - return joinedMemberCount.omitted() && invitedMemberCount.omitted() && - heroes.omitted(); + return joinedMemberCount.omitted() && invitedMemberCount.omitted() + && heroes.omitted(); } bool RoomSummary::merge(const RoomSummary& other) { // Using bitwise OR to prevent computation shortcut. - return - joinedMemberCount.merge(other.joinedMemberCount) | - invitedMemberCount.merge(other.invitedMemberCount) | - heroes.merge(other.heroes); + return joinedMemberCount.merge(other.joinedMemberCount) + | invitedMemberCount.merge(other.invitedMemberCount) + | heroes.merge(other.heroes); } QDebug QMatrixClient::operator<<(QDebug dbg, const RoomSummary& rs) @@ -78,32 +77,33 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, template inline EventsArrayT load(const QJsonObject& batches, StrT keyName) { - return fromJson(batches[keyName].toObject().value("events"_ls)); + return fromJson( + batches[keyName].toObject().value("events"_ls)); } SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) - : roomId(roomId_) - , joinState(joinState_) - , summary(fromJson(room_["summary"])) - , state(load(room_, joinState == JoinState::Invite - ? "invite_state"_ls : "state"_ls)) + : roomId(roomId_), + joinState(joinState_), + summary(fromJson(room_["summary"])), + state(load(room_, + joinState == JoinState::Invite ? "invite_state"_ls + : "state"_ls)) { switch (joinState) { - case JoinState::Join: - ephemeral = load(room_, "ephemeral"_ls); - FALLTHROUGH; - case JoinState::Leave: - { - accountData = load(room_, "account_data"_ls); - timeline = load(room_, "timeline"_ls); - const auto timelineJson = room_.value("timeline"_ls).toObject(); - timelineLimited = timelineJson.value("limited"_ls).toBool(); - timelinePrevBatch = timelineJson.value("prev_batch"_ls).toString(); - - break; - } - default: /* nothing on top of state */; + case JoinState::Join: + ephemeral = load(room_, "ephemeral"_ls); + FALLTHROUGH; + case JoinState::Leave: { + accountData = load(room_, "account_data"_ls); + timeline = load(room_, "timeline"_ls); + const auto timelineJson = room_.value("timeline"_ls).toObject(); + timelineLimited = timelineJson.value("limited"_ls).toBool(); + timelinePrevBatch = timelineJson.value("prev_batch"_ls).toString(); + + break; + } + default: /* nothing on top of state */; } const auto unreadJson = room_.value("unread_notifications"_ls).toObject(); @@ -121,20 +121,17 @@ SyncData::SyncData(const QString& cacheFileName) QFileInfo cacheFileInfo { cacheFileName }; auto json = loadJson(cacheFileName); auto requiredVersion = std::get<0>(cacheVersion()); - auto actualVersion = json.value("cache_version").toObject() - .value("major").toInt(); + auto actualVersion = + json.value("cache_version").toObject().value("major").toInt(); if (actualVersion == requiredVersion) parseJson(json, cacheFileInfo.absolutePath() + '/'); else - qCWarning(MAIN) - << "Major version of the cache file is" << actualVersion << "but" - << requiredVersion << "is required; discarding the cache"; + qCWarning(MAIN) << "Major version of the cache file is" << actualVersion + << "but" << requiredVersion + << "is required; discarding the cache"; } -SyncDataList&& SyncData::takeRoomData() -{ - return move(roomData); -} +SyncDataList&& SyncData::takeRoomData() { return move(roomData); } QString SyncData::fileNameForRoom(QString roomId) { @@ -142,31 +139,20 @@ QString SyncData::fileNameForRoom(QString roomId) return roomId + ".json"; } -Events&& SyncData::takePresenceData() -{ - return std::move(presenceData); -} +Events&& SyncData::takePresenceData() { return std::move(presenceData); } -Events&& SyncData::takeAccountData() -{ - return std::move(accountData); -} +Events&& SyncData::takeAccountData() { return std::move(accountData); } -Events&& SyncData::takeToDeviceEvents() -{ - return std::move(toDeviceEvents); -} +Events&& SyncData::takeToDeviceEvents() { return std::move(toDeviceEvents); } QJsonObject SyncData::loadJson(const QString& fileName) { QFile roomFile { fileName }; - if (!roomFile.exists()) - { + if (!roomFile.exists()) { qCWarning(MAIN) << "No state cache file" << fileName; return {}; } - if(!roomFile.open(QIODevice::ReadOnly)) - { + if (!roomFile.open(QIODevice::ReadOnly)) { qCWarning(MAIN) << "Failed to open state cache file" << roomFile.fileName(); return {}; @@ -174,10 +160,10 @@ QJsonObject SyncData::loadJson(const QString& fileName) auto data = roomFile.readAll(); const auto json = - (data.startsWith('{') ? QJsonDocument::fromJson(data) - : QJsonDocument::fromBinaryData(data)).object(); - if (json.isEmpty()) - { + (data.startsWith('{') ? QJsonDocument::fromJson(data) + : QJsonDocument::fromBinaryData(data)) + .object(); + if (json.isEmpty()) { qCWarning(MAIN) << "State cache in" << fileName << "is broken or empty, discarding"; } @@ -186,7 +172,8 @@ QJsonObject SyncData::loadJson(const QString& fileName) void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) { - QElapsedTimer et; et.start(); + QElapsedTimer et; + et.start(); nextBatch_ = json.value("next_batch"_ls).toString(); presenceData = load(json, "presence"_ls); @@ -197,25 +184,22 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) JoinStates::Int ii = 1; // ii is used to make a JoinState value auto totalRooms = 0; auto totalEvents = 0; - for (size_t i = 0; i < JoinStateStrings.size(); ++i, ii <<= 1) - { + 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(rs.size())); - for(auto roomIt = rs.begin(); roomIt != rs.end(); ++roomIt) - { + for (auto roomIt = rs.begin(); roomIt != rs.end(); ++roomIt) { auto roomJson = roomIt->isObject() - ? roomIt->toObject() - : loadJson(baseDir + fileNameForRoom(roomIt.key())); - if (roomJson.isEmpty()) - { + ? roomIt->toObject() + : loadJson(baseDir + fileNameForRoom(roomIt.key())); + if (roomJson.isEmpty()) { unresolvedRoomIds.push_back(roomIt.key()); continue; } roomData.emplace_back(roomIt.key(), JoinState(ii), roomJson); const auto& r = roomData.back(); - totalEvents += r.state.size() + r.ephemeral.size() + - r.accountData.size() + r.timeline.size(); + totalEvents += r.state.size() + r.ephemeral.size() + + r.accountData.size() + r.timeline.size(); } totalRooms += rs.size(); } @@ -223,6 +207,6 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) qCWarning(MAIN) << "Unresolved rooms:" << unresolvedRoomIds.join(','); if (totalRooms > 9 || et.nsecsElapsed() >= profilerMinNsecs()) qCDebug(PROFILER) << "*** SyncData::parseJson(): batch with" - << totalRooms << "room(s)," - << totalEvents << "event(s) in" << et; + << totalRooms << "room(s)," << totalEvents + << "event(s) in" << et; } diff --git a/lib/syncdata.h b/lib/syncdata.h index 8694626e..139af130 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -13,13 +13,13 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include "joinstate.h" #include "events/stateevent.h" +#include "joinstate.h" namespace QMatrixClient { /// Room summary, as defined in MSC688 @@ -30,11 +30,11 @@ namespace QMatrixClient { * means that nothing has come from the server; heroes.value().isEmpty() * means a peculiar case of a room with the only member - the current user. */ - struct RoomSummary - { + struct RoomSummary { Omittable joinedMemberCount; Omittable invitedMemberCount; - Omittable heroes; //< mxids of users to take part in the room name + Omittable + heroes; //< mxids of users to take part in the room name bool isEmpty() const; /// Merge the contents of another RoomSummary object into this one @@ -44,9 +44,7 @@ namespace QMatrixClient { friend QDebug operator<<(QDebug dbg, const RoomSummary& rs); }; - template <> - struct JsonObjectConverter - { + template <> struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomSummary& rs); static void fillFrom(const QJsonObject& jo, RoomSummary& rs); }; @@ -54,26 +52,26 @@ namespace QMatrixClient { class SyncRoomData { public: - QString roomId; - JoinState joinState; - RoomSummary summary; - StateEvents state; - RoomEvents timeline; - Events ephemeral; - Events 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; + QString roomId; + JoinState joinState; + RoomSummary summary; + StateEvents state; + RoomEvents timeline; + Events ephemeral; + Events 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. @@ -82,35 +80,35 @@ namespace QMatrixClient { class SyncData { public: - SyncData() = default; - explicit SyncData(const QString& cacheFileName); - /** Parse sync response into room events - * \param json response from /sync or a room state cache - * \return the list of rooms with missing cache files; always - * empty when parsing response from /sync - */ - void parseJson(const QJsonObject& json, const QString& baseDir = {}); + SyncData() = default; + explicit SyncData(const QString& cacheFileName); + /** Parse sync response into room events + * \param json response from /sync or a room state cache + * \return the list of rooms with missing cache files; always + * empty when parsing response from /sync + */ + void parseJson(const QJsonObject& json, const QString& baseDir = {}); - Events&& takePresenceData(); - Events&& takeAccountData(); - Events&& takeToDeviceEvents(); - SyncDataList&& takeRoomData(); + Events&& takePresenceData(); + Events&& takeAccountData(); + Events&& takeToDeviceEvents(); + SyncDataList&& takeRoomData(); - QString nextBatch() const { return nextBatch_; } + QString nextBatch() const { return nextBatch_; } - QStringList unresolvedRooms() const { return unresolvedRoomIds; } + QStringList unresolvedRooms() const { return unresolvedRoomIds; } - static std::pair cacheVersion() { return { 10, 0 }; } - static QString fileNameForRoom(QString roomId); + static std::pair cacheVersion() { return { 10, 0 }; } + static QString fileNameForRoom(QString roomId); private: - QString nextBatch_; - Events presenceData; - Events accountData; - Events toDeviceEvents; - SyncDataList roomData; - QStringList unresolvedRoomIds; - - static QJsonObject loadJson(const QString& fileName); + QString nextBatch_; + Events presenceData; + Events accountData; + Events toDeviceEvents; + SyncDataList roomData; + QStringList unresolvedRoomIds; + + static QJsonObject loadJson(const QString& fileName); }; -} // namespace QMatrixClient +} // namespace QMatrixClient diff --git a/lib/user.cpp b/lib/user.cpp index eec41957..43792e74 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -13,25 +13,25 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "user.h" -#include "connection.h" -#include "room.h" #include "avatar.h" +#include "connection.h" +#include "csapi/content-repo.h" +#include "csapi/profile.h" +#include "csapi/room_state.h" #include "events/event.h" #include "events/roommemberevent.h" -#include "csapi/room_state.h" -#include "csapi/profile.h" -#include "csapi/content-repo.h" +#include "room.h" -#include -#include +#include #include +#include #include -#include +#include #include @@ -42,42 +42,38 @@ using std::move; class User::Private { public: - static Avatar makeAvatar(QUrl url) - { - return Avatar(move(url)); - } + static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } - Private(QString userId, Connection* connection) - : userId(move(userId)), connection(connection) - { } - - QString userId; - Connection* connection; - - QString bridged; - QString mostUsedName; - QMultiHash otherNames; - Avatar mostUsedAvatar { makeAvatar({}) }; - std::vector otherAvatars; - auto otherAvatar(QUrl url) - { - return std::find_if(otherAvatars.begin(), otherAvatars.end(), - [&url] (const auto& av) { return av.url() == url; }); - } - QMultiHash avatarsToRooms; + Private(QString userId, Connection* connection) + : userId(move(userId)), connection(connection) + { + } - mutable int totalRooms = 0; + QString userId; + Connection* connection; - QString nameForRoom(const Room* r, const QString& hint = {}) const; - void setNameForRoom(const Room* r, QString newName, QString oldName); - QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; - void setAvatarForRoom(const Room* r, const QUrl& newUrl, - const QUrl& oldUrl); + QString bridged; + QString mostUsedName; + QMultiHash otherNames; + Avatar mostUsedAvatar { makeAvatar({}) }; + std::vector otherAvatars; + auto otherAvatar(QUrl url) + { + return std::find_if(otherAvatars.begin(), otherAvatars.end(), + [&url](const auto& av) { return av.url() == url; }); + } + QMultiHash avatarsToRooms; - void setAvatarOnServer(QString contentUri, User* q); + mutable int totalRooms = 0; -}; + QString nameForRoom(const Room* r, const QString& hint = {}) const; + void setNameForRoom(const Room* r, QString newName, QString oldName); + QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; + void setAvatarForRoom(const Room* r, const QUrl& newUrl, + const QUrl& oldUrl); + void setAvatarOnServer(QString contentUri, User* q); +}; QString User::Private::nameForRoom(const Room* r, const QString& hint) const { @@ -94,30 +90,27 @@ void User::Private::setNameForRoom(const Room* r, QString newName, { Q_ASSERT(oldName != newName); Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r)); - if (totalRooms < 2) - { + if (totalRooms < 2) { Q_ASSERT_X(totalRooms > 0 && otherNames.empty(), __FUNCTION__, "Internal structures inconsistency"); mostUsedName = move(newName); return; } otherNames.remove(oldName, r); - if (newName != mostUsedName) - { + if (newName != mostUsedName) { // Check if the newName is about to become most used. - if (otherNames.count(newName) >= totalRooms - otherNames.size()) - { + if (otherNames.count(newName) >= totalRooms - otherNames.size()) { Q_ASSERT(totalRooms > 1); QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - { - qCDebug(MAIN) << "Switching the most used name of user" << userId - << "from" << mostUsedName << "to" << newName; + if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { + qCDebug(MAIN) + << "Switching the most used name of user" << userId + << "from" << mostUsedName << "to" << newName; qCDebug(MAIN) << "The user is in" << totalRooms << "rooms"; et.start(); } - for (auto* r1: connection->roomMap()) + for (auto* r1 : connection->roomMap()) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -125,8 +118,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, otherNames.remove(newName); if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) qCDebug(PROFILER) << et << "to switch the most used name"; - } - else + } else otherNames.insert(newName, r); } } @@ -144,40 +136,37 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, const QUrl& oldUrl) { Q_ASSERT(oldUrl != newUrl); - Q_ASSERT(oldUrl == mostUsedAvatar.url() || - avatarsToRooms.contains(oldUrl, r)); - if (totalRooms < 2) - { + Q_ASSERT(oldUrl == mostUsedAvatar.url() + || avatarsToRooms.contains(oldUrl, r)); + if (totalRooms < 2) { Q_ASSERT_X(totalRooms > 0 && otherAvatars.empty(), __FUNCTION__, "Internal structures inconsistency"); mostUsedAvatar.updateUrl(newUrl); return; } avatarsToRooms.remove(oldUrl, r); - if (!avatarsToRooms.contains(oldUrl)) - { + if (!avatarsToRooms.contains(oldUrl)) { auto it = otherAvatar(oldUrl); if (it != otherAvatars.end()) otherAvatars.erase(it); } - if (newUrl != mostUsedAvatar.url()) - { + if (newUrl != mostUsedAvatar.url()) { // Check if the new avatar is about to become most used. - if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size()) - { + if (avatarsToRooms.count(newUrl) + >= totalRooms - avatarsToRooms.size()) { QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - { - qCDebug(MAIN) << "Switching the most used avatar of user" << userId - << "from" << mostUsedAvatar.url().toDisplayString() - << "to" << newUrl.toDisplayString(); + if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { + qCDebug(MAIN) + << "Switching the most used avatar of user" << userId + << "from" << mostUsedAvatar.url().toDisplayString() + << "to" << newUrl.toDisplayString(); et.start(); } avatarsToRooms.remove(newUrl); auto nextMostUsedIt = otherAvatar(newUrl); Q_ASSERT(nextMostUsedIt != otherAvatars.end()); std::swap(mostUsedAvatar, *nextMostUsedIt); - for (const auto* r1: connection->roomMap()) + for (const auto* r1 : connection->roomMap()) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); @@ -205,29 +194,23 @@ Connection* User::connection() const User::~User() = default; -QString User::id() const -{ - return d->userId; -} +QString User::id() const { return d->userId; } bool User::isGuest() const { Q_ASSERT(!d->userId.isEmpty() && d->userId.startsWith('@')); auto it = std::find_if_not(d->userId.begin() + 1, d->userId.end(), - [] (QChar c) { return c.isDigit(); }); + [](QChar c) { return c.isDigit(); }); Q_ASSERT(it != d->userId.end()); return *it == ':'; } -QString User::name(const Room* room) const -{ - return d->nameForRoom(room); -} +QString User::name(const Room* room) const { return d->nameForRoom(room); } QString User::rawName(const Room* room) const { - return d->bridged.isEmpty() ? name(room) : - name(room) % " (" % d->bridged % ')'; + return d->bridged.isEmpty() ? name(room) + : name(room) % " (" % d->bridged % ')'; } void User::updateName(const QString& newName, const Room* room) @@ -238,9 +221,9 @@ void User::updateName(const QString& newName, const Room* room) void User::updateName(const QString& newName, const QString& oldName, const Room* room) { - Q_ASSERT(oldName == d->mostUsedName || d->otherNames.contains(oldName, room)); - if (newName != oldName) - { + Q_ASSERT(oldName == d->mostUsedName + || d->otherNames.contains(oldName, room)); + if (newName != oldName) { emit nameAboutToChange(newName, oldName, room); d->setNameForRoom(room, newName, oldName); setObjectName(displayname()); @@ -251,15 +234,13 @@ void User::updateName(const QString& newName, const QString& oldName, void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, const Room* room) { - Q_ASSERT(oldUrl == d->mostUsedAvatar.url() || - d->avatarsToRooms.contains(oldUrl, room)); - if (newUrl != oldUrl) - { + Q_ASSERT(oldUrl == d->mostUsedAvatar.url() + || d->avatarsToRooms.contains(oldUrl, room)); + if (newUrl != oldUrl) { d->setAvatarForRoom(room, newUrl, oldUrl); setObjectName(displayname()); emit avatarChanged(this, room); } - } void User::rename(const QString& newName) @@ -270,8 +251,7 @@ void User::rename(const QString& newName) void User::rename(const QString& newName, const Room* r) { - if (!r) - { + if (!r) { qCWarning(MAIN) << "Passing a null room to two-argument User::rename()" "is incorrect; client developer, please fix it"; rename(newName); @@ -287,35 +267,25 @@ void User::rename(const QString& newName, const Room* r) bool User::setAvatar(const QString& fileName) { - return avatarObject().upload(connection(), fileName, - std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); + return avatarObject().upload( + connection(), fileName, + std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); } bool User::setAvatar(QIODevice* source) { - return avatarObject().upload(connection(), source, - std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); + return avatarObject().upload( + connection(), source, + std::bind(&Private::setAvatarOnServer, d.data(), _1, this)); } -void User::requestDirectChat() -{ - connection()->requestDirectChat(this); -} +void User::requestDirectChat() { connection()->requestDirectChat(this); } -void User::ignore() -{ - connection()->addToIgnoredUsers(this); -} +void User::ignore() { connection()->addToIgnoredUsers(this); } -void User::unmarkIgnore() -{ - connection()->removeFromIgnoredUsers(this); -} +void User::unmarkIgnore() { connection()->removeFromIgnoredUsers(this); } -bool User::isIgnored() const -{ - return connection()->isIgnored(this); -} +bool User::isIgnored() const { return connection()->isIgnored(this); } void User::Private::setAvatarOnServer(QString contentUri, User* q) { @@ -339,10 +309,7 @@ QString User::fullName(const Room* room) const return name.isEmpty() ? d->userId : name % " (" % d->userId % ')'; } -QString User::bridged() const -{ - return d->bridged; -} +QString User::bridged() const { return d->bridged; } const Avatar& User::avatarObject(const Room* room) const { @@ -357,14 +324,16 @@ QImage User::avatar(int dimension, const Room* room) QImage User::avatar(int width, int height, const Room* room) { - return avatar(width, height, room, []{}); + return avatar(width, height, room, [] {}); } QImage User::avatar(int width, int height, const Room* room, const Avatar::get_callback_t& callback) { - return avatarObject(room).get(d->connection, width, height, - [=] { emit avatarChanged(this, room); callback(); }); + return avatarObject(room).get(d->connection, width, height, [=] { + emit avatarChanged(this, room); + callback(); + }); } QString User::avatarMediaId(const Room* room) const @@ -380,13 +349,13 @@ QUrl User::avatarUrl(const Room* room) const void User::processEvent(const RoomMemberEvent& event, const Room* room) { Q_ASSERT(room); - if (event.membership() != MembershipType::Invite && - event.membership() != MembershipType::Join) + if (event.membership() != MembershipType::Invite + && event.membership() != MembershipType::Join) return; - auto aboutToEnter = room->memberJoinState(this) == JoinState::Leave && - (event.membership() == MembershipType::Join || - event.membership() == MembershipType::Invite); + auto aboutToEnter = room->memberJoinState(this) == JoinState::Leave + && (event.membership() == MembershipType::Join + || event.membership() == MembershipType::Invite); if (aboutToEnter) ++d->totalRooms; @@ -399,26 +368,25 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room) // bridge tags are not supported at all. QRegularExpression reSuffix(" \\((IRC|Gitter|Telegram)\\)$"); auto match = reSuffix.match(newName); - if (match.hasMatch()) - { - if (d->bridged != match.captured(1)) - { + if (match.hasMatch()) { + if (d->bridged != match.captured(1)) { if (!d->bridged.isEmpty()) - qCWarning(MAIN) << "Bridge for user" << id() << "changed:" - << d->bridged << "->" << match.captured(1); + qCWarning(MAIN) + << "Bridge for user" << id() << "changed:" << d->bridged + << "->" << match.captured(1); d->bridged = match.captured(1); } newName.truncate(match.capturedStart(0)); } - if (event.prevContent()) - { + if (event.prevContent()) { // FIXME: the hint doesn't work for bridged users auto oldNameHint = d->nameForRoom(room, event.prevContent()->displayName); updateName(newName, oldNameHint, room); - updateAvatarUrl(event.avatarUrl(), - d->avatarUrlForRoom(room, event.prevContent()->avatarUrl), - room); + updateAvatarUrl( + event.avatarUrl(), + d->avatarUrlForRoom(room, event.prevContent()->avatarUrl), + room); } else { updateName(newName, room); updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room); diff --git a/lib/user.h b/lib/user.h index 0023b44a..c174314e 100644 --- a/lib/user.h +++ b/lib/user.h @@ -13,138 +13,141 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once -#include -#include #include "avatar.h" +#include +#include -namespace QMatrixClient -{ +namespace QMatrixClient { class Connection; class Room; class RoomMemberEvent; - class User: public QObject + class User : public QObject { - Q_OBJECT - Q_PROPERTY(QString id READ id CONSTANT) - Q_PROPERTY(bool isGuest READ isGuest CONSTANT) - Q_PROPERTY(QString name READ name NOTIFY nameChanged) - Q_PROPERTY(QString displayName READ displayname NOTIFY nameChanged STORED false) - Q_PROPERTY(QString fullName READ fullName NOTIFY nameChanged STORED false) - Q_PROPERTY(QString bridgeName READ bridged NOTIFY nameChanged STORED false) - Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) - Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) + Q_OBJECT + Q_PROPERTY(QString id READ id CONSTANT) + Q_PROPERTY(bool isGuest READ isGuest CONSTANT) + Q_PROPERTY(QString name READ name NOTIFY nameChanged) + Q_PROPERTY(QString displayName READ displayname NOTIFY nameChanged + STORED false) + Q_PROPERTY( + QString fullName READ fullName NOTIFY nameChanged STORED false) + Q_PROPERTY( + QString bridgeName READ bridged NOTIFY nameChanged STORED false) + Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged + STORED false) + Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) public: - User(QString userId, Connection* connection); - ~User() override; - - Connection* connection() const; - - /** Get unique stable user id - * User id is generated by the server and is not changed ever. - */ - QString id() const; - - /** Get the name chosen by the user - * This may be empty if the user didn't choose the name or cleared - * it. If the user is bridged, the bridge postfix (such as '(IRC)') - * is stripped out. No disambiguation for the room is done. - * \sa displayName, rawName - */ - QString name(const Room* room = nullptr) const; - - /** Get the user name along with the bridge postfix - * This function is similar to name() but appends the bridge postfix - * (such as '(IRC)') to the user name. No disambiguation is done. - * \sa name, displayName - */ - QString rawName(const Room* room = nullptr) const; - - /** Get the displayed user name - * When \p room is null, this method returns result of name() if - * the name is non-empty; otherwise it returns user id. - * When \p room is non-null, this call is equivalent to - * Room::roomMembername invocation for the user (i.e. the user's - * disambiguated room-specific name is returned). - * \sa name, id, fullName, Room::roomMembername - */ - QString displayname(const Room* room = nullptr) const; - - /** Get user name and id in one string - * The constructed string follows the format 'name (id)' - * which the spec recommends for users disambiguation in - * a room context and in other places. - * \sa displayName, Room::roomMembername - */ - QString fullName(const Room* room = nullptr) const; - - /** - * Returns the name of bridge the user is connected from or empty. - */ - QString bridged() const; - - /** Whether the user is a guest - * As of now, the function relies on the convention used in Synapse - * that guests and only guests have all-numeric IDs. This may or - * may not work with non-Synapse servers. - */ - bool isGuest() const; - - const Avatar& avatarObject(const Room* room = nullptr) const; - Q_INVOKABLE QImage avatar(int dimension, const Room* room = nullptr); - Q_INVOKABLE QImage avatar(int requestedWidth, int requestedHeight, - const Room* room = nullptr); - QImage avatar(int width, int height, const Room* room, - const Avatar::get_callback_t& callback); - - QString avatarMediaId(const Room* room = nullptr) const; - QUrl avatarUrl(const Room* room = nullptr) const; - - void processEvent(const RoomMemberEvent& event, const Room* r); + User(QString userId, Connection* connection); + ~User() override; + + Connection* connection() const; + + /** Get unique stable user id + * User id is generated by the server and is not changed ever. + */ + QString id() const; + + /** Get the name chosen by the user + * This may be empty if the user didn't choose the name or cleared + * it. If the user is bridged, the bridge postfix (such as '(IRC)') + * is stripped out. No disambiguation for the room is done. + * \sa displayName, rawName + */ + QString name(const Room* room = nullptr) const; + + /** Get the user name along with the bridge postfix + * This function is similar to name() but appends the bridge postfix + * (such as '(IRC)') to the user name. No disambiguation is done. + * \sa name, displayName + */ + QString rawName(const Room* room = nullptr) const; + + /** Get the displayed user name + * When \p room is null, this method returns result of name() if + * the name is non-empty; otherwise it returns user id. + * When \p room is non-null, this call is equivalent to + * Room::roomMembername invocation for the user (i.e. the user's + * disambiguated room-specific name is returned). + * \sa name, id, fullName, Room::roomMembername + */ + QString displayname(const Room* room = nullptr) const; + + /** Get user name and id in one string + * The constructed string follows the format 'name (id)' + * which the spec recommends for users disambiguation in + * a room context and in other places. + * \sa displayName, Room::roomMembername + */ + QString fullName(const Room* room = nullptr) const; + + /** + * Returns the name of bridge the user is connected from or empty. + */ + QString bridged() const; + + /** Whether the user is a guest + * As of now, the function relies on the convention used in Synapse + * that guests and only guests have all-numeric IDs. This may or + * may not work with non-Synapse servers. + */ + bool isGuest() const; + + const Avatar& avatarObject(const Room* room = nullptr) const; + Q_INVOKABLE QImage avatar(int dimension, const Room* room = nullptr); + Q_INVOKABLE QImage avatar(int requestedWidth, int requestedHeight, + const Room* room = nullptr); + QImage avatar(int width, int height, const Room* room, + const Avatar::get_callback_t& callback); + + QString avatarMediaId(const Room* room = nullptr) const; + QUrl avatarUrl(const Room* room = nullptr) const; + + void processEvent(const RoomMemberEvent& event, const Room* r); public slots: - /** Set a new name in the global user profile */ - void rename(const QString& newName); - /** Set a new name for the user in one room */ - void rename(const QString& newName, const Room* r); - /** Upload the file and use it as an avatar */ - bool setAvatar(const QString& fileName); - /** Upload contents of the QIODevice and set that as an avatar */ - bool setAvatar(QIODevice* source); - /** Create or find a direct chat with this user - * The resulting chat is returned asynchronously via - * Connection::directChatAvailable() - */ - void requestDirectChat(); - /** Add the user to the ignore list */ - void ignore(); - /** Remove the user from the ignore list */ - void unmarkIgnore(); - /** Check whether the user is in ignore list */ - bool isIgnored() const; + /** Set a new name in the global user profile */ + void rename(const QString& newName); + /** Set a new name for the user in one room */ + void rename(const QString& newName, const Room* r); + /** Upload the file and use it as an avatar */ + bool setAvatar(const QString& fileName); + /** Upload contents of the QIODevice and set that as an avatar */ + bool setAvatar(QIODevice* source); + /** Create or find a direct chat with this user + * The resulting chat is returned asynchronously via + * Connection::directChatAvailable() + */ + void requestDirectChat(); + /** Add the user to the ignore list */ + void ignore(); + /** Remove the user from the ignore list */ + void unmarkIgnore(); + /** Check whether the user is in ignore list */ + bool isIgnored() const; signals: - void nameAboutToChange(QString newName, QString oldName, - const Room* roomContext); - void nameChanged(QString newName, QString oldName, - const Room* roomContext); - void avatarChanged(User* user, const Room* roomContext); + void nameAboutToChange(QString newName, QString oldName, + const Room* roomContext); + void nameChanged(QString newName, QString oldName, + const Room* roomContext); + void avatarChanged(User* user, const Room* roomContext); private slots: - void updateName(const QString& newName, const Room* room = nullptr); - void updateName(const QString& newName, const QString& oldName, - const Room* room = nullptr); - void updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, - const Room* room = nullptr); + void updateName(const QString& newName, const Room* room = nullptr); + void updateName(const QString& newName, const QString& oldName, + const Room* room = nullptr); + void updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, + const Room* room = nullptr); private: - class Private; - QScopedPointer d; + class Private; + QScopedPointer d; }; } Q_DECLARE_METATYPE(QMatrixClient::User*) diff --git a/lib/util.cpp b/lib/util.cpp index d042aa34..a7c745d4 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -13,20 +13,19 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "util.h" +#include #include #include -#include #include -static const auto RegExpOptions = - QRegularExpression::CaseInsensitiveOption - | QRegularExpression::OptimizeOnFirstUsageOption - | QRegularExpression::UseUnicodePropertiesOption; +static const auto RegExpOptions = QRegularExpression::CaseInsensitiveOption + | QRegularExpression::OptimizeOnFirstUsageOption + | QRegularExpression::UseUnicodePropertiesOption; // Converts all that looks like a URL into HTML links static void linkifyUrls(QString& htmlEscapedText) @@ -39,34 +38,37 @@ static void linkifyUrls(QString& htmlEscapedText) // Note: outer parentheses are a part of C++ raw string delimiters, not of // the regex (see http://en.cppreference.com/w/cpp/language/string_literal). // Note2: yet another pair of outer parentheses are \1 in the replacement. - static const QRegularExpression FullUrlRegExp(QStringLiteral( - R"(((www\.(?!\.)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" - ), RegExpOptions); + static const QRegularExpression FullUrlRegExp( + QStringLiteral( + R"(((www\.(?!\.)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))"), + RegExpOptions); // email address: // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] - static const QRegularExpression EmailAddressRegExp(QStringLiteral( - R"((mailto:)?(\b(\w|\.|-)+@(\w|\.|-)+\.\w+\b))" - ), RegExpOptions); + static const QRegularExpression EmailAddressRegExp( + QStringLiteral(R"((mailto:)?(\b(\w|\.|-)+@(\w|\.|-)+\.\w+\b))"), + RegExpOptions); // An interim liberal implementation of // https://matrix.org/docs/spec/appendices.html#identifier-grammar - static const QRegularExpression MxIdRegExp(QStringLiteral( - R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:[-.a-z0-9]+))" - ), RegExpOptions); + static const QRegularExpression MxIdRegExp( + QStringLiteral( + R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:[-.a-z0-9]+))"), + RegExpOptions); // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,& htmlEscapedText.replace(EmailAddressRegExp, - QStringLiteral(R"(\1\2)")); + QStringLiteral(R"(\1\2)")); htmlEscapedText.replace(FullUrlRegExp, - QStringLiteral(R"(\1)")); - htmlEscapedText.replace(MxIdRegExp, - QStringLiteral(R"(\1\2)")); + QStringLiteral(R"(\1)")); + htmlEscapedText.replace( + MxIdRegExp, + QStringLiteral(R"(\1\2)")); } QString QMatrixClient::prettyPrint(const QString& plainText) { - auto pt = QStringLiteral("") + - plainText.toHtmlEscaped() + QStringLiteral(""); + auto pt = QStringLiteral("") + + plainText.toHtmlEscaped() + QStringLiteral(""); pt.replace('\n', QStringLiteral("
")); linkifyUrls(pt); @@ -76,7 +78,7 @@ QString QMatrixClient::prettyPrint(const QString& plainText) QString QMatrixClient::cacheLocation(const QString& dirName) { const QString cachePath = - QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStandardPaths::writableLocation(QStandardPaths::CacheLocation) % '/' % dirName % '/'; QDir dir; if (!dir.exists(cachePath)) @@ -104,17 +106,24 @@ void f2(int, QString); static_assert(std::is_same, QString>::value, "Test fn_arg_t<>"); -struct S { int mf(); }; +struct S { + int mf(); +}; static_assert(is_callable_v, "Test member function"); -static_assert(returns(), "Test returns<> with member function"); +static_assert(returns(), + "Test returns<> with member function"); -struct Fo { int operator()(); }; +struct Fo { + int operator()(); +}; static_assert(is_callable_v, "Test is_callable<> with function object"); static_assert(function_traits::arg_number == 0, "Test function object"); static_assert(std::is_same, int>::value, "Test return type of function object"); -struct Fo1 { void operator()(int); }; +struct Fo1 { + void operator()(int); +}; static_assert(function_traits::arg_number == 1, "Test function object 1"); static_assert(is_callable_v, "Test is_callable<> with function object 1"); static_assert(std::is_same, int>(), @@ -127,14 +136,10 @@ static_assert(std::is_same, int>::value, "Test fn_return_t<> with lambda"); #endif -template -struct fn_object -{ +template struct fn_object { static int smf(double) { return 0; } }; -template <> -struct fn_object -{ +template <> struct fn_object { void operator()(QString); }; static_assert(is_callable_v>, "Test function object"); @@ -142,13 +147,12 @@ static_assert(returns>(), "Test returns<> with function object"); static_assert(!is_callable_v>, "Test non-function object"); // FIXME: These two don't work -//static_assert(is_callable_v::smf)>, +// static_assert(is_callable_v::smf)>, // "Test static member function"); -//static_assert(returns::smf)>(), +// static_assert(returns::smf)>(), // "Test returns<> with static member function"); -template -QString ft(T&&); +template QString ft(T&&); static_assert(std::is_same)>, QString&&>(), "Test function templates"); diff --git a/lib/util.h b/lib/util.h index f7f646da..fbcafc0d 100644 --- a/lib/util.h +++ b/lib/util.h @@ -13,7 +13,7 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -21,8 +21,8 @@ #include #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) -#include #include +#include #endif #include @@ -39,41 +39,43 @@ #endif // Along the lines of Q_DISABLE_COPY -#define DISABLE_MOVE(_ClassName) \ - _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ +#define DISABLE_MOVE(_ClassName) \ + _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) // Copy-pasted from Qt 5.10 template -Q_DECL_CONSTEXPR typename std::add_const::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } +Q_DECL_CONSTEXPR typename std::add_const::type& qAsConst(T& t) Q_DECL_NOTHROW +{ + return t; +} // prevent rvalue arguments: -template -static void qAsConst(const T &&) Q_DECL_EQ_DELETE; +template static void qAsConst(const T&&) Q_DECL_EQ_DELETE; #endif // MSVC 2015 and older GCC's don't handle initialisation from initializer lists // right in the absense of a constructor; MSVC 2015, notably, fails with // "error C2440: 'return': cannot convert from 'initializer list' to ''" -#if (defined(_MSC_VER) && _MSC_VER < 1910) || \ - (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) -# define BROKEN_INITIALIZER_LISTS +#if (defined(_MSC_VER) && _MSC_VER < 1910) \ + || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) +#define BROKEN_INITIALIZER_LISTS #endif -namespace QMatrixClient -{ +namespace QMatrixClient { // The below enables pretty-printing of enums in logs #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) #define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) #else // Thanks to Olivier for spelling it and for making Q_ENUM to replace it: // https://woboq.com/blog/q_enum.html -#define REGISTER_ENUM(EnumName) \ - Q_ENUMS(EnumName) \ - friend QDebug operator<<(QDebug dbg, EnumName val) \ - { \ - static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ - return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey(int(val)); \ +#define REGISTER_ENUM(EnumName) \ + Q_ENUMS(EnumName) \ + friend QDebug operator<<(QDebug dbg, EnumName val) \ + { \ + static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ + return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey( \ + int(val)); \ } #endif @@ -84,97 +86,101 @@ namespace QMatrixClient return std::unique_ptr(static_cast(p.release())); } - struct NoneTag {}; + struct NoneTag { + }; constexpr NoneTag none {}; /** A crude substitute for `optional` while we're not C++17 * * Only works with default-constructible types. */ - template - class Omittable + template class Omittable { - static_assert(!std::is_reference::value, - "You cannot make an Omittable<> with a reference type"); + static_assert(!std::is_reference::value, + "You cannot make an Omittable<> with a reference type"); + public: - using value_type = std::decay_t; - - explicit Omittable() : Omittable(none) { } - Omittable(NoneTag) : _value(value_type()), _omitted(true) { } - Omittable(const value_type& val) : _value(val) { } - Omittable(value_type&& val) : _value(std::move(val)) { } - Omittable& operator=(const value_type& val) - { - _value = val; - _omitted = false; - return *this; - } - Omittable& operator=(value_type&& val) - { - // For some reason GCC complains about -Wmaybe-uninitialized - // in the context of using Omittable with converters.h; - // though the logic looks very much benign (GCC bug???) - _value = std::move(val); - _omitted = false; - return *this; - } - - bool operator==(const value_type& rhs) const - { - return !omitted() && value() == rhs; - } - friend bool operator==(const value_type& lhs, - const Omittable& rhs) - { - return rhs == lhs; - } - bool operator!=(const value_type& rhs) const - { - return !operator==(rhs); - } - friend bool operator!=(const value_type& lhs, - const Omittable& rhs) - { - return !(rhs == lhs); - } - - bool omitted() const { return _omitted; } - const value_type& value() const - { - Q_ASSERT(!_omitted); - return _value; - } - value_type& editValue() - { - _omitted = false; - return _value; - } - /// Merge the value from another Omittable - /// \return true if \p other is not omitted and the value of - /// the current Omittable was different (or omitted); - /// in other words, if the current Omittable has changed; - /// false otherwise - template - auto merge(const Omittable& other) + using value_type = std::decay_t; + + explicit Omittable() : Omittable(none) {} + Omittable(NoneTag) : _value(value_type()), _omitted(true) {} + Omittable(const value_type& val) : _value(val) {} + Omittable(value_type&& val) : _value(std::move(val)) {} + Omittable& operator=(const value_type& val) + { + _value = val; + _omitted = false; + return *this; + } + Omittable& operator=(value_type&& val) + { + // For some reason GCC complains about -Wmaybe-uninitialized + // in the context of using Omittable with converters.h; + // though the logic looks very much benign (GCC bug???) + _value = std::move(val); + _omitted = false; + return *this; + } + + bool operator==(const value_type& rhs) const + { + return !omitted() && value() == rhs; + } + friend bool operator==(const value_type& lhs, + const Omittable& rhs) + { + return rhs == lhs; + } + bool operator!=(const value_type& rhs) const + { + return !operator==(rhs); + } + friend bool operator!=(const value_type& lhs, + const Omittable& rhs) + { + return !(rhs == lhs); + } + + bool omitted() const { return _omitted; } + const value_type& value() const + { + Q_ASSERT(!_omitted); + return _value; + } + value_type& editValue() + { + _omitted = false; + return _value; + } + /// Merge the value from another Omittable + /// \return true if \p other is not omitted and the value of + /// the current Omittable was different (or omitted); + /// in other words, if the current Omittable has changed; + /// false otherwise + template + auto merge(const Omittable& other) -> std::enable_if_t::value, bool> - { - if (other.omitted() || - (!_omitted && _value == other.value())) - return false; - _omitted = false; - _value = other.value(); - return true; - } - value_type&& release() { _omitted = true; return std::move(_value); } - - const value_type* operator->() const & { return &value(); } - value_type* operator->() & { return &editValue(); } - const value_type& operator*() const & { return value(); } - value_type& operator*() & { return editValue(); } + { + if (other.omitted() || (!_omitted && _value == other.value())) + return false; + _omitted = false; + _value = other.value(); + return true; + } + value_type&& release() + { + _omitted = true; + return std::move(_value); + } + + const value_type* operator->() const& { return &value(); } + value_type* operator->() & { return &editValue(); } + const value_type& operator*() const& { return value(); } + value_type& operator*() & { return editValue(); } private: - T _value; - bool _omitted = false; + T _value; + bool _omitted = false; }; namespace _impl { @@ -184,15 +190,16 @@ namespace QMatrixClient /** Determine traits of an arbitrary function/lambda/functor * Doesn't work with generic lambdas and function objects that have * operator() overloaded. - * \sa https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 + * \sa + * https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 */ template - struct function_traits : public _impl::fn_traits {}; + struct function_traits : public _impl::fn_traits { + }; // Specialisation for a function template - struct function_traits - { + struct function_traits { static constexpr auto is_callable = true; using return_type = ReturnT; using arg_types = std::tuple; @@ -201,39 +208,37 @@ namespace QMatrixClient }; namespace _impl { - template - struct fn_traits - { + template struct fn_traits { static constexpr auto is_callable = false; }; template struct fn_traits - : public fn_traits - { }; // A generic function object that has (non-overloaded) operator() + : public fn_traits { + }; // A generic function object that has (non-overloaded) operator() // Specialisation for a member function template - struct fn_traits - : function_traits - { }; + struct fn_traits + : function_traits { + }; // Specialisation for a const member function template - struct fn_traits - : function_traits - { }; - } // namespace _impl + struct fn_traits + : function_traits { + }; + } // namespace _impl template using fn_return_t = typename function_traits::return_type; template using fn_arg_t = - std::tuple_element_t::arg_types>; + std::tuple_element_t::arg_types>; - template - constexpr bool returns() + template constexpr bool returns() { return std::is_same, R>::value; } @@ -251,31 +256,31 @@ namespace QMatrixClient * This is a very basic range type over a container with iterators that * are at least ForwardIterators. Inspired by Ranges TS. */ - template - class Range + template class Range { - // Looking forward for Ranges TS to produce something (in C++23?..) - using iterator = typename ArrayT::iterator; - using const_iterator = typename ArrayT::const_iterator; - using size_type = typename ArrayT::size_type; + // Looking forward for Ranges TS to produce something (in C++23?..) + using iterator = typename ArrayT::iterator; + using const_iterator = typename ArrayT::const_iterator; + using size_type = typename ArrayT::size_type; + public: - Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) { } - Range(iterator from, iterator to) : from(from), to(to) { } - - size_type size() const - { - Q_ASSERT(std::distance(from, to) >= 0); - return size_type(std::distance(from, to)); - } - bool empty() const { return from == to; } - const_iterator begin() const { return from; } - const_iterator end() const { return to; } - iterator begin() { return from; } - iterator end() { return to; } + Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {} + Range(iterator from, iterator to) : from(from), to(to) {} + + size_type size() const + { + Q_ASSERT(std::distance(from, to) >= 0); + return size_type(std::distance(from, to)); + } + bool empty() const { return from == to; } + const_iterator begin() const { return from; } + const_iterator end() const { return to; } + iterator begin() { return from; } + iterator end() { return to; } private: - iterator from; - iterator to; + iterator from; + iterator to; }; /** A replica of std::find_first_of that returns a pair of iterators @@ -283,10 +288,10 @@ namespace QMatrixClient * Convenient for cases when you need to know which particular "first of" * [sFirst, sLast) has been found in [first, last). */ - template - inline std::pair findFirstOf( - InputIt first, InputIt last, ForwardIt sFirst, ForwardIt sLast, - Pred pred) + template + inline std::pair + findFirstOf(InputIt first, InputIt last, ForwardIt sFirst, ForwardIt sLast, + Pred pred) { for (; first != last; ++first) for (auto it = sFirst; it != sLast; ++it) @@ -306,5 +311,4 @@ namespace QMatrixClient * \param dir path to cache directory relative to the standard cache path */ QString cacheLocation(const QString& dirName); -} // namespace QMatrixClient - +} // namespace QMatrixClient -- cgit v1.2.3 From ced57b074d0dc4e6e582a4bddca7a07e41d2063f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 12 Mar 2019 21:16:54 +0900 Subject: Make Room::prettyPrint Q_INVOKABLE So that it can be called from QML code --- lib/room.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index f4ecef42..f7add412 100644 --- a/lib/room.h +++ b/lib/room.h @@ -365,7 +365,7 @@ namespace QMatrixClient * in the future, it will also linkify room aliases, mxids etc. * using the room context. */ - QString prettyPrint(const QString& plainText) const; + Q_INVOKABLE QString prettyPrint(const QString& plainText) const; MemberSorter memberSorter() const; -- cgit v1.2.3 From b6b4b5e82c8984c0c7e29c4ccf5c2d25cda563f9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 13 Mar 2019 18:07:03 +0900 Subject: Room: make notificationCount, highlightCount Q_PROPERTYs Closes #299. Due to restrictions for the NOTIFY signal notificationCountChanged and highlightCountChanged no more carry Room* as a parameter, breaking back-compatibility. --- lib/room.cpp | 8 ++++---- lib/room.h | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index f2e03e94..a4946b65 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -815,7 +815,7 @@ void Room::resetNotificationCount() if( d->notificationCount == 0 ) return; d->notificationCount = 0; - emit notificationCountChanged(this); + emit notificationCountChanged(); } int Room::highlightCount() const @@ -828,7 +828,7 @@ void Room::resetHighlightCount() if( d->highlightCount == 0 ) return; d->highlightCount = 0; - emit highlightCountChanged(this); + emit highlightCountChanged(); } void Room::switchVersion(QString newVersion) @@ -1343,12 +1343,12 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) if( data.highlightCount != d->highlightCount ) { d->highlightCount = data.highlightCount; - emit highlightCountChanged(this); + emit highlightCountChanged(); } if( data.notificationCount != d->notificationCount ) { d->notificationCount = data.notificationCount; - emit notificationCountChanged(this); + emit notificationCountChanged(); } if (roomChanges != Change::NoChange) { diff --git a/lib/room.h b/lib/room.h index f7add412..352c098b 100644 --- a/lib/room.h +++ b/lib/room.h @@ -108,6 +108,8 @@ namespace QMatrixClient Q_PROPERTY(QString readMarkerEventId READ readMarkerEventId WRITE markMessagesAsRead NOTIFY readMarkerMoved) Q_PROPERTY(bool hasUnreadMessages READ hasUnreadMessages NOTIFY unreadMessagesChanged) Q_PROPERTY(int unreadCount READ unreadCount NOTIFY unreadMessagesChanged) + Q_PROPERTY(int highlightCount READ highlightCount NOTIFY highlightCountChanged RESET resetHighlightCount) + Q_PROPERTY(int notificationCount READ notificationCount NOTIFY notificationCountChanged RESET resetNotificationCount) Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged) Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY tagsChanged) Q_PROPERTY(bool isLowPriority READ isLowPriority NOTIFY tagsChanged) @@ -517,8 +519,8 @@ namespace QMatrixClient void joinStateChanged(JoinState oldState, JoinState newState); void typingChanged(); - void highlightCountChanged(Room* room); - void notificationCountChanged(Room* room); + void highlightCountChanged(); + void notificationCountChanged(); void displayedChanged(bool displayed); void firstDisplayedEventChanged(); -- cgit v1.2.3 From d6cddf3e016a4fe3e906e2ab815a1e8b775c284c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 14 Mar 2019 07:42:24 +0900 Subject: Fix read receipts and redactions on v3 rooms Previously slashes in eventIds (that come plenty in v3 due to base64 encoding) were not properly encoded - they are now. --- lib/jobs/basejob.cpp | 2 +- lib/room.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 8c3381ae..f738ce7a 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -186,7 +186,7 @@ QUrl BaseJob::makeRequestUrl(QUrl baseUrl, if (!pathBase.endsWith('/') && !path.startsWith('/')) pathBase.push_back('/'); - baseUrl.setPath( pathBase + path ); + baseUrl.setPath(pathBase + path, QUrl::TolerantMode); baseUrl.setQuery(query); return baseUrl; } diff --git a/lib/room.cpp b/lib/room.cpp index a4946b65..7494917d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -579,8 +579,8 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) { if ((*upToMarker)->senderId() != q->localUser()->id()) { - connection->callApi(id, "m.read", - (*upToMarker)->id()); + connection->callApi(id, QStringLiteral("m.read"), + QUrl::toPercentEncoding((*upToMarker)->id())); break; } } @@ -1734,8 +1734,8 @@ void Room::unban(const QString& userId) void Room::redactEvent(const QString& eventId, const QString& reason) { - connection()->callApi( - id(), eventId, connection()->generateTxnId(), reason); + connection()->callApi(id(), + QUrl::toPercentEncoding(eventId), connection()->generateTxnId(), reason); } void Room::uploadFile(const QString& id, const QUrl& localFilename, -- cgit v1.2.3 From 266f4b8a82fa437bbd49c8e3d85313677e068699 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 21 Mar 2019 15:41:06 +0900 Subject: Room::displayName: fix NOTIFY signal for Q_PROPERTY --- lib/room.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 352c098b..85c67bfb 100644 --- a/lib/room.h +++ b/lib/room.h @@ -88,7 +88,7 @@ namespace QMatrixClient Q_PROPERTY(QString name READ name NOTIFY namesChanged) Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) - Q_PROPERTY(QString displayName READ displayName NOTIFY namesChanged) + Q_PROPERTY(QString displayName READ displayName NOTIFY displaynameChanged) Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) Q_PROPERTY(QString avatarMediaId READ avatarMediaId NOTIFY avatarChanged STORED false) Q_PROPERTY(QUrl avatarUrl READ avatarUrl NOTIFY avatarChanged) -- cgit v1.2.3 From 01d9f7b3f1785034503497798fb732ee6ee5fba3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 23 Mar 2019 15:51:04 +0900 Subject: Update to the latest CS API definitions No breaking changes; GetAccountDataJob/GetAccountDataPerRoomJob added. --- lib/csapi/account-data.cpp | 28 ++++++++++++++++++++ lib/csapi/account-data.h | 66 ++++++++++++++++++++++++++++++++++++++++++---- lib/csapi/capabilities.h | 8 +++--- lib/csapi/room_upgrades.h | 4 +-- 4 files changed, 94 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/csapi/account-data.cpp b/lib/csapi/account-data.cpp index 5021c73a..96b32a92 100644 --- a/lib/csapi/account-data.cpp +++ b/lib/csapi/account-data.cpp @@ -21,6 +21,20 @@ SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, setRequestData(Data(toJson(content))); } +QUrl GetAccountDataJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& type) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath % "/user/" % userId % "/account_data/" % type); +} + +static const auto GetAccountDataJobName = QStringLiteral("GetAccountDataJob"); + +GetAccountDataJob::GetAccountDataJob(const QString& userId, const QString& type) + : BaseJob(HttpVerb::Get, GetAccountDataJobName, + basePath % "/user/" % userId % "/account_data/" % type) +{ +} + static const auto SetAccountDataPerRoomJobName = QStringLiteral("SetAccountDataPerRoomJob"); SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content) @@ -30,3 +44,17 @@ SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const setRequestData(Data(toJson(content))); } +QUrl GetAccountDataPerRoomJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& type) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type); +} + +static const auto GetAccountDataPerRoomJobName = QStringLiteral("GetAccountDataPerRoomJob"); + +GetAccountDataPerRoomJob::GetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type) + : BaseJob(HttpVerb::Get, GetAccountDataPerRoomJobName, + basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type) +{ +} + diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index f3656a14..b067618f 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -22,8 +22,8 @@ namespace QMatrixClient public: /*! Set some account_data for the user. * \param userId - * The id of the user to set account_data for. The access token must be - * authorized to make requests for this user id. + * The ID of the user to set account_data for. The access token must be + * authorized to make requests for this user ID. * \param type * The event type of the account_data to set. Custom types should be * namespaced to avoid clashes. @@ -33,6 +33,33 @@ namespace QMatrixClient explicit SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content = {}); }; + /// Get some account_data for the user. + /// + /// Get some account_data for the client. This config is only visible to the user + /// that set the account_data. + class GetAccountDataJob : public BaseJob + { + public: + /*! Get some account_data for the user. + * \param userId + * The ID of the user to get account_data for. The access token must be + * authorized to make requests for this user ID. + * \param type + * The event type of the account_data to get. Custom types should be + * namespaced to avoid clashes. + */ + explicit GetAccountDataJob(const QString& userId, const QString& type); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetAccountDataJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& type); + + }; + /// Set some account_data for the user. /// /// Set some account_data for the client on a given room. This config is only @@ -43,10 +70,10 @@ namespace QMatrixClient public: /*! Set some account_data for the user. * \param userId - * The id of the user to set account_data for. The access token must be - * authorized to make requests for this user id. + * The ID of the user to set account_data for. The access token must be + * authorized to make requests for this user ID. * \param roomId - * The id of the room to set account_data on. + * The ID of the room to set account_data on. * \param type * The event type of the account_data to set. Custom types should be * namespaced to avoid clashes. @@ -55,4 +82,33 @@ namespace QMatrixClient */ explicit SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content = {}); }; + + /// Get some account_data for the user. + /// + /// Get some account_data for the client on a given room. This config is only + /// visible to the user that set the account_data. + class GetAccountDataPerRoomJob : public BaseJob + { + public: + /*! Get some account_data for the user. + * \param userId + * The ID of the user to set account_data for. The access token must be + * authorized to make requests for this user ID. + * \param roomId + * The ID of the room to get account_data for. + * \param type + * The event type of the account_data to get. Custom types should be + * namespaced to avoid clashes. + */ + explicit GetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type); + + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * GetAccountDataPerRoomJob is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& type); + + }; } // namespace QMatrixClient diff --git a/lib/csapi/capabilities.h b/lib/csapi/capabilities.h index 39e2f4d1..06a8bf0d 100644 --- a/lib/csapi/capabilities.h +++ b/lib/csapi/capabilities.h @@ -39,8 +39,8 @@ namespace QMatrixClient QHash available; }; - /// Gets information about the server's supported feature set - /// and other relevant capabilities. + /// The custom capabilities the server supports, using the + /// Java package naming convention. struct Capabilities { /// Capability to indicate if the user can change their password. @@ -68,8 +68,8 @@ namespace QMatrixClient // Result properties - /// Gets information about the server's supported feature set - /// and other relevant capabilities. + /// The custom capabilities the server supports, using the + /// Java package naming convention. const Capabilities& capabilities() const; protected: diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index 6f712f10..4da5941a 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -13,9 +13,7 @@ namespace QMatrixClient /// Upgrades a room to a new room version. /// - /// Upgrades the given room to a particular room version, migrating as much - /// data as possible over to the new room. See the `room_upgrades <#room-upgrades>`_ - /// module for more information on what this entails. + /// Upgrades the given room to a particular room version. class UpgradeRoomJob : public BaseJob { public: -- cgit v1.2.3 From 9ba481f2c8e7f1db6144ece7119d8cc314c57bc5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 23 Mar 2019 20:43:02 +0900 Subject: Room::downloadFile(): Tighten URL validations Check the URL before passing over to Connection::downloadFile(), not only the file name. --- lib/events/eventcontent.cpp | 6 ++++++ lib/events/eventcontent.h | 2 ++ lib/room.cpp | 9 ++++++++- 3 files changed, 16 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 9a5e872c..77f756cd 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -50,6 +50,12 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); } +bool FileInfo::isValid() const +{ + return url.scheme() == "mxc" + && (url.authority() + url.path()).count('/') == 1; +} + void FileInfo::fillInfoJson(QJsonObject* infoJson) const { Q_ASSERT(infoJson); diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 0588c0e2..ab31a75d 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -94,6 +94,8 @@ namespace QMatrixClient FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}); + bool isValid() const; + void fillInfoJson(QJsonObject* infoJson) const; /** diff --git a/lib/room.cpp b/lib/room.cpp index 7494917d..ce7bae04 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1785,7 +1785,14 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) Q_ASSERT(false); return; } - const auto fileUrl = event->content()->fileInfo()->url; + const auto* const fileInfo = event->content()->fileInfo(); + if (!fileInfo->isValid()) + { + qCWarning(MAIN) << "Event" << eventId + << "has an empty or malformed mxc URL; won't download"; + return; + } + const auto fileUrl = fileInfo->url; auto filePath = localFilename.toLocalFile(); if (filePath.isEmpty()) { -- cgit v1.2.3 From 21e5138f6cf1e96d3cac702e2ada2a0148a3ec92 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 24 Mar 2019 18:51:08 +0900 Subject: linkifyUrls(): fix linkification of emails containing "www." Closes #303. --- lib/util.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index d042aa34..e1f312ee 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -38,14 +38,14 @@ static void linkifyUrls(QString& htmlEscapedText) // comma or dot // Note: outer parentheses are a part of C++ raw string delimiters, not of // the regex (see http://en.cppreference.com/w/cpp/language/string_literal). - // Note2: yet another pair of outer parentheses are \1 in the replacement. + // Note2: the next-outer parentheses are \N in the replacement. static const QRegularExpression FullUrlRegExp(QStringLiteral( - R"(((www\.(?!\.)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" + R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" ), RegExpOptions); // email address: // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] static const QRegularExpression EmailAddressRegExp(QStringLiteral( - R"((mailto:)?(\b(\w|\.|-)+@(\w|\.|-)+\.\w+\b))" + R"(\b(mailto:)?((\w|\.|-)+@(\w|\.|-)+\.\w+\b))" ), RegExpOptions); // An interim liberal implementation of // https://matrix.org/docs/spec/appendices.html#identifier-grammar @@ -53,7 +53,7 @@ static void linkifyUrls(QString& htmlEscapedText) R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:[-.a-z0-9]+))" ), RegExpOptions); - // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,& + // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,&," htmlEscapedText.replace(EmailAddressRegExp, QStringLiteral(R"(\1\2)")); -- cgit v1.2.3 From e855085835909549aa866ed968e24902eb378b5a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 17 Mar 2019 09:03:34 +0900 Subject: RoomMemberEvent: sanitize user display names MemberEventContent::displayName() will strip away Unicode text direction override characters. Direct access to JSON can still provide "raw" data. --- lib/events/roommemberevent.cpp | 2 +- lib/util.cpp | 10 +++++++++- lib/util.h | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index a5ac3c5f..6da76526 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -52,7 +52,7 @@ using namespace QMatrixClient; MemberEventContent::MemberEventContent(const QJsonObject& json) : membership(fromJson(json["membership"_ls])) , isDirect(json["is_direct"_ls].toBool()) - , displayName(json["displayname"_ls].toString()) + , displayName(sanitized(json["displayname"_ls].toString())) , avatarUrl(json["avatar_url"_ls].toString()) { } diff --git a/lib/util.cpp b/lib/util.cpp index e1f312ee..8d16cfc8 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -63,10 +63,18 @@ static void linkifyUrls(QString& htmlEscapedText) QStringLiteral(R"(\1\2)")); } +QString QMatrixClient::sanitized(const QString& plainText) +{ + auto text = plainText; + text.remove(QChar(0x202e)); + text.remove(QChar(0x202d)); + return text; +} + QString QMatrixClient::prettyPrint(const QString& plainText) { auto pt = QStringLiteral("") + - plainText.toHtmlEscaped() + QStringLiteral(""); + plainText.toHtmlEscaped() + QStringLiteral(""); pt.replace('\n', QStringLiteral("
")); linkifyUrls(pt); diff --git a/lib/util.h b/lib/util.h index f7f646da..beb3c697 100644 --- a/lib/util.h +++ b/lib/util.h @@ -296,7 +296,12 @@ namespace QMatrixClient return std::make_pair(last, sLast); } - /** Pretty-prints plain text into HTML + /** Sanitize the text before showing in HTML + * This does toHtmlEscaped() and removes Unicode BiDi marks. + */ + QString sanitized(const QString& plainText); + + /** Pretty-print plain text into HTML * This includes HTML escaping of <,>,",& and URLs linkification. */ QString prettyPrint(const QString& plainText); -- cgit v1.2.3 From e2dea5a7e263707c283b63f0c31b4fae8399db3b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 17 Mar 2019 18:58:49 +0900 Subject: User: strip RLO/LRO markers on renaming as well Continuation of work on #545. --- lib/user.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index eec41957..93cfbffd 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -264,8 +264,9 @@ void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, void User::rename(const QString& newName) { - auto job = connection()->callApi(id(), newName); - connect(job, &BaseJob::success, this, [=] { updateName(newName); }); + const auto actualNewName = sanitized(newName); + connect(connection()->callApi(id(), actualNewName), + &BaseJob::success, this, [=] { updateName(actualNewName); }); } void User::rename(const QString& newName, const Room* r) @@ -279,10 +280,11 @@ void User::rename(const QString& newName, const Room* r) } Q_ASSERT_X(r->memberJoinState(this) == JoinState::Join, __FUNCTION__, "Attempt to rename a user that's not a room member"); + const auto actualNewName = sanitized(newName); MemberEventContent evtC; - evtC.displayName = newName; - auto job = r->setMemberState(id(), RoomMemberEvent(move(evtC))); - connect(job, &BaseJob::success, this, [=] { updateName(newName, r); }); + evtC.displayName = actualNewName; + connect(r->setMemberState(id(), RoomMemberEvent(move(evtC))), + &BaseJob::success, this, [=] { updateName(actualNewName, r); }); } bool User::setAvatar(const QString& fileName) -- cgit v1.2.3 From adcea5868d45610be0539af3e1cfc15f8495815c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 24 Mar 2019 19:09:48 +0900 Subject: Expose linkifyUrls() into library API for future use --- lib/util.cpp | 11 ++++++----- lib/util.h | 5 ++++- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 8d16cfc8..fe6286f3 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -29,16 +29,17 @@ static const auto RegExpOptions = | QRegularExpression::UseUnicodePropertiesOption; // Converts all that looks like a URL into HTML links -static void linkifyUrls(QString& htmlEscapedText) +void QMatrixClient::linkifyUrls(QString& htmlEscapedText) { + // Note: outer parentheses are a part of C++ raw string delimiters, not of + // the regex (see http://en.cppreference.com/w/cpp/language/string_literal). + // Note2: the next-outer parentheses are \N in the replacement. + + // generic url: // regexp is originally taken from Konsole (https://github.com/KDE/konsole) - // full url: // protocolname:// or www. followed by anything other than whitespaces, // <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, ), :, // comma or dot - // Note: outer parentheses are a part of C++ raw string delimiters, not of - // the regex (see http://en.cppreference.com/w/cpp/language/string_literal). - // Note2: the next-outer parentheses are \N in the replacement. static const QRegularExpression FullUrlRegExp(QStringLiteral( R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" ), RegExpOptions); diff --git a/lib/util.h b/lib/util.h index beb3c697..f08c1c95 100644 --- a/lib/util.h +++ b/lib/util.h @@ -296,13 +296,16 @@ namespace QMatrixClient return std::make_pair(last, sLast); } + /** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */ + void linkifyUrls(QString& htmlEscapedText); + /** Sanitize the text before showing in HTML * This does toHtmlEscaped() and removes Unicode BiDi marks. */ QString sanitized(const QString& plainText); /** Pretty-print plain text into HTML - * This includes HTML escaping of <,>,",& and URLs linkification. + * This includes HTML escaping of <,>,",& and calling linkifyUrls() */ QString prettyPrint(const QString& plainText); -- cgit v1.2.3 From c8becd7ba500802bbb23b986108ad965d4f31df5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 26 Mar 2019 11:58:12 +0900 Subject: Room::canSwitchVersions(): return false on tombstoned rooms A softer take on #306. --- lib/room.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index ce7bae04..18dacbaa 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -600,6 +600,9 @@ void Room::markAllMessagesAsRead() bool Room::canSwitchVersions() const { + if (!successorId().isEmpty()) + return false; // Noone can upgrade a room that's already upgraded + // TODO, #276: m.room.power_levels const auto* plEvt = d->currentState.value({"m.room.power_levels", ""}); -- cgit v1.2.3 From d1efa846ee2e454b56dc77db403e40f1d9dfc7cd Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 26 Mar 2019 12:00:14 +0900 Subject: Room::switchVersion(): refuse to switch a version if a tombstone is already there Closes #306. --- lib/room.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 18dacbaa..2930875f 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -836,10 +836,17 @@ void Room::resetHighlightCount() void Room::switchVersion(QString newVersion) { - auto* job = connection()->callApi(id(), newVersion); - connect(job, &BaseJob::failure, this, [this,job] { - emit upgradeFailed(job->errorString()); - }); + if (!successorId().isEmpty()) + { + Q_ASSERT(!successorId().isEmpty()); + emit upgradeFailed(tr("The room is already upgraded")); + } + if (auto* job = connection()->callApi(id(), newVersion)) + connect(job, &BaseJob::failure, this, [this,job] { + emit upgradeFailed(job->errorString()); + }); + else + emit upgradeFailed(tr("Couldn't initiate upgrade")); } bool Room::hasAccountData(const QString& type) const -- cgit v1.2.3 From fe3141f7bc3d2c92dcfe8ee5b3eb8d3ca6f0ab17 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 27 Mar 2019 19:06:34 +0900 Subject: Connection::domain() --- lib/connection.cpp | 5 +++++ lib/connection.h | 4 ++++ 2 files changed, 9 insertions(+) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 59aca025..07c24c92 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -788,6 +788,11 @@ QUrl Connection::homeserver() const return d->data->baseUrl(); } +QString Connection::domain() const +{ + return d->userId.section(':', 1); +} + Room* Connection::room(const QString& roomId, JoinStates states) const { Room* room = d->roomMap.value({roomId, false}, nullptr); diff --git a/lib/connection.h b/lib/connection.h index b22d63da..ea5be51a 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -104,6 +104,7 @@ namespace QMatrixClient Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded) Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) + Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) @@ -241,7 +242,10 @@ namespace QMatrixClient /** Get the full list of users known to this account */ QMap users() const; + /** Get the base URL of the homeserver to connect to */ QUrl homeserver() const; + /** Get the domain name used for ids/aliases on the server */ + QString domain() const; /** Find a room by its id and a mask of applicable states */ Q_INVOKABLE Room* room(const QString& roomId, JoinStates states = JoinState::Invite|JoinState::Join) const; -- cgit v1.2.3 From 01c5a35398a55dfc4a30e466aeb13419387555d3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Mar 2019 13:02:12 +0900 Subject: Room::processRedaction(): avoid accidental creation of entries in currentState; cleanup --- lib/room.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 2930875f..19658be0 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1966,10 +1966,10 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) { const StateEventKey evtKey { oldEvent->matrixType(), oldEvent->stateKey() }; Q_ASSERT(currentState.contains(evtKey)); - if (currentState[evtKey] == oldEvent.get()) + if (currentState.value(evtKey) == oldEvent.get()) { Q_ASSERT(ti.index() >= 0); // Historical states can't be in currentState - qCDebug(MAIN).nospace() << "Reverting state " + qCDebug(MAIN).nospace() << "Redacting state " << oldEvent->matrixType() << "/" << oldEvent->stateKey(); // Retarget the current state to the newly made event. if (q->processStateEvent(*ti)) @@ -2170,7 +2170,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) Q_ASSERT(!oldStateEvent || (oldStateEvent->matrixType() == e.matrixType() && oldStateEvent->stateKey() == e.stateKey())); - if (!is(e)) + if (!is(e)) // Room member events are too numerous qCDebug(EVENTS) << "Room state event:" << e; return visit(e -- cgit v1.2.3 From a28892ca3b40a32556ee7615116c322f6b2a4ae5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Mar 2019 13:26:36 +0900 Subject: Room::processStateEvent, User: take the previous membership state from oldStateEvent memberJoinState() just happens to return the not-yet-updated state, making its use around state changes very sensitive to moving things around. The event's own prevContent is unsigned, therefore untrusted. --- lib/room.cpp | 24 ++++++++++++++++-------- lib/user.cpp | 13 ++++++------- lib/user.h | 6 +++++- 3 files changed, 27 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 19658be0..789800c6 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1184,7 +1184,11 @@ void Room::Private::insertMemberIntoMap(User *u) const auto userName = u->name(q); // If there is exactly one namesake of the added user, signal member renaming // for that other one because the two should be disambiguated now. - auto namesakes = membersMap.values(userName); + const auto namesakes = membersMap.values(userName); + + // Callers should check they are not adding an existing user once more. + Q_ASSERT(!namesakes.contains(u)); + if (namesakes.size() == 1) emit q->memberAboutToRename(namesakes.front(), namesakes.front()->fullName(q)); @@ -2196,16 +2200,20 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) emit avatarChanged(); return AvatarChange; } - , [this] (const RoomMemberEvent& evt) { + , [this,oldStateEvent] (const RoomMemberEvent& evt) { auto* u = user(evt.userId()); - u->processEvent(evt, this); - if (u == localUser() && memberJoinState(u) == JoinState::Invite + const auto* oldMemberEvent = + static_cast(oldStateEvent); + u->processEvent(evt, this, oldMemberEvent == nullptr); + const auto prevMembership = oldMemberEvent + ? oldMemberEvent->membership() : MembershipType::Leave; + if (u == localUser() && evt.membership() == MembershipType::Invite && evt.isDirect()) connection()->addToDirectChats(this, user(evt.senderId())); - if( evt.membership() == MembershipType::Join ) + if (evt.membership() == MembershipType::Join) { - if (memberJoinState(u) != JoinState::Join) + if (prevMembership != MembershipType::Join) { d->insertMemberIntoMap(u); connect(u, &User::nameAboutToChange, this, @@ -2221,9 +2229,9 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) emit userAdded(u); } } - else if( evt.membership() != MembershipType::Join ) + else if (evt.membership() != MembershipType::Join) { - if (memberJoinState(u) == JoinState::Join) + if (prevMembership == MembershipType::Join) { if (evt.membership() == MembershipType::Invite) qCWarning(MAIN) << "Invalid membership change:" << evt; diff --git a/lib/user.cpp b/lib/user.cpp index 93cfbffd..c373a067 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -379,19 +379,18 @@ QUrl User::avatarUrl(const Room* room) const return avatarObject(room).url(); } -void User::processEvent(const RoomMemberEvent& event, const Room* room) +void User::processEvent(const RoomMemberEvent& event, const Room* room, + bool firstMention) { Q_ASSERT(room); + + if (firstMention) + ++d->totalRooms; + if (event.membership() != MembershipType::Invite && event.membership() != MembershipType::Join) return; - auto aboutToEnter = room->memberJoinState(this) == JoinState::Leave && - (event.membership() == MembershipType::Join || - event.membership() == MembershipType::Invite); - if (aboutToEnter) - ++d->totalRooms; - auto newName = event.displayName(); // `bridged` value uses the same notification signal as the name; // it is assumed that first setting of the bridge occurs together with diff --git a/lib/user.h b/lib/user.h index 0023b44a..7c9ed55f 100644 --- a/lib/user.h +++ b/lib/user.h @@ -105,7 +105,11 @@ namespace QMatrixClient QString avatarMediaId(const Room* room = nullptr) const; QUrl avatarUrl(const Room* room = nullptr) const; - void processEvent(const RoomMemberEvent& event, const Room* r); + /// This method is for internal use and should not be called + /// from client code + // FIXME: Move it away to private in lib 0.6 + void processEvent(const RoomMemberEvent& event, const Room* r, + bool firstMention); public slots: /** Set a new name in the global user profile */ -- cgit v1.2.3 From e2b30ad0dc6f5a369cbd1dce3f9c92ef5d801dae Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 Mar 2019 13:30:03 +0900 Subject: Connection: make sure to mark rooms supposed to be direct chats as such Closes #305. Relies on correct tracking of Invite membership from the previous commit. --- lib/connection.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 07c24c92..c09de979 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -609,8 +609,17 @@ CreateRoomJob* Connection::createRoom(RoomVisibility visibility, : QStringLiteral("private"), alias, name, topic, invites, invite3pids, roomVersion, creationContent, initialState, presetName, isDirect); - connect(job, &BaseJob::success, this, [this,job] { - emit createdRoom(provideRoom(job->roomId(), JoinState::Join)); + connect(job, &BaseJob::success, this, [this,job,invites,isDirect] { + auto* room = provideRoom(job->roomId(), JoinState::Join); + if (!room) + { + Q_ASSERT_X(room, "Connection::createRoom", "Failed to create a room"); + return; + } + emit createdRoom(room); + if (isDirect) + for (const auto& i: invites) + addToDirectChats(room, user(i)); }); return job; } @@ -1161,6 +1170,9 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) emit leftRoom(room, prevInvite); if (prevInvite) { + const auto dcUsers = prevInvite->directChatUsers(); + for (auto* u: dcUsers) + addToDirectChats(room, u); qCDebug(MAIN) << "Deleting Invite state for room" << prevInvite->id(); emit prevInvite->beforeDestruction(prevInvite); prevInvite->deleteLater(); -- cgit v1.2.3 From 91e70b1242e6e40170285bd29fdfd7c82fd45691 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 30 Mar 2019 20:52:43 +0900 Subject: User::nameForRoom(): null hint is not a hint This caused the library to erroneously believe that users with no representation in other rooms have no display name even if that display name is provided for the given room. --- lib/user.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index c373a067..951ad87d 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -82,7 +82,8 @@ class User::Private QString User::Private::nameForRoom(const Room* r, const QString& hint) const { // If the hint is accurate, this function is O(1) instead of O(n) - if (hint == mostUsedName || otherNames.contains(hint, r)) + if (!hint.isNull() + && (hint == mostUsedName || otherNames.contains(hint, r))) return hint; return otherNames.key(r, mostUsedName); } -- cgit v1.2.3 From 27ba75b458d2418051b170eb811ab88c00c336db Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 31 Mar 2019 18:41:12 +0900 Subject: Room::refreshDisplayName() - for debugging purposes only Clients should not need to call this method explicitly. --- lib/room.cpp | 5 +++++ lib/room.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 789800c6..bf51584d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -389,6 +389,11 @@ QString Room::displayName() const return d->displayname; } +void Room::refreshDisplayName() +{ + d->updateDisplayname(); +} + QString Room::topic() const { return d->getCurrentState()->topic(); diff --git a/lib/room.h b/lib/room.h index 85c67bfb..8440543e 100644 --- a/lib/room.h +++ b/lib/room.h @@ -410,6 +410,9 @@ namespace QMatrixClient void setAliases(const QStringList& aliases); void setTopic(const QString& newTopic); + /// You shouldn't normally call this method; it's here for debugging + void refreshDisplayName(); + void getPreviousContent(int limit = 10); void inviteToRoom(const QString& memberId); -- cgit v1.2.3 From 1cc9a93bb403ba50aa80524bc9d9dae44241941e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 31 Mar 2019 18:42:38 +0900 Subject: Room::updateData(): recalculate room name only when state changes occur --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index bf51584d..746dd688 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1346,7 +1346,6 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) emit memberListChanged(); roomChanges |= d->setSummary(move(data.summary)); - d->updateDisplayname(); for( auto&& ephemeralEvent: data.ephemeral ) roomChanges |= processEphemeralEvent(move(ephemeralEvent)); @@ -1371,6 +1370,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) } if (roomChanges != Change::NoChange) { + d->updateDisplayname(); emit changed(roomChanges); if (!fromCache) connection()->saveRoomState(this); -- cgit v1.2.3 From 2a53784faa9b776406900db0a7a750c8963ced92 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 31 Mar 2019 18:44:55 +0900 Subject: Room: track invited users; polish the room naming algorithm It's no more entirely along the spec lines but gives better results with or without lazy-loading, across a wide range of cases. Closes #310. --- lib/room.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 746dd688..b64ed3a1 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -105,6 +105,7 @@ class Room::Private members_map_t membersMap; QList usersTyping; QMultiHash eventIdReadUsers; + QList usersInvited; QList membersLeft; int unreadMessages = 0; bool displayed = false; @@ -1231,7 +1232,6 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) membersMap.remove(username, u); // If there was one namesake besides the removed user, signal member renaming // for it because it doesn't need to be disambiguated anymore. - // TODO: Think about left users. if (namesake) emit q->memberRenamed(namesake); } @@ -2216,8 +2216,38 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) && evt.isDirect()) connection()->addToDirectChats(this, user(evt.senderId())); - if (evt.membership() == MembershipType::Join) + switch (prevMembership) { + case MembershipType::Invite: + if (evt.membership() != prevMembership) + { + d->usersInvited.removeOne(u); + Q_ASSERT(!d->usersInvited.contains(u)); + } + break; + case MembershipType::Join: + if (evt.membership() == MembershipType::Invite) + qCWarning(MAIN) + << "Invalid membership change from Join to Invite:" + << evt; + if (evt.membership() != prevMembership) + { + d->removeMemberFromMap(u->name(this), u); + emit userRemoved(u); + } + break; + default: + if (evt.membership() == MembershipType::Invite + || evt.membership() == MembershipType::Join) + { + d->membersLeft.removeOne(u); + Q_ASSERT(!d->membersLeft.contains(u)); + } + } + + switch(evt.membership()) + { + case MembershipType::Join: if (prevMembership != MembershipType::Join) { d->insertMemberIntoMap(u); @@ -2233,18 +2263,14 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) }); emit userAdded(u); } - } - else if (evt.membership() != MembershipType::Join) - { - if (prevMembership == MembershipType::Join) - { - if (evt.membership() == MembershipType::Invite) - qCWarning(MAIN) << "Invalid membership change:" << evt; - if (!d->membersLeft.contains(u)) - d->membersLeft.append(u); - d->removeMemberFromMap(u->name(this), u); - emit userRemoved(u); - } + break; + case MembershipType::Invite: + if (!d->usersInvited.contains(u)) + d->usersInvited.push_back(u); + break; + default: + if (!d->membersLeft.contains(u)) + d->membersLeft.append(u); } return MembersChange; } @@ -2425,42 +2451,59 @@ QString Room::Private::calculateDisplayname() const return dispName; // Using m.room.aliases in naming is explicitly discouraged by the spec - //if (!q->aliases().empty() && !q->aliases().at(0).isEmpty()) - // return q->aliases().at(0); // Supplementary code for 3 and 4: build the shortlist of users whose names // will be used to construct the room name. Takes into account MSC688's // "heroes" if available. + const bool localUserIsIn = joinState == JoinState::Join; const bool emptyRoom = membersMap.isEmpty() || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); - const auto shortlist = - !summary.heroes.omitted() ? buildShortlist(summary.heroes.value()) : - !emptyRoom ? buildShortlist(membersMap) : - buildShortlist(membersLeft); + const bool nonEmptySummary = + !summary.heroes.omitted() && !summary.heroes->empty(); + auto shortlist = nonEmptySummary ? buildShortlist(summary.heroes.value()) : + !emptyRoom ? buildShortlist(membersMap) : + users_shortlist_t { }; + + // When lazy-loading is on, we can rely on the heroes list. + // If it's off, the below code gathers invited and left members. + // NB: including invitations, if any, into naming is a spec extension. + // This kicks in when there's no lazy loading and it's a room with + // the local user as the only member, with more users invited. + if (!shortlist.front() && localUserIsIn) + shortlist = buildShortlist(usersInvited); + + if (!shortlist.front()) // Still empty shortlist; use left members + shortlist = buildShortlist(membersLeft); QStringList names; for (auto u: shortlist) { if (u == nullptr || isLocalUser(u)) break; - names.push_back(q->roomMembername(u)); + // Only disambiguate if the room is not empty + names.push_back(u->displayname(emptyRoom ? nullptr : q)); } - auto usersCountExceptLocal = emptyRoom - ? membersLeft.size() - int(joinState == JoinState::Leave) - : q->joinedCount() - int(joinState == JoinState::Join); + const auto usersCountExceptLocal = + !emptyRoom ? q->joinedCount() - int(joinState == JoinState::Join) : + !usersInvited.empty() ? usersInvited.count() : + membersLeft.size() - int(joinState == JoinState::Leave); if (usersCountExceptLocal > int(shortlist.size())) names << tr("%Ln other(s)", "Used to make a room name from user names: A, B and _N others_", - usersCountExceptLocal); - auto namesList = QLocale().createSeparatedList(names); + usersCountExceptLocal - int(shortlist.size())); + const auto namesList = QLocale().createSeparatedList(names); // 3. Room members if (!emptyRoom) return namesList; + // (Spec extension) Invited users + if (!usersInvited.empty()) + return tr("Empty room (invited: %1)").arg(namesList); + // 4. Users that previously left the room if (membersLeft.size() > 0) return tr("Empty room (was: %1)").arg(namesList); -- cgit v1.2.3 From 008207f178475531c0a8a86b2192c09d7134ad0e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 3 Apr 2019 19:33:24 +0900 Subject: room.h: more doc-comments --- lib/room.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 8440543e..db4336ea 100644 --- a/lib/room.h +++ b/lib/room.h @@ -358,8 +358,28 @@ namespace QMatrixClient Q_INVOKABLE QUrl urlToThumbnail(const QString& eventId) const; Q_INVOKABLE QUrl urlToDownload(const QString& eventId) const; + + /// Get a file name for downloading for a given event id + /*! + * The event MUST be RoomMessageEvent and have content + * for downloading. \sa RoomMessageEvent::hasContent + */ Q_INVOKABLE QString fileNameToDownload(const QString& eventId) const; + + /// Get information on file upload/download + /*! + * \param id uploads are identified by the corresponding event's + * transactionId (because uploads are done before + * the event is even sent), while downloads are using + * the normal event id for identifier. + */ Q_INVOKABLE FileTransferInfo fileTransferInfo(const QString& id) const; + + /// Get the URL to the actual file source in a unified way + /*! + * For uploads it will return a URL to a local file; for downloads + * the URL will be taken from the corresponding room event. + */ Q_INVOKABLE QUrl fileSource(const QString& id) const; /** Pretty-prints plain text into HTML -- cgit v1.2.3 From 8035ec464ce797abab5e464c2cfb485f7fa14d06 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 3 Apr 2019 20:08:48 +0900 Subject: Room::postFile: initiate uploading the file even before adding a pending event This is to make sure a pending event with file transfer already placed. --- lib/room.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index b64ed3a1..587b598d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1540,12 +1540,17 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, { QFileInfo localFile { localPath.toLocalFile() }; Q_ASSERT(localFile.isFile()); + + const auto txnId = connection()->generateTxnId(); // Remote URL will only be known after upload; fill in the local path // to enable the preview while the event is pending. - const auto txnId = d->addAsPending(makeEvent( - plainText, localFile, asGenericFile) - )->transactionId(); uploadFile(txnId, localPath); + { + auto&& event = + makeEvent(plainText, localFile, asGenericFile); + event->setTransactionId(txnId); + d->addAsPending(std::move(event)); + } auto* context = new QObject(this); connect(this, &Room::fileTransferCompleted, context, [context,this,txnId] (const QString& id, QUrl, const QUrl& mxcUri) { -- cgit v1.2.3 From 432e7fd7107d8260e0016a1adcd8d94263dc1044 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 4 Apr 2019 21:27:38 +0900 Subject: Clean up on clang-tidy/clazy analysis --- lib/avatar.cpp | 2 +- lib/connection.cpp | 25 +++++++++++++++---------- lib/connectiondata.cpp | 6 +++--- lib/events/event.cpp | 3 ++- lib/events/stateevent.cpp | 2 +- lib/jobs/basejob.cpp | 2 +- lib/jobs/downloadfilejob.cpp | 5 +++-- lib/jobs/mediathumbnailjob.cpp | 2 +- lib/networkaccessmanager.cpp | 3 ++- lib/networksettings.cpp | 2 +- lib/room.cpp | 32 ++++++++++++++++---------------- lib/settings.cpp | 21 ++++++++++++--------- lib/settings.h | 2 +- lib/syncdata.cpp | 8 ++++---- lib/user.cpp | 14 ++++++++------ lib/util.cpp | 2 +- 16 files changed, 72 insertions(+), 59 deletions(-) (limited to 'lib') diff --git a/lib/avatar.cpp b/lib/avatar.cpp index c0ef3cba..9279ef9d 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -191,7 +191,7 @@ bool Avatar::Private::checkUrl(const QUrl& url) const } QString Avatar::Private::localFile() const { - static const auto cachePath = cacheLocation("avatars"); + static const auto cachePath = cacheLocation(QStringLiteral("avatars")); return cachePath % _url.authority() % '_' % _url.fileName() % ".png"; } diff --git a/lib/connection.cpp b/lib/connection.cpp index c09de979..5ed72616 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -717,8 +717,8 @@ void Connection::doInDirectChat(User* u, CreateRoomJob* Connection::createDirectChat(const QString& userId, const QString& topic, const QString& name) { - return createRoom(UnpublishRoom, "", name, topic, {userId}, - "trusted_private_chat", {}, true); + return createRoom(UnpublishRoom, {}, name, topic, {userId}, + QStringLiteral("trusted_private_chat"), {}, true); } ForgetRoomJob* Connection::forgetRoom(const QString& id) @@ -964,7 +964,8 @@ QHash> Connection::tagsToRooms() const QHash> result; for (auto* r: qAsConst(d->roomMap)) { - for (const auto& tagName: r->tagNames()) + const auto& tagNames = r->tagNames(); + for (const auto& tagName: tagNames) result[tagName].push_back(r); } for (auto it = result.begin(); it != result.end(); ++it) @@ -979,9 +980,12 @@ QStringList Connection::tagNames() const { QStringList tags ({FavouriteTag}); for (auto* r: qAsConst(d->roomMap)) - for (const auto& tag: r->tagNames()) + { + const auto& tagNames = r->tagNames(); + for (const auto& tag: tagNames) if (tag != LowPriorityTag && !tags.contains(tag)) tags.push_back(tag); + } tags.push_back(LowPriorityTag); return tags; } @@ -1264,18 +1268,19 @@ void Connection::saveState() const { QJsonObject rooms; QJsonObject inviteRooms; - for (const auto* i : roomMap()) // Pass on rooms in Leave state + const auto& rs = roomMap(); // Pass on rooms in Leave state + for (const auto* i : rs) (i->joinState() == JoinState::Invite ? inviteRooms : rooms) .insert(i->id(), QJsonValue::Null); QJsonObject roomObj; if (!rooms.isEmpty()) - roomObj.insert("join", rooms); + roomObj.insert(QStringLiteral("join"), rooms); if (!inviteRooms.isEmpty()) - roomObj.insert("invite", inviteRooms); + roomObj.insert(QStringLiteral("invite"), inviteRooms); - rootObj.insert("next_batch", d->data->lastEvent()); - rootObj.insert("rooms", roomObj); + rootObj.insert(QStringLiteral("next_batch"), d->data->lastEvent()); + rootObj.insert(QStringLiteral("rooms"), roomObj); } { QJsonArray accountDataEvents { @@ -1285,7 +1290,7 @@ void Connection::saveState() const accountDataEvents.append( basicEventJson(e.first, e.second->contentJson())); - rootObj.insert("account_data", + rootObj.insert(QStringLiteral("account_data"), QJsonObject {{ QStringLiteral("events"), accountDataEvents }}); } diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index eb516ef7..91cda09f 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -25,7 +25,7 @@ using namespace QMatrixClient; struct ConnectionData::Private { - explicit Private(const QUrl& url) : baseUrl(url) { } + explicit Private(QUrl url) : baseUrl(std::move(url)) { } QUrl baseUrl; QByteArray accessToken; @@ -37,7 +37,7 @@ struct ConnectionData::Private }; ConnectionData::ConnectionData(QUrl baseUrl) - : d(std::make_unique(baseUrl)) + : d(std::make_unique(std::move(baseUrl))) { } ConnectionData::~ConnectionData() = default; @@ -98,7 +98,7 @@ QString ConnectionData::lastEvent() const void ConnectionData::setLastEvent(QString identifier) { - d->lastEvent = identifier; + d->lastEvent = std::move(identifier); } QByteArray ConnectionData::generateTxnId() const diff --git a/lib/events/event.cpp b/lib/events/event.cpp index c98dfbb6..6505d89a 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -38,7 +38,8 @@ event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId) QString EventTypeRegistry::getMatrixType(event_type_t typeId) { - return typeId < get().eventTypes.size() ? get().eventTypes[typeId] : ""; + return typeId < get().eventTypes.size() + ? get().eventTypes[typeId] : QString(); } Event::Event(Type type, const QJsonObject& json) diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index e96614d2..a84f302b 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -27,7 +27,7 @@ using namespace QMatrixClient; RoomEvent::factory_t::addMethod( [] (const QJsonObject& json, const QString& matrixType) -> StateEventPtr { - if (!json.contains("state_key")) + if (!json.contains("state_key"_ls)) return nullptr; if (auto e = StateEventBase::factory_t::make(json, matrixType)) diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index f738ce7a..f521cc4b 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -430,7 +430,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) { d->rawResponse = reply->readAll(); - QJsonParseError error; + QJsonParseError error { 0, QJsonParseError::MissingObject }; const auto& json = QJsonDocument::fromJson(d->rawResponse, &error); if( error.error == QJsonParseError::NoError ) return parseJson(json); diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 2bf9dd8f..672a7b2d 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -22,7 +22,8 @@ class DownloadFileJob::Private QUrl DownloadFileJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri) { - return makeRequestUrl(baseUrl, mxcUri.authority(), mxcUri.path().mid(1)); + return makeRequestUrl( + std::move(baseUrl), mxcUri.authority(), mxcUri.path().mid(1)); } DownloadFileJob::DownloadFileJob(const QString& serverName, @@ -31,7 +32,7 @@ DownloadFileJob::DownloadFileJob(const QString& serverName, : GetContentJob(serverName, mediaId) , d(localFilename.isEmpty() ? new Private : new Private(localFilename)) { - setObjectName("DownloadFileJob"); + setObjectName(QStringLiteral("DownloadFileJob")); } QString DownloadFileJob::targetFileName() const diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index aeb49839..edb9b156 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -59,5 +59,5 @@ BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply) if( _thumbnail.loadFromData(data()->readAll()) ) return Success; - return { IncorrectResponseError, "Could not read image data" }; + return { IncorrectResponseError, QStringLiteral("Could not read image data") }; } diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 89967a8a..7d9cb360 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -29,7 +29,8 @@ class NetworkAccessManager::Private QList ignoredSslErrors; }; -NetworkAccessManager::NetworkAccessManager(QObject* parent) : d(std::make_unique()) +NetworkAccessManager::NetworkAccessManager(QObject* parent) + : QNetworkAccessManager(parent), d(std::make_unique()) { } QList NetworkAccessManager::ignoredSslErrors() const diff --git a/lib/networksettings.cpp b/lib/networksettings.cpp index 48bd09f3..6ff2bc1f 100644 --- a/lib/networksettings.cpp +++ b/lib/networksettings.cpp @@ -27,5 +27,5 @@ void NetworkSettings::setupApplicationProxy() const } QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) -QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", "", setProxyHostName) +QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", {}, setProxyHostName) QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, setProxyPort) diff --git a/lib/room.cpp b/lib/room.cpp index 587b598d..f031273a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -168,7 +168,7 @@ class Room::Private //void inviteUser(User* u); // We might get it at some point in time. void insertMemberIntoMap(User* u); - void renameMember(User* u, QString oldName); + void renameMember(User* u, const QString& oldName); void removeMemberFromMap(const QString& username, User* u); // This updates the room displayname field (which is the way a room @@ -185,7 +185,7 @@ class Room::Private void getPreviousContent(int limit = 10); template - const EventT* getCurrentState(QString stateKey = {}) const + const EventT* getCurrentState(const QString& stateKey = {}) const { static const EventT empty; const auto* evt = @@ -236,8 +236,8 @@ class Room::Private * @param placement - position and direction of insertion: Older for * historical messages, Newer for new ones */ - Timeline::difference_type moveEventsToTimeline(RoomEventsRange events, - EventsPlacement placement); + Timeline::size_type moveEventsToTimeline(RoomEventsRange events, + EventsPlacement placement); /** * Remove events from the passed container that are already in the timeline @@ -341,7 +341,7 @@ const QString& Room::id() const QString Room::version() const { const auto v = d->getCurrentState()->version(); - return v.isEmpty() ? "1" : v; + return v.isEmpty() ? QStringLiteral("1") : v; } bool Room::isUnstable() const @@ -546,8 +546,8 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, { const auto oldUnreadCount = unreadMessages; QElapsedTimer et; et.start(); - unreadMessages = count_if(eagerMarker, timeline.cend(), - std::bind(&Room::Private::isEventNotable, this, _1)); + unreadMessages = int(count_if(eagerMarker, timeline.cend(), + std::bind(&Room::Private::isEventNotable, this, _1))); if (et.nsecsElapsed() > profilerMinNsecs() / 10) qCDebug(PROFILER) << "Recounting unread messages took" << et; @@ -611,7 +611,7 @@ bool Room::canSwitchVersions() const // TODO, #276: m.room.power_levels const auto* plEvt = - d->currentState.value({"m.room.power_levels", ""}); + d->currentState.value({QStringLiteral("m.room.power_levels"), {}}); if (!plEvt) return true; @@ -621,7 +621,7 @@ bool Room::canSwitchVersions() const .value(localUser()->id()).toInt( plJson.value("users_default"_ls).toInt()); const auto tombstonePowerLevel = - plJson.value("events").toObject() + plJson.value("events"_ls).toObject() .value("m.room.tombstone"_ls).toInt( plJson.value("state_default"_ls).toInt()); return currentUserLevel >= tombstonePowerLevel; @@ -954,7 +954,7 @@ void Room::Private::setTags(TagsMap newTags) } tags = move(newTags); qCDebug(MAIN) << "Room" << q->objectName() << "is tagged with" - << q->tagNames().join(", "); + << q->tagNames().join(QStringLiteral(", ")); emit q->tagsChanged(); } @@ -1203,7 +1203,7 @@ void Room::Private::insertMemberIntoMap(User *u) emit q->memberRenamed(namesakes.front()); } -void Room::Private::renameMember(User* u, QString oldName) +void Room::Private::renameMember(User* u, const QString& oldName) { if (u->name(q) == oldName) { @@ -1241,7 +1241,7 @@ inline auto makeErrorStr(const Event& e, QByteArray msg) return msg.append("; event dump follows:\n").append(e.originalJson()); } -Room::Timeline::difference_type Room::Private::moveEventsToTimeline( +Room::Timeline::size_type Room::Private::moveEventsToTimeline( RoomEventsRange events, EventsPlacement placement) { Q_ASSERT(!events.empty()); @@ -1414,7 +1414,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) return; } it->setDeparted(); - emit q->pendingEventChanged(it - unsyncedEvents.begin()); + emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); }); Room::connect(call, &BaseJob::failure, q, std::bind(&Room::Private::onEventSendingFailure, this, txnId, call)); @@ -1430,7 +1430,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) } it->setReachedServer(call->eventId()); - emit q->pendingEventChanged(it - unsyncedEvents.begin()); + emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); }); } else onEventSendingFailure(txnId); @@ -1449,7 +1449,7 @@ void Room::Private::onEventSendingFailure(const QString& txnId, BaseJob* call) it->setSendingFailed(call ? call->statusCaption() % ": " % call->errorString() : tr("The call could not be started")); - emit q->pendingEventChanged(it - unsyncedEvents.begin()); + emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); } QString Room::retryMessage(const QString& txnId) @@ -2052,7 +2052,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) roomChanges |= q->processStateEvent(*eptr); auto timelineSize = timeline.size(); - auto totalInserted = 0; + size_t totalInserted = 0; for (auto it = events.begin(); it != events.end();) { auto nextPendingPair = findFirstOf(it, events.end(), diff --git a/lib/settings.cpp b/lib/settings.cpp index 852e19cb..124d7042 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -84,18 +84,21 @@ void SettingsGroup::remove(const QString& key) Settings::remove(fullKey); } -QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", "", setDeviceId) -QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", "", setDeviceName) +QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", {}, setDeviceId) +QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, setDeviceName) QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) +static const auto HomeserverKey = QStringLiteral("homeserver"); +static const auto AccessTokenKey = QStringLiteral("access_token"); + QUrl AccountSettings::homeserver() const { - return QUrl::fromUserInput(value("homeserver").toString()); + return QUrl::fromUserInput(value(HomeserverKey).toString()); } void AccountSettings::setHomeserver(const QUrl& url) { - setValue("homeserver", url.toString()); + setValue(HomeserverKey, url.toString()); } QString AccountSettings::userId() const @@ -105,19 +108,19 @@ QString AccountSettings::userId() const QString AccountSettings::accessToken() const { - return value("access_token").toString(); + return value(AccessTokenKey).toString(); } void AccountSettings::setAccessToken(const QString& accessToken) { qCWarning(MAIN) << "Saving access_token to QSettings is insecure." " Developers, please save access_token separately."; - setValue("access_token", accessToken); + setValue(AccessTokenKey, accessToken); } void AccountSettings::clearAccessToken() { - legacySettings.remove("access_token"); - legacySettings.remove("device_id"); // Force the server to re-issue it - remove("access_token"); + legacySettings.remove(AccessTokenKey); + legacySettings.remove(QStringLiteral("device_id")); // Force the server to re-issue it + remove(AccessTokenKey); } diff --git a/lib/settings.h b/lib/settings.h index 0b3ecaff..759bda35 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -119,7 +119,7 @@ type classname::propname() const \ \ void classname::setter(type newValue) \ { \ - setValue(QStringLiteral(qsettingname), newValue); \ + setValue(QStringLiteral(qsettingname), std::move(newValue)); \ } \ class AccountSettings: public SettingsGroup diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index f55d4396..21517884 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -72,7 +72,7 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, { fromJson(jo["m.joined_member_count"_ls], rs.joinedMemberCount); fromJson(jo["m.invited_member_count"_ls], rs.invitedMemberCount); - fromJson(jo["m.heroes"], rs.heroes); + fromJson(jo["m.heroes"_ls], rs.heroes); } template @@ -85,7 +85,7 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) : roomId(roomId_) , joinState(joinState_) - , summary(fromJson(room_["summary"])) + , summary(fromJson(room_["summary"_ls])) , state(load(room_, joinState == JoinState::Invite ? "invite_state"_ls : "state"_ls)) { @@ -121,8 +121,8 @@ SyncData::SyncData(const QString& cacheFileName) QFileInfo cacheFileInfo { cacheFileName }; auto json = loadJson(cacheFileName); auto requiredVersion = std::get<0>(cacheVersion()); - auto actualVersion = json.value("cache_version").toObject() - .value("major").toInt(); + auto actualVersion = json.value("cache_version"_ls).toObject() + .value("major"_ls).toInt(); if (actualVersion == requiredVersion) parseJson(json, cacheFileInfo.absolutePath() + '/'); else diff --git a/lib/user.cpp b/lib/user.cpp index 951ad87d..17db5760 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -59,7 +59,7 @@ class User::Private QMultiHash otherNames; Avatar mostUsedAvatar { makeAvatar({}) }; std::vector otherAvatars; - auto otherAvatar(QUrl url) + auto otherAvatar(const QUrl& url) { return std::find_if(otherAvatars.begin(), otherAvatars.end(), [&url] (const auto& av) { return av.url() == url; }); @@ -69,7 +69,7 @@ class User::Private mutable int totalRooms = 0; QString nameForRoom(const Room* r, const QString& hint = {}) const; - void setNameForRoom(const Room* r, QString newName, QString oldName); + void setNameForRoom(const Room* r, QString newName, const QString& oldName); QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; void setAvatarForRoom(const Room* r, const QUrl& newUrl, const QUrl& oldUrl); @@ -91,7 +91,7 @@ QString User::Private::nameForRoom(const Room* r, const QString& hint) const static constexpr int MIN_JOINED_ROOMS_TO_LOG = 20; void User::Private::setNameForRoom(const Room* r, QString newName, - QString oldName) + const QString& oldName) { Q_ASSERT(oldName != newName); Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r)); @@ -118,7 +118,8 @@ void User::Private::setNameForRoom(const Room* r, QString newName, et.start(); } - for (auto* r1: connection->roomMap()) + const auto& roomMap = connection->roomMap(); + for (auto* r1: roomMap) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -178,7 +179,8 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, auto nextMostUsedIt = otherAvatar(newUrl); Q_ASSERT(nextMostUsedIt != otherAvatars.end()); std::swap(mostUsedAvatar, *nextMostUsedIt); - for (const auto* r1: connection->roomMap()) + const auto& roomMap = connection->roomMap(); + for (const auto* r1: roomMap) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); @@ -399,7 +401,7 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, // exceptionally rare (the only reasonable case being that the bridge // changes the naming convention). For the same reason room-specific // bridge tags are not supported at all. - QRegularExpression reSuffix(" \\((IRC|Gitter|Telegram)\\)$"); + QRegularExpression reSuffix(QStringLiteral(" \\((IRC|Gitter|Telegram)\\)$")); auto match = reSuffix.match(newName); if (match.hasMatch()) { diff --git a/lib/util.cpp b/lib/util.cpp index fe6286f3..c3e21c8e 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -157,7 +157,7 @@ static_assert(!is_callable_v>, "Test non-function object"); // "Test returns<> with static member function"); template -QString ft(T&&); +QString ft(T&&) { return {}; } static_assert(std::is_same)>, QString&&>(), "Test function templates"); -- cgit v1.2.3 From c36341512560cedf063279848fa43c44b146161b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Apr 2019 20:32:09 +0900 Subject: Room::processStateEvent: be more careful with signals handling at user renames --- lib/room.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index f031273a..e363767a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1216,7 +1216,6 @@ void Room::Private::renameMember(User* u, const QString& oldName) removeMemberFromMap(oldName, u); insertMemberIntoMap(u); } - emit q->memberRenamed(u); } void Room::Private::removeMemberFromMap(const QString& username, User* u) @@ -2237,6 +2236,8 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) << evt; if (evt.membership() != prevMembership) { + disconnect(u, &User::nameAboutToChange, this, nullptr); + disconnect(u, &User::nameChanged, this, nullptr); d->removeMemberFromMap(u->name(this), u); emit userRemoved(u); } @@ -2264,7 +2265,10 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) connect(u, &User::nameChanged, this, [=] (QString, QString oldName, const Room* context) { if (context == this) + { d->renameMember(u, oldName); + emit memberRenamed(u); + } }); emit userAdded(u); } -- cgit v1.2.3 From a6df4183d6da324f2c2329f21d732071b3337cb8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 15 Apr 2019 20:29:00 +0900 Subject: BaseJob: fix a possible crash upon logout See https://github.com/QMatrixClient/Quaternion/issues/566 for details. --- lib/jobs/basejob.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index f521cc4b..a79d0e03 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -600,10 +600,25 @@ QUrl BaseJob::errorUrl() const void BaseJob::setStatus(Status s) { + // The crash that led to this code has been reported in + // https://github.com/QMatrixClient/Quaternion/issues/566 - basically, + // when cleaning up childrent of a deleted Connection, there's a chance + // of pending jobs being abandoned, calling setStatus(Abandoned). + // There's nothing wrong with this; however, the safety check for + // cleartext access tokens below uses d->connection - which is a dangling + // pointer. + // To alleviate that, a stricter condition is applied, that for Abandoned + // and to-be-Abandoned jobs the status message will be disregarded entirely. + // For 0.6 we might rectify the situation by making d->connection + // a QPointer<> (and derive ConnectionData from QObject, respectively). + if (d->status.code == Abandoned || s.code == Abandoned) + s.message.clear(); + if (d->status == s) return; - if (d->connection && !d->connection->accessToken().isEmpty()) + if (!s.message.isEmpty() + && d->connection && !d->connection->accessToken().isEmpty()) s.message.replace(d->connection->accessToken(), "(REDACTED)"); if (!s.good()) qCWarning(d->logCat) << this << "status" << s; -- cgit v1.2.3 From 8a3bb2d5b27824890482fa7185bbc1fcda9dd49c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 16 Apr 2019 16:21:53 +0900 Subject: BaseJob: preserve the calculated error code if JSON error code is unknown Resetting the code to IncorrectRequestError has been a part of the cause for the incorrect Quaternion behaviour on expired tokens. --- lib/jobs/basejob.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index a79d0e03..0d9b9f10 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -334,8 +334,7 @@ void BaseJob::gotReply() tr("Requested room version: %1") .arg(json.value("room_version").toString()); } else if (!json.isEmpty()) // Not localisable on the client side - setStatus(IncorrectRequestError, - json.value("error"_ls).toString()); + setStatus(d->status.code, json.value("error"_ls).toString()); } } -- cgit v1.2.3 From 1103c1fa00d99c4e090f76fd07a384032f79f0c1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 16 Apr 2019 16:22:54 +0900 Subject: Connection::logout: ignore ContentAccessError Closes #316. --- lib/connection.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5ed72616..fa358e17 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -321,11 +321,14 @@ void Connection::checkAndConnect(const QString& userId, void Connection::logout() { auto job = callApi(); - connect( job, &LogoutJob::success, this, [this] { - stopSync(); - d->data->setToken({}); - emit stateChanged(); - emit loggedOut(); + connect( job, &LogoutJob::finished, this, [job,this] { + if (job->status().good() || job->error() == BaseJob::ContentAccessError) + { + stopSync(); + d->data->setToken({}); + emit stateChanged(); + emit loggedOut(); + } }); } -- cgit v1.2.3 From 05c9984fd1d369397fbf3c9cad67413906e817cc Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 24 Apr 2019 21:18:24 +0800 Subject: Make ignore-user related method Q_INVOKABLE. --- lib/connection.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index ea5be51a..42e678ff 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -219,10 +219,10 @@ namespace QMatrixClient QList directChatUsers(const Room* room) const; /** Check whether a particular user is in the ignore list */ - bool isIgnored(const User* user) const; + Q_INVOKABLE bool isIgnored(const User* user) const; /** Get the whole list of ignored users */ - IgnoredUsersList ignoredUsers() const; + Q_INVOKABLE IgnoredUsersList ignoredUsers() const; /** Add the user to the ignore list * The change signal is emitted synchronously, without waiting @@ -230,14 +230,14 @@ namespace QMatrixClient * * \sa ignoredUsersListChanged */ - void addToIgnoredUsers(const User* user); + Q_INVOKABLE void addToIgnoredUsers(const User* user); /** Remove the user from the ignore list */ /** Similar to adding, the change signal is emitted synchronously. * * \sa ignoredUsersListChanged */ - void removeFromIgnoredUsers(const User* user); + Q_INVOKABLE void removeFromIgnoredUsers(const User* user); /** Get the full list of users known to this account */ QMap users() const; -- cgit v1.2.3 From 0378e830c40a1d7302a8108d460940b6cd15201e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 3 May 2019 22:56:42 +0900 Subject: Connection::stateCacheDir Same as stateCachePath but returns QDir. --- lib/connection.cpp | 16 +++++++++++++--- lib/connection.h | 28 ++++++++++++++++++---------- 2 files changed, 31 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index fa358e17..d75d8e56 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -133,6 +133,10 @@ class Connection::Private packAndSendAccountData( makeEvent(std::forward(content))); } + QString topLevelStatePath() const + { + return q->stateCacheDir().filePath("state.json"); + } }; Connection::Connection(const QUrl& server, QObject* parent) @@ -1232,7 +1236,8 @@ void Connection::saveRoomState(Room* r) const if (!d->cacheState) return; - QFile outRoomFile { stateCachePath() % SyncData::fileNameForRoom(r->id()) }; + QFile outRoomFile { + stateCacheDir().filePath(SyncData::fileNameForRoom(r->id())) }; if (outRoomFile.open(QFile::WriteOnly)) { QJsonDocument json { r->toJson() }; @@ -1253,7 +1258,7 @@ void Connection::saveState() const QElapsedTimer et; et.start(); - QFile outFile { stateCachePath() % "state.json" }; + QFile outFile { d->topLevelStatePath() }; if (!outFile.open(QFile::WriteOnly)) { qCWarning(MAIN) << "Error opening" << outFile.fileName() @@ -1313,7 +1318,7 @@ void Connection::loadState() QElapsedTimer et; et.start(); - SyncData sync { stateCachePath() % "state.json" }; + SyncData sync { d->topLevelStatePath() }; if (sync.nextBatch().isEmpty()) // No token means no cache by definition return; @@ -1330,6 +1335,11 @@ void Connection::loadState() } QString Connection::stateCachePath() const +{ + return stateCacheDir().path() % '/'; +} + +QDir Connection::stateCacheDir() const { auto safeUserId = userId(); safeUserId.replace(':', '_'); diff --git a/lib/connection.h b/lib/connection.h index 42e678ff..2ff27ea6 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -305,8 +306,8 @@ namespace QMatrixClient * Call this before first sync to load from previously saved file. * * \param fromFile A local path to read the state from. Uses QUrl - * to be QML-friendly. Empty parameter means using a path - * defined by stateCachePath(). + * to be QML-friendly. Empty parameter means saving to the directory + * defined by stateCachePath() / stateCacheDir(). */ Q_INVOKABLE void loadState(); /** @@ -315,23 +316,30 @@ namespace QMatrixClient * loadState() on a next run of the client. * * \param toFile A local path to save the state to. Uses QUrl to be - * QML-friendly. Empty parameter means using a path defined by - * stateCachePath(). + * QML-friendly. Empty parameter means saving to the directory + * defined by stateCachePath() / stateCacheDir(). */ Q_INVOKABLE void saveState() const; /// This method saves the current state of a single room. void saveRoomState(Room* r) const; + /// Get the default directory path to save the room state to + /** \sa stateCacheDir */ + Q_INVOKABLE QString stateCachePath() const; + + /// Get the default directory to save the room state to /** - * The default path to store the cached room state, defined as - * follows: + * This function returns the default directory to store the cached + * room state, defined as follows: + * \code * QStandardPaths::writeableLocation(QStandardPaths::CacheLocation) + _safeUserId + "_state.json" - * where `_safeUserId` is userId() with `:` (colon) replaced with - * `_` (underscore) - * /see loadState(), saveState() + * \endcode + * where `_safeUserId` is userId() with `:` (colon) replaced by + * `_` (underscore), as colons are reserved characters on Windows. + * \sa loadState, saveState, stateCachePath */ - Q_INVOKABLE QString stateCachePath() const; + QDir stateCacheDir() const; bool cacheState() const; void setCacheState(bool newValue); -- cgit v1.2.3 From 2d39b1e51e500fa17788630cd0e6a57a451eb475 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 4 May 2019 08:34:18 +0900 Subject: Room::allHistoryLoaded --- lib/room.cpp | 5 +++++ lib/room.h | 9 +++++++++ 2 files changed, 14 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index e363767a..2ce37acc 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -370,6 +370,11 @@ const Room::PendingEvents& Room::pendingEvents() const return d->unsyncedEvents; } +bool Room::allHistoryLoaded() const +{ + return !d->timeline.empty() && is(*d->timeline.front()); +} + QString Room::name() const { return d->getCurrentState()->name(); diff --git a/lib/room.h b/lib/room.h index db4336ea..055da3da 100644 --- a/lib/room.h +++ b/lib/room.h @@ -110,6 +110,7 @@ namespace QMatrixClient Q_PROPERTY(int unreadCount READ unreadCount NOTIFY unreadMessagesChanged) Q_PROPERTY(int highlightCount READ highlightCount NOTIFY highlightCountChanged RESET resetHighlightCount) Q_PROPERTY(int notificationCount READ notificationCount NOTIFY notificationCountChanged RESET resetNotificationCount) + Q_PROPERTY(bool allHistoryLoaded READ allHistoryLoaded NOTIFY addedMessages STORED false) Q_PROPERTY(QStringList tagNames READ tagNames NOTIFY tagsChanged) Q_PROPERTY(bool isFavourite READ isFavourite NOTIFY tagsChanged) Q_PROPERTY(bool isLowPriority READ isLowPriority NOTIFY tagsChanged) @@ -228,6 +229,14 @@ namespace QMatrixClient const Timeline& messageEvents() const; const PendingEvents& pendingEvents() const; + + /// Check whether all historical messages are already loaded + /** + * \return true if the "oldest" event in the timeline is + * a room creation event and there's no further history + * to load; false otherwise + */ + bool allHistoryLoaded() const; /** * A convenience method returning the read marker to the position * before the "oldest" event; same as messageEvents().crend() -- cgit v1.2.3 From 346adee1810109f4b7b14298e55d29a44c076a66 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 13 May 2019 20:41:04 +0900 Subject: prettyPrint(): First linkify, than add more tags Otherwise the linkification gets confused by HTML tags being already there and doesn't linkify what has to be linkified if that occurs at the beginning of the message. --- lib/util.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index c3e21c8e..0248e521 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -74,12 +74,11 @@ QString QMatrixClient::sanitized(const QString& plainText) QString QMatrixClient::prettyPrint(const QString& plainText) { - auto pt = QStringLiteral("") + - plainText.toHtmlEscaped() + QStringLiteral(""); - pt.replace('\n', QStringLiteral("
")); - + auto pt = plainText.toHtmlEscaped(); linkifyUrls(pt); - return pt; + pt.replace('\n', QStringLiteral("
")); + return QStringLiteral("") + pt + + QStringLiteral(""); } QString QMatrixClient::cacheLocation(const QString& dirName) -- cgit v1.2.3 From 3c253ed025246a34d849d14aac6feaee672d7e63 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 13 May 2019 20:42:50 +0900 Subject: linkifyUrls(): be more conservative in parsing serverparts Closes #321. --- lib/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 0248e521..883db2ea 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -51,7 +51,7 @@ void QMatrixClient::linkifyUrls(QString& htmlEscapedText) // An interim liberal implementation of // https://matrix.org/docs/spec/appendices.html#identifier-grammar static const QRegularExpression MxIdRegExp(QStringLiteral( - R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:[-.a-z0-9]+))" + R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:(?:\w|\.|-)+\.\w+(?::\d{1,5})?))" ), RegExpOptions); // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,&," -- cgit v1.2.3 From 1a034626bcbe064ebe0ada8cdfe1a47f2d82e477 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 13 May 2019 21:43:54 +0900 Subject: sanitized(): add object replacement character (0xfffc) to the blacklist --- lib/util.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 883db2ea..4e17d2f9 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -57,18 +57,19 @@ void QMatrixClient::linkifyUrls(QString& htmlEscapedText) // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,&," htmlEscapedText.replace(EmailAddressRegExp, - QStringLiteral(R"(\1\2)")); + QStringLiteral(R"(\1\2)")); htmlEscapedText.replace(FullUrlRegExp, - QStringLiteral(R"(\1)")); + QStringLiteral(R"(\1)")); htmlEscapedText.replace(MxIdRegExp, - QStringLiteral(R"(\1\2)")); + QStringLiteral(R"(\1\2)")); } QString QMatrixClient::sanitized(const QString& plainText) { auto text = plainText; - text.remove(QChar(0x202e)); - text.remove(QChar(0x202d)); + text.remove(QChar(0x202e)); // RLO + text.remove(QChar(0x202d)); // LRO + text.remove(QChar(0xfffc)); // Object replacement character return text; } -- cgit v1.2.3 From 74fa9bc64128d88939259ccb2ba4dca51571559a Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Wed, 6 Mar 2019 22:13:05 +0300 Subject: Provide a colour code for the user Contributes to #296 --- lib/user.cpp | 28 +++++++++++++++++++++++++++- lib/user.h | 11 +++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index eec41957..b13f98b4 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -33,6 +33,9 @@ #include #include +#include +#include + #include using namespace QMatrixClient; @@ -47,8 +50,21 @@ class User::Private return Avatar(move(url)); } + qreal makeHueF(QString userId) + { + QByteArray hash = QCryptographicHash::hash(userId.toUtf8(), + QCryptographicHash::Sha1); + QDataStream dataStream(qToLittleEndian(hash).left(2)); + dataStream.setByteOrder(QDataStream::LittleEndian); + quint16 hashValue; + dataStream >> hashValue; + qreal hueF = static_cast(hashValue)/std::numeric_limits::max(); + Q_ASSERT((0 <= hueF) && (hueF <= 1)); + return hueF; + } + Private(QString userId, Connection* connection) - : userId(move(userId)), connection(connection) + : userId(move(userId)), connection(connection), hueF(makeHueF(userId)) { } QString userId; @@ -57,6 +73,7 @@ class User::Private QString bridged; QString mostUsedName; QMultiHash otherNames; + qreal hueF; Avatar mostUsedAvatar { makeAvatar({}) }; std::vector otherAvatars; auto otherAvatar(QUrl url) @@ -219,6 +236,11 @@ bool User::isGuest() const return *it == ':'; } +int User::hue() const +{ + return int(hueF()*359); +} + QString User::name(const Room* room) const { return d->nameForRoom(room); @@ -424,3 +446,7 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room) updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room); } } + +qreal User::hueF() const { + return d->hueF; +} diff --git a/lib/user.h b/lib/user.h index 0023b44a..af1abfa2 100644 --- a/lib/user.h +++ b/lib/user.h @@ -33,6 +33,8 @@ namespace QMatrixClient Q_OBJECT Q_PROPERTY(QString id READ id CONSTANT) Q_PROPERTY(bool isGuest READ isGuest CONSTANT) + Q_PROPERTY(int hue READ hue CONSTANT) + Q_PROPERTY(qreal hueF READ hueF CONSTANT) Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString displayName READ displayname NOTIFY nameChanged STORED false) Q_PROPERTY(QString fullName READ fullName NOTIFY nameChanged STORED false) @@ -95,6 +97,15 @@ namespace QMatrixClient */ bool isGuest() const; + /** Hue color component of this user based on id. + * The implementation is based on XEP-0392: + * https://xmpp.org/extensions/xep-0392.html + * Naming and ranges are the same as QColor's hue methods: + * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision + */ + int hue() const; + qreal hueF() const; + const Avatar& avatarObject(const Room* room = nullptr) const; Q_INVOKABLE QImage avatar(int dimension, const Room* room = nullptr); Q_INVOKABLE QImage avatar(int requestedWidth, int requestedHeight, -- cgit v1.2.3 From d6f39dcb0de69322479f287514a8c36afcb3fe7b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 14 May 2019 15:29:56 +0900 Subject: User::Private::makeHueF: Fix trying to use the moved value --- lib/user.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index fdb82a38..7f3f11f6 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -50,21 +50,23 @@ class User::Private return Avatar(move(url)); } - qreal makeHueF(QString userId) + qreal makeHueF() { + Q_ASSERT(!userId.isEmpty()); QByteArray hash = QCryptographicHash::hash(userId.toUtf8(), QCryptographicHash::Sha1); QDataStream dataStream(qToLittleEndian(hash).left(2)); dataStream.setByteOrder(QDataStream::LittleEndian); quint16 hashValue; dataStream >> hashValue; - qreal hueF = static_cast(hashValue)/std::numeric_limits::max(); + const auto hueF = + qreal(hashValue)/std::numeric_limits::max(); Q_ASSERT((0 <= hueF) && (hueF <= 1)); return hueF; } Private(QString userId, Connection* connection) - : userId(move(userId)), connection(connection), hueF(makeHueF(userId)) + : userId(move(userId)), connection(connection), hueF(makeHueF()) { } QString userId; -- cgit v1.2.3 From 44ce399ab59204864b8c0c70d9b92e73e7e0135a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 17 May 2019 21:19:32 +0900 Subject: Minor code refactoring --- lib/events/accountdataevents.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index a99d85ac..ffee5ba6 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -85,7 +85,7 @@ namespace QMatrixClient toJson(std::move(content)) } }) \ { } \ auto _ContentKey() const \ - { return fromJson(contentJson()[#_ContentKey##_ls]); } \ + { return content(#_ContentKey##_ls); } \ }; \ REGISTER_EVENT_TYPE(_Name) \ // End of macro -- cgit v1.2.3 From 9ca07298008d891250ffc79771bc5ef8c97950cb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 17 May 2019 21:20:30 +0900 Subject: Drop unneeded #include --- lib/avatar.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/avatar.cpp b/lib/avatar.cpp index 9279ef9d..0d849eae 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -1,5 +1,3 @@ -#include - /****************************************************************************** * Copyright (C) 2017 Kitsune Ral * -- cgit v1.2.3 From 7374cef74ab92dd2165831c73db51c7dfa4a3511 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 18 May 2019 21:48:04 +0900 Subject: event.h: add doc-comments; deprecate ptrCast() --- lib/events/event.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/event.h b/lib/events/event.h index d7ac4292..b7bbd83e 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -32,19 +32,23 @@ namespace QMatrixClient template using event_ptr_tt = std::unique_ptr; + /// Unwrap a plain pointer from a smart pointer template - inline EventT* rawPtr(const event_ptr_tt& ptr) // unwrap + inline EventT* rawPtr(const event_ptr_tt& ptr) { return ptr.get(); } + /// Unwrap a plain pointer and downcast it to the specified type template inline TargetEventT* weakPtrCast(const event_ptr_tt& ptr) { return static_cast(rawPtr(ptr)); } + /// Re-wrap a smart pointer to base into a smart pointer to derived template + [[deprecated("Consider using eventCast() or visit() instead")]] inline event_ptr_tt ptrCast(event_ptr_tt&& ptr) { return unique_ptr_cast(ptr); -- cgit v1.2.3 From 1033f3b4249b8b0224ba725b220338e6c9621084 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 18 May 2019 21:50:08 +0900 Subject: Connection::onSyncSuccess(): fix using after move() Also rewrite the account data piece with visit(). --- lib/connection.cpp | 116 +++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 56 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index d75d8e56..cd0d96f7 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -411,63 +411,67 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { // Let UI update itself after updating each room QCoreApplication::processEvents(); } - for (auto&& accountEvent: data.takeAccountData()) - { - if (is(*accountEvent)) - { - const auto usersToDCs = ptrCast(move(accountEvent)) - ->usersToDirectChats(); - DirectChatsMap removals = - erase_if(d->directChats, [&usersToDCs] (auto it) { - return !usersToDCs.contains(it.key()->id(), it.value()); - }); - erase_if(d->directChatUsers, [&usersToDCs] (auto it) { - return !usersToDCs.contains(it.value()->id(), it.key()); - }); - if (MAIN().isDebugEnabled()) - for (auto it = removals.begin(); it != removals.end(); ++it) - qCDebug(MAIN) << it.value() - << "is no more a direct chat with" << it.key()->id(); - - DirectChatsMap additions; - for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) - { - if (auto* u = user(it.key())) - { - if (!d->directChats.contains(u, it.value())) - { - Q_ASSERT(!d->directChatUsers.contains(it.value(), u)); - additions.insert(u, it.value()); - d->directChats.insert(u, it.value()); - d->directChatUsers.insert(it.value(), u); - qCDebug(MAIN) << "Marked room" << it.value() + // After running this loop, the account data events not saved in + // d->accountData (see the end of the loop body) are auto-cleaned away + for (auto& eventPtr: data.takeAccountData()) + visit(*eventPtr, + [this](const DirectChatEvent& dce) { + const auto& usersToDCs = dce.usersToDirectChats(); + DirectChatsMap removals = + erase_if(d->directChats, [&usersToDCs](auto it) { + return !usersToDCs.contains(it.key()->id(), + it.value()); + }); + erase_if(d->directChatUsers, [&usersToDCs](auto it) { + return !usersToDCs.contains(it.value()->id(), it.key()); + }); + if (MAIN().isDebugEnabled()) + for (auto it = removals.begin(); it != removals.end(); + ++it) + qCDebug(MAIN) << it.value() + << "is no more a direct chat with" + << it.key()->id(); + + DirectChatsMap additions; + for (auto it = usersToDCs.begin(); it != usersToDCs.end(); + ++it) { + if (auto* u = user(it.key())) { + if (!d->directChats.contains(u, it.value())) { + Q_ASSERT(!d->directChatUsers.contains(it.value(), + u)); + additions.insert(u, it.value()); + d->directChats.insert(u, it.value()); + d->directChatUsers.insert(it.value(), u); + qCDebug(MAIN) + << "Marked room" << it.value() << "as a direct chat with" << u->id(); - } - } else - qCWarning(MAIN) - << "Couldn't get a user object for" << it.key(); - } - if (!additions.isEmpty() || !removals.isEmpty()) - emit directChatsListChanged(additions, removals); - - continue; - } - if (is(*accountEvent)) - qCDebug(MAIN) << "Users ignored by" << d->userId << "updated:" - << QStringList::fromSet(ignoredUsers()).join(','); - - auto& currentData = d->accountData[accountEvent->matrixType()]; - // A polymorphic event-specific comparison might be a bit more - // efficient; maaybe do it another day - if (!currentData || - currentData->contentJson() != accountEvent->contentJson()) - { - currentData = std::move(accountEvent); - qCDebug(MAIN) << "Updated account data of type" - << currentData->matrixType(); - emit accountDataChanged(currentData->matrixType()); - } - } + } + } else + qCWarning(MAIN) << "Couldn't get a user object for" + << it.key(); + } + if (!additions.isEmpty() || !removals.isEmpty()) + emit directChatsListChanged(additions, removals); + }, + // catch-all, passing eventPtr for a possible take-over + [this, &eventPtr](const Event& accountEvent) { + if (is(accountEvent)) + qCDebug(MAIN) + << "Users ignored by" << d->userId << "updated:" + << QStringList::fromSet(ignoredUsers()).join(','); + + auto& currentData = d->accountData[accountEvent.matrixType()]; + // A polymorphic event-specific comparison might be a bit more + // efficient; maaybe do it another day + if (!currentData + || currentData->contentJson() + != accountEvent.contentJson()) { + currentData = std::move(eventPtr); + qCDebug(MAIN) << "Updated account data of type" + << currentData->matrixType(); + emit accountDataChanged(currentData->matrixType()); + } + }); } void Connection::stopSync() -- cgit v1.2.3 From bb6d33182269425d98f7aa4e22aa5478cff57550 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 19 May 2019 08:47:23 +0900 Subject: Move a doc-comment to its place --- lib/connection.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index 2ff27ea6..018c0459 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -96,9 +96,6 @@ namespace QMatrixClient class Connection: public QObject { Q_OBJECT - /** Whether or not the rooms state should be cached locally - * \sa loadState(), saveState() - */ Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged) Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) @@ -341,6 +338,9 @@ namespace QMatrixClient */ QDir stateCacheDir() const; + /** Whether or not the rooms state should be cached locally + * \sa loadState(), saveState() + */ bool cacheState() const; void setCacheState(bool newValue); -- cgit v1.2.3 From 626b2711a49e581babafd179ce362a1c88db8b85 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 19 May 2019 08:54:00 +0900 Subject: Connection: use QScopedPointer instead of unique_ptr While theoretically less robust (no equivalent of make_unique), QScopedPointer is navigable in Qt Creator debug views, unlike unique_ptr. Of course this will eventually be fixed; but given that inability to create an owning pointer object means sure abnormal termination of our code shortly afterwards, having make_unique in this particular case doesn't help in any way at all; so unique_ptr has zero advantages over QScopedPointer in this setting. --- lib/connection.cpp | 3 ++- lib/connection.h | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index cd0d96f7..e3da3c16 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -141,7 +141,7 @@ class Connection::Private Connection::Connection(const QUrl& server, QObject* parent) : QObject(parent) - , d(std::make_unique(std::make_unique(server))) + , d(new Private(std::make_unique(server))) { d->q = this; // All d initialization should occur before this line } @@ -294,6 +294,7 @@ void Connection::Private::connectWithToken(const QString& user, q->user(); // Creates a User object for the local user data->setToken(accessToken.toLatin1()); data->setDeviceId(deviceId); + q->setObjectName(userId % '/' % deviceId); qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() << "by user" << userId << "from device" << deviceId; emit q->stateChanged(); diff --git a/lib/connection.h b/lib/connection.h index 018c0459..cc2feed8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -29,7 +29,6 @@ #include #include -#include namespace QMatrixClient { @@ -744,7 +743,7 @@ namespace QMatrixClient private: class Private; - std::unique_ptr d; + QScopedPointer d; /** * A single entry for functions that need to check whether the -- cgit v1.2.3 From 4ae7fe50e5aae28dee3d207b0ca02207c46bb781 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 19 May 2019 15:35:52 +0900 Subject: Improve wording in a comment --- lib/connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index e3da3c16..98cd1ef1 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -80,8 +80,8 @@ class Connection::Private std::unique_ptr data; // A complex key below is a pair of room name and whether its // state is Invited. The spec mandates to keep Invited room state - // separately so we should, e.g., keep objects for Invite and - // Leave state of the same room. + // separately; specifically, we should keep objects for Invite and + // Leave state of the same room if the two happen to co-exist. QHash, Room*> roomMap; // Mapping from aliases to room ids, as per the last sync QHash roomAliasMap; -- cgit v1.2.3 From 89e0cec42fe9538cbd170a9dc3fcf6dd18b5ac02 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 20 May 2019 07:46:03 +0900 Subject: Connection: Fix a race condition in direct chats handling upon initial sync Closes #323. --- lib/connection.cpp | 129 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 75 insertions(+), 54 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 98cd1ef1..a7eae30f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -73,8 +73,7 @@ class Connection::Private : data(move(connection)) { } Q_DISABLE_COPY(Private) - Private(Private&&) = delete; - Private operator=(Private&&) = delete; + DISABLE_MOVE(Private) Connection* q = nullptr; std::unique_ptr data; @@ -91,6 +90,10 @@ class Connection::Private QMap userMap; DirectChatsMap directChats; DirectChatUsersMap directChatUsers; + // The below two variables track local changes between sync completions. + // See also: https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events + DirectChatsMap dcLocalAdditions; + DirectChatsMap dcLocalRemovals; std::unordered_map accountData; QString userId; int syncLoopTimeout = -1; @@ -107,8 +110,6 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); - void broadcastDirectChatUpdates(const DirectChatsMap& additions, - const DirectChatsMap& removals); template EventT* unpackAccountData() const @@ -378,6 +379,20 @@ void Connection::syncLoop(int timeout) syncLoopIteration(); // initial sync to start the loop } +QJsonObject toJson(const Connection::DirectChatsMap& directChats) +{ + QJsonObject json; + for (auto it = directChats.begin(); it != directChats.end();) + { + QJsonArray roomIds; + const auto* user = it.key(); + for (; it != directChats.end() && it.key() == user; ++it) + roomIds.append(*it); + json.insert(user->id(), roomIds); + } + return json; +} + void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { d->data->setLastEvent(data.nextBatch()); for (auto&& roomData: data.takeRoomData()) @@ -414,33 +429,43 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { } // After running this loop, the account data events not saved in // d->accountData (see the end of the loop body) are auto-cleaned away - for (auto& eventPtr: data.takeAccountData()) + for (auto& eventPtr : data.takeAccountData()) + { visit(*eventPtr, [this](const DirectChatEvent& dce) { + // See https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events const auto& usersToDCs = dce.usersToDirectChats(); - DirectChatsMap removals = - erase_if(d->directChats, [&usersToDCs](auto it) { - return !usersToDCs.contains(it.key()->id(), - it.value()); - }); - erase_if(d->directChatUsers, [&usersToDCs](auto it) { - return !usersToDCs.contains(it.value()->id(), it.key()); + DirectChatsMap remoteRemovals = + erase_if(d->directChats, [&usersToDCs, this](auto it) { + return !(usersToDCs.contains(it.key()->id(), it.value()) + || d->dcLocalAdditions.contains(it.key(), + it.value())); + }); + erase_if(d->directChatUsers, [&remoteRemovals](auto it) { + return remoteRemovals.contains(it.value(), it.key()); + }); + // Remove from dcLocalRemovals what the server already has. + erase_if(d->dcLocalRemovals, [&remoteRemovals](auto it) { + return remoteRemovals.contains(it.key(), it.value()); }); if (MAIN().isDebugEnabled()) - for (auto it = removals.begin(); it != removals.end(); - ++it) - qCDebug(MAIN) << it.value() - << "is no more a direct chat with" - << it.key()->id(); - - DirectChatsMap additions; + for (auto it = remoteRemovals.begin(); + it != remoteRemovals.end(); ++it) { + qCDebug(MAIN) + << it.value() << "is no more a direct chat with" + << it.key()->id(); + } + + DirectChatsMap remoteAdditions; for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { if (auto* u = user(it.key())) { - if (!d->directChats.contains(u, it.value())) { - Q_ASSERT(!d->directChatUsers.contains(it.value(), - u)); - additions.insert(u, it.value()); + if (!d->directChats.contains(u, it.value()) + && !d->dcLocalRemovals.contains(u, it.value())) + { + Q_ASSERT( + !d->directChatUsers.contains(it.value(), u)); + remoteAdditions.insert(u, it.value()); d->directChats.insert(u, it.value()); d->directChatUsers.insert(it.value(), u); qCDebug(MAIN) @@ -451,8 +476,12 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { qCWarning(MAIN) << "Couldn't get a user object for" << it.key(); } - if (!additions.isEmpty() || !removals.isEmpty()) - emit directChatsListChanged(additions, removals); + // Remove from dcLocalAdditions what the server already has. + erase_if(d->dcLocalAdditions, [&remoteAdditions](auto it) { + return remoteAdditions.contains(it.key(), it.value()); + }); + if (!remoteAdditions.isEmpty() || !remoteRemovals.isEmpty()) + emit directChatsListChanged(remoteAdditions, remoteRemovals); }, // catch-all, passing eventPtr for a possible take-over [this, &eventPtr](const Event& accountEvent) { @@ -473,6 +502,16 @@ void Connection::onSyncSuccess(SyncData &&data, bool fromCache) { emit accountDataChanged(currentData->matrixType()); } }); + } + if (!d->dcLocalAdditions.isEmpty() || !d->dcLocalRemovals.isEmpty()) { + qDebug(MAIN) << "Sending updated direct chats to the server:" + << d->dcLocalRemovals.size() << "removal(s)," + << d->dcLocalAdditions.size() << "addition(s)"; + callApi(d->userId, QStringLiteral("m.direct"), + toJson(d->directChats)); + d->dcLocalAdditions.clear(); + d->dcLocalRemovals.clear(); + } } void Connection::stopSync() @@ -667,8 +706,8 @@ void Connection::doInDirectChat(User* u, { Q_ASSERT(u); const auto& userId = u->id(); - // There can be more than one DC; find the first valid, and delete invalid - // (left/forgotten) ones along the way. + // There can be more than one DC; find the first valid (existing and + // not left), and delete inexistent (forgotten?) ones along the way. DirectChatsMap removals; for (auto it = d->directChats.find(u); it != d->directChats.end() && it.key() == u; ++it) @@ -705,6 +744,8 @@ void Connection::doInDirectChat(User* u, << roomId << "is not valid and will be discarded"; // Postpone actual deletion until we finish iterating d->directChats. removals.insert(it.key(), it.value()); + // Add to the list of updates to send to the server upon the next sync. + d->dcLocalRemovals.insert(it.key(), it.value()); } if (!removals.isEmpty()) { @@ -714,7 +755,7 @@ void Connection::doInDirectChat(User* u, d->directChatUsers.remove(it.value(), const_cast(it.key())); // FIXME } - d->broadcastDirectChatUpdates({}, removals); + emit directChatsListChanged({}, removals); } auto j = createDirectChat(userId); @@ -1015,28 +1056,6 @@ Connection::DirectChatsMap Connection::directChats() const return d->directChats; } -QJsonObject toJson(const Connection::DirectChatsMap& directChats) -{ - QJsonObject json; - for (auto it = directChats.begin(); it != directChats.end();) - { - QJsonArray roomIds; - const auto* user = it.key(); - for (; it != directChats.end() && it.key() == user; ++it) - roomIds.append(*it); - json.insert(user->id(), roomIds); - } - return json; -} - -void Connection::Private::broadcastDirectChatUpdates(const DirectChatsMap& additions, - const DirectChatsMap& removals) -{ - q->callApi(userId, QStringLiteral("m.direct"), - toJson(directChats)); - emit q->directChatsListChanged(additions, removals); -} - void Connection::addToDirectChats(const Room* room, User* user) { Q_ASSERT(room != nullptr && user != nullptr); @@ -1045,8 +1064,8 @@ void Connection::addToDirectChats(const Room* room, User* user) Q_ASSERT(!d->directChatUsers.contains(room->id(), user)); d->directChats.insert(user, room->id()); d->directChatUsers.insert(room->id(), user); - DirectChatsMap additions { { user, room->id() } }; - d->broadcastDirectChatUpdates(additions, {}); + d->dcLocalAdditions.insert(user, room->id()); + emit directChatsListChanged({ { user, room->id() } }, {}); } void Connection::removeFromDirectChats(const QString& roomId, User* user) @@ -1059,15 +1078,17 @@ void Connection::removeFromDirectChats(const QString& roomId, User* user) DirectChatsMap removals; if (user != nullptr) { - removals.insert(user, roomId); d->directChats.remove(user, roomId); d->directChatUsers.remove(roomId, user); + removals.insert(user, roomId); + d->dcLocalRemovals.insert(user, roomId); } else { removals = erase_if(d->directChats, [&roomId] (auto it) { return it.value() == roomId; }); d->directChatUsers.remove(roomId); + d->dcLocalRemovals += removals; } - d->broadcastDirectChatUpdates({}, removals); + emit directChatsListChanged({}, removals); } bool Connection::isDirectChat(const QString& roomId) const -- cgit v1.2.3 From 48ffee5fa78f44bd00d76772ad79ae4eeb6c8dc4 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Wed, 15 May 2019 16:36:02 +0300 Subject: Move out the logic of the hue calculation to utils --- lib/user.cpp | 20 +------------------- lib/util.cpp | 19 +++++++++++++++++++ lib/util.h | 8 ++++++++ 3 files changed, 28 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 7f3f11f6..7b695618 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -33,9 +33,6 @@ #include #include -#include -#include - #include using namespace QMatrixClient; @@ -50,23 +47,8 @@ class User::Private return Avatar(move(url)); } - qreal makeHueF() - { - Q_ASSERT(!userId.isEmpty()); - QByteArray hash = QCryptographicHash::hash(userId.toUtf8(), - QCryptographicHash::Sha1); - QDataStream dataStream(qToLittleEndian(hash).left(2)); - dataStream.setByteOrder(QDataStream::LittleEndian); - quint16 hashValue; - dataStream >> hashValue; - const auto hueF = - qreal(hashValue)/std::numeric_limits::max(); - Q_ASSERT((0 <= hueF) && (hueF <= 1)); - return hueF; - } - Private(QString userId, Connection* connection) - : userId(move(userId)), connection(connection), hueF(makeHueF()) + : userId(move(userId)), connection(connection), hueF(stringToHueF(this->userId)) { } QString userId; diff --git a/lib/util.cpp b/lib/util.cpp index 4e17d2f9..01d4e77b 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -23,6 +23,10 @@ #include #include +#include +#include +#include + static const auto RegExpOptions = QRegularExpression::CaseInsensitiveOption | QRegularExpression::OptimizeOnFirstUsageOption @@ -93,6 +97,21 @@ QString QMatrixClient::cacheLocation(const QString& dirName) return cachePath; } +qreal QMatrixClient::stringToHueF(const QString &string) +{ + Q_ASSERT(!string.isEmpty()); + QByteArray hash = QCryptographicHash::hash(string.toUtf8(), + QCryptographicHash::Sha1); + QDataStream dataStream(qToLittleEndian(hash).left(2)); + dataStream.setByteOrder(QDataStream::LittleEndian); + quint16 hashValue; + dataStream >> hashValue; + const auto hueF = + qreal(hashValue)/std::numeric_limits::max(); + Q_ASSERT((0 <= hueF) && (hueF <= 1)); + return hueF; +} + // Tests for function_traits<> #ifdef Q_CC_CLANG diff --git a/lib/util.h b/lib/util.h index f08c1c95..eda817a1 100644 --- a/lib/util.h +++ b/lib/util.h @@ -314,5 +314,13 @@ namespace QMatrixClient * \param dir path to cache directory relative to the standard cache path */ QString cacheLocation(const QString& dirName); + + /** Hue color component of based of the hash of the string. + * The implementation is based on XEP-0392: + * https://xmpp.org/extensions/xep-0392.html + * Naming and range are the same as QColor's hueF method: + * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision + */ + qreal stringToHueF(const QString& string); } // namespace QMatrixClient -- cgit v1.2.3 From 26c51455901a6c0112531d86d61d65d31a70dfaa Mon Sep 17 00:00:00 2001 From: Ville Ranki Date: Thu, 4 Apr 2019 11:45:41 +0300 Subject: Ignore some errors on leaving rooms, add new error enum. Fixes #307 --- lib/connection.cpp | 56 ++++++++++++++++++++++++++++++++++++---------------- lib/jobs/basejob.cpp | 3 +++ lib/jobs/basejob.h | 1 + 3 files changed, 43 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index a7eae30f..5bf89815 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -110,6 +110,7 @@ class Connection::Private void connectWithToken(const QString& user, const QString& accessToken, const QString& deviceId); + void removeRoom(const QString& roomId); template EventT* unpackAccountData() const @@ -790,29 +791,36 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) if (room && room->joinState() != JoinState::Leave) { auto leaveJob = room->leaveRoom(); - connect(leaveJob, &BaseJob::success, this, [this, forgetJob, room] { - forgetJob->start(connectionData()); - // If the matching /sync response hasn't arrived yet, mark the room - // for explicit deletion - if (room->joinState() != JoinState::Leave) - d->roomIdsToForget.push_back(room->id()); + connect(leaveJob, &BaseJob::result, this, [this, leaveJob, forgetJob, room] { + // After leave, continue if there is no error or the room id is not found (IncorrectRequestError) + if(!leaveJob->error() || leaveJob->error() == BaseJob::StatusCode::UnknownObjectError) { + forgetJob->start(connectionData()); + // If the matching /sync response hasn't arrived yet, mark the room + // for explicit deletion + if (room->joinState() != JoinState::Leave) + d->roomIdsToForget.push_back(room->id()); + } else { + qCWarning(MAIN) << "Error leaving room " + << room->name() << ":" + << leaveJob->errorString(); + forgetJob->abandon(); + } }); connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon); } else forgetJob->start(connectionData()); - connect(forgetJob, &BaseJob::success, this, [this, id] + connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob] { - // Delete whatever instances of the room are still in the map. - for (auto f: {false, true}) - if (auto r = d->roomMap.take({ id, f })) - { - qCDebug(MAIN) << "Room" << r->objectName() - << "in state" << toCString(r->joinState()) - << "will be deleted"; - emit r->beforeDestruction(r); - r->deleteLater(); - } + // Leave room in case of success, or room not known by server + if(!forgetJob->error() || forgetJob->error() == BaseJob::StatusCode::IncorrectRequestError || forgetJob->error() == BaseJob::StatusCode::UnknownObjectError) { + // Delete the room from roomMap + d->removeRoom(id); + } else { + qCWarning(MAIN) << "Error forgetting room " + << id << ":" + << forgetJob->errorString(); + } }); return forgetJob; } @@ -1056,6 +1064,20 @@ Connection::DirectChatsMap Connection::directChats() const return d->directChats; } +// Removes room with given id from roomMap +void Connection::Private::removeRoom(const QString& roomId) +{ + for (auto f: {false, true}) + if (auto r = roomMap.take({ roomId, f })) + { + qCDebug(MAIN) << "Room" << r->objectName() + << "in state" << toCString(r->joinState()) + << "will be deleted"; + emit r->beforeDestruction(r); + r->deleteLater(); + } +} + void Connection::addToDirectChats(const Room* room, User* user) { Q_ASSERT(room != nullptr && user != nullptr); diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 0d9b9f10..97b5a904 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -325,6 +325,9 @@ void BaseJob::gotReply() d->status.code = UserConsentRequiredError; d->errorUrl = json.value("consent_uri"_ls).toString(); } + else if (errCode == "M_UNKNOWN") { + d->status.code = UnknownObjectError; + } else if (errCode == "M_UNSUPPORTED_ROOM_VERSION" || errCode == "M_INCOMPATIBLE_ROOM_VERSION") { diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 4c1c7706..c1747cca 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -67,6 +67,7 @@ namespace QMatrixClient , UnsupportedRoomVersionError , NetworkAuthRequiredError , UserConsentRequiredError + , UnknownObjectError // Unknown room or other item (M_UNKNOWN) , UserDefinedError = 200 }; -- cgit v1.2.3 From 7ea51059b86d8124c077ed84ad3b91770af8d610 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 2 Jun 2019 13:22:11 +0900 Subject: lib/room.*: Spelling fixes in comments [ci skip] --- lib/room.cpp | 13 +++++++------ lib/room.h | 8 ++++---- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 2ce37acc..9042130a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -75,7 +75,8 @@ enum EventsPlacement : int { Older = -1, Newer = 1 }; class Room::Private { public: - /** Map of user names to users. User names potentially duplicate, hence a multi-hashmap. */ + /// Map of user names to users + /** User names potentially duplicate, hence QMultiHash. */ using members_map_t = QMultiHash; Private(Connection* c, QString id_, JoinState initialJoinState) @@ -515,7 +516,7 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) if(newUnreadMessages > 0) { - // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count + // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (unreadMessages < 0) unreadMessages = 0; @@ -556,7 +557,7 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, if (et.nsecsElapsed() > profilerMinNsecs() / 10) qCDebug(PROFILER) << "Recounting unread messages took" << et; - // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count + // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (unreadMessages == 0) unreadMessages = -1; @@ -612,7 +613,7 @@ void Room::markAllMessagesAsRead() bool Room::canSwitchVersions() const { if (!successorId().isEmpty()) - return false; // Noone can upgrade a room that's already upgraded + return false; // No one can upgrade a room that's already upgraded // TODO, #276: m.room.power_levels const auto* plEvt = @@ -1235,7 +1236,7 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) } membersMap.remove(username, u); // If there was one namesake besides the removed user, signal member renaming - // for it because it doesn't need to be disambiguated anymore. + // for it because it doesn't need to be disambiguated any more. if (namesake) emit q->memberRenamed(namesake); } @@ -1354,7 +1355,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) for( auto&& ephemeralEvent: data.ephemeral ) roomChanges |= processEphemeralEvent(move(ephemeralEvent)); - // See https://github.com/QMatrixClient/libqmatrixclient/wiki/unread_count + // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; diff --git a/lib/room.h b/lib/room.h index 055da3da..d4a1b959 100644 --- a/lib/room.h +++ b/lib/room.h @@ -322,7 +322,7 @@ namespace QMatrixClient bool hasAccountData(const QString& type) const; /** Get a generic account data event of the given type - * This returns a generic hashmap for any room account data event + * This returns a generic hash map for any room account data event * stored on the server. Tags and read markers cannot be retrieved * using this method _yet_. */ @@ -516,7 +516,7 @@ namespace QMatrixClient /** A common signal for various kinds of changes in the room * Aside from all changes in the room state - * @param changes a set of flags describing what changes occured + * @param changes a set of flags describing what changes occurred * upon the last sync * \sa StateChange */ @@ -524,7 +524,7 @@ namespace QMatrixClient /** * \brief The room name, the canonical alias or other aliases changed * - * Not triggered when displayname changes. + * Not triggered when display name changes. */ void namesChanged(Room* room); void displaynameAboutToChange(Room* room); @@ -581,7 +581,7 @@ namespace QMatrixClient /// The room's version stability may have changed void stabilityUpdated(QString recommendedDefault, QStringList stableVersions); - /// This room has been upgraded and won't receive updates anymore + /// This room has been upgraded and won't receive updates any more void upgraded(QString serverMessage, Room* successor); /// An attempted room upgrade has failed void upgradeFailed(QString errorMessage); -- cgit v1.2.3 From e8718d8b1d61d6f70e61da0436f793a461e83bd7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 7 Jun 2019 19:23:55 +0900 Subject: BaseJob::StatusCode: add Error-less synonyms; officially deprecate JsonParseError --- lib/jobs/basejob.cpp | 2 -- lib/jobs/basejob.h | 34 +++++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 0d9b9f10..34fc0f57 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -555,8 +555,6 @@ QString BaseJob::statusCaption() const return tr("Request was abandoned"); case NetworkError: return tr("Network problems"); - case JsonParseError: - return tr("Response could not be parsed"); case TimeoutError: return tr("Request timed out"); case ContentAccessError: diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 4c1c7706..4ee63adb 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -46,28 +46,36 @@ namespace QMatrixClient Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) 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 , WarningLevel = 20 - , UnexpectedResponseTypeWarning = 21 + , UnexpectedResponseType = 21 + , UnexpectedResponseTypeWarning = UnexpectedResponseType , Abandoned = 50 //< A very brief period between abandoning and object deletion , ErrorLevel = 100 //< Errors have codes starting from this , NetworkError = 100 - , JsonParseError // TODO: Merge into IncorrectResponseError - , TimeoutError + , Timeout + , TimeoutError = Timeout , ContentAccessError , NotFoundError - , IncorrectRequestError - , IncorrectResponseError - , TooManyRequestsError - , RequestNotImplementedError - , UnsupportedRoomVersionError - , NetworkAuthRequiredError - , UserConsentRequiredError - , UserDefinedError = 200 + , IncorrectRequest + , IncorrectRequestError = IncorrectRequest + , IncorrectResponse + , IncorrectResponseError = IncorrectResponse + , JsonParseError //< deprecated; Use IncorrectResponse instead + = IncorrectResponse + , TooManyRequests + , TooManyRequestsError = TooManyRequests + , RequestNotImplemented + , RequestNotImplementedError = RequestNotImplemented + , UnsupportedRoomVersion + , UnsupportedRoomVersionError = UnsupportedRoomVersion + , NetworkAuthRequired + , NetworkAuthRequiredError = NetworkAuthRequired + , UserConsentRequired + , UserConsentRequiredError = UserConsentRequired + , UserDefinedError = 256 }; /** -- cgit v1.2.3 From 10c4be59438ac614a01800fb44e351e7039fe6e6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 7 Jun 2019 10:37:41 +0900 Subject: *.cpp.mustache: switch from now-deprecated JsonParseError to IncorrectResponse --- lib/csapi/{{base}}.cpp.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache index ff888d76..010f9116 100644 --- a/lib/csapi/{{base}}.cpp.mustache +++ b/lib/csapi/{{base}}.cpp.mustache @@ -115,7 +115,7 @@ BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data {{#inlineResponse}} fromJson(data, d->{{paramName}}); {{/inlineResponse}}{{^inlineResponse}} auto json = data.object(); {{#properties}}{{#required?}} if (!json.contains("{{baseName}}"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key '{{baseName}}' not found in the response" }; {{/required?}} fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); {{/properties}}{{/inlineResponse}} return Success; -- cgit v1.2.3 From 93f0c8fe89f448d1d58caa757573f17102369471 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 7 Jun 2019 19:16:33 +0900 Subject: Generated files in csapi/: switch from now-deprecated JsonParserError to IncorrectResponse --- lib/csapi/capabilities.cpp | 2 +- lib/csapi/content-repo.cpp | 2 +- lib/csapi/create_room.cpp | 2 +- lib/csapi/filter.cpp | 2 +- lib/csapi/joining.cpp | 4 ++-- lib/csapi/keys.cpp | 2 +- lib/csapi/list_joined_rooms.cpp | 2 +- lib/csapi/notifications.cpp | 2 +- lib/csapi/openid.cpp | 8 ++++---- lib/csapi/presence.cpp | 2 +- lib/csapi/pushrules.cpp | 6 +++--- lib/csapi/registration.cpp | 2 +- lib/csapi/room_upgrades.cpp | 2 +- lib/csapi/search.cpp | 2 +- lib/csapi/users.cpp | 4 ++-- lib/csapi/versions.cpp | 2 +- lib/csapi/whoami.cpp | 2 +- 17 files changed, 24 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/csapi/capabilities.cpp b/lib/csapi/capabilities.cpp index 210423f5..fb506784 100644 --- a/lib/csapi/capabilities.cpp +++ b/lib/csapi/capabilities.cpp @@ -76,7 +76,7 @@ BaseJob::Status GetCapabilitiesJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("capabilities"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'capabilities' not found in the response" }; fromJson(json.value("capabilities"_ls), d->capabilities); return Success; diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index 22223985..7e490604 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -50,7 +50,7 @@ BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("content_uri"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'content_uri' not found in the response" }; fromJson(json.value("content_uri"_ls), d->contentUri); return Success; diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index 448547ae..3101152a 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -77,7 +77,7 @@ BaseJob::Status CreateRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index 982e60b5..9f412d53 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -39,7 +39,7 @@ BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("filter_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'filter_id' not found in the response" }; fromJson(json.value("filter_id"_ls), d->filterId); return Success; diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index 00d930fa..544f442f 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -57,7 +57,7 @@ BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; @@ -124,7 +124,7 @@ BaseJob::Status JoinRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("room_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'room_id' not found in the response" }; fromJson(json.value("room_id"_ls), d->roomId); return Success; diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index 6c16a8a3..5bbc1aab 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -42,7 +42,7 @@ BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("one_time_key_counts"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'one_time_key_counts' not found in the response" }; fromJson(json.value("one_time_key_counts"_ls), d->oneTimeKeyCounts); return Success; diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index 85a9cae4..297a5ae0 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -44,7 +44,7 @@ BaseJob::Status GetJoinedRoomsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("joined_rooms"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'joined_rooms' not found in the response" }; fromJson(json.value("joined_rooms"_ls), d->joinedRooms); return Success; diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index c00b7cb0..5d3bdb47 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -80,7 +80,7 @@ BaseJob::Status GetNotificationsJob::parseJson(const QJsonDocument& data) auto json = data.object(); fromJson(json.value("next_token"_ls), d->nextToken); if (!json.contains("notifications"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'notifications' not found in the response" }; fromJson(json.value("notifications"_ls), d->notifications); return Success; diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index b27fe0b8..03d24790 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -57,19 +57,19 @@ BaseJob::Status RequestOpenIdTokenJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("access_token"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'access_token' not found in the response" }; fromJson(json.value("access_token"_ls), d->accessToken); if (!json.contains("token_type"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'token_type' not found in the response" }; fromJson(json.value("token_type"_ls), d->tokenType); if (!json.contains("matrix_server_name"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'matrix_server_name' not found in the response" }; fromJson(json.value("matrix_server_name"_ls), d->matrixServerName); if (!json.contains("expires_in"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'expires_in' not found in the response" }; fromJson(json.value("expires_in"_ls), d->expiresIn); return Success; diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 024d7a34..210ee0ae 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -74,7 +74,7 @@ BaseJob::Status GetPresenceJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("presence"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'presence' not found in the response" }; fromJson(json.value("presence"_ls), d->presence); fromJson(json.value("last_active_ago"_ls), d->lastActiveAgo); diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index b91d18f7..9b5b7cd1 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -44,7 +44,7 @@ BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("global"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'global' not found in the response" }; fromJson(json.value("global"_ls), d->global); return Success; @@ -152,7 +152,7 @@ BaseJob::Status IsPushRuleEnabledJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("enabled"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'enabled' not found in the response" }; fromJson(json.value("enabled"_ls), d->enabled); return Success; @@ -201,7 +201,7 @@ BaseJob::Status GetPushRuleActionsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("actions"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'actions' not found in the response" }; fromJson(json.value("actions"_ls), d->actions); return Success; diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 5dc9c1e5..76741a50 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -74,7 +74,7 @@ BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("user_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'user_id' not found in the response" }; fromJson(json.value("user_id"_ls), d->userId); fromJson(json.value("access_token"_ls), d->accessToken); diff --git a/lib/csapi/room_upgrades.cpp b/lib/csapi/room_upgrades.cpp index f58fd675..f80c3aba 100644 --- a/lib/csapi/room_upgrades.cpp +++ b/lib/csapi/room_upgrades.cpp @@ -41,7 +41,7 @@ BaseJob::Status UpgradeRoomJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("replacement_room"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'replacement_room' not found in the response" }; fromJson(json.value("replacement_room"_ls), d->replacementRoom); return Success; diff --git a/lib/csapi/search.cpp b/lib/csapi/search.cpp index a5f83c79..ad2c34a3 100644 --- a/lib/csapi/search.cpp +++ b/lib/csapi/search.cpp @@ -164,7 +164,7 @@ BaseJob::Status SearchJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("search_categories"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'search_categories' not found in the response" }; fromJson(json.value("search_categories"_ls), d->searchCategories); return Success; diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index 97d8962d..0d867145 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -63,11 +63,11 @@ BaseJob::Status SearchUserDirectoryJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("results"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'results' not found in the response" }; fromJson(json.value("results"_ls), d->results); if (!json.contains("limited"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'limited' not found in the response" }; fromJson(json.value("limited"_ls), d->limited); return Success; diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 6ee6725d..4b7c4ced 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -50,7 +50,7 @@ BaseJob::Status GetVersionsJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("versions"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'versions' not found in the response" }; fromJson(json.value("versions"_ls), d->versions); fromJson(json.value("unstable_features"_ls), d->unstableFeatures); diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index aebdf5d3..ce024c33 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -44,7 +44,7 @@ BaseJob::Status GetTokenOwnerJob::parseJson(const QJsonDocument& data) { auto json = data.object(); if (!json.contains("user_id"_ls)) - return { JsonParseError, + return { IncorrectResponse, "The key 'user_id' not found in the response" }; fromJson(json.value("user_id"_ls), d->userId); return Success; -- cgit v1.2.3 From 7591ec34cee15a58611408a996bdb1b92b6ffb98 Mon Sep 17 00:00:00 2001 From: Ville Ranki Date: Tue, 11 Jun 2019 11:58:57 +0300 Subject: Remove unnecessary error checks in lib/connection.cpp Co-Authored-By: Kitsune Ral --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5bf89815..27211a77 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -813,7 +813,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob] { // Leave room in case of success, or room not known by server - if(!forgetJob->error() || forgetJob->error() == BaseJob::StatusCode::IncorrectRequestError || forgetJob->error() == BaseJob::StatusCode::UnknownObjectError) { + if(!forgetJob->error() || forgetJob->error() == BaseJob::UnknownObjectError) { // Delete the room from roomMap d->removeRoom(id); } else { -- cgit v1.2.3 From a19e12544d174588bb99d1d9d5b2576f0ea1e037 Mon Sep 17 00:00:00 2001 From: Ville Ranki Date: Tue, 11 Jun 2019 11:59:40 +0300 Subject: Comment change as requested Co-Authored-By: Kitsune Ral --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 27211a77..af2aa2ab 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -792,7 +792,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) { auto leaveJob = room->leaveRoom(); connect(leaveJob, &BaseJob::result, this, [this, leaveJob, forgetJob, room] { - // After leave, continue if there is no error or the room id is not found (IncorrectRequestError) + // After leave, continue if there is no error or the room id is not found if(!leaveJob->error() || leaveJob->error() == BaseJob::StatusCode::UnknownObjectError) { forgetJob->start(connectionData()); // If the matching /sync response hasn't arrived yet, mark the room -- cgit v1.2.3 From e083d327e6f6581210f8d077d8bbe1151e81e82c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 23 Jun 2019 19:50:09 +0900 Subject: csapi/: Make Mustache files more readable Now that clang-format takes care of formatting, the template readability can be prioritised. --- lib/csapi/gtad.yaml | 12 ++- lib/csapi/{{base}}.cpp.mustache | 173 +++++++++++++++++++++++----------------- lib/csapi/{{base}}.h.mustache | 145 ++++++++++++++++++--------------- 3 files changed, 193 insertions(+), 137 deletions(-) (limited to 'lib') diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index a44f803a..301ee0b6 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -118,24 +118,32 @@ mustache: # _rightQuote: '"' # _joinChar: ',' # The character used by {{_join}} - not working yet _comment: '//' + copyrightName: Kitsune Ral + copyrightEmail: + partials: _typeRenderer: "{{#scope}}{{scopeCamelCase}}Job::{{/scope}}{{>name}}" omittedValue: '{}' # default value to initialize omitted parameters with initializer: '{{defaultValue}}' cjoin: '{{#hasMore}}, {{/hasMore}}' + openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" + maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" + maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" qualifiedMaybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + + maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" + takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' - copyrightName: Kitsune Ral - copyrightEmail: templates: - "{{base}}.h.mustache" diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache index ff888d76..f9a63412 100644 --- a/lib/csapi/{{base}}.cpp.mustache +++ b/lib/csapi/{{base}}.cpp.mustache @@ -1,80 +1,98 @@ {{>preamble}} #include "{{filenameBase}}.h" {{^models}} -#include "converters.h" -{{/models}}{{#operations}} -{{#producesNonJson?}}#include -{{/producesNonJson?}}#include +#include "converters.h"{{/models}} +{{#operations}} +{{#producesNonJson?}} +#include +{{/producesNonJson?}} +#include {{/operations}} using namespace QMatrixClient; -{{#models.model}}{{#in?}} -void JsonObjectConverter<{{qualifiedName}}>::dumpTo( - QJsonObject& jo, const {{qualifiedName}}& pod) +{{#models.model}} + {{#in?}} +void JsonObjectConverter<{{qualifiedName}}>::dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) { -{{#propertyMap}} fillJson(jo, pod.{{nameCamelCase}}); -{{/propertyMap}}{{#parents}} fillJson<{{name}}>(jo, pod); -{{/parents}}{{#vars}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); -{{/vars}}}{{!<- dumpTo() ends here}} -{{/in?}}{{#out?}} -void JsonObjectConverter<{{qualifiedName}}>::fillFrom( - {{^propertyMap}}const QJsonObject&{{/propertyMap - }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{qualifiedName}}& result) +{{#propertyMap +}} fillJson(jo, pod.{{nameCamelCase}}); +{{/propertyMap}}{{#parents +}} fillJson<{{name}}>(jo, pod); +{{/parents}}{{#vars +}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); +{{/vars}} +} + {{/in?}}{{#out?}} +void JsonObjectConverter<{{qualifiedName}}>::fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) { -{{#parents}} fillFromJson<{{qualifiedName}}>(jo, result); -{{/parents}}{{#vars}} fromJson(jo.{{#propertyMap}}take{{/propertyMap - }}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls), result.{{nameCamelCase}}); -{{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); -{{/propertyMap}}} -{{/out?}}{{/models.model}}{{#operations}} +{{#parents +}} fillFromJson<{{qualifiedName}}>(jo, result); +{{/parents}}{{#vars +}} fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); +{{/vars}}{{#propertyMap +}} fromJson(jo, result.{{nameCamelCase}}); +{{/propertyMap}} +} + {{/out?}} +{{/models.model}} +{{#operations}} static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); -{{# operation}}{{#models}} + {{#operation}}{{#models}} +// Converters namespace QMatrixClient { - // Converters -{{#model}} - template <> struct JsonObjectConverter<{{qualifiedName}}> + {{#model}} +template <> struct JsonObjectConverter<{{qualifiedName}}> +{ + {{#in? +}} static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) + { +{{#propertyMap +}} fillJson(jo, pod.{{nameCamelCase}}); +{{/propertyMap}}{{#parents +}} fillJson<{{name}}>(jo, pod); +{{/parents}}{{#vars +}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); +{{/vars}} + } + {{/in?}}{{#out? +}} static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) { -{{#in?}} static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) - { -{{#propertyMap}} fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}}fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars -}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); -{{/vars}} } -{{/in?}}{{#out? -}} static void fillFrom({{^propertyMap}}const QJsonObject&{{/propertyMap - }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{qualifiedName}}& result) - { -{{#parents}} fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); - {{/parents}}{{#vars -}} fromJson(jo.{{#propertyMap}}take{{/propertyMap - }}{{^propertyMap}}value{{/propertyMap}}("{{baseName}}"_ls), result.{{nameCamelCase}}); -{{/vars}}{{#propertyMap}} fromJson(jo, result.{{nameCamelCase}}); -{{/propertyMap}} } -{{/out?}} }; -{{/model}}} // namespace QMatrixClient -{{/ models}}{{#responses}}{{#normalResponse?}}{{#allProperties?}} +{{#parents +}} fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); +{{/parents}}{{#vars +}} fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); +{{/vars}}{{#propertyMap +}} fromJson(jo, result.{{nameCamelCase}}); +{{/propertyMap}} + } + {{/out?}} +}; + {{/model}} +} // namespace QMatrixClient + {{/models}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} class {{camelCaseOperationId}}Job::Private { public:{{#allProperties}} {{>maybeOmittableType}} {{paramName}};{{/allProperties}} }; -{{/ allProperties?}}{{/normalResponse?}}{{/responses}}{{#queryParams?}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} + {{#queryParams?}} BaseJob::Query queryTo{{camelCaseOperationId}}({{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) { BaseJob::Query _q;{{#queryParams}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} return _q; } -{{/queryParams?}}{{^bodyParams}} + {{/queryParams?}} + {{^bodyParams}} QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) { return BaseJob::makeRequestUrl(std::move(baseUrl), basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); } -{{/ bodyParams}} + {{/bodyParams}} static const auto {{camelCaseOperationId}}JobName = QStringLiteral("{{camelCaseOperationId}}Job"); {{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDef}}{{/allParams}}) @@ -84,40 +102,53 @@ static const auto {{camelCaseOperationId}}JobName = QStringLiteral("{{camelCaseO {}{{/queryParams?}}, false{{/skipAuth}}){{#responses}}{{#normalResponse?}}{{#allProperties?}} , d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} { -{{#headerParams?}}{{#headerParams}} setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); -{{/headerParams}} -{{/headerParams? -}}{{#bodyParams? -}}{{#inlineBody}} setRequestData(Data({{! - }}{{#consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson? - }}{{^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson?}}));{{/inlineBody -}}{{^inlineBody}} QJsonObject _data;{{#bodyParams}} +{{#headerParams?}}{{#headerParams +}} setRequestHeader("{{baseName}}", {{paramName}}.toLatin1());{{/headerParams}} +{{/headerParams?}}{{#bodyParams? +}}{{#inlineBody +}} setRequestData(Data({{#consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson? + }}{{^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson?}})); +{{/inlineBody}}{{^inlineBody +}} QJsonObject _data;{{#bodyParams}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, QStringLiteral("{{baseName}}"), {{paramName}});{{/bodyParams}} - setRequestData(_data);{{/inlineBody}} -{{/bodyParams?}}{{#producesNonJson?}} setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); -{{/producesNonJson?}}}{{!<- mind the actual brace}} -{{# responses}}{{#normalResponse?}}{{#allProperties?}} + setRequestData(_data); +{{/inlineBody}}{{/bodyParams? +}}{{#producesNonJson? +}} setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); +{{/producesNonJson?}} +} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} {{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; -{{# allProperties}} + {{#allProperties}} {{>qualifiedMaybeCrefType}} {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} { return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; } -{{/ allProperties}}{{#producesNonJson?}} + {{/allProperties}} + {{#producesNonJson?}} BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) { {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}");{{! We don't check for required headers yet }} {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} return Success; -}{{/ producesNonJson?}}{{^producesNonJson?}} +} + {{/producesNonJson?}}{{^producesNonJson?}} BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) { -{{#inlineResponse}} fromJson(data, d->{{paramName}}); -{{/inlineResponse}}{{^inlineResponse}} auto json = data.object(); -{{#properties}}{{#required?}} if (!json.contains("{{baseName}}"_ls)) +{{#inlineResponse +}} fromJson(data, d->{{paramName}}); +{{/inlineResponse}}{{^inlineResponse +}} auto json = data.object(); +{{# properties}}{{#required? +}} if (!json.contains("{{baseName}}"_ls)) return { JsonParseError, "The key '{{baseName}}' not found in the response" }; -{{/required?}} fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); -{{/properties}}{{/inlineResponse}} return Success; -}{{/ producesNonJson?}} -{{/allProperties?}}{{/normalResponse?}}{{/responses}}{{/operation}}{{/operations}} +{{/required? +}} fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); +{{/ properties}} +{{/inlineResponse +}} return Success; +} + {{/producesNonJson?}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +{{/operation}}{{/operations}} diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache index a9c3a63a..61380ec6 100644 --- a/lib/csapi/{{base}}.h.mustache +++ b/lib/csapi/{{base}}.h.mustache @@ -1,78 +1,95 @@ {{>preamble}} #pragma once -{{#operations}}#include "jobs/basejob.h" -{{/operations}}{{#models}}#include "converters.h" -{{/models}} -{{#imports}}#include {{_}} -{{/imports}} +{{#operations}} +#include "jobs/basejob.h"{{/operations}} +{{#models}} +#include "converters.h"{{/models}} +{{#imports}} +#include {{_}}{{/imports}} + namespace QMatrixClient { -{{#models}} // Data structures -{{# model}}{{#description}} - /// {{_}}{{/description}} +{{#models}} +// Data structures +{{# model}} +{{#description}}/// {{_}}{{/description}} +struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} +{ +{{# vars}}{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{>maybeOmittableType}} {{nameCamelCase}}; +{{/ vars}} +{{# propertyMap}} +{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{>maybeOmittableType}} {{nameCamelCase}}; +{{/ propertyMap}} +}; + +template <> struct JsonObjectConverter<{{name}}> +{ + {{#in?}}static void dumpTo(QJsonObject& jo, const {{name}}& pod);{{/in?}} + {{#out?}}static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod);{{/out?}}}; +{{/ model}} +{{/models}} +{{#operations}}// Operations +{{# operation}} +{{#summary}}/// {{summary}}{{/summary}} +{{#description?}}/*!{{#description}} + * {{_}}{{/description}} + */{{/description?}} +class {{camelCaseOperationId}}Job : public BaseJob +{ +public:{{#models}} + // Inner data structures +{{# model}} +{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} { -{{#vars}}{{#description}} /// {{_}} -{{/description}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/vars}}{{#propertyMap}}{{#description}} /// {{_}} -{{/description}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/propertyMap}} }; - template <> struct JsonObjectConverter<{{name}}> - { - {{#in?}}static void dumpTo(QJsonObject& jo, const {{name}}& pod); - {{/in?}}{{#out?}}static void fillFrom({{^propertyMap}}const QJsonObject&{{/propertyMap - }}{{#propertyMap}}QJsonObject{{/propertyMap}} jo, {{name}}& pod); -{{/out?}} }; -{{/model}} -{{/models}}{{#operations}} // Operations -{{# operation}}{{#summary}} - /// {{summary}}{{#description?}}{{!add a linebreak between summary and description if both exist}} - ///{{/description?}}{{/summary}}{{#description}} - /// {{_}}{{/description}} - class {{camelCaseOperationId}}Job : public BaseJob - { - public:{{#models}} - // Inner data structures -{{# model}}{{#description}} - /// {{_}}{{/description}} - struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} - { -{{#vars}}{{#description}} /// {{_}} -{{/description}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/vars}}{{#propertyMap}}{{#description}} /// {{_}} -{{/description}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/propertyMap}} }; +{{# vars}}{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{>maybeOmittableType}} {{nameCamelCase}}; +{{/ vars}} +{{# propertyMap}} +{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{>maybeOmittableType}} {{nameCamelCase}}; +{{/ propertyMap}} + }; {{/ model}} - // Construction/destruction + // Construction/destruction {{/ models}}{{#allParams?}} - /*! {{summary}}{{#allParams}} - * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}}{{/allParams}} - */{{/allParams?}} - explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}});{{^bodyParams}} + /*! {{summary}}{{#allParams}} + * \param {{nameCamelCase}}{{#description}} + * {{_}}{{/description}}{{/allParams}} + */{{/allParams?}} + explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); +{{^ bodyParams}} - /*! Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for - * {{camelCaseOperationId}}Job is necessary but the job - * itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); -{{/bodyParams}}{{# responses}}{{#normalResponse?}}{{#allProperties?}} - ~{{camelCaseOperationId}}Job() override; + /*! Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for + * {{camelCaseOperationId}}Job is necessary but the job + * itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); +{{/ bodyParams}} +{{# responses}}{{#normalResponse?}}{{#allProperties?}} + ~{{camelCaseOperationId}}Job() override; - // Result properties + // Result properties {{#allProperties}}{{#description}} - /// {{_}}{{/description}} - {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}};{{/allProperties}} + /// {{_}}{{/description}} + {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}};{{/allProperties}} - protected: - Status {{#producesNonJson?}}parseReply(QNetworkReply* reply){{/producesNonJson?}}{{^producesNonJson?}}parseJson(const QJsonDocument& data){{/producesNonJson?}} override; +protected:{{#producesNonJson?}} + Status parseReply(QNetworkReply* reply) override; +{{/producesNonJson?}}{{^producesNonJson?}} + Status parseJson(const QJsonDocument& data) override; +{{/producesNonJson?}} - private: - class Private; - QScopedPointer d;{{/allProperties?}}{{/normalResponse?}}{{/responses}} - }; -{{/operation}}{{/operations}}{{!skip EOL -}}} // namespace QMatrixClient +private: + class Private; + QScopedPointer d; +{{/ allProperties?}}{{/normalResponse?}}{{/responses}} +}; +{{/ operation}} +{{/operations}} +} // namespace QMatrixClient -- cgit v1.2.3 From 9e96bc9b8dec6613e3e2f2fd564e9f6bee9bbce4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 24 Jun 2019 07:38:50 +0900 Subject: csapi/*/**: clang-format contents of csapi subdirs as well --- lib/csapi/definitions/auth_data.cpp | 15 ++--- lib/csapi/definitions/auth_data.h | 17 +++--- lib/csapi/definitions/client_device.cpp | 9 +-- lib/csapi/definitions/client_device.h | 23 +++---- lib/csapi/definitions/device_keys.cpp | 15 ++--- lib/csapi/definitions/device_keys.h | 26 ++++---- lib/csapi/definitions/event_filter.cpp | 15 ++--- lib/csapi/definitions/event_filter.h | 31 +++++----- lib/csapi/definitions/public_rooms_response.cpp | 36 +++++------ lib/csapi/definitions/public_rooms_response.h | 35 ++++++----- lib/csapi/definitions/push_condition.cpp | 15 ++--- lib/csapi/definitions/push_condition.h | 26 ++++---- lib/csapi/definitions/push_rule.cpp | 12 +--- lib/csapi/definitions/push_rule.h | 24 ++++---- lib/csapi/definitions/push_ruleset.cpp | 15 ++--- lib/csapi/definitions/push_ruleset.h | 15 ++--- lib/csapi/definitions/room_event_filter.cpp | 15 ++--- lib/csapi/definitions/room_event_filter.h | 25 ++++---- lib/csapi/definitions/sync_filter.cpp | 42 ++++++------- lib/csapi/definitions/sync_filter.h | 71 ++++++++++++++-------- lib/csapi/definitions/user_identifier.cpp | 15 ++--- lib/csapi/definitions/user_identifier.h | 15 ++--- lib/csapi/definitions/wellknown/full.cpp | 18 +++--- lib/csapi/definitions/wellknown/full.h | 25 ++++---- lib/csapi/definitions/wellknown/homeserver.cpp | 15 ++--- lib/csapi/definitions/wellknown/homeserver.h | 13 ++-- .../definitions/wellknown/identity_server.cpp | 15 ++--- lib/csapi/definitions/wellknown/identity_server.h | 13 ++-- 28 files changed, 266 insertions(+), 345 deletions(-) (limited to 'lib') diff --git a/lib/csapi/definitions/auth_data.cpp b/lib/csapi/definitions/auth_data.cpp index b0303a19..3bb51626 100644 --- a/lib/csapi/definitions/auth_data.cpp +++ b/lib/csapi/definitions/auth_data.cpp @@ -4,25 +4,20 @@ #include "auth_data.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const AuthenticationData& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const AuthenticationData& pod) { fillJson(jo, pod.authInfo); addParam<>(jo, QStringLiteral("type"), pod.type); addParam(jo, QStringLiteral("session"), pod.session); - } - -void JsonObjectConverter::fillFrom(QJsonObject jo, AuthenticationData& result) + +void JsonObjectConverter::fillFrom(QJsonObject jo, + AuthenticationData& result) { fromJson(jo.take("type"_ls), result.type); fromJson(jo.take("session"_ls), result.session); fromJson(jo, result.authInfo); - } - - - diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index 689caf49..9e46812c 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -4,19 +4,18 @@ #pragma once - - #include "converters.h" -#include #include +#include namespace QMatrixClient { // Data structures -/// Used by clients to submit authentication information to the interactive-authentication API +/// Used by clients to submit authentication information to the +/// interactive-authentication API struct AuthenticationData { /// The login type that the client is attempting to complete. @@ -24,17 +23,15 @@ struct AuthenticationData /// The value of the session key given by the homeserver. QString session; - /// Keys dependent on the login type QHash authInfo; - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const AuthenticationData& pod); - static void fillFrom(QJsonObject jo, AuthenticationData& pod);}; - - + static void fillFrom(QJsonObject jo, AuthenticationData& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/client_device.cpp b/lib/csapi/definitions/client_device.cpp index 5710537d..c9e6a223 100644 --- a/lib/csapi/definitions/client_device.cpp +++ b/lib/csapi/definitions/client_device.cpp @@ -4,27 +4,20 @@ #include "client_device.h" - using namespace QMatrixClient; - void JsonObjectConverter::dumpTo(QJsonObject& jo, const Device& pod) { addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); addParam(jo, QStringLiteral("display_name"), pod.displayName); addParam(jo, QStringLiteral("last_seen_ip"), pod.lastSeenIp); addParam(jo, QStringLiteral("last_seen_ts"), pod.lastSeenTs); - } - + void JsonObjectConverter::fillFrom(const QJsonObject& jo, Device& result) { fromJson(jo.value("device_id"_ls), result.deviceId); fromJson(jo.value("display_name"_ls), result.displayName); fromJson(jo.value("last_seen_ip"_ls), result.lastSeenIp); fromJson(jo.value("last_seen_ts"_ls), result.lastSeenTs); - } - - - diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index 7c63a9b6..e4accc35 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -4,10 +4,6 @@ #pragma once - - -#include "converters.h" - #include "converters.h" namespace QMatrixClient @@ -20,21 +16,22 @@ struct Device { /// Identifier of this device. QString deviceId; - /// Display name set by the user for this device. Absent if no name has beenset. + /// Display name set by the user for this device. Absent if no name has + /// beenset. QString displayName; - /// The IP address where this device was last seen. (May be a few minutes outof date, for efficiency reasons). + /// The IP address where this device was last seen. (May be a few minutes + /// outof date, for efficiency reasons). QString lastSeenIp; - /// The timestamp (in milliseconds since the unix epoch) when this deviceswas last seen. (May be a few minutes out of date, for efficiencyreasons). + /// The timestamp (in milliseconds since the unix epoch) when this deviceswas + /// last seen. (May be a few minutes out of date, for efficiencyreasons). Omittable lastSeenTs; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Device& pod); - static void fillFrom(const QJsonObject& jo, Device& pod);}; - - + static void fillFrom(const QJsonObject& jo, Device& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/device_keys.cpp b/lib/csapi/definitions/device_keys.cpp index ffe0cfbe..cc5262b7 100644 --- a/lib/csapi/definitions/device_keys.cpp +++ b/lib/csapi/definitions/device_keys.cpp @@ -4,29 +4,24 @@ #include "device_keys.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const DeviceKeys& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const DeviceKeys& pod) { addParam<>(jo, QStringLiteral("user_id"), pod.userId); addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); addParam<>(jo, QStringLiteral("algorithms"), pod.algorithms); addParam<>(jo, QStringLiteral("keys"), pod.keys); addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, DeviceKeys& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + DeviceKeys& result) { fromJson(jo.value("user_id"_ls), result.userId); fromJson(jo.value("device_id"_ls), result.deviceId); fromJson(jo.value("algorithms"_ls), result.algorithms); fromJson(jo.value("keys"_ls), result.keys); fromJson(jo.value("signatures"_ls), result.signatures); - } - - - diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index c86db46a..6bd96584 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -4,8 +4,6 @@ #pragma once - - #include "converters.h" #include @@ -18,25 +16,29 @@ namespace QMatrixClient /// Device identity keys struct DeviceKeys { - /// The ID of the user the device belongs to. Must match the user ID usedwhen logging in. + /// The ID of the user the device belongs to. Must match the user ID + /// usedwhen logging in. QString userId; - /// The ID of the device these keys belong to. Must match the device ID usedwhen logging in. + /// The ID of the device these keys belong to. Must match the device ID + /// usedwhen logging in. QString deviceId; /// The encryption algorithms supported by this device. QStringList algorithms; - /// Public identity keys. The names of the properties should be in theformat ``:``. The keys themselves should beencoded as specified by the key algorithm. + /// Public identity keys. The names of the properties should be in theformat + /// ``:``. The keys themselves should beencoded as + /// specified by the key algorithm. QHash keys; - /// Signatures for the device key object. A map from user ID, to a map from``:`` to the signature.The signature is calculated using the process described at `SigningJSON`_. + /// Signatures for the device key object. A map from user ID, to a map + /// from``:`` to the signature.The signature is + /// calculated using the process described at `SigningJSON`_. QHash> signatures; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DeviceKeys& pod); - static void fillFrom(const QJsonObject& jo, DeviceKeys& pod);}; - - + static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/event_filter.cpp b/lib/csapi/definitions/event_filter.cpp index 8be98c94..9b2c7a33 100644 --- a/lib/csapi/definitions/event_filter.cpp +++ b/lib/csapi/definitions/event_filter.cpp @@ -4,29 +4,24 @@ #include "event_filter.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const EventFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const EventFilter& pod) { addParam(jo, QStringLiteral("limit"), pod.limit); addParam(jo, QStringLiteral("not_senders"), pod.notSenders); addParam(jo, QStringLiteral("not_types"), pod.notTypes); addParam(jo, QStringLiteral("senders"), pod.senders); addParam(jo, QStringLiteral("types"), pod.types); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, EventFilter& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + EventFilter& result) { fromJson(jo.value("limit"_ls), result.limit); fromJson(jo.value("not_senders"_ls), result.notSenders); fromJson(jo.value("not_types"_ls), result.notTypes); fromJson(jo.value("senders"_ls), result.senders); fromJson(jo.value("types"_ls), result.types); - } - - - diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index b8b2a983..9b9b3fa3 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -4,10 +4,6 @@ #pragma once - - -#include "converters.h" - #include "converters.h" namespace QMatrixClient @@ -15,28 +11,33 @@ namespace QMatrixClient // Data structures - struct EventFilter { /// The maximum number of events to return. Omittable limit; - /// A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the ``'senders'`` filter. + /// A list of sender IDs to exclude. If this list is absent then no senders + /// are excluded. A matching sender will be excluded even if it is listed in + /// the ``'senders'`` filter. QStringList notSenders; - /// A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the ``'types'`` filter. A '*' can be used as a wildcard to match any sequence of characters. + /// A list of event types to exclude. If this list is absent then no event + /// types are excluded. A matching type will be excluded even if it is + /// listed in the ``'types'`` filter. A '*' can be used as a wildcard to + /// match any sequence of characters. QStringList notTypes; - /// A list of senders IDs to include. If this list is absent then all senders are included. + /// A list of senders IDs to include. If this list is absent then all + /// senders are included. QStringList senders; - /// A list of event types to include. If this list is absent then all event types are included. A ``'*'`` can be used as a wildcard to match any sequence of characters. + /// A list of event types to include. If this list is absent then all event + /// types are included. A ``'*'`` can be used as a wildcard to match any + /// sequence of characters. QStringList types; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const EventFilter& pod); - static void fillFrom(const QJsonObject& jo, EventFilter& pod);}; - - + static void fillFrom(const QJsonObject& jo, EventFilter& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/public_rooms_response.cpp b/lib/csapi/definitions/public_rooms_response.cpp index 2c03b7d3..d07b1494 100644 --- a/lib/csapi/definitions/public_rooms_response.cpp +++ b/lib/csapi/definitions/public_rooms_response.cpp @@ -4,14 +4,14 @@ #include "public_rooms_response.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PublicRoomsChunk& pod) { addParam(jo, QStringLiteral("aliases"), pod.aliases); - addParam(jo, QStringLiteral("canonical_alias"), pod.canonicalAlias); + addParam(jo, QStringLiteral("canonical_alias"), + pod.canonicalAlias); addParam(jo, QStringLiteral("name"), pod.name); addParam<>(jo, QStringLiteral("num_joined_members"), pod.numJoinedMembers); addParam<>(jo, QStringLiteral("room_id"), pod.roomId); @@ -19,10 +19,10 @@ void JsonObjectConverter::dumpTo(QJsonObject& jo, const Public addParam<>(jo, QStringLiteral("world_readable"), pod.worldReadable); addParam<>(jo, QStringLiteral("guest_can_join"), pod.guestCanJoin); addParam(jo, QStringLiteral("avatar_url"), pod.avatarUrl); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, PublicRoomsChunk& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PublicRoomsChunk& result) { fromJson(jo.value("aliases"_ls), result.aliases); fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); @@ -33,28 +33,24 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, Publ fromJson(jo.value("world_readable"_ls), result.worldReadable); fromJson(jo.value("guest_can_join"_ls), result.guestCanJoin); fromJson(jo.value("avatar_url"_ls), result.avatarUrl); - } - - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const PublicRoomsResponse& pod) { addParam<>(jo, QStringLiteral("chunk"), pod.chunk); addParam(jo, QStringLiteral("next_batch"), pod.nextBatch); addParam(jo, QStringLiteral("prev_batch"), pod.prevBatch); - addParam(jo, QStringLiteral("total_room_count_estimate"), pod.totalRoomCountEstimate); - + addParam(jo, QStringLiteral("total_room_count_estimate"), + pod.totalRoomCountEstimate); } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, PublicRoomsResponse& result) + +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, PublicRoomsResponse& result) { fromJson(jo.value("chunk"_ls), result.chunk); fromJson(jo.value("next_batch"_ls), result.nextBatch); fromJson(jo.value("prev_batch"_ls), result.prevBatch); - fromJson(jo.value("total_room_count_estimate"_ls), result.totalRoomCountEstimate); - + fromJson(jo.value("total_room_count_estimate"_ls), + result.totalRoomCountEstimate); } - - - diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index d282a592..e86e306f 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -4,11 +4,8 @@ #pragma once - - #include "converters.h" -#include "converters.h" #include namespace QMatrixClient @@ -16,7 +13,6 @@ namespace QMatrixClient // Data structures - struct PublicRoomsChunk { /// Aliases of the room. May be empty. @@ -33,39 +29,42 @@ struct PublicRoomsChunk QString topic; /// Whether the room may be viewed by guest users without joining. bool worldReadable; - /// Whether guest users may join the room and participate in it.If they can, they will be subject to ordinary power levelrules like any other user. + /// Whether guest users may join the room and participate in it.If they can, + /// they will be subject to ordinary power levelrules like any other user. bool guestCanJoin; /// The URL for the room's avatar, if one is set. QString avatarUrl; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod); - static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod);}; + static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod); +}; /// A list of the rooms on the server. struct PublicRoomsResponse { /// A paginated chunk of public rooms. QVector chunk; - /// A pagination token for the response. The absence of this tokenmeans there are no more results to fetch and the client shouldstop paginating. + /// A pagination token for the response. The absence of this tokenmeans + /// there are no more results to fetch and the client shouldstop paginating. QString nextBatch; - /// A pagination token that allows fetching previous results. Theabsence of this token means there are no results before thisbatch, i.e. this is the first batch. + /// A pagination token that allows fetching previous results. Theabsence of + /// this token means there are no results before thisbatch, i.e. this is the + /// first batch. QString prevBatch; - /// An estimate on the total number of public rooms, if theserver has an estimate. + /// An estimate on the total number of public rooms, if theserver has an + /// estimate. Omittable totalRoomCountEstimate; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod); - static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod);}; - - + static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_condition.cpp b/lib/csapi/definitions/push_condition.cpp index 86b3107e..5bcb845e 100644 --- a/lib/csapi/definitions/push_condition.cpp +++ b/lib/csapi/definitions/push_condition.cpp @@ -4,27 +4,22 @@ #include "push_condition.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushCondition& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PushCondition& pod) { addParam<>(jo, QStringLiteral("kind"), pod.kind); addParam(jo, QStringLiteral("key"), pod.key); addParam(jo, QStringLiteral("pattern"), pod.pattern); addParam(jo, QStringLiteral("is"), pod.is); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, PushCondition& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushCondition& result) { fromJson(jo.value("kind"_ls), result.kind); fromJson(jo.value("key"_ls), result.key); fromJson(jo.value("pattern"_ls), result.pattern); fromJson(jo.value("is"_ls), result.is); - } - - - diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index e61fb24e..2c17023e 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -4,36 +4,36 @@ #pragma once - - #include "converters.h" - namespace QMatrixClient { // Data structures - struct PushCondition { QString kind; - /// Required for ``event_match`` conditions. The dot-separated field of theevent to match. + /// Required for ``event_match`` conditions. The dot-separated field of + /// theevent to match. QString key; - /// Required for ``event_match`` conditions. The glob-style pattern tomatch against. Patterns with no special glob characters should betreated as having asterisks prepended and appended when testing thecondition. + /// Required for ``event_match`` conditions. The glob-style pattern tomatch + /// against. Patterns with no special glob characters should betreated as + /// having asterisks prepended and appended when testing thecondition. QString pattern; - /// Required for ``room_member_count`` conditions. A decimal integeroptionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matchesrooms where the member count is strictly less than the given number andso forth. If no prefix is present, this parameter defaults to ==. + /// Required for ``room_member_count`` conditions. A decimal integeroptionally + /// prefixed by one of, ==, <, >, >= or <=. A prefix of < matchesrooms where + /// the member count is strictly less than the given number andso forth. If + /// no prefix is present, this parameter defaults to ==. QString is; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushCondition& pod); - static void fillFrom(const QJsonObject& jo, PushCondition& pod);}; - - + static void fillFrom(const QJsonObject& jo, PushCondition& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_rule.cpp b/lib/csapi/definitions/push_rule.cpp index bfa8a7ef..fc2be2c7 100644 --- a/lib/csapi/definitions/push_rule.cpp +++ b/lib/csapi/definitions/push_rule.cpp @@ -4,10 +4,8 @@ #include "push_rule.h" - using namespace QMatrixClient; - void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRule& pod) { addParam<>(jo, QStringLiteral("actions"), pod.actions); @@ -16,10 +14,10 @@ void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRule& pod) addParam<>(jo, QStringLiteral("rule_id"), pod.ruleId); addParam(jo, QStringLiteral("conditions"), pod.conditions); addParam(jo, QStringLiteral("pattern"), pod.pattern); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, PushRule& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushRule& result) { fromJson(jo.value("actions"_ls), result.actions); fromJson(jo.value("default"_ls), result.isDefault); @@ -27,8 +25,4 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, PushRule& re fromJson(jo.value("rule_id"_ls), result.ruleId); fromJson(jo.value("conditions"_ls), result.conditions); fromJson(jo.value("pattern"_ls), result.pattern); - } - - - diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index 98bd904d..fe6eb0e6 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -4,14 +4,12 @@ #pragma once - - #include "converters.h" -#include "converters.h" -#include #include "csapi/definitions/push_condition.h" + #include +#include #include namespace QMatrixClient @@ -19,7 +17,6 @@ namespace QMatrixClient // Data structures - struct PushRule { /// The actions to perform when this rule is matched. @@ -30,19 +27,20 @@ struct PushRule bool enabled; /// The ID of this rule. QString ruleId; - /// The conditions that must hold true for an event in order for a rule to beapplied to an event. A rule with no conditions always matches. Onlyapplicable to ``underride`` and ``override`` rules. + /// The conditions that must hold true for an event in order for a rule to + /// beapplied to an event. A rule with no conditions always matches. + /// Onlyapplicable to ``underride`` and ``override`` rules. QVector conditions; - /// The glob-style pattern to match against. Only applicable to ``content``rules. + /// The glob-style pattern to match against. Only applicable to + /// ``content``rules. QString pattern; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRule& pod); - static void fillFrom(const QJsonObject& jo, PushRule& pod);}; - - + static void fillFrom(const QJsonObject& jo, PushRule& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/push_ruleset.cpp b/lib/csapi/definitions/push_ruleset.cpp index 4754e07b..6f48d27b 100644 --- a/lib/csapi/definitions/push_ruleset.cpp +++ b/lib/csapi/definitions/push_ruleset.cpp @@ -4,29 +4,24 @@ #include "push_ruleset.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRuleset& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const PushRuleset& pod) { addParam(jo, QStringLiteral("content"), pod.content); addParam(jo, QStringLiteral("override"), pod.override); addParam(jo, QStringLiteral("room"), pod.room); addParam(jo, QStringLiteral("sender"), pod.sender); addParam(jo, QStringLiteral("underride"), pod.underride); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, PushRuleset& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + PushRuleset& result) { fromJson(jo.value("content"_ls), result.content); fromJson(jo.value("override"_ls), result.override); fromJson(jo.value("room"_ls), result.room); fromJson(jo.value("sender"_ls), result.sender); fromJson(jo.value("underride"_ls), result.underride); - } - - - diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index e1a2c142..f9aedad8 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -4,12 +4,10 @@ #pragma once - - #include "converters.h" #include "csapi/definitions/push_rule.h" -#include "converters.h" + #include namespace QMatrixClient @@ -17,7 +15,6 @@ namespace QMatrixClient // Data structures - struct PushRuleset { @@ -30,15 +27,13 @@ struct PushRuleset QVector sender; QVector underride; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRuleset& pod); - static void fillFrom(const QJsonObject& jo, PushRuleset& pod);}; - - + static void fillFrom(const QJsonObject& jo, PushRuleset& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp index fc859395..bd38ebc7 100644 --- a/lib/csapi/definitions/room_event_filter.cpp +++ b/lib/csapi/definitions/room_event_filter.cpp @@ -4,27 +4,22 @@ #include "room_event_filter.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const RoomEventFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const RoomEventFilter& pod) { fillJson(jo, pod); addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); addParam(jo, QStringLiteral("contains_url"), pod.containsUrl); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, RoomEventFilter& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + RoomEventFilter& result) { fillFromJson(jo, result); fromJson(jo.value("not_rooms"_ls), result.notRooms); fromJson(jo.value("rooms"_ls), result.rooms); fromJson(jo.value("contains_url"_ls), result.containsUrl); - } - - - diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 92e210fe..72bf34d3 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -4,36 +4,35 @@ #pragma once - - #include "converters.h" #include "csapi/definitions/event_filter.h" -#include "converters.h" namespace QMatrixClient { // Data structures - struct RoomEventFilter : EventFilter { - /// A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` filter. + /// A list of room IDs to exclude. If this list is absent then no rooms are + /// excluded. A matching room will be excluded even if it is listed in the + /// ``'rooms'`` filter. QStringList notRooms; - /// A list of room IDs to include. If this list is absent then all rooms are included. + /// A list of room IDs to include. If this list is absent then all rooms are + /// included. QStringList rooms; - /// If ``true``, includes only events with a ``url`` key in their content. If ``false``, excludes those events. If omitted, ``url`` key is not considered for filtering. + /// If ``true``, includes only events with a ``url`` key in their content. + /// If ``false``, excludes those events. If omitted, ``url`` key is not + /// considered for filtering. Omittable containsUrl; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod); - static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod);}; - - + static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/sync_filter.cpp b/lib/csapi/definitions/sync_filter.cpp index a1ef53c5..87274a06 100644 --- a/lib/csapi/definitions/sync_filter.cpp +++ b/lib/csapi/definitions/sync_filter.cpp @@ -4,29 +4,29 @@ #include "sync_filter.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const StateFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const StateFilter& pod) { fillJson(jo, pod); - addParam(jo, QStringLiteral("lazy_load_members"), pod.lazyLoadMembers); - addParam(jo, QStringLiteral("include_redundant_members"), pod.includeRedundantMembers); - + addParam(jo, QStringLiteral("lazy_load_members"), + pod.lazyLoadMembers); + addParam(jo, QStringLiteral("include_redundant_members"), + pod.includeRedundantMembers); } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, StateFilter& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + StateFilter& result) { fillFromJson(jo, result); fromJson(jo.value("lazy_load_members"_ls), result.lazyLoadMembers); - fromJson(jo.value("include_redundant_members"_ls), result.includeRedundantMembers); - + fromJson(jo.value("include_redundant_members"_ls), + result.includeRedundantMembers); } - - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const RoomFilter& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const RoomFilter& pod) { addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); addParam(jo, QStringLiteral("rooms"), pod.rooms); @@ -35,10 +35,10 @@ void JsonObjectConverter::dumpTo(QJsonObject& jo, const RoomFilter& addParam(jo, QStringLiteral("state"), pod.state); addParam(jo, QStringLiteral("timeline"), pod.timeline); addParam(jo, QStringLiteral("account_data"), pod.accountData); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, RoomFilter& result) + +void JsonObjectConverter::fillFrom(const QJsonObject& jo, + RoomFilter& result) { fromJson(jo.value("not_rooms"_ls), result.notRooms); fromJson(jo.value("rooms"_ls), result.rooms); @@ -47,11 +47,8 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, RoomFilter fromJson(jo.value("state"_ls), result.state); fromJson(jo.value("timeline"_ls), result.timeline); fromJson(jo.value("account_data"_ls), result.accountData); - } - - void JsonObjectConverter::dumpTo(QJsonObject& jo, const Filter& pod) { addParam(jo, QStringLiteral("event_fields"), pod.eventFields); @@ -59,9 +56,8 @@ void JsonObjectConverter::dumpTo(QJsonObject& jo, const Filter& pod) addParam(jo, QStringLiteral("presence"), pod.presence); addParam(jo, QStringLiteral("account_data"), pod.accountData); addParam(jo, QStringLiteral("room"), pod.room); - } - + void JsonObjectConverter::fillFrom(const QJsonObject& jo, Filter& result) { fromJson(jo.value("event_fields"_ls), result.eventFields); @@ -69,8 +65,4 @@ void JsonObjectConverter::fillFrom(const QJsonObject& jo, Filter& result fromJson(jo.value("presence"_ls), result.presence); fromJson(jo.value("account_data"_ls), result.accountData); fromJson(jo.value("room"_ls), result.room); - } - - - diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index 551ba2fd..1d6a845c 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -4,12 +4,9 @@ #pragma once - - #include "converters.h" #include "csapi/definitions/event_filter.h" -#include "converters.h" #include "csapi/definitions/room_event_filter.h" namespace QMatrixClient @@ -20,27 +17,48 @@ namespace QMatrixClient /// The state events to include for rooms. struct StateFilter : RoomEventFilter { - /// If ``true``, the only ``m.room.member`` events returned inthe ``state`` section of the ``/sync`` response are thosewhich are definitely necessary for a client to displaythe ``sender`` of the timeline events in that response.If ``false``, ``m.room.member`` events are not filtered.By default, servers should suppress duplicate redundantlazy-loaded ``m.room.member`` events from being sent to a givenclient across multiple calls to ``/sync``, given that most clientscache membership events (see ``include_redundant_members``to change this behaviour). + /// If ``true``, the only ``m.room.member`` events returned inthe ``state`` + /// section of the ``/sync`` response are thosewhich are definitely necessary + /// for a client to displaythe ``sender`` of the timeline events in that + /// response.If ``false``, ``m.room.member`` events are not filtered.By + /// default, servers should suppress duplicate redundantlazy-loaded + /// ``m.room.member`` events from being sent to a givenclient across multiple + /// calls to ``/sync``, given that most clientscache membership events (see + /// ``include_redundant_members``to change this behaviour). Omittable lazyLoadMembers; - /// If ``true``, the ``state`` section of the ``/sync`` response willalways contain the ``m.room.member`` events required to displaythe ``sender`` of the timeline events in that response, assuming``lazy_load_members`` is enabled. This means that redundantduplicate member events may be returned across multiple calls to``/sync``. This is useful for naive clients who never trackmembership data. If ``false``, duplicate ``m.room.member`` eventsmay be suppressed by the server across multiple calls to ``/sync``.If ``lazy_load_members`` is ``false`` this field is ignored. + /// If ``true``, the ``state`` section of the ``/sync`` response willalways + /// contain the ``m.room.member`` events required to displaythe ``sender`` + /// of the timeline events in that response, assuming``lazy_load_members`` + /// is enabled. This means that redundantduplicate member events may be + /// returned across multiple calls to``/sync``. This is useful for naive + /// clients who never trackmembership data. If ``false``, duplicate + /// ``m.room.member`` eventsmay be suppressed by the server across multiple + /// calls to ``/sync``.If ``lazy_load_members`` is ``false`` this field is + /// ignored. Omittable includeRedundantMembers; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const StateFilter& pod); - static void fillFrom(const QJsonObject& jo, StateFilter& pod);}; + static void fillFrom(const QJsonObject& jo, StateFilter& pod); +}; /// Filters to be applied to room data. struct RoomFilter { - /// A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the ``'rooms'`` filter. This filter is applied before the filters in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` + /// A list of room IDs to exclude. If this list is absent then no rooms are + /// excluded. A matching room will be excluded even if it is listed in the + /// ``'rooms'`` filter. This filter is applied before the filters in + /// ``ephemeral``, ``state``, ``timeline`` or ``account_data`` QStringList notRooms; - /// A list of room IDs to include. If this list is absent then all rooms are included. This filter is applied before the filters in ``ephemeral``, ``state``, ``timeline`` or ``account_data`` + /// A list of room IDs to include. If this list is absent then all rooms are + /// included. This filter is applied before the filters in ``ephemeral``, + /// ``state``, ``timeline`` or ``account_data`` QStringList rooms; - /// The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms. + /// The events that aren't recorded in the room history, e.g. typing and + /// receipts, to include for rooms. Omittable ephemeral; /// Include rooms that the user has left in the sync, default false Omittable includeLeave; @@ -50,21 +68,26 @@ struct RoomFilter Omittable timeline; /// The per user account data to include for rooms. Omittable accountData; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomFilter& pod); - static void fillFrom(const QJsonObject& jo, RoomFilter& pod);}; - + static void fillFrom(const QJsonObject& jo, RoomFilter& pod); +}; struct Filter { - /// List of event fields to include. If this list is absent then all fields are included. The entries may include '.' charaters to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' object. A literal '.' character in a field name may be escaped using a '\\'. A server may include more fields than were requested. + /// List of event fields to include. If this list is absent then all fields + /// are included. The entries may include '.' charaters to indicate + /// sub-fields. So ['content.body'] will include the 'body' field of the + /// 'content' object. A literal '.' character in a field name may be escaped + /// using a '\\'. A server may include more fields than were requested. QStringList eventFields; - /// The format to use for events. 'client' will return the events in a format suitable for clients. 'federation' will return the raw event as receieved over federation. The default is 'client'. + /// The format to use for events. 'client' will return the events in a + /// format suitable for clients. 'federation' will return the raw event as + /// receieved over federation. The default is 'client'. QString eventFormat; /// The presence updates to include. Omittable presence; @@ -72,15 +95,13 @@ struct Filter Omittable accountData; /// Filters to be applied to room data. Omittable room; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Filter& pod); - static void fillFrom(const QJsonObject& jo, Filter& pod);}; - - + static void fillFrom(const QJsonObject& jo, Filter& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/user_identifier.cpp b/lib/csapi/definitions/user_identifier.cpp index 02201179..52bb1ae6 100644 --- a/lib/csapi/definitions/user_identifier.cpp +++ b/lib/csapi/definitions/user_identifier.cpp @@ -4,23 +4,18 @@ #include "user_identifier.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const UserIdentifier& pod) +void JsonObjectConverter::dumpTo(QJsonObject& jo, + const UserIdentifier& pod) { fillJson(jo, pod.additionalProperties); addParam<>(jo, QStringLiteral("type"), pod.type); - } - -void JsonObjectConverter::fillFrom(QJsonObject jo, UserIdentifier& result) + +void JsonObjectConverter::fillFrom(QJsonObject jo, + UserIdentifier& result) { fromJson(jo.take("type"_ls), result.type); fromJson(jo, result.additionalProperties); - } - - - diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index 831ba191..51c47cca 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -4,8 +4,6 @@ #pragma once - - #include "converters.h" #include @@ -18,20 +16,19 @@ namespace QMatrixClient /// Identification information for a user struct UserIdentifier { - /// The type of identification. See `Identifier types`_ for supported values and additional property descriptions. + /// The type of identification. See `Identifier types`_ for supported + /// values and additional property descriptions. QString type; - /// Identification information for a user QVariantHash additionalProperties; - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const UserIdentifier& pod); - static void fillFrom(QJsonObject jo, UserIdentifier& pod);}; - - + static void fillFrom(QJsonObject jo, UserIdentifier& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/wellknown/full.cpp b/lib/csapi/definitions/wellknown/full.cpp index a2d71db0..34d3bfbe 100644 --- a/lib/csapi/definitions/wellknown/full.cpp +++ b/lib/csapi/definitions/wellknown/full.cpp @@ -4,25 +4,21 @@ #include "full.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const DiscoveryInformation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const DiscoveryInformation& pod) { fillJson(jo, pod.additionalProperties); addParam<>(jo, QStringLiteral("m.homeserver"), pod.homeserver); - addParam(jo, QStringLiteral("m.identity_server"), pod.identityServer); - + addParam(jo, QStringLiteral("m.identity_server"), + pod.identityServer); } - -void JsonObjectConverter::fillFrom(QJsonObject jo, DiscoveryInformation& result) + +void JsonObjectConverter::fillFrom( + QJsonObject jo, DiscoveryInformation& result) { fromJson(jo.take("m.homeserver"_ls), result.homeserver); fromJson(jo.take("m.identity_server"_ls), result.identityServer); fromJson(jo, result.additionalProperties); - } - - - diff --git a/lib/csapi/definitions/wellknown/full.h b/lib/csapi/definitions/wellknown/full.h index ef975969..ddc06653 100644 --- a/lib/csapi/definitions/wellknown/full.h +++ b/lib/csapi/definitions/wellknown/full.h @@ -4,40 +4,39 @@ #pragma once - - #include "converters.h" -#include -#include "converters.h" #include "csapi/definitions/wellknown/homeserver.h" #include "csapi/definitions/wellknown/identity_server.h" + #include +#include namespace QMatrixClient { // Data structures -/// Used by clients to determine the homeserver, identity server, and other/// optional components they should be interacting with. +/// Used by clients to determine the homeserver, identity server, and other/// +/// optional components they should be interacting with. struct DiscoveryInformation { - /// Used by clients to determine the homeserver, identity server, and otheroptional components they should be interacting with. + /// Used by clients to determine the homeserver, identity server, and + /// otheroptional components they should be interacting with. HomeserverInformation homeserver; - /// Used by clients to determine the homeserver, identity server, and otheroptional components they should be interacting with. + /// Used by clients to determine the homeserver, identity server, and + /// otheroptional components they should be interacting with. Omittable identityServer; - /// Application-dependent keys using Java package naming convention. QHash additionalProperties; - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DiscoveryInformation& pod); - static void fillFrom(QJsonObject jo, DiscoveryInformation& pod);}; - - + static void fillFrom(QJsonObject jo, DiscoveryInformation& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/wellknown/homeserver.cpp b/lib/csapi/definitions/wellknown/homeserver.cpp index a8a5077b..f5746ede 100644 --- a/lib/csapi/definitions/wellknown/homeserver.cpp +++ b/lib/csapi/definitions/wellknown/homeserver.cpp @@ -4,21 +4,16 @@ #include "homeserver.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const HomeserverInformation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const HomeserverInformation& pod) { addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, HomeserverInformation& result) + +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, HomeserverInformation& result) { fromJson(jo.value("base_url"_ls), result.baseUrl); - } - - - diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index fe6af172..b73cee17 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -4,11 +4,8 @@ #pragma once - - #include "converters.h" - namespace QMatrixClient { @@ -19,15 +16,13 @@ struct HomeserverInformation { /// The base URL for the homeserver for client-server connections. QString baseUrl; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod); - static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod);}; - - + static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); +}; } // namespace QMatrixClient diff --git a/lib/csapi/definitions/wellknown/identity_server.cpp b/lib/csapi/definitions/wellknown/identity_server.cpp index a9fae614..922d4665 100644 --- a/lib/csapi/definitions/wellknown/identity_server.cpp +++ b/lib/csapi/definitions/wellknown/identity_server.cpp @@ -4,21 +4,16 @@ #include "identity_server.h" - using namespace QMatrixClient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const IdentityServerInformation& pod) +void JsonObjectConverter::dumpTo( + QJsonObject& jo, const IdentityServerInformation& pod) { addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); - } - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, IdentityServerInformation& result) + +void JsonObjectConverter::fillFrom( + const QJsonObject& jo, IdentityServerInformation& result) { fromJson(jo.value("base_url"_ls), result.baseUrl); - } - - - diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index 4462f86e..a35644fc 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -4,11 +4,8 @@ #pragma once - - #include "converters.h" - namespace QMatrixClient { @@ -19,15 +16,13 @@ struct IdentityServerInformation { /// The base URL for the identity server for client-server connections. QString baseUrl; - - }; -template <> struct JsonObjectConverter +template <> +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod); - static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod);}; - - + static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); +}; } // namespace QMatrixClient -- cgit v1.2.3 From 1cf67730a0880a520ae04bdf4ef61592daa9fe06 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 26 Sep 2017 20:57:28 +0900 Subject: Introduce EncryptionEvent class This allows to detect if a room has been encrypted (no room state, just an event as of yet). Closes #84. --- lib/events/encryptionevent.cpp | 5 +++++ lib/events/encryptionevent.h | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 lib/events/encryptionevent.cpp create mode 100644 lib/events/encryptionevent.h (limited to 'lib') diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp new file mode 100644 index 00000000..7b620bce --- /dev/null +++ b/lib/events/encryptionevent.cpp @@ -0,0 +1,5 @@ +// +// Created by rusakov on 26/09/2017. +// + +#include "encryptionevent.h" diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h new file mode 100644 index 00000000..b44e0eeb --- /dev/null +++ b/lib/events/encryptionevent.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * 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 "roomevent.h" + +namespace QMatrixClient +{ + class EncryptionEvent : public RoomEvent + { + public: + DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) + + explicit EncryptionEvent(const QJsonObject& obj) + : RoomEvent(typeId(), obj) + , _algorithm(contentJson()["algorithm"].toString()) + { } + + QString algorithm() const { return _algorithm; } + + private: + QString _algorithm; + }; + REGISTER_EVENT_TYPE(EncryptionEvent) + DEFINE_EVENTTYPE_ALIAS(Encryption, EncryptionEvent) +} // namespace QMatrixClient + -- cgit v1.2.3 From 5722ceaf4bd10c29f1091e3dc5a87f5650ea8c71 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 26 Jun 2019 07:51:08 +0900 Subject: BaseJob::Status: fromHttpCode --- lib/jobs/basejob.cpp | 61 +++++++++++++++++++++++++--------------------------- lib/jobs/basejob.h | 4 ++-- 2 files changed, 31 insertions(+), 34 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index a0a3dc29..9c0b431c 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -348,6 +348,34 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } +BaseJob::Status BaseJob::Status::fromHttpCode(int httpCode, QString msg) +{ + // clang-format off + return { [httpCode]() -> StatusCode { + if (httpCode / 10 == 41) // 41x errors + return httpCode == 410 ? IncorrectRequestError : NotFoundError; + switch (httpCode) { + case 401: case 403: case 407: + return ContentAccessError; + case 404: + return NotFoundError; + case 400: case 405: case 406: case 426: case 428: case 505: + case 494: // Unofficial nginx "Request header too large" + case 497: // Unofficial nginx "HTTP request sent to HTTPS port" + return IncorrectRequestError; + case 429: + return TooManyRequestsError; + case 501: case 510: + return RequestNotImplementedError; + case 511: + return NetworkAuthRequiredError; + default: + return NetworkError; + } + }(), msg }; + // clang-format on +} + BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const { // QNetworkReply error codes seem to be flawed when it comes to HTTP; @@ -381,38 +409,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const qCWarning(d->logCat).noquote().nospace() << this << urlString; qCWarning(d->logCat).noquote() << " " << httpCode << reason << replyState; - return { [httpCode]() -> StatusCode { - if (httpCode / 10 == 41) - return httpCode == 410 ? IncorrectRequestError - : NotFoundError; - switch (httpCode) { - case 401: - case 403: - case 407: - return ContentAccessError; - case 404: - return NotFoundError; - case 400: - case 405: - case 406: - case 426: - case 428: - case 505: - case 494: // Unofficial nginx "Request header too large" - case 497: // Unofficial nginx "HTTP request sent to HTTPS port" - return IncorrectRequestError; - case 429: - return TooManyRequestsError; - case 501: - case 510: - return RequestNotImplementedError; - case 511: - return NetworkAuthRequiredError; - default: - return NetworkError; - } - }(), - reply->errorString() }; + return Status::fromHttpCode(httpCode, reply->errorString()); } BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 04d79c66..4d379f26 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -116,9 +116,8 @@ public: * along the lines of StatusCode, with additional values * starting at UserDefinedError */ - class Status + struct Status { - public: Status(StatusCode c) : code(c) {} @@ -126,6 +125,7 @@ public: : code(c) , message(std::move(m)) {} + static Status fromHttpCode(int httpCode, QString msg = {}); bool good() const { return code < ErrorLevel; } friend QDebug operator<<(QDebug dbg, const Status& s) -- cgit v1.2.3 From 12478cf7330727083103d22f76de92c4aa476f5b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 1 Jul 2019 16:10:35 +0900 Subject: Handle M_UNKNOWN as The Spec says; factor out BaseJob::parseError() --- lib/connection.cpp | 46 ++++++++++++------------- lib/jobs/basejob.cpp | 96 +++++++++++++++++++++++++++------------------------- lib/jobs/basejob.h | 14 ++++++-- 3 files changed, 83 insertions(+), 73 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index af2aa2ab..4c068b8f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -791,21 +791,23 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) if (room && room->joinState() != JoinState::Leave) { auto leaveJob = room->leaveRoom(); - connect(leaveJob, &BaseJob::result, this, [this, leaveJob, forgetJob, room] { - // After leave, continue if there is no error or the room id is not found - if(!leaveJob->error() || leaveJob->error() == BaseJob::StatusCode::UnknownObjectError) { - forgetJob->start(connectionData()); - // If the matching /sync response hasn't arrived yet, mark the room - // for explicit deletion - if (room->joinState() != JoinState::Leave) - d->roomIdsToForget.push_back(room->id()); - } else { - qCWarning(MAIN) << "Error leaving room " - << room->name() << ":" - << leaveJob->errorString(); - forgetJob->abandon(); - } - }); + connect(leaveJob, &BaseJob::result, this, + [this, leaveJob, forgetJob, room] { + if (leaveJob->error() == BaseJob::Success + || leaveJob->error() == BaseJob::NotFoundError) + { + forgetJob->start(connectionData()); + // If the matching /sync response hasn't arrived yet, + // mark the room for explicit deletion + if (room->joinState() != JoinState::Leave) + d->roomIdsToForget.push_back(room->id()); + } else { + qCWarning(MAIN).nospace() + << "Error leaving room " << room->objectName() + << ": " << leaveJob->errorString(); + forgetJob->abandon(); + } + }); connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon); } else @@ -813,14 +815,12 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob] { // Leave room in case of success, or room not known by server - if(!forgetJob->error() || forgetJob->error() == BaseJob::UnknownObjectError) { - // Delete the room from roomMap - d->removeRoom(id); - } else { - qCWarning(MAIN) << "Error forgetting room " - << id << ":" - << forgetJob->errorString(); - } + if(forgetJob->error() == BaseJob::Success + || forgetJob->error() == BaseJob::NotFoundError) + d->removeRoom(id); // Delete the room from roomMap + else + qCWarning(MAIN).nospace() << "Error forgetting room " << id << ": " + << forgetJob->errorString(); }); return forgetJob; } diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 97b5a904..dbf197ec 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -289,59 +289,23 @@ void BaseJob::gotReply() if (status().good()) setStatus(parseReply(d->reply.data())); else { - // FIXME: Factor out to smth like BaseJob::handleError() d->rawResponse = d->reply->readAll(); const auto jsonBody = - d->reply->rawHeader("Content-Type") == "application/json"; + d->reply->rawHeader("Content-Type") == "application/json"; qCDebug(d->logCat).noquote() - << "Error body (truncated if long):" << d->rawResponse.left(500); + << "Error body (truncated if long):" << d->rawResponse.left(500); if (jsonBody) - { - auto json = QJsonDocument::fromJson(d->rawResponse).object(); - const auto errCode = json.value("errcode"_ls).toString(); - if (error() == TooManyRequestsError || - errCode == "M_LIMIT_EXCEEDED") - { - QString msg = tr("Too many requests"); - auto retryInterval = json.value("retry_after_ms"_ls).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 << "ms"; - d->retryTimer.start(retryInterval); - emit retryScheduled(d->retriesTaken, retryInterval); - return; - } - if (errCode == "M_CONSENT_NOT_GIVEN") - { - d->status.code = UserConsentRequiredError; - d->errorUrl = json.value("consent_uri"_ls).toString(); - } - else if (errCode == "M_UNKNOWN") { - d->status.code = UnknownObjectError; - } - else if (errCode == "M_UNSUPPORTED_ROOM_VERSION" || - errCode == "M_INCOMPATIBLE_ROOM_VERSION") - { - d->status.code = UnsupportedRoomVersionError; - if (json.contains("room_version")) - d->status.message = - tr("Requested room version: %1") - .arg(json.value("room_version").toString()); - } else if (!json.isEmpty()) // Not localisable on the client side - setStatus(d->status.code, json.value("error"_ls).toString()); - } + setStatus( + parseError(d->reply.data(), + QJsonDocument::fromJson(d->rawResponse).object())); } - finishJob(); + if (error() != TooManyRequestsError) + finishJob(); + else { + stop(); + emit retryScheduled(d->retriesTaken, d->retryTimer.interval()); + } } bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) @@ -445,8 +409,46 @@ BaseJob::Status BaseJob::parseJson(const QJsonDocument&) return Success; } +BaseJob::Status BaseJob::parseError(QNetworkReply* reply, + const QJsonObject& errorJson) +{ + const auto errCode = errorJson.value("errcode"_ls).toString(); + if (error() == TooManyRequestsError || errCode == "M_LIMIT_EXCEEDED") { + QString msg = tr("Too many requests"); + auto retryInterval = errorJson.value("retry_after_ms"_ls).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(); + + qCWarning(d->logCat) << this << "will retry in" << retryInterval << "ms"; + d->retryTimer.start(retryInterval); + + return { TooManyRequestsError, msg }; + } + if (errCode == "M_CONSENT_NOT_GIVEN") { + d->errorUrl = errorJson.value("consent_uri"_ls).toString(); + return { UserConsentRequiredError }; + } + if (errCode == "M_UNSUPPORTED_ROOM_VERSION" + || errCode == "M_INCOMPATIBLE_ROOM_VERSION") + return { UnsupportedRoomVersionError, + errorJson.contains("room_version"_ls) + ? tr("Requested room version: %1") + .arg(errorJson.value("room_version"_ls).toString()) + : errorJson.value("error"_ls).toString() }; + + // Not localisable on the client side + if (errorJson.contains("error"_ls)) + d->status.message = errorJson.value("error"_ls).toString(); + + return d->status; +} + void BaseJob::stop() { + // This method is used to semi-finalise the job before retrying; so + // stop the timeout timer but keep the retry timer running. d->timer.stop(); if (d->reply) { diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index c1747cca..30ecceb3 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -67,7 +67,6 @@ namespace QMatrixClient , UnsupportedRoomVersionError , NetworkAuthRequiredError , UserConsentRequiredError - , UnknownObjectError // Unknown room or other item (M_UNKNOWN) , UserDefinedError = 200 }; @@ -303,7 +302,7 @@ namespace QMatrixClient * 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) + * @param reply raw contents of a HTTP reply from the server * * @see gotReply, parseJson */ @@ -311,7 +310,7 @@ namespace QMatrixClient /** * Processes the JSON document received from the Matrix server. - * By default returns succesful status without analysing the JSON. + * By default returns successful status without analysing the JSON. * * @param json valid JSON document received from the server * @@ -319,6 +318,15 @@ namespace QMatrixClient */ virtual Status parseJson(const QJsonDocument&); + /** + * Processes the reply in case of unsuccessful HTTP code. + * The body is already loaded from the reply object to errorJson. + * @param reply the HTTP reply from the server + * @param errorJson the JSON payload describing the error + */ + virtual Status parseError(QNetworkReply* reply, + const QJsonObject& errorJson); + void setStatus(Status s); void setStatus(int code, QString message); -- cgit v1.2.3 From ddc5a60184972e1449191ce77561b875a145a665 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 2 Jul 2019 08:53:25 +0900 Subject: linkifyUrls: support matrix: scheme and relative URLs --- lib/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 01d4e77b..88cba959 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -45,7 +45,7 @@ void QMatrixClient::linkifyUrls(QString& htmlEscapedText) // <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, ), :, // comma or dot static const QRegularExpression FullUrlRegExp(QStringLiteral( - R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp|magnet)://)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" + R"(\b((www\.(?!\.)(?!(\w|\.|-)+@)|(https?|ftp|magnet|matrix):(//)?)(&(?![lg]t;)|[^&\s<>'"])+(&(?![lg]t;)|[^&!,.\s<>'"\]):])))" ), RegExpOptions); // email address: // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] -- cgit v1.2.3 From 6a6857b9d4dbf22402f2871494bdd06cdccdf366 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 3 Jul 2019 23:28:09 +0900 Subject: Room/Connection: make room aliases work properly Closes #301. --- lib/connection.cpp | 49 ++++++++++++++++++++++++++++++++----------------- lib/connection.h | 10 ++++++---- lib/room.cpp | 39 +++++++++++++++++++++++++++++++++++---- lib/room.h | 14 ++++++++++---- 4 files changed, 83 insertions(+), 29 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 4c068b8f..783e12c0 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -82,8 +82,9 @@ class Connection::Private // separately; specifically, we should keep objects for Invite and // Leave state of the same room if the two happen to co-exist. QHash, Room*> roomMap; - // Mapping from aliases to room ids, as per the last sync - QHash roomAliasMap; + /// Mapping from serverparts to alias/room id mappings, + /// as of the last sync + QHash> roomAliasMap; QVector roomIdsToForget; QVector firstTimeRooms; QVector pendingStateRoomIds; @@ -158,20 +159,31 @@ Connection::~Connection() stopSync(); } -void Connection::resolveServer(const QString& mxidOrDomain) +static const auto ServerPartRegEx = QStringLiteral( + "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address + "(?::(\\d{1,5}))?" // Optional port +); + +QString serverPart(const QString& mxId) { - // At this point we may have something as complex as - // @username:[IPv6:address]:port, or as simple as a plain domain name. + static auto re = "^[@!#$+].+?:(" // Localpart and colon + % ServerPartRegEx % ")$"; + static QRegularExpression parser(re, + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits + return parser.match(mxId).captured(1); +} - // Try to parse as an FQID; if there's no @ part, assume it's a domain name. - QRegularExpression parser( +void Connection::resolveServer(const QString& mxidOrDomain) +{ + // mxIdOrDomain may be something as complex as + // @username:[IPv6:address]:port, or as simple as a plain serverpart. + static QRegularExpression parser( "^(@.+?:)?" // Optional username (allow everything for compatibility) - "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address - "(:\\d{1,5})?$", // Optional port - QRegularExpression::UseUnicodePropertiesOption); // Because asian digits + % ServerPartRegEx % '$', + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits auto match = parser.match(mxidOrDomain); - QUrl maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); + auto maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http" if (!match.hasMatch() || !maybeBaseUrl.isValid()) { @@ -883,33 +895,36 @@ Room* Connection::room(const QString& roomId, JoinStates states) const Room* Connection::roomByAlias(const QString& roomAlias, JoinStates states) const { - const auto id = d->roomAliasMap.value(roomAlias); + const auto id = + d->roomAliasMap.value(serverPart(roomAlias)).value(roomAlias); if (!id.isEmpty()) return room(id, states); + qCWarning(MAIN) << "Room for alias" << roomAlias << "is not found under account" << userId(); return nullptr; } void Connection::updateRoomAliases(const QString& roomId, + const QString& aliasServer, const QStringList& previousRoomAliases, const QStringList& roomAliases) { + auto& aliasMap = d->roomAliasMap[aliasServer]; // Allocate if necessary for (const auto& a: previousRoomAliases) - if (d->roomAliasMap.remove(a) == 0) + if (aliasMap.remove(a) == 0) qCWarning(MAIN) << "Alias" << a << "is not found (already deleted?)"; for (const auto& a: roomAliases) { - auto& mappedId = d->roomAliasMap[a]; + auto& mappedId = aliasMap[a]; if (!mappedId.isEmpty()) { if (mappedId == roomId) - qCDebug(MAIN) << "Alias" << a << "is already mapped to room" + qCDebug(MAIN) << "Alias" << a << "is already mapped to" << roomId; else - qCWarning(MAIN) << "Alias" << a - << "will be force-remapped from room" + qCWarning(MAIN) << "Alias" << a << "will be force-remapped from" << mappedId << "to" << roomId; } mappedId = roomId; diff --git a/lib/connection.h b/lib/connection.h index cc2feed8..f688c10b 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -250,11 +250,13 @@ namespace QMatrixClient Q_INVOKABLE Room* roomByAlias(const QString& roomAlias, JoinStates states = JoinState::Invite|JoinState::Join) const; /** Update the internal map of room aliases to IDs */ - /// This is used for internal bookkeeping of rooms. Do NOT use - /// it to try change aliases, use Room::setAliases instead + /// This is used to maintain the internal index of room aliases. + /// It does NOT change aliases on the server, + /// \sa Room::setLocalAliases void updateRoomAliases(const QString& roomId, - const QStringList& previousRoomAliases, - const QStringList& roomAliases); + const QString& aliasServer, + const QStringList& previousRoomAliases, + const QStringList& roomAliases); Q_INVOKABLE Room* invitation(const QString& roomId) const; Q_INVOKABLE User* user(const QString& userId); const User* user() const; diff --git a/lib/room.cpp b/lib/room.cpp index 9042130a..06f3490c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -96,9 +96,14 @@ class Room::Private /// The state of the room at timeline position after-maxTimelineIndex() /// \sa Room::syncEdge QHash currentState; + /// Servers with aliases for this room except the one of the local user + /// \sa Room::remoteAliases + QSet aliasServers; + Timeline timeline; PendingEvents unsyncedEvents; QHash eventsIndex; + QString displayname; Avatar avatar; int highlightCount = 0; @@ -381,9 +386,18 @@ QString Room::name() const return d->getCurrentState()->name(); } -QStringList Room::aliases() const +QStringList Room::localAliases() const { - return d->getCurrentState()->aliases(); + return d->getCurrentState( + connection()->homeserver().authority())->aliases(); +} + +QStringList Room::remoteAliases() const +{ + QStringList result; + for (const auto& s: d->aliasServers) + result += d->getCurrentState(s)->aliases(); + return result; } QString Room::canonicalAlias() const @@ -1624,7 +1638,7 @@ void Room::setCanonicalAlias(const QString& newAlias) d->requestSetState(RoomCanonicalAliasEvent(newAlias)); } -void Room::setAliases(const QStringList& aliases) +void Room::setLocalAliases(const QStringList& aliases) { d->requestSetState(RoomAliasesEvent(aliases)); } @@ -2192,16 +2206,30 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!is(e)) // Room member events are too numerous qCDebug(EVENTS) << "Room state event:" << e; + // clang-format off return visit(e , [] (const RoomNameEvent&) { return NameChange; } , [this,oldStateEvent] (const RoomAliasesEvent& ae) { + // clang-format on + if (ae.aliases().isEmpty()) { + qDebug(MAIN).noquote() << ae.stateKey() + << "no more has aliases for room" << objectName(); + d->aliasServers.remove(ae.stateKey()); + } else { + d->aliasServers.insert(ae.stateKey()); + qDebug(MAIN).nospace().noquote() + << "New server with aliases for room " << objectName() + << ": " << ae.stateKey(); + } const auto previousAliases = oldStateEvent ? static_cast(oldStateEvent)->aliases() : QStringList(); - connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); + connection()->updateRoomAliases(id(), ae.stateKey(), + previousAliases, ae.aliases()); return OtherChange; + // clang-format off } , [this] (const RoomCanonicalAliasEvent& evt) { setObjectName(evt.alias().isEmpty() ? d->id : evt.alias()); @@ -2216,6 +2244,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return AvatarChange; } , [this,oldStateEvent] (const RoomMemberEvent& evt) { + // clang-format on auto* u = user(evt.userId()); const auto* oldMemberEvent = static_cast(oldStateEvent); @@ -2288,6 +2317,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) d->membersLeft.append(u); } return MembersChange; + // clang-format off } , [this] (const EncryptionEvent&) { emit encryption(); // It can only be done once, so emit it here. @@ -2310,6 +2340,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return OtherChange; } ); + // clang-format on } Room::Changes Room::processEphemeralEvent(EventPtr&& event) diff --git a/lib/room.h b/lib/room.h index d4a1b959..7c85e4ed 100644 --- a/lib/room.h +++ b/lib/room.h @@ -86,7 +86,8 @@ namespace QMatrixClient Q_PROPERTY(QString predecessorId READ predecessorId NOTIFY baseStateLoaded) Q_PROPERTY(QString successorId READ successorId NOTIFY upgraded) Q_PROPERTY(QString name READ name NOTIFY namesChanged) - Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) + Q_PROPERTY(QStringList localAliases READ localAliases NOTIFY namesChanged) + Q_PROPERTY(QStringList remoteAliases READ remoteAliases NOTIFY namesChanged) Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) Q_PROPERTY(QString displayName READ displayName NOTIFY displaynameChanged) Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) @@ -156,7 +157,12 @@ namespace QMatrixClient QString predecessorId() const; QString successorId() const; QString name() const; - QStringList aliases() const; + /// Room aliases defined on the current user's server + /// \sa remoteAliases, setLocalAliases + QStringList localAliases() const; + /// Room aliases defined on other servers + /// \sa localAliases + QStringList remoteAliases() const; QString canonicalAlias() const; QString displayName() const; QString topic() const; @@ -436,7 +442,8 @@ namespace QMatrixClient void discardMessage(const QString& txnId); void setName(const QString& newName); void setCanonicalAlias(const QString& newAlias); - void setAliases(const QStringList& aliases); + /// Set room aliases on the user's current server + void setLocalAliases(const QStringList& aliases); void setTopic(const QString& newTopic); /// You shouldn't normally call this method; it's here for debugging @@ -590,7 +597,6 @@ namespace QMatrixClient void beforeDestruction(Room*); protected: - /// Returns true if any of room names/aliases has changed virtual Changes processStateEvent(const RoomEvent& e); virtual Changes processEphemeralEvent(EventPtr&& event); virtual Changes processAccountDataEvent(EventPtr&& event); -- cgit v1.2.3 From fe82533860a327fbbaa0c980188ccd56d8463b1c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 4 Jul 2019 13:17:36 +0900 Subject: Connection::token() is no more Use Connection::accessToken() instead. --- lib/connection.cpp | 5 ----- lib/connection.h | 2 -- 2 files changed, 7 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 783e12c0..cd02f6d7 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -973,11 +973,6 @@ QString Connection::deviceId() const return d->data->deviceId(); } -QString Connection::token() const -{ - return accessToken(); -} - QByteArray Connection::accessToken() const { return d->data->accessToken(); diff --git a/lib/connection.h b/lib/connection.h index f688c10b..eca3c5be 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -267,8 +267,6 @@ namespace QMatrixClient Q_INVOKABLE SyncJob* syncJob() const; Q_INVOKABLE int millisToReconnect() const; - [[deprecated("Use accessToken() instead")]] - Q_INVOKABLE QString token() const; Q_INVOKABLE void getTurnServers(); struct SupportedRoomVersion -- cgit v1.2.3 From af623d68df8cec92277e9a40f18beac617b5d397 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 24 May 2019 15:18:14 +0300 Subject: Add libQtOlm --- lib/libQtOlm | 1 + 1 file changed, 1 insertion(+) create mode 160000 lib/libQtOlm (limited to 'lib') diff --git a/lib/libQtOlm b/lib/libQtOlm new file mode 160000 index 00000000..37bebdc9 --- /dev/null +++ b/lib/libQtOlm @@ -0,0 +1 @@ +Subproject commit 37bebdc96aee620df44d211ef91b55b86d8cfbc7 -- cgit v1.2.3 From bb98bd26bdd72998e09553e1703d552d1d217f86 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 25 May 2019 03:31:55 +0300 Subject: Add full EncryptionEvent to room logic. Issue #95 --- lib/events/encryptionevent.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++ lib/events/encryptionevent.h | 47 +++++++++++++++++++++++++++++++++++------ lib/events/simplestateevents.h | 2 -- lib/room.cpp | 1 + 4 files changed, 89 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index 7b620bce..b8e2b575 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -1,5 +1,53 @@ // // Created by rusakov on 26/09/2017. +// Contributed by andreev on 27/06/2019. // #include "encryptionevent.h" + +#include "converters.h" +#include "logging.h" + +#include + +static const std::array encryptionStrings = { { + QStringLiteral("m.megolm.v1.aes-sha2") +} }; + +namespace QMatrixClient { + template <> + struct JsonConverter + { + static EncryptionType load(const QJsonValue& jv) + { + const auto& encryptionString = jv.toString(); + for (auto it = encryptionStrings.begin(); + it != encryptionStrings.end(); ++it) + if (encryptionString == *it) + return EncryptionType(it - encryptionStrings.begin()); + + qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString; + return EncryptionType::Undefined; + } + }; +} + +using namespace QMatrixClient; + +EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) + : encryption(fromJson(json["algorithm"_ls])) + , algorithm(sanitized(json["algorithm"_ls].toString())) + , rotationPeriodMs(json["rotation_period_ms"_ls].toInt(604800000)) + , rotationPeriodMsgs(json["rotation_period_msgs"_ls].toInt(100)) +{ } + +void EncryptionEventContent::fillJson(QJsonObject* o) const +{ + Q_ASSERT(o); + Q_ASSERT_X(encryption != EncryptionType::Undefined, __FUNCTION__, + "The key 'algorithm' must be explicit in EncryptionEventContent"); + if (encryption != EncryptionType::Undefined) + o->insert(QStringLiteral("algorithm"), algorithm); + o->insert(QStringLiteral("rotation_period_ms"), rotationPeriodMs); + o->insert(QStringLiteral("rotation_period_msgs"), rotationPeriodMsgs); +} diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index b44e0eeb..6a4a1c67 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -18,25 +18,58 @@ #pragma once -#include "roomevent.h" +#include "stateevent.h" +#include "eventcontent.h" namespace QMatrixClient { - class EncryptionEvent : public RoomEvent + class EncryptionEventContent: public EventContent::Base { + public: + enum EncryptionType : size_t { MegolmV1AesSha2 = 0, + Undefined }; + + explicit EncryptionEventContent(EncryptionType et = Undefined) + : encryption(et) + { } + explicit EncryptionEventContent(const QJsonObject& json); + + EncryptionType encryption; + QString algorithm; + int rotationPeriodMs; + int rotationPeriodMsgs; + + protected: + void fillJson(QJsonObject* o) const override; + }; + + using EncryptionType = EncryptionEventContent::EncryptionType; + + class EncryptionEvent : public StateEvent + { + Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) - explicit EncryptionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - , _algorithm(contentJson()["algorithm"].toString()) + using EncryptionType = EncryptionEventContent::EncryptionType; + + explicit EncryptionEvent(const QJsonObject& obj = {}) // TODO: apropriate default value + : StateEvent(typeId(), obj) { } + EncryptionEvent(EncryptionEventContent&& c) + : StateEvent(typeId(), matrixTypeId(), c) + { } + + EncryptionType encryption() const { return content().encryption; } - QString algorithm() const { return _algorithm; } + QString algorithm() const { return content().algorithm; } + int rotationPeriodMs() const { return content().rotationPeriodMs; } + int rotationPeriodMsgs() const { return content().rotationPeriodMsgs; } private: - QString _algorithm; + REGISTER_ENUM(EncryptionType) }; + REGISTER_EVENT_TYPE(EncryptionEvent) DEFINE_EVENTTYPE_ALIAS(Encryption, EncryptionEvent) } // namespace QMatrixClient diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 2c23d9ca..81401532 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -86,7 +86,5 @@ namespace QMatrixClient DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) - DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - QString, algorithm) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) } // namespace QMatrixClient diff --git a/lib/room.cpp b/lib/room.cpp index 2ce37acc..14e16850 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -31,6 +31,7 @@ #include "csapi/tags.h" #include "csapi/room_upgrades.h" #include "events/simplestateevents.h" +#include "events/encryptionevent.h" #include "events/roomcreateevent.h" #include "events/roomtombstoneevent.h" #include "events/roomavatarevent.h" -- cgit v1.2.3 From 46a7350086e42ea3960fe54ef5c68b2207527899 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Wed, 29 May 2019 02:22:06 +0300 Subject: Update libQtOlm --- lib/libQtOlm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/libQtOlm b/lib/libQtOlm index 37bebdc9..5bfc4241 160000 --- a/lib/libQtOlm +++ b/lib/libQtOlm @@ -1 +1 @@ -Subproject commit 37bebdc96aee620df44d211ef91b55b86d8cfbc7 +Subproject commit 5bfc42417d8ee741d2f5a723a2939c895734b92b -- cgit v1.2.3 From 69ba71cca36b4a90328d169c845195f39c041a3a Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Add EncryptionManager class. Add AccountSettings::encryptionAccountPickle logic. --- lib/connection.cpp | 10 ++++ lib/encryptionmanager.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++++++ lib/encryptionmanager.h | 31 ++++++++++++ lib/settings.cpp | 22 ++++++++- lib/settings.h | 5 ++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 lib/encryptionmanager.cpp create mode 100644 lib/encryptionmanager.h (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index a7eae30f..d7c3d78f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -23,6 +23,7 @@ #include "events/eventloader.h" #include "room.h" #include "settings.h" +#include "encryptionmanager.h" #include "csapi/login.h" #include "csapi/capabilities.h" #include "csapi/logout.h" @@ -101,6 +102,8 @@ class Connection::Private GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; + QScopedPointer encryptionManager; + SyncJob* syncJob = nullptr; bool cacheState = true; @@ -235,6 +238,13 @@ void Connection::doConnectToServer(const QString& user, const QString& password, [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); + + AccountSettings accountSettings(loginJob->userId()); + d->encryptionManager.reset(new EncryptionManager(accountSettings.encryptionAccountPickle())); + + d->encryptionManager->uploadIdentityKeys(this); + d->encryptionManager->uploadOneTimeKeys(this); + }); connect(loginJob, &BaseJob::failure, this, [this, loginJob] { diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp new file mode 100644 index 00000000..b40305ea --- /dev/null +++ b/lib/encryptionmanager.cpp @@ -0,0 +1,117 @@ +#include "encryptionmanager.h" + +#include +#include +#include +#include +#include // QtOlm + +#include "csapi/keys.h" +#include "connection.h" + +using namespace QMatrixClient; +using namespace QtOlm; +using std::move; + +static const auto ed25519Name = QStringLiteral("ed25519"); +static const auto Curve25519Name = QStringLiteral("curve25519"); +static const auto SignedCurve25519Name = QStringLiteral("signed_curve25519"); +static const auto OlmCurve25519AesSha256AlgoName = QStringLiteral("m.olm.curve25519-aes-sha256"); +static const auto MegolmV1AesShaAlgoName = QStringLiteral("m.megolm.v1.aes-sha"); + +class EncryptionManager::Private +{ + public: + explicit Private(const QByteArray& encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold) + : olmAccount(new Account(encryptionAccountPickle)), // TODO: passphrase even with qtkeychain? + signedKeysProportion(move(signedKeysProportion)), + oneTimeKeyThreshold(move(oneTimeKeyThreshold)), + targetKeysNumber(olmAccount->maxOneTimeKeys()) // 2 // see note below + { + Q_ASSERT((0 <= signedKeysProportion) && (signedKeysProportion <= 1)); + Q_ASSERT((0 <= oneTimeKeyThreshold) && (oneTimeKeyThreshold <= 1)); + /* + * Note about targetKeysNumber: + * + * From: https://github.com/Zil0/matrix-python-sdk/ + * File: matrix_client/crypto/olm_device.py + * + * Try to maintain half the number of one-time keys libolm can hold uploaded + * on the HS. This is because some keys will be claimed by peers but not + * used instantly, and we want them to stay in libolm, until the limit is reached + * and it starts discarding keys, starting by the oldest. + */ + } + ~Private() + { + delete olmAccount; + } + + UploadKeysJob* uploadIdentityKeysJob = nullptr; + UploadKeysJob* uploadOneTimeKeysJob = nullptr; + + Account* olmAccount; + const QByteArray encryptionAccountPickle; + + float signedKeysProportion; + float oneTimeKeyThreshold; + int targetKeysNumber; + + void updateKeysToUpload(); + bool oneTimeKeyShouldUpload(); + + QHash oneTimeKeyCounts; + void setOneTimeKeyCounts(const QHash oneTimeKeyCountsNewValue) + { + oneTimeKeyCounts = oneTimeKeyCountsNewValue; + updateKeysToUpload(); + } + QHash oneTimeKeysToUploadCounts; + QHash targetOneTimeKeyCounts + { + {SignedCurve25519Name, qRound(signedKeysProportion * targetKeysNumber)}, + {Curve25519Name, qRound((1-signedKeysProportion) * targetKeysNumber)} + }; +}; + +EncryptionManager::EncryptionManager(const QByteArray &encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold, + QObject* parent) + : QObject(parent), + d(std::make_unique(std::move(encryptionAccountPickle), std::move(signedKeysProportion), std::move(oneTimeKeyThreshold))) +{ + +} + +EncryptionManager::~EncryptionManager() = default; + +void EncryptionManager::uploadIdentityKeys(Connection* connection) +{ + // TODO +} + +void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) +{ + // TODO +} + +void EncryptionManager::Private::updateKeysToUpload() +{ + for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) + { + int numKeys = oneTimeKeyCounts.value(it.key(), 0); + int numToCreate = qMax(it.value() - numKeys, 0); + oneTimeKeysToUploadCounts.insert(it.key(), numToCreate); + } +} + +bool EncryptionManager::Private::oneTimeKeyShouldUpload() +{ + if (oneTimeKeyCounts.empty()) + return true; + for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) + { + if (oneTimeKeyCounts.value(it.key(), 0) < it.value() * oneTimeKeyThreshold) + return true; + } + return false; +} diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h new file mode 100644 index 00000000..0bd05432 --- /dev/null +++ b/lib/encryptionmanager.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +namespace QMatrixClient +{ + class Connection; + + class EncryptionManager: public QObject + { + Q_OBJECT + + public: + // TODO: store constats separately? + // TODO: 0.5 oneTimeKeyThreshold instead of 0.1? + explicit EncryptionManager(const QByteArray& encryptionAccountPickle, float signedKeysProportion = 1, float oneTimeKeyThreshold = float(0.1), + QObject* parent = nullptr); + ~EncryptionManager(); + + void uploadIdentityKeys(Connection* connection); + void uploadOneTimeKeys(Connection* connection, bool forceUpdate = false); + + private: + class Private; + std::unique_ptr d; + + }; + +} // namespace QMatrixClient diff --git a/lib/settings.cpp b/lib/settings.cpp index 124d7042..5f10299c 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -90,6 +90,7 @@ QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, static const auto HomeserverKey = QStringLiteral("homeserver"); static const auto AccessTokenKey = QStringLiteral("access_token"); +static const auto EncryptionAccountPickleKey = QStringLiteral("encryption_account_pickle"); QUrl AccountSettings::homeserver() const { @@ -114,7 +115,7 @@ QString AccountSettings::accessToken() const void AccountSettings::setAccessToken(const QString& accessToken) { qCWarning(MAIN) << "Saving access_token to QSettings is insecure." - " Developers, please save access_token separately."; + " Developers, do it manually or contribute to share QtKeychain logic to libQuotient."; setValue(AccessTokenKey, accessToken); } @@ -124,3 +125,22 @@ void AccountSettings::clearAccessToken() legacySettings.remove(QStringLiteral("device_id")); // Force the server to re-issue it remove(AccessTokenKey); } + +QByteArray AccountSettings::encryptionAccountPickle() +{ + QString passphrase = ""; // FIXME: add QtKeychain + return value("encryption_account_pickle", "").toByteArray(); +} + +void AccountSettings::setEncryptionAccountPickle(const QByteArray& encryptionAccountPickle) +{ + qCWarning(MAIN) << "Saving encryption_account_pickle to QSettings is insecure." + " Developers, do it manually or contribute to share QtKeychain logic to libQuotient."; + QString passphrase = ""; // FIXME: add QtKeychain + setValue("encryption_account_pickle", encryptionAccountPickle); +} + +void AccountSettings::clearEncryptionAccountPickle() +{ + remove(EncryptionAccountPickleKey); // TODO: Force to re-issue it? +} diff --git a/lib/settings.h b/lib/settings.h index 759bda35..61e5232a 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -131,6 +131,7 @@ void classname::setter(type newValue) \ QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) /** \deprecated \sa setAccessToken */ Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) + Q_PROPERTY(QByteArray encryptionAccountPickle READ encryptionAccountPickle WRITE setEncryptionAccountPickle) public: template explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) @@ -148,5 +149,9 @@ void classname::setter(type newValue) \ * see QMatrixClient/Quaternion#181 */ void setAccessToken(const QString& accessToken); Q_INVOKABLE void clearAccessToken(); + + QByteArray encryptionAccountPickle(); + void setEncryptionAccountPickle(const QByteArray& encryptionAccountPickle); + Q_INVOKABLE void clearEncryptionAccountPickle(); }; } // namespace QMatrixClient -- cgit v1.2.3 From 79f6f33bbbd60eafe92b5187a3bf5fd8966f8bf5 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Upload device public keys. Issue #87 --- lib/encryptionmanager.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index b40305ea..80fdcebd 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -18,6 +18,7 @@ static const auto Curve25519Name = QStringLiteral("curve25519"); static const auto SignedCurve25519Name = QStringLiteral("signed_curve25519"); static const auto OlmCurve25519AesSha256AlgoName = QStringLiteral("m.olm.curve25519-aes-sha256"); static const auto MegolmV1AesShaAlgoName = QStringLiteral("m.megolm.v1.aes-sha"); +static const QStringList SupportedAlgorithms = { OlmCurve25519AesSha256AlgoName, MegolmV1AesShaAlgoName }; class EncryptionManager::Private { @@ -86,7 +87,52 @@ EncryptionManager::~EncryptionManager() = default; void EncryptionManager::uploadIdentityKeys(Connection* connection) { - // TODO + // https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-keys-upload + DeviceKeys deviceKeys + { + /* + * The ID of the user the device belongs to. Must match the user ID used when logging in. + * The ID of the device these keys belong to. Must match the device ID used when logging in. + * The encryption algorithms supported by this device. + */ + connection->userId(), connection->deviceId(), SupportedAlgorithms, + /* + * Public identity keys. The names of the properties should be in the format :. + * The keys themselves should be encoded as specified by the key algorithm. + */ + { + { + Curve25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->curve25519IdentityKey() + }, + { + ed25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->ed25519IdentityKey() + } + }, + /* + * Signatures for the device key object. + * A map from user ID, to a map from : to the signature. + * The signature is calculated using the process called Signing JSON. + */ + { + { + connection->userId(), + { + { + ed25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->sign(toJson(deviceKeys)) + } + } + } + } + }; + + connect(d->uploadIdentityKeysJob, &BaseJob::success, this, [this] { + d->setOneTimeKeyCounts(d->uploadIdentityKeysJob->oneTimeKeyCounts()); + qDebug() << QString("Uploaded identity keys."); + }); + d->uploadIdentityKeysJob = connection->callApi(deviceKeys); } void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) -- cgit v1.2.3 From e1bee9d71d88d6500d2c6124689a0e8685aeab90 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 22 Jun 2019 19:57:18 +0300 Subject: Upload one-time keys. Issue #88 --- lib/encryptionmanager.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 80fdcebd..1e1fc669 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -137,7 +137,50 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) { - // TODO + if (forceUpdate || d->oneTimeKeyCounts.isEmpty()) + { + auto job = connection->callApi(); + connect(job, &BaseJob::success, this, [job,this] { + d->setOneTimeKeyCounts(job->oneTimeKeyCounts()); + }); + + } + + int signedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(SignedCurve25519Name, 0); + int unsignedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(Curve25519Name, 0); + + d->olmAccount->generateOneTimeKeys(signedKeysToUploadCount + unsignedKeysToUploadCount); + + QHash oneTimeKeys = {}; + const auto& olmAccountCurve25519OneTimeKeys = d->olmAccount->curve25519OneTimeKeys(); + + int oneTimeKeysCounter = 0; + for (auto it = olmAccountCurve25519OneTimeKeys.cbegin(); it != olmAccountCurve25519OneTimeKeys.cend(); ++it) + { + QString keyId = it.key(); + QString keyType; + QVariant key; + if (oneTimeKeysCounter < signedKeysToUploadCount) + { + QJsonObject message + { + {QStringLiteral("key"), it.value().toString()} + }; + key = d->olmAccount->sign(message); + keyType = SignedCurve25519Name; + + } else { + key = it.value(); + keyType = Curve25519Name; + } + ++oneTimeKeysCounter; + oneTimeKeys.insert(QString("%1:%2").arg(keyType).arg(keyId), key); + } + + d->uploadOneTimeKeysJob = connection->callApi(none, oneTimeKeys); + d->olmAccount->markKeysAsPublished(); + qDebug() << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.") + .arg(signedKeysToUploadCount).arg(unsignedKeysToUploadCount); } void EncryptionManager::Private::updateKeysToUpload() -- cgit v1.2.3 From b5f9e1bd20f985c18ec630fa496510018547b728 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Wed, 26 Jun 2019 16:36:45 +0300 Subject: Change libQtOlm location. Fix .travis.yml and .appveyor.yml --- lib/libQtOlm | 1 - 1 file changed, 1 deletion(-) delete mode 160000 lib/libQtOlm (limited to 'lib') diff --git a/lib/libQtOlm b/lib/libQtOlm deleted file mode 160000 index 5bfc4241..00000000 --- a/lib/libQtOlm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5bfc42417d8ee741d2f5a723a2939c895734b92b -- cgit v1.2.3 From a9984d39e5186e2cfd105cf2ffb4148d9a8573a2 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Thu, 4 Jul 2019 21:52:02 +0800 Subject: Connection.cpp: Add .well-known parsing in resolveServer() --- lib/connection.cpp | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index cd02f6d7..4391a1b2 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -32,6 +32,7 @@ #include "csapi/joining.h" #include "csapi/to_device.h" #include "csapi/room_send.h" +#include "csapi/wellknown.h" #include "jobs/syncjob.h" #include "jobs/mediathumbnailjob.h" #include "jobs/downloadfilejob.h" @@ -194,37 +195,24 @@ void Connection::resolveServer(const QString& mxidOrDomain) } setHomeserver(maybeBaseUrl); - emit resolved(); - return; - // FIXME, #178: The below code is incorrect and is no more executed. The - // correct server resolution should be done from .well-known/matrix/client auto domain = maybeBaseUrl.host(); qCDebug(MAIN) << "Finding the server" << domain; - // Check if the Matrix server has a dedicated service record. - auto* dns = new QDnsLookup(); - dns->setType(QDnsLookup::SRV); - dns->setName("_matrix._tcp." + domain); - - connect(dns, &QDnsLookup::finished, [this,dns,maybeBaseUrl]() { - QUrl baseUrl { maybeBaseUrl }; - if (dns->error() == QDnsLookup::NoError && - dns->serviceRecords().isEmpty()) - { - auto record = dns->serviceRecords().front(); - baseUrl.setHost(record.target()); - baseUrl.setPort(record.port()); - qCDebug(MAIN) << "SRV record for" << maybeBaseUrl.host() - << "is" << baseUrl.authority(); + + auto job = callApi(); + connect(job, &BaseJob::finished, [this, job, maybeBaseUrl] { + QUrl baseUrl(job->data().homeserver.baseUrl); + if (!baseUrl.isValid() || job->status() != BaseJob::Success) { + baseUrl = maybeBaseUrl; + qCDebug(MAIN) << maybeBaseUrl.host() << "doesn't have .well-known record" << "- using the hostname as is"; } else { - qCDebug(MAIN) << baseUrl.host() << "doesn't have SRV record" - << dns->name() << "- using the hostname as is"; + qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.authority(); } + setHomeserver(baseUrl); emit resolved(); - dns->deleteLater(); + job->deleteLater(); }); - dns->lookup(); } void Connection::connectToServer(const QString& user, const QString& password, -- cgit v1.2.3 From 5d9d56a4fe9f3178c8bc9b3839ec608659c0c371 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Fri, 5 Jul 2019 10:15:54 +0800 Subject: Clean up .well-known logic --- lib/connection.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 4391a1b2..98555104 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -203,13 +203,12 @@ void Connection::resolveServer(const QString& mxidOrDomain) connect(job, &BaseJob::finished, [this, job, maybeBaseUrl] { QUrl baseUrl(job->data().homeserver.baseUrl); if (!baseUrl.isValid() || job->status() != BaseJob::Success) { - baseUrl = maybeBaseUrl; - qCDebug(MAIN) << maybeBaseUrl.host() << "doesn't have .well-known record" << "- using the hostname as is"; + qCDebug(MAIN) << maybeBaseUrl.host() << "doesn't have valid .well-known file" << "- using the hostname as is"; } else { qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.authority(); + setHomeserver(baseUrl); } - setHomeserver(baseUrl); emit resolved(); job->deleteLater(); }); -- cgit v1.2.3 From 281e3235d3d4618afd9f01049b8a2acbe1c8475c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 11:41:08 +0900 Subject: Connection::serverPart: replace auto with QString because of QStringBuilder See https://github.com/KDE/clazy/blob/master/docs/checks/README-auto-unexpected-qstringbuilder.md Closes #613. --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index cd02f6d7..5d377173 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -166,7 +166,7 @@ static const auto ServerPartRegEx = QStringLiteral( QString serverPart(const QString& mxId) { - static auto re = "^[@!#$+].+?:(" // Localpart and colon + static QString re = "^[@!#$+].+?:(" // Localpart and colon % ServerPartRegEx % ")$"; static QRegularExpression parser(re, QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits -- cgit v1.2.3 From 9ad25f204fa6caadf93203c42f3ea7de17dceab0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 12:35:45 +0900 Subject: Room::setLocalAliases: now actually working (with a caveat) The caveat is that the library doesn't support .well-known yet, therefore will work not fully correctly (in particular - won't correctly set aliases) with servers that have serverpart different from the homeserver hostname used to connect to it. --- lib/room.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 06f3490c..c5367047 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1640,7 +1640,8 @@ void Room::setCanonicalAlias(const QString& newAlias) void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(RoomAliasesEvent(aliases)); + d->requestSetState(connection()->homeserver().authority(), + RoomAliasesEvent(aliases)); } void Room::setTopic(const QString& newTopic) -- cgit v1.2.3 From 5b8079ca3d35360b7d98814654f37885f21bcb7e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 19:53:23 +0900 Subject: Fix clazy warnings --- lib/connection.h | 2 +- lib/events/event.h | 2 +- lib/events/eventcontent.h | 8 +++++++- lib/joinstate.h | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index eca3c5be..4ab8d5ba 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -122,7 +122,7 @@ namespace QMatrixClient explicit Connection(QObject* parent = nullptr); explicit Connection(const QUrl& server, QObject* parent = nullptr); - virtual ~Connection(); + ~Connection() override; /** Get all Invited and Joined rooms * \return a hashmap from a composite key - room name and whether diff --git a/lib/events/event.h b/lib/events/event.h index b7bbd83e..5248472c 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -259,7 +259,7 @@ namespace QMatrixClient } template - T content(const QLatin1String& key) const + T content(QLatin1String key) const { return fromJson(contentJson()[key]); } diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index ab31a75d..254eb9a9 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -53,6 +53,9 @@ namespace QMatrixClient QJsonObject originalJson; protected: + Base(const Base&) = default; + Base(Base&&) = default; + virtual void fillJson(QJsonObject* o) const = 0; }; @@ -167,11 +170,14 @@ namespace QMatrixClient class TypedBase: public Base { public: - explicit TypedBase(const QJsonObject& o = {}) : Base(o) { } + explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) { } virtual QMimeType type() const = 0; virtual const FileInfo* fileInfo() const { return nullptr; } virtual FileInfo* fileInfo() { return nullptr; } virtual const Thumbnail* thumbnailInfo() const { return nullptr; } + + protected: + using Base::Base; }; /** diff --git a/lib/joinstate.h b/lib/joinstate.h index 379183f6..4ae67de8 100644 --- a/lib/joinstate.h +++ b/lib/joinstate.h @@ -41,7 +41,7 @@ namespace QMatrixClient inline const char* toCString(JoinState js) { size_t state = size_t(js), index = 0; - while (state >>= 1) ++index; + while (state >>= 1u) ++index; return JoinStateStrings[index]; } } // namespace QMatrixClient -- cgit v1.2.3 From 01cce0e39d255cbcf39f6a1aa58c6d7ab1d995d1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 19:58:29 +0900 Subject: Convenience: StateKeyKey, StateKeyKeyL, basicStateEventJson() --- lib/events/event.h | 4 +++- lib/events/roomevent.cpp | 2 +- lib/events/roommemberevent.h | 2 +- lib/events/stateevent.cpp | 2 +- lib/events/stateevent.h | 11 +++++++++++ lib/room.cpp | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/events/event.h b/lib/events/event.h index 5248472c..b3a58806 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -60,14 +60,16 @@ namespace QMatrixClient static const auto ContentKey = QStringLiteral("content"); static const auto EventIdKey = QStringLiteral("event_id"); static const auto UnsignedKey = QStringLiteral("unsigned"); + static const auto StateKeyKey = QStringLiteral("state_key"); static const auto TypeKeyL = "type"_ls; static const auto ContentKeyL = "content"_ls; static const auto EventIdKeyL = "event_id"_ls; static const auto UnsignedKeyL = "unsigned"_ls; static const auto RedactedCauseKeyL = "redacted_because"_ls; static const auto PrevContentKeyL = "prev_content"_ls; + static const auto StateKeyKeyL = "state_key"_ls; - // Minimal correct Matrix event JSON + /// Make a minimal correct Matrix event JSON template inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content) diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 3d03509f..62c2a76d 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -78,7 +78,7 @@ QString RoomEvent::transactionId() const QString RoomEvent::stateKey() const { - return fullJson()["state_key"_ls].toString(); + return fullJson()[StateKeyKeyL].toString(); } void RoomEvent::setTransactionId(const QString& txnId) diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index b8224033..4490fe65 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -76,7 +76,7 @@ namespace QMatrixClient MembershipType membership() const { return content().membership; } QString userId() const - { return fullJson()["state_key"_ls].toString(); } + { return fullJson()[StateKeyKeyL].toString(); } bool isDirect() const { return content().isDirect; } QString displayName() const { return content().displayName; } QUrl avatarUrl() const { return content().avatarUrl; } diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index a84f302b..476e0fd6 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -27,7 +27,7 @@ using namespace QMatrixClient; RoomEvent::factory_t::addMethod( [] (const QJsonObject& json, const QString& matrixType) -> StateEventPtr { - if (!json.contains("state_key"_ls)) + if (!json.contains(StateKeyKeyL)) return nullptr; if (auto e = StateEventBase::factory_t::make(json, matrixType)) diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 3f54f7bf..5dadac7f 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -21,6 +21,17 @@ #include "roomevent.h" namespace QMatrixClient { + + /// Make a minimal correct Matrix state event JSON + template + inline QJsonObject basicStateEventJson(StrT matrixType, + const QJsonObject& content, const QString& stateKey = {}) + { + return { { TypeKey, std::forward(matrixType) }, + { StateKeyKey, stateKey }, + { ContentKey, content } }; + } + class StateEventBase: public RoomEvent { public: diff --git a/lib/room.cpp b/lib/room.cpp index c5367047..8c9f8760 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1920,7 +1920,7 @@ RoomEventPtr makeRedacted(const RoomEvent& target, auto originalJson = target.originalJsonObject(); static const QStringList keepKeys { EventIdKey, TypeKey, QStringLiteral("room_id"), - QStringLiteral("sender"), QStringLiteral("state_key"), + QStringLiteral("sender"), StateKeyKey, QStringLiteral("prev_content"), ContentKey, QStringLiteral("hashes"), QStringLiteral("signatures"), QStringLiteral("depth"), QStringLiteral("prev_events"), -- cgit v1.2.3 From cbb4f219d5c79f81706019c0679222d5ccee4a4c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 19:59:34 +0900 Subject: loadStateEvent() --- lib/events/eventloader.h | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index da663392..d0fa60a2 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -32,7 +32,8 @@ namespace QMatrixClient { } } - /** Create an event with proper type from a JSON object + /*! Create an event with proper type from a JSON object + * * Use this factory template to detect the type from the JSON object * contents (the detected event type should derive from the template * parameter type) and create an event object of that type. @@ -44,7 +45,8 @@ namespace QMatrixClient { fullJson[TypeKeyL].toString()); } - /** Create an event from a type string and content JSON + /*! Create an event from a type string and content JSON + * * Use this factory template to resolve the C++ type from the Matrix * type string in \p matrixType and create an event of that type that has * its content part set to \p content. @@ -57,6 +59,20 @@ namespace QMatrixClient { matrixType); } + /*! Create a state event from a type string, content JSON and state key + * + * Use this factory to resolve the C++ type from the Matrix type string + * in \p matrixType and create a state event of that type with content part + * set to \p content and state key set to \p stateKey (empty by default). + */ + inline StateEventPtr loadStateEvent(const QString& matrixType, + const QJsonObject& content, + const QString& stateKey = {}) + { + return _impl::loadEvent( + basicStateEventJson(matrixType, content, stateKey), matrixType); + } + template struct JsonConverter> { static auto load(const QJsonValue& jv) -- cgit v1.2.3 From 025e5ab7d90ce8cf474567457301de32d5a3e34a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 20:13:38 +0900 Subject: Be stricter on usage of stateKey A few places in the library dealt with state events without any notion of state_key inside events, including StateEvent[Base] and relevant functions in Room. A number of workarounds have been made; e.g., Room::setMemberState() accepted userId as a separate parameter, ignoring the state key inside the RoomMemberEvent already passed to it, and Room::setLocalAliases() had a bug in the initial version where the function still tried to pass aliases in an event with an empty state key. This commit fixes this shortcoming: StateEventBase now gets stateKey as one more parameter, Room::Private::getCurrentState() respects stateKey and returns properly constructed stub events, and Room::setMemberState() gives way to a more generic Room::setState() that works uniformly with whatever state event you pass to it. --- lib/events/roommemberevent.h | 8 +++++- lib/events/simplestateevents.h | 24 ++++++++++++----- lib/events/stateevent.cpp | 6 +++++ lib/events/stateevent.h | 10 +++++-- lib/room.cpp | 60 ++++++++++++++++++++++++++++-------------- lib/room.h | 4 +++ lib/user.cpp | 2 +- 7 files changed, 84 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 4490fe65..39aa280c 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -56,8 +56,14 @@ namespace QMatrixClient explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) { } + [[deprecated("Use RoomMemberEvent(userId, contentArgs) instead")]] RoomMemberEvent(MemberEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) + : StateEvent(typeId(), matrixTypeId(), QString(), c) + { } + template + RoomMemberEvent(const QString& userId, ArgTs&&... contentArgs) + : StateEvent(typeId(), matrixTypeId(), userId, + std::forward(contentArgs)...) { } /// A special constructor to create unknown RoomMemberEvents diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 2c23d9ca..dc6a0868 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -20,8 +20,6 @@ #include "stateevent.h" -#include "converters.h" - namespace QMatrixClient { namespace EventContent @@ -63,7 +61,7 @@ namespace QMatrixClient explicit _Name() : _Name(value_type()) { } \ template \ explicit _Name(T&& value) \ - : StateEvent(typeId(), matrixTypeId(), \ + : StateEvent(typeId(), matrixTypeId(), QString(), \ QStringLiteral(#_ContentKey), \ std::forward(value)) \ { } \ @@ -78,9 +76,6 @@ namespace QMatrixClient DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - QStringList, aliases) - DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", QString, alias) DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) @@ -89,4 +84,21 @@ namespace QMatrixClient DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", QString, algorithm) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) + + class RoomAliasesEvent + : public StateEvent> + { + public: + DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent) + explicit RoomAliasesEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj, QStringLiteral("aliases")) + { } + RoomAliasesEvent(const QString& server, const QStringList& aliases) + : StateEvent(typeId(), matrixTypeId(), server, + QStringLiteral("aliases"), aliases) + { } + QString server() const { return stateKey(); } + QStringList aliases() const { return content().value; } + }; + REGISTER_EVENT_TYPE(RoomAliasesEvent) } // namespace QMatrixClient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index 476e0fd6..6a6e7782 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -36,6 +36,12 @@ using namespace QMatrixClient; return makeEvent(unknownEventTypeId(), json); }); +StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType, + const QString &stateKey, + const QJsonObject &contentJson) + : RoomEvent(type, basicStateEventJson(matrixType, contentJson, stateKey)) +{ } + bool StateEventBase::repeatsState() const { const auto prevContentJson = unsignedJson().value(PrevContentKeyL); diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 5dadac7f..692f2685 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -37,7 +37,12 @@ namespace QMatrixClient { public: using factory_t = EventFactory; - using RoomEvent::RoomEvent; + StateEventBase(Type type, const QJsonObject& json) + : RoomEvent(type, json) + { } + StateEventBase(Type type, event_mtype_t matrixType, + const QString& stateKey = {}, + const QJsonObject& contentJson = {}); ~StateEventBase() override = default; bool isStateEvent() const override { return true; } @@ -94,8 +99,9 @@ namespace QMatrixClient { } template explicit StateEvent(Type type, event_mtype_t matrixType, + const QString& stateKey = {}, ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType) + : StateEventBase(type, matrixType, stateKey) , _content(std::forward(contentParams)...) { editJson().insert(ContentKey, _content.toJson()); diff --git a/lib/room.cpp b/lib/room.cpp index 8c9f8760..44cd0d06 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -93,6 +93,8 @@ class Room::Private /// The state of the room at timeline position before-0 /// \sa timelineBase std::unordered_map baseState; + /// State event stubs - events without content, just type and state key + static decltype(baseState) stubbedState; /// The state of the room at timeline position after-maxTimelineIndex() /// \sa Room::syncEdge QHash currentState; @@ -193,9 +195,22 @@ class Room::Private template const EventT* getCurrentState(const QString& stateKey = {}) const { - static const EventT empty; - const auto* evt = - currentState.value({EventT::matrixTypeId(), stateKey}, &empty); + const StateEventKey evtKey { EventT::matrixTypeId(), stateKey }; + const auto* evt = currentState.value(evtKey, nullptr); + if (!evt) { + if (stubbedState.find(evtKey) == stubbedState.end()) { + // In the absence of a real event, make a stub as-if an event + // with empty content has been received. Event classes should be + // prepared for empty/invalid/malicious content anyway. + stubbedState.emplace(evtKey, + loadStateEvent(EventT::matrixTypeId(), + {}, stateKey)); + qCDebug(MAIN) << "A new stub event created for key {" + << evtKey.first << evtKey.second << "}"; + } + evt = stubbedState[evtKey].get(); + Q_ASSERT(evt); + } Q_ASSERT(evt->type() == EventT::typeId() && evt->matrixType() == EventT::matrixTypeId()); return static_cast(evt); @@ -272,28 +287,26 @@ class Room::Private QString doSendEvent(const RoomEvent* pEvent); void onEventSendingFailure(const QString& txnId, BaseJob* call = nullptr); - template - SetRoomStateWithKeyJob* requestSetState(const QString& stateKey, - const EvT& event) + SetRoomStateWithKeyJob* requestSetState(const StateEventBase& event) { if (q->successorId().isEmpty()) { // TODO: Queue up state events sending (see #133). return connection->callApi( - id, EvT::matrixTypeId(), stateKey, event.contentJson()); + id, event.matrixType(), + event.stateKey(), event.contentJson()); } qCWarning(MAIN) << q << "has been upgraded, state won't be set"; return nullptr; } - template - auto requestSetState(const EvT& event) + template + auto requestSetState(ArgTs&&... args) { - return connection->callApi( - id, EvT::matrixTypeId(), event.contentJson()); + return requestSetState(EvT(std::forward(args)...)); } - /** + /** * @brief Apply redaction to the timeline * * Tries to find an event in the timeline and redact it; deletes the @@ -317,6 +330,8 @@ class Room::Private } }; +decltype(Room::Private::baseState) Room::Private::stubbedState { }; + Room::Room(Connection* connection, QString id, JoinState initialJoinState) : QObject(connection), d(new Private(connection, id, initialJoinState)) { @@ -1625,28 +1640,32 @@ QString Room::postEvent(RoomEvent* event) QString Room::postJson(const QString& matrixType, const QJsonObject& eventContent) { - return d->sendEvent(loadEvent(basicEventJson(matrixType, eventContent))); + return d->sendEvent(loadEvent(matrixType, eventContent)); +} + +SetRoomStateWithKeyJob* Room::setState(const StateEventBase& evt) const { + return d->requestSetState(evt); } void Room::setName(const QString& newName) { - d->requestSetState(RoomNameEvent(newName)); + d->requestSetState(newName); } void Room::setCanonicalAlias(const QString& newAlias) { - d->requestSetState(RoomCanonicalAliasEvent(newAlias)); + d->requestSetState(newAlias); } void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(connection()->homeserver().authority(), - RoomAliasesEvent(aliases)); + d->requestSetState( + connection()->homeserver().authority(), aliases); } void Room::setTopic(const QString& newTopic) { - d->requestSetState(RoomTopicEvent(newTopic)); + d->requestSetState(newTopic); } bool isEchoEvent(const RoomEventPtr& le, const PendingEventItem& re) @@ -1756,9 +1775,10 @@ LeaveRoomJob* Room::leaveRoom() return connection()->leaveRoom(this); } -SetRoomStateWithKeyJob*Room::setMemberState(const QString& memberId, const RoomMemberEvent& event) const +SetRoomStateWithKeyJob* Room::setMemberState( + const QString& memberId, const RoomMemberEvent& event) const { - return d->requestSetState(memberId, event); + return d->requestSetState(memberId, event.content()); } void Room::kickMember(const QString& memberId, const QString& reason) diff --git a/lib/room.h b/lib/room.h index 7c85e4ed..3abf262d 100644 --- a/lib/room.h +++ b/lib/room.h @@ -440,6 +440,9 @@ namespace QMatrixClient const QJsonObject& eventContent); QString retryMessage(const QString& txnId); void discardMessage(const QString& txnId); + + /// Send a request to update the room state with the given event + SetRoomStateWithKeyJob* setState(const StateEventBase& evt) const; void setName(const QString& newName); void setCanonicalAlias(const QString& newAlias); /// Set room aliases on the user's current server @@ -453,6 +456,7 @@ namespace QMatrixClient void inviteToRoom(const QString& memberId); LeaveRoomJob* leaveRoom(); + /// \deprecated - use setState() instead") SetRoomStateWithKeyJob* setMemberState( const QString& memberId, const RoomMemberEvent& event) const; void kickMember(const QString& memberId, const QString& reason = {}); diff --git a/lib/user.cpp b/lib/user.cpp index 7b695618..8bdcbe97 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -292,7 +292,7 @@ void User::rename(const QString& newName, const Room* r) const auto actualNewName = sanitized(newName); MemberEventContent evtC; evtC.displayName = actualNewName; - connect(r->setMemberState(id(), RoomMemberEvent(move(evtC))), + connect(r->setState(RoomMemberEvent(id(), move(evtC))), &BaseJob::success, this, [=] { updateName(actualNewName, r); }); } -- cgit v1.2.3 From f58819e4e930ee66e790eccaedf551f807956d72 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 21:01:23 +0900 Subject: Fix building with Clang --- lib/events/stateevent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 692f2685..3b56a265 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -99,7 +99,7 @@ namespace QMatrixClient { } template explicit StateEvent(Type type, event_mtype_t matrixType, - const QString& stateKey = {}, + const QString& stateKey, ContentParamTs&&... contentParams) : StateEventBase(type, matrixType, stateKey) , _content(std::forward(contentParams)...) -- cgit v1.2.3 From 4e521ae29a55deaab2ca9d62cddd3791015c6cfb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jul 2019 21:58:41 +0900 Subject: EncryptionEvent: Adjust upon merge from master --- lib/events/encryptionevent.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index 6a4a1c67..b9e108f0 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -56,8 +56,10 @@ namespace QMatrixClient explicit EncryptionEvent(const QJsonObject& obj = {}) // TODO: apropriate default value : StateEvent(typeId(), obj) { } - EncryptionEvent(EncryptionEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) + template + EncryptionEvent(ArgTs&&... contentArgs) + : StateEvent(typeId(), matrixTypeId(), QString(), + std::forward(contentArgs)...) { } EncryptionType encryption() const { return content().encryption; } -- cgit v1.2.3 From 40002b09f741e5ce1ed618141b2a218438d97084 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jul 2019 16:47:09 +0900 Subject: Logging categories: libqmatrixclient.* -> quotient.* --- lib/logging.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/logging.cpp b/lib/logging.cpp index 7476781f..a0c7f7bb 100644 --- a/lib/logging.cpp +++ b/lib/logging.cpp @@ -25,9 +25,9 @@ #endif // Use LOGGING_CATEGORY instead of Q_LOGGING_CATEGORY in the rest of the code -LOGGING_CATEGORY(MAIN, "libqmatrixclient.main") -LOGGING_CATEGORY(PROFILER, "libqmatrixclient.profiler") -LOGGING_CATEGORY(EVENTS, "libqmatrixclient.events") -LOGGING_CATEGORY(EPHEMERAL, "libqmatrixclient.events.ephemeral") -LOGGING_CATEGORY(JOBS, "libqmatrixclient.jobs") -LOGGING_CATEGORY(SYNCJOB, "libqmatrixclient.jobs.sync") +LOGGING_CATEGORY(MAIN, "quotient.main") +LOGGING_CATEGORY(PROFILER, "quotient.profiler") +LOGGING_CATEGORY(EVENTS, "quotient.events") +LOGGING_CATEGORY(EPHEMERAL, "quotient.events.ephemeral") +LOGGING_CATEGORY(JOBS, "quotient.jobs") +LOGGING_CATEGORY(SYNCJOB, "quotient.jobs.sync") -- cgit v1.2.3 From 0bfb1c1c69c02c7936cb018ead496616322a1cf7 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sun, 7 Jul 2019 21:53:22 +0300 Subject: E2EE: add new account generation logic --- lib/connection.cpp | 3 +++ lib/encryptionmanager.cpp | 37 ++++++++++++++++++++++--------------- lib/encryptionmanager.h | 3 ++- 3 files changed, 27 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 20fb367c..ff066def 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -254,6 +254,9 @@ void Connection::doConnectToServer(const QString& user, const QString& password, AccountSettings accountSettings(loginJob->userId()); d->encryptionManager.reset(new EncryptionManager(accountSettings.encryptionAccountPickle())); + if (accountSettings.encryptionAccountPickle().isEmpty()) { + accountSettings.setEncryptionAccountPickle(d->encryptionManager->olmAccountPickle()); + } d->encryptionManager->uploadIdentityKeys(this); d->encryptionManager->uploadOneTimeKeys(this); diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 1e1fc669..a62775d9 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -24,13 +24,17 @@ class EncryptionManager::Private { public: explicit Private(const QByteArray& encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold) - : olmAccount(new Account(encryptionAccountPickle)), // TODO: passphrase even with qtkeychain? - signedKeysProportion(move(signedKeysProportion)), - oneTimeKeyThreshold(move(oneTimeKeyThreshold)), - targetKeysNumber(olmAccount->maxOneTimeKeys()) // 2 // see note below + : signedKeysProportion(move(signedKeysProportion)), + oneTimeKeyThreshold(move(oneTimeKeyThreshold)) { Q_ASSERT((0 <= signedKeysProportion) && (signedKeysProportion <= 1)); Q_ASSERT((0 <= oneTimeKeyThreshold) && (oneTimeKeyThreshold <= 1)); + if (encryptionAccountPickle.isEmpty()) + { + olmAccount.reset(new Account()); + } else { + olmAccount.reset(new Account(encryptionAccountPickle)); // TODO: passphrase even with qtkeychain? + } /* * Note about targetKeysNumber: * @@ -42,17 +46,19 @@ class EncryptionManager::Private * used instantly, and we want them to stay in libolm, until the limit is reached * and it starts discarding keys, starting by the oldest. */ + targetKeysNumber = olmAccount->maxOneTimeKeys(); // 2 // see note below + targetOneTimeKeyCounts = + { + {SignedCurve25519Name, qRound(signedKeysProportion * targetKeysNumber)}, + {Curve25519Name, qRound((1-signedKeysProportion) * targetKeysNumber)} + }; } - ~Private() - { - delete olmAccount; - } + ~Private() = default; UploadKeysJob* uploadIdentityKeysJob = nullptr; UploadKeysJob* uploadOneTimeKeysJob = nullptr; - Account* olmAccount; - const QByteArray encryptionAccountPickle; + QScopedPointer olmAccount; float signedKeysProportion; float oneTimeKeyThreshold; @@ -68,11 +74,7 @@ class EncryptionManager::Private updateKeysToUpload(); } QHash oneTimeKeysToUploadCounts; - QHash targetOneTimeKeyCounts - { - {SignedCurve25519Name, qRound(signedKeysProportion * targetKeysNumber)}, - {Curve25519Name, qRound((1-signedKeysProportion) * targetKeysNumber)} - }; + QHash targetOneTimeKeyCounts; }; EncryptionManager::EncryptionManager(const QByteArray &encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold, @@ -183,6 +185,11 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpda .arg(signedKeysToUploadCount).arg(unsignedKeysToUploadCount); } +QByteArray EncryptionManager::olmAccountPickle() +{ + return d->olmAccount->pickle(); // TODO: passphrase even with qtkeychain? +} + void EncryptionManager::Private::updateKeysToUpload() { for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index 0bd05432..40fe7383 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -15,12 +15,13 @@ namespace QMatrixClient public: // TODO: store constats separately? // TODO: 0.5 oneTimeKeyThreshold instead of 0.1? - explicit EncryptionManager(const QByteArray& encryptionAccountPickle, float signedKeysProportion = 1, float oneTimeKeyThreshold = float(0.1), + explicit EncryptionManager(const QByteArray& encryptionAccountPickle = QByteArray(), float signedKeysProportion = 1, float oneTimeKeyThreshold = float(0.1), QObject* parent = nullptr); ~EncryptionManager(); void uploadIdentityKeys(Connection* connection); void uploadOneTimeKeys(Connection* connection, bool forceUpdate = false); + QByteArray olmAccountPickle(); private: class Private; -- cgit v1.2.3 From 8c49ac34c94b53f74c23f67ce3a2720673ab2fac Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Mon, 8 Jul 2019 12:45:27 +0300 Subject: E2EE: fix uploadIdentityKeys signatures generation --- lib/encryptionmanager.cpp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index a62775d9..c2e244fc 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -112,19 +112,29 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) d->olmAccount->ed25519IdentityKey() } }, - /* - * Signatures for the device key object. - * A map from user ID, to a map from : to the signature. - * The signature is calculated using the process called Signing JSON. - */ + /* signatures should be provided after the unsigned deviceKeys generation */ + {} + }; + + QJsonObject deviceKeysJsonObject = toJson(deviceKeys); + /* additionally removing signatures key, + * since we could not initialize deviceKeys + * without an empty signatures value: + */ + deviceKeysJsonObject.remove(QStringLiteral("signatures")); + /* + * Signatures for the device key object. + * A map from user ID, to a map from : to the signature. + * The signature is calculated using the process called Signing JSON. + */ + deviceKeys.signatures = + { { + connection->userId(), { - connection->userId(), { - { - ed25519Name + QStringLiteral(":") + connection->deviceId(), - d->olmAccount->sign(toJson(deviceKeys)) - } + ed25519Name + QStringLiteral(":") + connection->deviceId(), + d->olmAccount->sign(deviceKeysJsonObject) } } } -- cgit v1.2.3 From 36f63f398b8a36e0b276e78eceaf3c4123be6b13 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 8 Jul 2019 19:36:27 +0900 Subject: RoomEvent::setRoomId()/setSender() --- lib/events/roomevent.cpp | 10 ++++++++++ lib/events/roomevent.h | 3 +++ 2 files changed, 13 insertions(+) (limited to 'lib') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 62c2a76d..f1e563ff 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -81,6 +81,16 @@ QString RoomEvent::stateKey() const return fullJson()[StateKeyKeyL].toString(); } +void RoomEvent::setRoomId(const QString& roomId) +{ + editJson().insert(QStringLiteral("room_id"), roomId); +} + +void RoomEvent::setSender(const QString& senderId) +{ + editJson().insert(QStringLiteral("sender"), senderId); +} + void RoomEvent::setTransactionId(const QString& txnId) { auto unsignedData = fullJson()[UnsignedKeyL].toObject(); diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index ce96174e..e26a7135 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -60,6 +60,9 @@ namespace QMatrixClient QString transactionId() const; QString stateKey() const; + void setRoomId(const QString& roomId); + void setSender(const QString& senderId); + /** * Sets the transaction id for locally created events. This should be * done before the event is exposed to any code using the respective -- cgit v1.2.3 From f0c1269bf592f5ba1ccde3b667e6737fe75a335d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 8 Jul 2019 19:37:23 +0900 Subject: Room: Set roomId and sender on pending events Also: refactoring to streamline state events submission. --- lib/room.cpp | 42 +++++++++++++++++++++++------------------- lib/room.h | 26 ++++++++++++++++---------- lib/user.cpp | 2 +- 3 files changed, 40 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index dea21082..cb368d9e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -290,15 +290,15 @@ class Room::Private SetRoomStateWithKeyJob* requestSetState(const StateEventBase& event) { - if (q->successorId().isEmpty()) - { - // TODO: Queue up state events sending (see #133). - return connection->callApi( - id, event.matrixType(), - event.stateKey(), event.contentJson()); - } - qCWarning(MAIN) << q << "has been upgraded, state won't be set"; - return nullptr; +// if (event.roomId().isEmpty()) +// event.setRoomId(id); +// if (event.senderId().isEmpty()) +// event.setSender(connection->userId()); + // TODO: Queue up state events sending (see #133). + // TODO: Maybe addAsPending() as well, despite having no txnId + return connection->callApi( + id, event.matrixType(), + event.stateKey(), event.contentJson()); } template @@ -1416,6 +1416,10 @@ RoomEvent* Room::Private::addAsPending(RoomEventPtr&& event) { if (event->transactionId().isEmpty()) event->setTransactionId(connection->generateTxnId()); + if (event->roomId().isEmpty()) + event->setRoomId(id); + if (event->senderId().isEmpty()) + event->setSender(connection->userId()); auto* pEvent = rawPtr(event); emit q->pendingEventAboutToAdd(pEvent); unsyncedEvents.emplace_back(move(event)); @@ -1425,6 +1429,11 @@ RoomEvent* Room::Private::addAsPending(RoomEventPtr&& event) QString Room::Private::sendEvent(RoomEventPtr&& event) { + if (q->usesEncryption()) + { + qCCritical(MAIN) << "Room" << q->objectName() + << "enforces encryption; sending encrypted messages is not supported yet"; + } if (q->successorId().isEmpty()) return doSendEvent(addAsPending(std::move(event))); @@ -1630,11 +1639,6 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, QString Room::postEvent(RoomEvent* event) { - if (usesEncryption()) - { - qCCritical(MAIN) << "Room" << displayName() - << "enforces encryption; sending encrypted messages is not supported yet"; - } return d->sendEvent(RoomEventPtr(event)); } @@ -1715,33 +1719,33 @@ void Room::inviteCall(const QString& callId, const int lifetime, const QString& sdp) { Q_ASSERT(supportsCalls()); - postEvent(new CallInviteEvent(callId, lifetime, sdp)); + d->sendEvent(callId, lifetime, sdp); } void Room::sendCallCandidates(const QString& callId, const QJsonArray& candidates) { Q_ASSERT(supportsCalls()); - postEvent(new CallCandidatesEvent(callId, candidates)); + d->sendEvent(callId, candidates); } void Room::answerCall(const QString& callId, const int lifetime, const QString& sdp) { Q_ASSERT(supportsCalls()); - postEvent(new CallAnswerEvent(callId, lifetime, sdp)); + d->sendEvent(callId, lifetime, sdp); } void Room::answerCall(const QString& callId, const QString& sdp) { Q_ASSERT(supportsCalls()); - postEvent(new CallAnswerEvent(callId, sdp)); + d->sendEvent(callId, sdp); } void Room::hangupCall(const QString& callId) { Q_ASSERT(supportsCalls()); - postEvent(new CallHangupEvent(callId)); + d->sendEvent(callId); } void Room::getPreviousContent(int limit) diff --git a/lib/room.h b/lib/room.h index 3abf262d..c79ca1e0 100644 --- a/lib/room.h +++ b/lib/room.h @@ -18,6 +18,7 @@ #pragma once +#include "connection.h" #include "csapi/message_pagination.h" #include "events/roommessageevent.h" #include "events/accountdataevents.h" @@ -36,7 +37,6 @@ namespace QMatrixClient class Avatar; class SyncRoomData; class RoomMemberEvent; - class Connection; class User; class MemberSorter; class LeaveRoomJob; @@ -406,17 +406,14 @@ namespace QMatrixClient MemberSorter memberSorter() const; - Q_INVOKABLE void inviteCall(const QString& callId, - const int lifetime, const QString& sdp); - Q_INVOKABLE void sendCallCandidates(const QString& callId, - const QJsonArray& candidates); - Q_INVOKABLE void answerCall(const QString& callId, const int lifetime, - const QString& sdp); - Q_INVOKABLE void answerCall(const QString& callId, - const QString& sdp); - Q_INVOKABLE void hangupCall(const QString& callId); Q_INVOKABLE bool supportsCalls() const; + template + auto setState(ArgTs&&... args) const + { + return setState(EvT(std::forward(args)...)); + } + public slots: /** Check whether the room should be upgraded */ void checkVersion(); @@ -481,6 +478,15 @@ namespace QMatrixClient /// Switch the room's version (aka upgrade) void switchVersion(QString newVersion); + void inviteCall(const QString& callId, + const int lifetime, const QString& sdp); + void sendCallCandidates(const QString& callId, + const QJsonArray& candidates); + void answerCall(const QString& callId, const int lifetime, + const QString& sdp); + void answerCall(const QString& callId, const QString& sdp); + void hangupCall(const QString& callId); + signals: /// Initial set of state events has been loaded /** diff --git a/lib/user.cpp b/lib/user.cpp index 8bdcbe97..7ca97b1a 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -292,7 +292,7 @@ void User::rename(const QString& newName, const Room* r) const auto actualNewName = sanitized(newName); MemberEventContent evtC; evtC.displayName = actualNewName; - connect(r->setState(RoomMemberEvent(id(), move(evtC))), + connect(r->setState(id(), move(evtC)), &BaseJob::success, this, [=] { updateName(actualNewName, r); }); } -- cgit v1.2.3 From 36ba515437198659205fceabcdcea0db2d3a6dba Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Mon, 8 Jul 2019 16:40:57 +0300 Subject: E2EE: fix EncryptionManager algorithm names --- lib/encryptionmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index c2e244fc..50db9889 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -16,9 +16,9 @@ using std::move; static const auto ed25519Name = QStringLiteral("ed25519"); static const auto Curve25519Name = QStringLiteral("curve25519"); static const auto SignedCurve25519Name = QStringLiteral("signed_curve25519"); -static const auto OlmCurve25519AesSha256AlgoName = QStringLiteral("m.olm.curve25519-aes-sha256"); -static const auto MegolmV1AesShaAlgoName = QStringLiteral("m.megolm.v1.aes-sha"); -static const QStringList SupportedAlgorithms = { OlmCurve25519AesSha256AlgoName, MegolmV1AesShaAlgoName }; +static const auto OlmV1Curve25519AesSha2AlgoName = QStringLiteral("m.olm.v1.curve25519-aes-sha2"); +static const auto MegolmV1AesSha2AlgoName = QStringLiteral("m.megolm.v1.aes-sha2"); +static const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoName, MegolmV1AesSha2AlgoName }; class EncryptionManager::Private { -- cgit v1.2.3 From 651478c1681ba6f93e22c20328a048dbbc263ffe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 8 Jul 2019 21:31:40 +0900 Subject: Move serverPart() to the public API Also: Connection::resolveServer() now only accepts MXIDs, not domains. --- lib/connection.cpp | 28 +++------------------------- lib/connection.h | 4 ++-- lib/util.cpp | 14 ++++++++++++++ lib/util.h | 8 +++++++- 4 files changed, 26 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index ff066def..6d06c603 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -162,33 +162,11 @@ Connection::~Connection() stopSync(); } -static const auto ServerPartRegEx = QStringLiteral( - "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address - "(?::(\\d{1,5}))?" // Optional port -); - -QString serverPart(const QString& mxId) +void Connection::resolveServer(const QString& mxid) { - static QString re = "^[@!#$+].+?:(" // Localpart and colon - % ServerPartRegEx % ")$"; - static QRegularExpression parser(re, - QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits - return parser.match(mxId).captured(1); -} - -void Connection::resolveServer(const QString& mxidOrDomain) -{ - // mxIdOrDomain may be something as complex as - // @username:[IPv6:address]:port, or as simple as a plain serverpart. - static QRegularExpression parser( - "^(@.+?:)?" // Optional username (allow everything for compatibility) - % ServerPartRegEx % '$', - QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits - auto match = parser.match(mxidOrDomain); - - auto maybeBaseUrl = QUrl::fromUserInput(match.captured(2)); + auto maybeBaseUrl = QUrl::fromUserInput(serverPart(mxid)); maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http" - if (!match.hasMatch() || !maybeBaseUrl.isValid()) + if (maybeBaseUrl.isEmpty() || !maybeBaseUrl.isValid()) { emit resolveError( tr("%1 is not a valid homeserver address") diff --git a/lib/connection.h b/lib/connection.h index 4ab8d5ba..11499a6e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -408,8 +408,8 @@ namespace QMatrixClient /** Set the homeserver base URL */ void setHomeserver(const QUrl& baseUrl); - /** Determine and set the homeserver from domain or MXID */ - void resolveServer(const QString& mxidOrDomain); + /** Determine and set the homeserver from MXID */ + void resolveServer(const QString& mxid); void connectToServer(const QString& user, const QString& password, const QString& initialDeviceName, diff --git a/lib/util.cpp b/lib/util.cpp index 88cba959..b9639843 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -112,6 +112,20 @@ qreal QMatrixClient::stringToHueF(const QString &string) return hueF; } +static const auto ServerPartRegEx = QStringLiteral( + "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address + "(?::(\\d{1,5}))?" // Optional port +); + +QString QMatrixClient::serverPart(const QString& mxId) +{ + static QString re = "^[@!#$+].+?:(" // Localpart and colon + % ServerPartRegEx % ")$"; + static QRegularExpression parser(re, + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits + return parser.match(mxId).captured(1); +} + // Tests for function_traits<> #ifdef Q_CC_CLANG diff --git a/lib/util.h b/lib/util.h index eda817a1..1106e774 100644 --- a/lib/util.h +++ b/lib/util.h @@ -300,27 +300,33 @@ namespace QMatrixClient void linkifyUrls(QString& htmlEscapedText); /** Sanitize the text before showing in HTML + * * This does toHtmlEscaped() and removes Unicode BiDi marks. */ QString sanitized(const QString& plainText); /** Pretty-print plain text into HTML + * * This includes HTML escaping of <,>,",& and calling linkifyUrls() */ QString prettyPrint(const QString& plainText); /** Return a path to cache directory after making sure that it exists + * * The returned path has a trailing slash, clients don't need to append it. * \param dir path to cache directory relative to the standard cache path */ QString cacheLocation(const QString& dirName); /** Hue color component of based of the hash of the string. + * * The implementation is based on XEP-0392: * https://xmpp.org/extensions/xep-0392.html * Naming and range are the same as QColor's hueF method: * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision */ qreal stringToHueF(const QString& string); -} // namespace QMatrixClient + /** Extract the serverpart from MXID */ + QString serverPart(const QString& mxId); +} // namespace QMatrixClient -- cgit v1.2.3 From f40d511917e7ac1e44106b24bfa3cdf93bd6f664 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Tue, 9 Jul 2019 20:46:49 +0800 Subject: Add more checks according to the spec --- lib/connection.cpp | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 98555104..511a0411 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -33,6 +33,7 @@ #include "csapi/to_device.h" #include "csapi/room_send.h" #include "csapi/wellknown.h" +#include "csapi/versions.h" #include "jobs/syncjob.h" #include "jobs/mediathumbnailjob.h" #include "jobs/downloadfilejob.h" @@ -199,18 +200,40 @@ void Connection::resolveServer(const QString& mxidOrDomain) auto domain = maybeBaseUrl.host(); qCDebug(MAIN) << "Finding the server" << domain; - auto job = callApi(); - connect(job, &BaseJob::finished, [this, job, maybeBaseUrl] { - QUrl baseUrl(job->data().homeserver.baseUrl); - if (!baseUrl.isValid() || job->status() != BaseJob::Success) { - qCDebug(MAIN) << maybeBaseUrl.host() << "doesn't have valid .well-known file" << "- using the hostname as is"; + auto getWellKnownJob = callApi(); + connect(getWellKnownJob, &BaseJob::finished, [this, getWellKnownJob, maybeBaseUrl] { + if (getWellKnownJob->status() == BaseJob::NotFoundError) { + qCDebug(MAIN) << "No .well-known file, IGNORE"; + } else if (getWellKnownJob->status() != BaseJob::Success) { + qCDebug(MAIN) << "Fetching .well-known file failed, FAIL_PROMPT"; + emit resolveError(tr("Fetching .well-known file failed")); + return; + } else if (getWellKnownJob->data().homeserver.baseUrl.isEmpty()) { + qCDebug(MAIN) << "base_url not provided, FAIL_PROMPT"; + emit resolveError(tr("base_url not provided")); + return; + } else if (!QUrl(getWellKnownJob->data().homeserver.baseUrl).isValid()) { + qCDebug(MAIN) << "base_url invalid, FAIL_ERROR"; + emit resolveError(tr("base_url invalid")); + return; } else { + QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl); + qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.authority(); setHomeserver(baseUrl); } - emit resolved(); - job->deleteLater(); + auto getVersionsJob = callApi(); + + connect(getVersionsJob, &BaseJob::finished, [this, getVersionsJob] { + if (getVersionsJob->status() == BaseJob::Success) { + qCDebug(MAIN) << "homeserver url is valid"; + emit resolved(); + } else { + qCDebug(MAIN) << "homeserver url invalid"; + emit resolveError(tr("homeserver url invalid")); + } + }); }); } -- cgit v1.2.3 From 74caea2669b8f76ca76507bc40321fdcd23dc522 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jul 2019 18:02:27 +0900 Subject: Minor polish --- lib/connection.cpp | 2 +- lib/jobs/basejob.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 79411dce..1bd2e32e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -200,7 +200,7 @@ void Connection::resolveServer(const QString& mxid) } else { QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl); - qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.authority(); + qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() << "is" << baseUrl.toString(); setHomeserver(baseUrl); } diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 6ad8d971..b3cfc527 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -300,7 +300,7 @@ void BaseJob::gotReply() QJsonDocument::fromJson(d->rawResponse).object())); } - if (error() != TooManyRequestsError) + if (status().code != TooManyRequestsError) finishJob(); else { stop(); -- cgit v1.2.3 From f1546e894b2a7550bce3e8d84067968bf5cf7087 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sun, 14 Jul 2019 02:33:39 +0300 Subject: E2EE: provide a shared header with encryption standard key names --- lib/e2ee.h | 21 +++++++++++++++++++++ lib/encryptionmanager.cpp | 26 ++++++++++---------------- lib/events/encryptionevent.cpp | 15 ++++++++------- 3 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 lib/e2ee.h (limited to 'lib') diff --git a/lib/e2ee.h b/lib/e2ee.h new file mode 100644 index 00000000..f663ddc3 --- /dev/null +++ b/lib/e2ee.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +namespace QMatrixClient +{ + static const auto AlgorithmKeyL = "algorithm"_ls; + static const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; + static const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; + + static const auto AlgorithmKey = QStringLiteral("algorithm"); + static const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); + static const auto RotationPeriodMsgsKey = QStringLiteral("rotation_period_msgs"); + + static const auto Ed25519Key = QStringLiteral("ed25519"); + static const auto Curve25519Key = QStringLiteral("curve25519"); + static const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); + static const auto OlmV1Curve25519AesSha2AlgoKey = QStringLiteral("m.olm.v1.curve25519-aes-sha2"); + static const auto MegolmV1AesSha2AlgoKey = QStringLiteral("m.megolm.v1.aes-sha2"); + static const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, MegolmV1AesSha2AlgoKey }; +} // namespace QMatrixClient diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 50db9889..08b68911 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -8,18 +8,12 @@ #include "csapi/keys.h" #include "connection.h" +#include "e2ee.h" using namespace QMatrixClient; using namespace QtOlm; using std::move; -static const auto ed25519Name = QStringLiteral("ed25519"); -static const auto Curve25519Name = QStringLiteral("curve25519"); -static const auto SignedCurve25519Name = QStringLiteral("signed_curve25519"); -static const auto OlmV1Curve25519AesSha2AlgoName = QStringLiteral("m.olm.v1.curve25519-aes-sha2"); -static const auto MegolmV1AesSha2AlgoName = QStringLiteral("m.megolm.v1.aes-sha2"); -static const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoName, MegolmV1AesSha2AlgoName }; - class EncryptionManager::Private { public: @@ -49,8 +43,8 @@ class EncryptionManager::Private targetKeysNumber = olmAccount->maxOneTimeKeys(); // 2 // see note below targetOneTimeKeyCounts = { - {SignedCurve25519Name, qRound(signedKeysProportion * targetKeysNumber)}, - {Curve25519Name, qRound((1-signedKeysProportion) * targetKeysNumber)} + {SignedCurve25519Key, qRound(signedKeysProportion * targetKeysNumber)}, + {Curve25519Key, qRound((1-signedKeysProportion) * targetKeysNumber)} }; } ~Private() = default; @@ -104,11 +98,11 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) */ { { - Curve25519Name + QStringLiteral(":") + connection->deviceId(), + Curve25519Key + QStringLiteral(":") + connection->deviceId(), d->olmAccount->curve25519IdentityKey() }, { - ed25519Name + QStringLiteral(":") + connection->deviceId(), + Ed25519Key + QStringLiteral(":") + connection->deviceId(), d->olmAccount->ed25519IdentityKey() } }, @@ -133,7 +127,7 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) connection->userId(), { { - ed25519Name + QStringLiteral(":") + connection->deviceId(), + Ed25519Key + QStringLiteral(":") + connection->deviceId(), d->olmAccount->sign(deviceKeysJsonObject) } } @@ -158,8 +152,8 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpda } - int signedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(SignedCurve25519Name, 0); - int unsignedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(Curve25519Name, 0); + int signedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(SignedCurve25519Key, 0); + int unsignedKeysToUploadCount = d->oneTimeKeysToUploadCounts.value(Curve25519Key, 0); d->olmAccount->generateOneTimeKeys(signedKeysToUploadCount + unsignedKeysToUploadCount); @@ -179,11 +173,11 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpda {QStringLiteral("key"), it.value().toString()} }; key = d->olmAccount->sign(message); - keyType = SignedCurve25519Name; + keyType = SignedCurve25519Key; } else { key = it.value(); - keyType = Curve25519Name; + keyType = Curve25519Key; } ++oneTimeKeysCounter; oneTimeKeys.insert(QString("%1:%2").arg(keyType).arg(keyId), key); diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index b8e2b575..ee6c92b1 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -7,11 +7,12 @@ #include "converters.h" #include "logging.h" +#include "e2ee.h" #include static const std::array encryptionStrings = { { - QStringLiteral("m.megolm.v1.aes-sha2") + QMatrixClient::MegolmV1AesSha2AlgoKey } }; namespace QMatrixClient { @@ -36,9 +37,9 @@ using namespace QMatrixClient; EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) : encryption(fromJson(json["algorithm"_ls])) - , algorithm(sanitized(json["algorithm"_ls].toString())) - , rotationPeriodMs(json["rotation_period_ms"_ls].toInt(604800000)) - , rotationPeriodMsgs(json["rotation_period_msgs"_ls].toInt(100)) + , algorithm(sanitized(json[AlgorithmKeyL].toString())) + , rotationPeriodMs(json[RotationPeriodMsKeyL].toInt(604800000)) + , rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100)) { } void EncryptionEventContent::fillJson(QJsonObject* o) const @@ -47,7 +48,7 @@ void EncryptionEventContent::fillJson(QJsonObject* o) const Q_ASSERT_X(encryption != EncryptionType::Undefined, __FUNCTION__, "The key 'algorithm' must be explicit in EncryptionEventContent"); if (encryption != EncryptionType::Undefined) - o->insert(QStringLiteral("algorithm"), algorithm); - o->insert(QStringLiteral("rotation_period_ms"), rotationPeriodMs); - o->insert(QStringLiteral("rotation_period_msgs"), rotationPeriodMsgs); + o->insert(AlgorithmKey, algorithm); + o->insert(RotationPeriodMsKey, rotationPeriodMs); + o->insert(RotationPeriodMsgsKey, rotationPeriodMsgs); } -- cgit v1.2.3 From 2737dc00334ad3a56c1b311435dbe84453ee389e Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sun, 14 Jul 2019 03:54:19 +0300 Subject: E2EE: introduce EncryptedEvent --- lib/connection.cpp | 5 +++ lib/connection.h | 5 +++ lib/e2ee.h | 5 +++ lib/encryptionmanager.cpp | 5 +++ lib/encryptionmanager.h | 6 +++ lib/events/encryptedevent.cpp | 29 ++++++++++++++ lib/events/encryptedevent.h | 66 +++++++++++++++++++++++++++++++ lib/events/event.h | 1 + lib/events/roommessageevent.cpp | 7 ++-- lib/room.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ lib/room.h | 6 +++ 11 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 lib/events/encryptedevent.cpp create mode 100644 lib/events/encryptedevent.h (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 1bd2e32e..b9ab5147 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -979,6 +979,11 @@ QByteArray Connection::accessToken() const return d->data->accessToken(); } +QtOlm::Account* Connection::olmAccount() const +{ + return d->encryptionManager->account(); +} + SyncJob* Connection::syncJob() const { return d->syncJob; diff --git a/lib/connection.h b/lib/connection.h index 11499a6e..199803d7 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -30,6 +30,10 @@ #include +namespace QtOlm { + class Account; +} + namespace QMatrixClient { class Room; @@ -264,6 +268,7 @@ namespace QMatrixClient QString userId() const; QString deviceId() const; QByteArray accessToken() const; + QtOlm::Account* olmAccount() const; Q_INVOKABLE SyncJob* syncJob() const; Q_INVOKABLE int millisToReconnect() const; diff --git a/lib/e2ee.h b/lib/e2ee.h index f663ddc3..4a42809d 100644 --- a/lib/e2ee.h +++ b/lib/e2ee.h @@ -4,6 +4,11 @@ namespace QMatrixClient { + static const auto CiphertextKeyL = "ciphertext"_ls; + static const auto SenderKeyKeyL = "sender_key"_ls; + static const auto DeviceIdKeyL = "device_id"_ls; + static const auto SessionIdKeyL = "session_id"_ls; + static const auto AlgorithmKeyL = "algorithm"_ls; static const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; static const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 08b68911..3533d791 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -194,6 +194,11 @@ QByteArray EncryptionManager::olmAccountPickle() return d->olmAccount->pickle(); // TODO: passphrase even with qtkeychain? } +QtOlm::Account* EncryptionManager::account() const +{ + return d->olmAccount.data(); +} + void EncryptionManager::Private::updateKeysToUpload() { for (auto it = targetOneTimeKeyCounts.cbegin(); it != targetOneTimeKeyCounts.cend(); ++it) diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index 40fe7383..0225969d 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -4,6 +4,10 @@ #include #include +namespace QtOlm { + class Account; +} + namespace QMatrixClient { class Connection; @@ -23,6 +27,8 @@ namespace QMatrixClient void uploadOneTimeKeys(Connection* connection, bool forceUpdate = false); QByteArray olmAccountPickle(); + QtOlm::Account* account() const; + private: class Private; std::unique_ptr d; diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp new file mode 100644 index 00000000..6942738a --- /dev/null +++ b/lib/events/encryptedevent.cpp @@ -0,0 +1,29 @@ +#include "encryptedevent.h" +#include "room.h" + +using namespace QMatrixClient; +using namespace QtOlm; + +EncryptedEvent::EncryptedEvent(const QJsonObject &ciphertext, const QString &senderKey) + : RoomEvent(typeId(), matrixTypeId(), + { { AlgorithmKeyL , OlmV1Curve25519AesSha2AlgoKey }, + { CiphertextKeyL , ciphertext }, + { SenderKeyKeyL, senderKey } + }) +{ } + +EncryptedEvent::EncryptedEvent(QByteArray ciphertext, const QString &senderKey, const QString& deviceId, const QString& sessionId) + : RoomEvent(typeId(), matrixTypeId(), + { { AlgorithmKeyL , MegolmV1AesSha2AlgoKey }, + { CiphertextKeyL , QString(ciphertext) }, + { DeviceIdKeyL, deviceId }, + { SenderKeyKeyL, senderKey }, + { SessionIdKeyL, sessionId }, + }) +{ } + +EncryptedEvent::EncryptedEvent(const QJsonObject &obj) + : RoomEvent(typeId(), obj) +{ + qCDebug(EVENTS) << "Encrypted event" << id(); +} diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h new file mode 100644 index 00000000..2f9e4422 --- /dev/null +++ b/lib/events/encryptedevent.h @@ -0,0 +1,66 @@ +#pragma once + +#include "roomevent.h" +#include "e2ee.h" + +namespace QMatrixClient +{ + class Room; + /* + * While the specification states: + * + * "This event type is used when sending encrypted events. + * It can be used either within a room + * (in which case it will have all of the Room Event fields), + * or as a to-device event." + * "The encrypted payload can contain any message event." + * https://matrix.org/docs/spec/client_server/latest#id493 + * + * -- for most of the cases the message event is the room message event. + * And even for the to-device events the context is for the room. + * + * So, to simplify integration to the timeline, EncryptedEvent is a RoomEvent inheritor. + * Strictly speaking though, it's not always a RoomEvent, but an Event in general. + * It's possible, because RoomEvent interface is similar to Event's one + * and doesn't add new restrictions, just provides additional features. + */ + class EncryptedEvent : public RoomEvent + { + Q_GADGET + public: + DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent) + + /* In case with Olm, the encrypted content of the event is + * a map from the recipient Curve25519 identity key to ciphertext information */ + explicit EncryptedEvent(const QJsonObject& ciphertext, + const QString& senderKey); + /* In case with Megolm, device_id and session_id are required */ + explicit EncryptedEvent(QByteArray ciphertext, + const QString& senderKey, + const QString& deviceId, + const QString& sessionId); + explicit EncryptedEvent(const QJsonObject& obj); + + QString algorithm() const + { + QString algo = content(AlgorithmKeyL); + if (!SupportedAlgorithms.contains(algo)) { + qWarning(MAIN) << "The EncryptedEvent's algorithm" << algo + << "is not supported"; + } + return algo; + } + QByteArray ciphertext() const { return content(CiphertextKeyL).toLatin1(); } + QJsonObject ciphertext(const QString& identityKey) const + { + return content(CiphertextKeyL).value(identityKey).toObject(); + } + QString senderKey() const { return content(SenderKeyKeyL); } + + /* device_id and session_id are required with Megolm */ + QString deviceId() const { return content(DeviceIdKeyL); } + QString sessionId() const { return content(SessionIdKeyL); } + }; + REGISTER_EVENT_TYPE(EncryptedEvent) + +} // namespace QMatrixClient diff --git a/lib/events/event.h b/lib/events/event.h index b3a58806..6f28c4fa 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -62,6 +62,7 @@ namespace QMatrixClient static const auto UnsignedKey = QStringLiteral("unsigned"); static const auto StateKeyKey = QStringLiteral("state_key"); static const auto TypeKeyL = "type"_ls; + static const auto BodyKeyL = "body"_ls; static const auto ContentKeyL = "content"_ls; static const auto EventIdKeyL = "event_id"_ls; static const auto UnsignedKeyL = "unsigned"_ls; diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 8f4e0ebc..ec18e962 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -32,7 +32,6 @@ using MsgType = RoomMessageEvent::MsgType; static const auto RelatesToKey = "m.relates_to"_ls; static const auto MsgTypeKey = "msgtype"_ls; -static const auto BodyKey = "body"_ls; static const auto FormattedBodyKey = "formatted_body"_ls; static const auto TextTypeKey = "m.text"; @@ -159,7 +158,7 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) if (isRedacted()) return; const QJsonObject content = contentJson(); - if ( content.contains(MsgTypeKey) && content.contains(BodyKey) ) + if ( content.contains(MsgTypeKey) && content.contains(BodyKeyL) ) { auto msgtype = content[MsgTypeKey].toString(); bool msgTypeFound = false; @@ -196,7 +195,7 @@ QString RoomMessageEvent::rawMsgtype() const QString RoomMessageEvent::plainBody() const { - return contentJson()[BodyKey].toString(); + return contentJson()[BodyKeyL].toString(); } QMimeType RoomMessageEvent::mimeType() const @@ -267,7 +266,7 @@ TextContent::TextContent(const QJsonObject& json) // Falling back to plain text, as there's no standard way to describe // rich text in messages. mimeType = PlainTextMimeType; - body = json[BodyKey].toString(); + body = json[BodyKeyL].toString(); } const auto replyJson = json[RelatesToKey].toObject() .value(RelatesTo::ReplyTypeId()).toObject(); diff --git a/lib/room.cpp b/lib/room.cpp index cb368d9e..0402ce67 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -52,6 +52,12 @@ #include "converters.h" #include "syncdata.h" +#include "e2ee.h" + +#include // QtOlm +#include // QtOlm +#include // QtOlm + #include #include // for efficient string concats (operator%) #include @@ -1187,6 +1193,87 @@ bool Room::usesEncryption() const return !d->getCurrentState()->algorithm().isEmpty(); } +const RoomEvent* Room::decryptMessage(EncryptedEvent *encryptedEvent) const +{ + if (encryptedEvent->algorithm() == OlmV1Curve25519AesSha2AlgoKey) { + QString identityKey = connection()->olmAccount()->curve25519IdentityKey(); + QJsonObject personalCipherObject = encryptedEvent->ciphertext(identityKey); + if (personalCipherObject.isEmpty()) { + qCDebug(EVENTS) << "Encrypted event is not for the current device"; + return nullptr; + } + return makeEvent(decryptMessage(personalCipherObject, encryptedEvent->senderKey().toLatin1())).get(); + } + if (encryptedEvent->algorithm() == MegolmV1AesSha2AlgoKey) { + return makeEvent(decryptMessage(encryptedEvent->ciphertext(), encryptedEvent->senderKey(), encryptedEvent->deviceId(), encryptedEvent->sessionId())).get(); + } + return nullptr; +} + +const QString Room::decryptMessage(QJsonObject personalCipherObject, QByteArray senderKey) const +{ + QString decrypted; + + using namespace QtOlm; + // TODO: new objects to private fields: + InboundSession* session; + + int type = personalCipherObject.value(TypeKeyL).toInt(-1); + QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1(); + + PreKeyMessage* preKeyMessage = new PreKeyMessage(body); + session = new InboundSession(connection()->olmAccount(), preKeyMessage, senderKey); + if (type == 0) { + if (!session->matches(preKeyMessage, senderKey)) + { + connection()->olmAccount()->removeOneTimeKeys(session); + } + try + { + decrypted = session->decrypt(preKeyMessage); + } + catch(std::runtime_error& e) + { + qWarning(EVENTS) << "Decrypt failed:" << e.what(); + } + } + else if (type == 1) + { + Message* message = new Message(body); + if (!session->matches(preKeyMessage, senderKey)) + { + qWarning(EVENTS) << "Invalid encrypted message"; + } + try + { + decrypted = session->decrypt(message); + } + catch(std::runtime_error& e) + { + qWarning(EVENTS) << "Decrypt failed:" << e.what(); + } + } + + return decrypted; +} + +const QString Room::sessionKey(const QString& senderKey, const QString& deviceId, const QString& sessionId) const +{ + // TODO: handling an m.room_key event + return ""; +} + +const QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, const QString& deviceId, const QString& sessionId) const +{ + QString decrypted; + using namespace QtOlm; + InboundGroupSession* groupSession; + groupSession = new InboundGroupSession(sessionKey(senderKey, deviceId, sessionId).toLatin1()); + groupSession->decrypt(cipher); + // TODO: avoid replay attacks + return decrypted; +} + int Room::joinedCount() const { return d->summary.joinedMemberCount.omitted() diff --git a/lib/room.h b/lib/room.h index c79ca1e0..e09556b6 100644 --- a/lib/room.h +++ b/lib/room.h @@ -24,8 +24,10 @@ #include "events/accountdataevents.h" #include "eventitem.h" #include "joinstate.h" +#include "events/encryptedevent.h" #include +#include #include #include @@ -179,6 +181,10 @@ namespace QMatrixClient int memberCount() const; int timelineSize() const; bool usesEncryption() const; + const RoomEvent *decryptMessage(EncryptedEvent* encryptedEvent) const; + const QString decryptMessage(QJsonObject personalCipherObject, QByteArray senderKey) const; + const QString sessionKey(const QString &senderKey, const QString &deviceId, const QString &sessionId) const; + const QString decryptMessage(QByteArray cipher, const QString& senderKey, const QString& deviceId, const QString& sessionId) const; int joinedCount() const; int invitedCount() const; int totalMemberCount() const; -- cgit v1.2.3 From b38d48e5bd2764568539089b248d0c3683783aa8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 13 Jul 2019 19:54:08 +0900 Subject: converters.h: more comments; documented addParam<>() [skip ci] --- lib/converters.h | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/converters.h b/lib/converters.h index af2be645..5f00dc43 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -107,6 +107,10 @@ namespace QMatrixClient return JsonConverter::load(jd); } + // Convenience fromJson() overloads that deduce T instead of requiring + // the coder to explicitly type it. They still enforce the + // overwrite-everything semantics of fromJson(), unlike fillFromJson() + template inline void fromJson(const QJsonValue& jv, T& pod) { @@ -357,7 +361,8 @@ namespace QMatrixClient q.addQueryItem(it.key(), it.value().toString()); } - // This one is for types that don't have isEmpty() + // This one is for types that don't have isEmpty() and for all types + // when Force is true template struct AddNode { @@ -369,7 +374,7 @@ namespace QMatrixClient } }; - // This one is for types that have isEmpty() + // This one is for types that have isEmpty() when Force is false template struct AddNode().isEmpty())> @@ -420,6 +425,29 @@ namespace QMatrixClient static constexpr bool IfNotEmpty = false; + /*! Add a key-value pair to QJsonObject or QUrlQuery + * + * Adds a key-value pair(s) specified by \p key and \p value to + * \p container, optionally (in case IfNotEmpty is passed for the first + * template parameter) taking into account the value "emptiness". + * With IfNotEmpty, \p value is NOT added to the container if and only if: + * - it has a method `isEmpty()` and `value.isEmpty() == true`, or + * - it's an `Omittable<>` and `value.omitted() == true`. + * + * If \p container is a QUrlQuery, an attempt to fit \p value into it is + * made as follows: + * - if \p value is a QJsonObject, \p key is ignored and pairs from \p value + * are copied to \p container, assuming that the value in each pair + * is a string; + * - if \p value is a QStringList, it is "exploded" into a list of key-value + * pairs with key equal to \p key and value taken from each list item; + * - if \p value is a bool, its OpenAPI (i.e. JSON) representation is added + * to the query (`true` or `false`, respectively). + * + * \tparam Force add the pair even if the value is empty. This is true + * by default; passing IfNotEmpty or false for this parameter + * enables emptiness checks as described above + */ template inline void addParam(ContT& container, const QString& key, ValT&& value) { -- cgit v1.2.3 From 26bc529ec86dce5478ab37222a27902af7f0dd5a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 13 Jul 2019 19:57:46 +0900 Subject: converters.h: fallback to intrusive toJson() for JsonObjectConverter; general improvements Single-argument fromJson>() now works as well. --- lib/converters.h | 53 +++++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/converters.h b/lib/converters.h index 5f00dc43..36b7ff15 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -60,8 +60,8 @@ namespace QMatrixClient template struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod; } - static void fillFrom(const QJsonObject& jo, T& pod) { pod = jo; } + static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); } + static void fillFrom(const QJsonObject& jo, T& pod) { pod = T(jo); } }; template @@ -89,14 +89,16 @@ namespace QMatrixClient return JsonConverter::dump(pod); } + inline auto toJson(const QJsonObject& jo) { return jo; } + template - inline auto fillJson(QJsonObject& json, const T& data) + inline void fillJson(QJsonObject& json, const T& data) { JsonObjectConverter::dumpTo(json, data); } template - inline auto fromJson(const QJsonValue& jv) + inline T fromJson(const QJsonValue& jv) { return JsonConverter::load(jv); } @@ -114,8 +116,7 @@ namespace QMatrixClient template inline void fromJson(const QJsonValue& jv, T& pod) { - if (!jv.isUndefined()) - pod = fromJson(jv); + pod = jv.isUndefined() ? T() : fromJson(jv); } template @@ -124,21 +125,13 @@ namespace QMatrixClient pod = fromJson(jd); } - // Unfolds Omittable<> - template - inline void fromJson(const QJsonValue& jv, Omittable& pod) - { - if (jv.isUndefined()) - pod = none; - else - pod = fromJson(jv); - } - template inline void fillFromJson(const QJsonValue& jv, T& pod) { if (jv.isObject()) JsonObjectConverter::fillFrom(jv.toObject(), pod); + else if (!jv.isUndefined()) + pod = fromJson(jv); } // JsonConverter<> specialisations @@ -224,6 +217,21 @@ namespace QMatrixClient static QVariant load(const QJsonValue& jv); }; + template + struct JsonConverter> + { + static QJsonValue dump(const Omittable& from) + { + return from.omitted() ? QJsonValue() : toJson(from.value()); + } + static Omittable load(const QJsonValue& jv) + { + if (jv.isUndefined()) + return none; + return fromJson(jv); + } + }; + template struct JsonArrayConverter @@ -384,23 +392,20 @@ namespace QMatrixClient ForwardedT&& value) { if (!value.isEmpty()) - AddNode::impl(container, - key, std::forward(value)); + addTo(container, key, std::forward(value)); } }; - // This is a special one that unfolds Omittable<> - template - struct AddNode, Force> + // This one unfolds Omittable<> (also only when Force is false) + template + struct AddNode, false> { template static void impl(ContT& container, const QString& key, const OmittableT& value) { if (!value.omitted()) - AddNode::impl(container, key, value.value()); - else if (Force) // Edge case, no value but must put something - AddNode::impl(container, key, QString{}); + addTo(container, key, value.value()); } }; -- cgit v1.2.3 From b87097866f38b90f36fb216b7516a135227930a1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 13 Jul 2019 22:43:47 +0900 Subject: Initial support for edited messages (receive only) --- lib/events/roomevent.cpp | 14 ++++++ lib/events/roomevent.h | 2 + lib/events/roommessageevent.cpp | 91 ++++++++++++++++++++++++++++++-------- lib/events/roommessageevent.h | 3 ++ lib/room.cpp | 96 +++++++++++++++++++++++++++++++++++++---- 5 files changed, 180 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index f1e563ff..0143826a 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -66,6 +66,20 @@ QString RoomEvent::senderId() const return fullJson()["sender"_ls].toString(); } +bool RoomEvent::isReplaced() const +{ + return unsignedJson()["m.relations"_ls].toObject().contains("m.replace"); +} + +QString RoomEvent::replacedBy() const +{ + // clang-format off + return unsignedJson()["m.relations"_ls].toObject() + .value("m.replace").toObject() + .value(EventIdKeyL).toString(); + // clang-format on +} + QString RoomEvent::redactionReason() const { return isRedacted() ? _redactedBecause->reason() : QString{}; diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index e26a7135..0e6f730d 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -51,6 +51,8 @@ namespace QMatrixClient QDateTime timestamp() const; QString roomId() const; QString senderId() const; + bool isReplaced() const; + QString replacedBy() const; bool isRedacted() const { return bool(_redactedBecause); } const event_ptr_tt& redactedBecause() const { diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 8f4e0ebc..d4b0d812 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -36,6 +36,7 @@ static const auto BodyKey = "body"_ls; static const auto FormattedBodyKey = "formatted_body"_ls; static const auto TextTypeKey = "m.text"; +static const auto EmoteTypeKey = "m.emote"; static const auto NoticeTypeKey = "m.notice"; static const auto HtmlContentTypeId = QStringLiteral("org.matrix.custom.html"); @@ -62,7 +63,7 @@ struct MsgTypeDesc const std::vector msgTypes = { { TextTypeKey, MsgType::Text, make } - , { QStringLiteral("m.emote"), MsgType::Emote, make } + , { EmoteTypeKey, MsgType::Emote, make } , { NoticeTypeKey, MsgType::Notice, make } , { QStringLiteral("m.image"), MsgType::Image, make } , { QStringLiteral("m.file"), MsgType::File, make } @@ -95,12 +96,25 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, const QString& jsonMsgType, TypedBase* content) { auto json = content ? content->toJson() : QJsonObject(); - if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey && - json.contains(RelatesToKey)) - { - json.remove(RelatesToKey); - qCWarning(EVENTS) << RelatesToKey << "cannot be used in" << jsonMsgType - << "messages; the relation has been stripped off"; + if (json.contains(RelatesToKey)) { + if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey + && jsonMsgType != EmoteTypeKey) { + json.remove(RelatesToKey); + qCWarning(EVENTS) + << RelatesToKey << "cannot be used in" << jsonMsgType + << "messages; the relation has been stripped off"; + } else { + // After the above, we know for sure that the content is TextContent + // and that its RelatesTo structure is not omitted + auto* textContent = static_cast(content); + if (textContent->relatesTo->type == RelatesTo::ReplacementTypeId()) { + auto newContentJson = json.take("m.new_content"_ls).toObject(); + newContentJson.insert(BodyKey, plainBody); + newContentJson.insert(TypeKey, jsonMsgType); + json.insert(QStringLiteral("m.new_content"), newContentJson); + json[BodyKey] = "* " + plainBody; + } + } } json.insert(QStringLiteral("msgtype"), jsonMsgType); json.insert(QStringLiteral("body"), plainBody); @@ -223,6 +237,16 @@ bool RoomMessageEvent::hasThumbnail() const return content() && content()->thumbnailInfo(); } +QString RoomMessageEvent::replacedEvent() const +{ + if (!content() || !hasTextContent()) + return {}; + + const auto& rel = static_cast(content())->relatesTo; + return !rel.omitted() && rel->type == RelatesTo::ReplacementTypeId() + ? rel->eventId : QString(); +} + QString rawMsgTypeForMimeType(const QMimeType& mimeType) { auto name = mimeType.name(); @@ -251,41 +275,72 @@ TextContent::TextContent(const QString& text, const QString& contentType, mimeType = QMimeDatabase().mimeTypeForName("text/html"); } +namespace QMatrixClient +{ +// Overload the default fromJson<> logic that defined in converters.h +// as we want +template <> +Omittable fromJson(const QJsonValue& jv) +{ + const auto jo = jv.toObject(); + if (jo.isEmpty()) + return none; + const auto replyJson = jo.value(RelatesTo::ReplyTypeId()).toObject(); + if (!replyJson.isEmpty()) + return replyTo(fromJson(replyJson[EventIdKeyL])); + + return RelatesTo { jo.value("rel_type"_ls).toString(), + jo.value(EventIdKeyL).toString() }; +} +} + TextContent::TextContent(const QJsonObject& json) + : relatesTo(fromJson>(json[RelatesToKey])) { QMimeDatabase db; static const auto PlainTextMimeType = db.mimeTypeForName("text/plain"); static const auto HtmlMimeType = db.mimeTypeForName("text/html"); + const auto actualJson = + relatesTo.omitted() || relatesTo->type != RelatesTo::ReplacementTypeId() + ? json : json.value("m.new_content"_ls).toObject(); // Special-casing the custom matrix.org's (actually, Riot's) way // of sending HTML messages. - if (json["format"_ls].toString() == HtmlContentTypeId) + if (actualJson["format"_ls].toString() == HtmlContentTypeId) { mimeType = HtmlMimeType; - body = json[FormattedBodyKey].toString(); + body = actualJson[FormattedBodyKey].toString(); } else { // Falling back to plain text, as there's no standard way to describe // rich text in messages. mimeType = PlainTextMimeType; - body = json[BodyKey].toString(); + body = actualJson[BodyKey].toString(); } - const auto replyJson = json[RelatesToKey].toObject() - .value(RelatesTo::ReplyTypeId()).toObject(); - if (!replyJson.isEmpty()) - relatesTo = replyTo(fromJson(replyJson[EventIdKeyL])); } void TextContent::fillJson(QJsonObject* json) const { + static const auto FormatKey = QStringLiteral("format"); + static const auto RichBodyKey = QStringLiteral("formatted_body"); + Q_ASSERT(json); if (mimeType.inherits("text/html")) { - json->insert(QStringLiteral("format"), HtmlContentTypeId); - json->insert(QStringLiteral("formatted_body"), body); + json->insert(FormatKey, HtmlContentTypeId); + json->insert(RichBodyKey, body); } - if (!relatesTo.omitted()) + if (!relatesTo.omitted()) { json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { relatesTo->type, relatesTo->eventId } }); + QJsonObject { { relatesTo->type, relatesTo->eventId } }); + if (relatesTo->type == RelatesTo::ReplacementTypeId()) { + QJsonObject newContentJson; + if (mimeType.inherits("text/html")) { + json->insert(FormatKey, HtmlContentTypeId); + json->insert(RichBodyKey, body); + } + json->insert(QStringLiteral("m.new_content"), newContentJson); + } + } } LocationContent::LocationContent(const QString& geoUri, diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index c2e075eb..7320e4ea 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -72,6 +72,7 @@ namespace QMatrixClient bool hasTextContent() const; bool hasFileContent() const; bool hasThumbnail() const; + QString replacedEvent() const; static QString rawMsgTypeForUrl(const QUrl& url); static QString rawMsgTypeForFile(const QFileInfo& fi); @@ -79,6 +80,7 @@ namespace QMatrixClient private: QScopedPointer _content; + // FIXME: should it really be static? static QJsonObject assembleContentJson(const QString& plainBody, const QString& jsonMsgType, EventContent::TypedBase* content); @@ -95,6 +97,7 @@ namespace QMatrixClient struct RelatesTo { static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; } + static constexpr const char* ReplacementTypeId() { return "m.replace"; } QString type; // The only supported relation so far QString eventId; }; diff --git a/lib/room.cpp b/lib/room.cpp index cb368d9e..958991b7 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -307,14 +307,22 @@ class Room::Private return requestSetState(EvT(std::forward(args)...)); } - /** - * @brief Apply redaction to the timeline + /*! Apply redaction to the timeline * * Tries to find an event in the timeline and redact it; deletes the * redaction event whether the redacted event was found or not. + * \return true if the event has been found and redacted; false otherwise */ bool processRedaction(const RedactionEvent& redaction); + /*! Apply a new revision of the event to the timeline + * + * Tries to find an event in the timeline and replace it with the new + * content passed in \p newMessage. + * \return true if the event has been found and replaced; false otherwise + */ + bool processReplacement(const RoomMessageEvent& newMessage); + void setTags(TagsMap newTags); QJsonObject toJson() const; @@ -2041,6 +2049,52 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) return true; } +/** Make a replaced event + * + * Takes \p target and returns a copy of it with content taken from + * \p replacement. Disposal of the original event after that is on the caller. + */ +RoomEventPtr makeReplaced(const RoomEvent& target, + const RoomMessageEvent& replacement) +{ + auto originalJson = target.originalJsonObject(); + originalJson[ContentKeyL] = replacement.contentJson(); + + auto unsignedData = originalJson.take(UnsignedKeyL).toObject(); + auto relations = unsignedData.take("m.relations"_ls).toObject(); + relations["m.replace"_ls] = replacement.id(); + unsignedData.insert(QStringLiteral("m.relations"), relations); + originalJson.insert(UnsignedKey, unsignedData); + + return loadEvent(originalJson); +} + +bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) +{ + // Can't use findInTimeline because it returns a const iterator, and + // we need to change the underlying TimelineItem. + const auto pIdx = eventsIndex.find(newEvent.replacedEvent()); + if (pIdx == eventsIndex.end()) + return false; + + Q_ASSERT(q->isValidIndex(*pIdx)); + + auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; + if (ti->replacedBy() == newEvent.id()) + { + qCDebug(MAIN) << "Event" << ti->id() << "is already replaced with" + << newEvent.id(); + return true; + } + + // Make a new event from the redacted JSON and put it in the timeline + // instead of the redacted one. oldEvent will be deleted on return. + auto oldEvent = ti.replaceEvent(makeReplaced(*ti, newEvent)); + qCDebug(MAIN) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); + emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); + return true; +} + Connection* Room::connection() const { Q_ASSERT(d->connection); @@ -2052,10 +2106,16 @@ User* Room::localUser() const return connection()->user(); } -inline bool isRedaction(const RoomEventPtr& ep) +/// Whether the event is a redaction or a replacement +inline bool isEditing(const RoomEventPtr& ep) { Q_ASSERT(ep); - return is(*ep); + if (is(*ep)) + return true; + if (auto* msgEvent = eventCast(ep)) + return msgEvent->replacedEvent().isEmpty(); + + return false; } Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) @@ -2067,18 +2127,19 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // Pre-process redactions so that events that get redacted in the same // batch landed in the timeline already redacted. // NB: We have to store redaction events to the timeline too - see #220. - auto redactionIt = std::find_if(events.begin(), events.end(), isRedaction); - for(const auto& eptr: RoomEventsRange(redactionIt, events.end())) + auto it = std::find_if(events.begin(), events.end(), isEditing); + for(const auto& eptr: RoomEventsRange(it, events.end())) + { if (auto* r = eventCast(eptr)) { // Try to find the target in the timeline, then in the batch. if (processRedaction(*r)) continue; - auto targetIt = std::find_if(events.begin(), redactionIt, + auto targetIt = std::find_if(events.begin(), it, [id=r->redactedEvent()] (const RoomEventPtr& ep) { return ep->id() == id; }); - if (targetIt != redactionIt) + if (targetIt != it) *targetIt = makeRedacted(**targetIt, *r); else qCDebug(MAIN) << "Redaction" << r->id() @@ -2086,6 +2147,25 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) << "is not found"; // If the target event comes later, it comes already redacted. } + if (auto* msg = eventCast(eptr)) { + if (!msg->replacedEvent().isEmpty()) { + if (processReplacement(*msg)) + continue; + auto targetIt = std::find_if(events.begin(), it, + [id=msg->replacedEvent()] (const RoomEventPtr& ep) { + return ep->id() == id; + }); + if (targetIt != it) + *targetIt = makeReplaced(**targetIt, *msg); + else // FIXME: don't ignore, just show it wherever it arrived + qCDebug(MAIN) << "Replacing event" << msg->id() + << "ignored: replaced event" << msg->replacedEvent() + << "is not found"; + // Same as with redactions above, the replaced event coming + // later will come already with the new content. + } + } + } // State changes arrive as a part of timeline; the current room state gets // updated before merging events to the timeline because that's what -- cgit v1.2.3 From b89d5c43746ed672d98bf350db2a9e1b0878e2d0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 18 May 2019 13:52:54 +0900 Subject: Support for receiving m.reaction events * struct EventRelation; class ReactionEvent; Room::relatedEvents() * Reaction events are processed in both history and sync batches * Redacting a reaction removes it from the list of related events * QMCTest::sendReaction() --- lib/events/reactionevent.cpp | 44 ++++++++++++++++ lib/events/reactionevent.h | 78 ++++++++++++++++++++++++++++ lib/room.cpp | 119 +++++++++++++++++++++++++++++-------------- lib/room.h | 7 +++ 4 files changed, 210 insertions(+), 38 deletions(-) create mode 100644 lib/events/reactionevent.cpp create mode 100644 lib/events/reactionevent.h (limited to 'lib') diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp new file mode 100644 index 00000000..0081edc2 --- /dev/null +++ b/lib/events/reactionevent.cpp @@ -0,0 +1,44 @@ +/****************************************************************************** + * Copyright (C) 2019 Kitsune Ral + * + * 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 "reactionevent.h" + +using namespace QMatrixClient; + +void QMatrixClient::JsonObjectConverter::dumpTo( + QJsonObject& jo, const EventRelation& pod) +{ + if (pod.type.isEmpty()) { + qCWarning(MAIN) << "Empty relation type; won't dump to JSON"; + return; + } + jo.insert(QStringLiteral("rel_type"), pod.type); + jo.insert(EventIdKey, pod.eventId); + if (pod.type == EventRelation::Annotation()) + jo.insert(QStringLiteral("key"), pod.key); +} + +void QMatrixClient::JsonObjectConverter::fillFrom( + const QJsonObject& jo, EventRelation& pod) +{ + // The experimental logic for generic relationships (MSC1849) + fromJson(jo["rel_type"_ls], pod.type); + fromJson(jo[EventIdKeyL], pod.eventId); + if (pod.type == EventRelation::Annotation()) + fromJson(jo["key"_ls], pod.key); +} diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h new file mode 100644 index 00000000..9a6adbbd --- /dev/null +++ b/lib/events/reactionevent.h @@ -0,0 +1,78 @@ +/****************************************************************************** + * Copyright (C) 2019 Kitsune Ral + * + * 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 "roomevent.h" + +namespace QMatrixClient { + +struct EventRelation +{ + using reltypeid_t = const char*; + static constexpr reltypeid_t Reply() { return "m.in_reply_to"; } + static constexpr reltypeid_t Annotation() { return "m.annotation"; } + static constexpr reltypeid_t Replacement() { return "m.replace"; } + + QString type; + QString eventId; + QString key = {}; // Only used for m.annotation for now + + static EventRelation replyTo(QString eventId) + { + return { Reply(), std::move(eventId) }; + } + static EventRelation annotate(QString eventId, QString key) + { + return { Annotation(), std::move(eventId), std::move(key) }; + } + static EventRelation replace(QString eventId) + { + return { Replacement(), std::move(eventId) }; + } +}; +template <> +struct JsonObjectConverter +{ + static void dumpTo(QJsonObject& jo, const EventRelation& pod); + static void fillFrom(const QJsonObject& jo, EventRelation& pod); +}; + +class ReactionEvent : public RoomEvent +{ +public: + DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent) + + explicit ReactionEvent(const EventRelation& value) + : RoomEvent(typeId(), matrixTypeId(), + { { QStringLiteral("m.relates_to"), toJson(value) } }) + {} + explicit ReactionEvent(const QJsonObject& obj) + : RoomEvent(typeId(), obj) + {} + EventRelation relation() const + { + return content(QStringLiteral("m.relates_to")); + } + +private: + EventRelation _relation; +}; +REGISTER_EVENT_TYPE(ReactionEvent) + +} // namespace QMatrixClient diff --git a/lib/room.cpp b/lib/room.cpp index 958991b7..8b05568a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -38,6 +38,7 @@ #include "events/roommemberevent.h" #include "events/typingevent.h" #include "events/receiptevent.h" +#include "events/reactionevent.h" #include "events/callinviteevent.h" #include "events/callcandidatesevent.h" #include "events/callanswerevent.h" @@ -106,7 +107,10 @@ class Room::Private Timeline timeline; PendingEvents unsyncedEvents; QHash eventsIndex; - + // A map from evtId to a map of relation type to a vector of event + // pointers. Not using QMultiHash, because we want to quickly return + // a number of relations for a given event without enumerating them. + QHash, RelatedEvents> relations; QString displayname; Avatar avatar; int highlightCount = 0; @@ -724,10 +728,10 @@ Room::rev_iter_t Room::findInTimeline(const QString& evtId) const if (!d->timeline.empty() && d->eventsIndex.contains(evtId)) { auto it = findInTimeline(d->eventsIndex.value(evtId)); - Q_ASSERT((*it)->id() == evtId); + Q_ASSERT(it != historyEdge() && (*it)->id() == evtId); return it; } - return timelineEdge(); + return historyEdge(); } Room::PendingEvents::iterator Room::findPendingEvent(const QString& txnId) @@ -743,6 +747,18 @@ Room::findPendingEvent(const QString& txnId) const [txnId] (const auto& item) { return item->transactionId() == txnId; }); } +const Room::RelatedEvents Room::relatedEvents(const QString& evtId, + const char* relType) const +{ + return d->relations.value({ evtId, relType }); +} + +const Room::RelatedEvents Room::relatedEvents(const RoomEvent& evt, + const char* relType) const +{ + return relatedEvents(evt.id(), relType); +} + void Room::Private::getAllMembers() { // If already loaded or already loading, there's nothing to do here. @@ -2044,6 +2060,14 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) updateDisplayname(); } } + if (const auto* reaction = eventCast(oldEvent)) { + const auto& targetEvtId = reaction->relation().eventId; + const auto lookupKey = qMakePair(targetEvtId, + EventRelation::Annotation()); + if (relations.contains(lookupKey)) { + relations[lookupKey].removeOne(reaction); + } + } q->onRedaction(*oldEvent, *ti); emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); return true; @@ -2124,45 +2148,49 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (events.empty()) return Change::NoChange; - // Pre-process redactions so that events that get redacted in the same - // batch landed in the timeline already redacted. - // NB: We have to store redaction events to the timeline too - see #220. - auto it = std::find_if(events.begin(), events.end(), isEditing); - for(const auto& eptr: RoomEventsRange(it, events.end())) { - if (auto* r = eventCast(eptr)) - { - // Try to find the target in the timeline, then in the batch. - if (processRedaction(*r)) - continue; - auto targetIt = std::find_if(events.begin(), it, - [id=r->redactedEvent()] (const RoomEventPtr& ep) { - return ep->id() == id; - }); - if (targetIt != it) - *targetIt = makeRedacted(**targetIt, *r); - else - qCDebug(MAIN) << "Redaction" << r->id() - << "ignored: target event" << r->redactedEvent() - << "is not found"; - // If the target event comes later, it comes already redacted. - } - if (auto* msg = eventCast(eptr)) { - if (!msg->replacedEvent().isEmpty()) { - if (processReplacement(*msg)) + // Pre-process redactions and edits so that events that get + // redacted/replaced in the same batch landed in the timeline already + // treated. + // NB: We have to store redacting/replacing events to the timeline too - + // see #220. + auto it = std::find_if(events.begin(), events.end(), isEditing); + for (const auto& eptr : RoomEventsRange(it, events.end())) { + if (auto* r = eventCast(eptr)) { + // Try to find the target in the timeline, then in the batch. + if (processRedaction(*r)) continue; auto targetIt = std::find_if(events.begin(), it, - [id=msg->replacedEvent()] (const RoomEventPtr& ep) { - return ep->id() == id; - }); + [id = r->redactedEvent()]( + const RoomEventPtr& ep) { + return ep->id() == id; + }); if (targetIt != it) - *targetIt = makeReplaced(**targetIt, *msg); - else // FIXME: don't ignore, just show it wherever it arrived - qCDebug(MAIN) << "Replacing event" << msg->id() - << "ignored: replaced event" << msg->replacedEvent() - << "is not found"; - // Same as with redactions above, the replaced event coming - // later will come already with the new content. + *targetIt = makeRedacted(**targetIt, *r); + else + qCDebug(MAIN) + << "Redaction" << r->id() << "ignored: target event" + << r->redactedEvent() << "is not found"; + // If the target event comes later, it comes already redacted. + } + if (auto* msg = eventCast(eptr)) { + if (!msg->replacedEvent().isEmpty()) { + if (processReplacement(*msg)) + continue; + auto targetIt = std::find_if(events.begin(), it, + [id = msg->replacedEvent()]( + const RoomEventPtr& ep) { + return ep->id() == id; + }); + if (targetIt != it) + *targetIt = makeReplaced(**targetIt, *msg); + else // FIXME: don't ignore, just show it wherever it arrived + qCDebug(MAIN) << "Replacing event" << msg->id() + << "ignored: replaced event" + << msg->replacedEvent() << "is not found"; + // Same as with redactions above, the replaced event coming + // later will come already with the new content. + } } } } @@ -2231,6 +2259,14 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (totalInserted > 0) { + for (auto it = from; it != timeline.cend(); ++it) { + if (const auto* reaction = it->viewAs()) { + const auto& relation = reaction->relation(); + relations[{ relation.eventId, relation.type }] << reaction; + emit q->updatedEvent(relation.eventId); + } + } + qCDebug(MAIN) << "Room" << q->objectName() << "received" << totalInserted << "new events; the last event is now" << timeline.back(); @@ -2289,6 +2325,13 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) q->onAddHistoricalTimelineEvents(from); emit q->addedMessages(timeline.front().index(), from->index()); + for (auto it = from; it != timeline.crend(); ++it) { + if (const auto* reaction = it->viewAs()) { + const auto& relation = reaction->relation(); + relations[{ relation.eventId, relation.type }] << reaction; + emit q->updatedEvent(relation.eventId); + } + } if (from <= q->readMarker()) updateUnreadCount(from, timeline.crend()); diff --git a/lib/room.h b/lib/room.h index c79ca1e0..65b9070a 100644 --- a/lib/room.h +++ b/lib/room.h @@ -121,6 +121,7 @@ namespace QMatrixClient public: using Timeline = std::deque; using PendingEvents = std::vector; + using RelatedEvents = QVector; using rev_iter_t = Timeline::const_reverse_iterator; using timeline_iter_t = Timeline::const_iterator; @@ -264,6 +265,11 @@ namespace QMatrixClient PendingEvents::iterator findPendingEvent(const QString & txnId); PendingEvents::const_iterator findPendingEvent(const QString & txnId) const; + const RelatedEvents relatedEvents(const QString& evtId, + const char* relType) const; + const RelatedEvents relatedEvents(const RoomEvent& evt, + const char* relType) const; + bool displayed() const; /// Mark the room as currently displayed to the user /** @@ -584,6 +590,7 @@ namespace QMatrixClient void tagsAboutToChange(); void tagsChanged(); + void updatedEvent(QString eventId); void replacedEvent(const RoomEvent* newEvent, const RoomEvent* oldEvent); -- cgit v1.2.3 From 1ea94b918569dd26452f285c408e605f9dc15343 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 30 Jul 2019 07:53:34 +0900 Subject: Room::postReaction() --- lib/room.cpp | 5 +++++ lib/room.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 8b05568a..6519db12 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1603,6 +1603,11 @@ QString Room::postHtmlText(const QString& plainText, const QString& html) return postHtmlMessage(plainText, html); } +QString Room::postReaction(const QString& eventId, const QString& key) +{ + return d->sendEvent(EventRelation::annotate(eventId, key)); +} + QString Room::postFile(const QString& plainText, const QUrl& localPath, bool asGenericFile) { diff --git a/lib/room.h b/lib/room.h index 65b9070a..470bb7f9 100644 --- a/lib/room.h +++ b/lib/room.h @@ -430,6 +430,8 @@ namespace QMatrixClient const QString& html, MessageEventType type = MessageEventType::Text); QString postHtmlText(const QString& plainText, const QString& html); + /// Send a reaction on a given event with a given key + QString postReaction(const QString& eventId, const QString& key); QString postFile(const QString& plainText, const QUrl& localPath, bool asGenericFile = false); /** Post a pre-created room message event -- cgit v1.2.3 From 83453eda5dc74c64548b51860d8f7ef08123bec3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 2 Aug 2019 14:52:25 +0900 Subject: e2ee.h: add a missing #include Revealed after reordering #includes with clang-format. --- lib/e2ee.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/e2ee.h b/lib/e2ee.h index 11f411f1..d3329def 100644 --- a/lib/e2ee.h +++ b/lib/e2ee.h @@ -1,5 +1,7 @@ #pragma once +#include "util.h" + #include namespace QMatrixClient -- cgit v1.2.3 From c05ade838f0fce81f2bbe80a3295618a8a26ff52 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 2 Aug 2019 19:59:40 +0900 Subject: Apply the new brace wrapping to source files --- lib/avatar.cpp | 15 ++----- lib/avatar.h | 6 +-- lib/connection.cpp | 79 ++++++++++++++++---------------- lib/connection.h | 21 +++------ lib/connectiondata.cpp | 7 +-- lib/connectiondata.h | 6 +-- lib/converters.h | 97 ++++++++++++++-------------------------- lib/e2ee.h | 3 +- lib/encryptionmanager.cpp | 3 +- lib/encryptionmanager.h | 9 ++-- lib/eventitem.h | 24 ++++------ lib/events/accountdataevents.h | 50 +++++++++------------ lib/events/callanswerevent.h | 6 +-- lib/events/callcandidatesevent.h | 6 +-- lib/events/callhangupevent.h | 6 +-- lib/events/callinviteevent.h | 6 +-- lib/events/directchatevent.h | 10 ++--- lib/events/encryptedevent.h | 6 +-- lib/events/encryptionevent.cpp | 6 +-- lib/events/encryptionevent.h | 15 ++----- lib/events/event.cpp | 4 +- lib/events/event.h | 24 ++++------ lib/events/eventcontent.cpp | 3 +- lib/events/eventcontent.h | 46 ++++++------------- lib/events/eventloader.h | 9 ++-- lib/events/reactionevent.h | 16 +++---- lib/events/receiptevent.cpp | 3 +- lib/events/receiptevent.h | 12 ++--- lib/events/redactionevent.h | 9 ++-- lib/events/roomavatarevent.h | 9 ++-- lib/events/roomcreateevent.h | 13 ++---- lib/events/roomevent.cpp | 3 +- lib/events/roomevent.h | 9 ++-- lib/events/roommemberevent.cpp | 6 +-- lib/events/roommemberevent.h | 22 +++------ lib/events/roommessageevent.cpp | 12 ++--- lib/events/roommessageevent.h | 24 ++++------ lib/events/roomtombstoneevent.h | 10 ++--- lib/events/simplestateevents.h | 25 ++++------- lib/events/stateevent.h | 21 +++------ lib/events/typingevent.cpp | 3 +- lib/events/typingevent.h | 6 +-- lib/jobs/basejob.cpp | 6 +-- lib/jobs/basejob.h | 35 ++++----------- lib/jobs/downloadfilejob.cpp | 7 +-- lib/jobs/downloadfilejob.h | 11 ++--- lib/jobs/mediathumbnailjob.h | 6 +-- lib/jobs/postreadmarkersjob.h | 3 +- lib/jobs/requestdata.cpp | 12 ++--- lib/jobs/requestdata.h | 9 ++-- lib/jobs/syncjob.h | 6 +-- lib/joinstate.h | 6 +-- lib/logging.h | 3 +- lib/networkaccessmanager.cpp | 6 +-- lib/networkaccessmanager.h | 6 +-- lib/networksettings.h | 6 +-- lib/qt_connection_util.h | 12 ++--- lib/room.cpp | 35 ++++++--------- lib/room.h | 28 +++--------- lib/settings.h | 16 +++---- lib/syncdata.h | 15 +++---- lib/user.cpp | 6 +-- lib/user.h | 6 +-- lib/util.cpp | 18 +++----- lib/util.h | 64 ++++++++------------------ 65 files changed, 333 insertions(+), 659 deletions(-) (limited to 'lib') diff --git a/lib/avatar.cpp b/lib/avatar.cpp index 0e58a1ce..614f008d 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -32,12 +32,9 @@ using namespace QMatrixClient; using std::move; -class Avatar::Private -{ +class Avatar::Private { public: - explicit Private(QUrl url = {}) - : _url(move(url)) - {} + explicit Private(QUrl url = {}) : _url(move(url)) {} ~Private() { if (isJobRunning(_thumbnailRequest)) @@ -65,13 +62,9 @@ public: mutable std::vector callbacks; }; -Avatar::Avatar() - : d(std::make_unique()) -{} +Avatar::Avatar() : d(std::make_unique()) {} -Avatar::Avatar(QUrl url) - : d(std::make_unique(std::move(url))) -{} +Avatar::Avatar(QUrl url) : d(std::make_unique(std::move(url))) {} Avatar::Avatar(Avatar&&) = default; diff --git a/lib/avatar.h b/lib/avatar.h index 37991192..c33e1982 100644 --- a/lib/avatar.h +++ b/lib/avatar.h @@ -24,12 +24,10 @@ #include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Connection; -class Avatar -{ +class Avatar { public: explicit Avatar(); explicit Avatar(QUrl url); diff --git a/lib/connection.cpp b/lib/connection.cpp index 6ebe05dc..6cd6ad0b 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -70,8 +70,7 @@ HashT erase_if(HashT& hashMap, Pred pred) return removals; } -class Connection::Private -{ +class Connection::Private { public: explicit Private(std::unique_ptr&& connection) : data(move(connection)) @@ -151,15 +150,12 @@ public: }; Connection::Connection(const QUrl& server, QObject* parent) - : QObject(parent) - , d(new Private(std::make_unique(server))) + : QObject(parent), d(new Private(std::make_unique(server))) { d->q = this; // All d initialization should occur before this line } -Connection::Connection(QObject* parent) - : Connection({}, parent) -{} +Connection::Connection(QObject* parent) : Connection({}, parent) {} Connection::~Connection() { @@ -183,45 +179,47 @@ void Connection::resolveServer(const QString& mxid) qCDebug(MAIN) << "Finding the server" << domain; auto getWellKnownJob = callApi(); - connect(getWellKnownJob, &BaseJob::finished, - [this, getWellKnownJob, maybeBaseUrl] { - if (getWellKnownJob->status() == BaseJob::NotFoundError) { - qCDebug(MAIN) << "No .well-known file, IGNORE"; - } else if (getWellKnownJob->status() != BaseJob::Success) { + connect( + getWellKnownJob, &BaseJob::finished, + [this, getWellKnownJob, maybeBaseUrl] { + if (getWellKnownJob->status() == BaseJob::NotFoundError) + qCDebug(MAIN) << "No .well-known file, IGNORE"; + else { + if (getWellKnownJob->status() != BaseJob::Success) { qCDebug(MAIN) << "Fetching .well-known file failed, FAIL_PROMPT"; emit resolveError(tr("Fetching .well-known file failed")); return; - } else if (getWellKnownJob->data().homeserver.baseUrl.isEmpty()) { + } + QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl); + if (baseUrl.isEmpty()) { qCDebug(MAIN) << "base_url not provided, FAIL_PROMPT"; emit resolveError(tr("base_url not provided")); return; - } else if (!QUrl(getWellKnownJob->data().homeserver.baseUrl) - .isValid()) { + } + if (!baseUrl.isValid()) { qCDebug(MAIN) << "base_url invalid, FAIL_ERROR"; emit resolveError(tr("base_url invalid")); return; - } else { - QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl); - - qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() - << "is" << baseUrl.toString(); - setHomeserver(baseUrl); } - auto getVersionsJob = callApi(); - - connect(getVersionsJob, &BaseJob::finished, - [this, getVersionsJob] { - if (getVersionsJob->status() == BaseJob::Success) { - qCDebug(MAIN) << "homeserver url is valid"; - emit resolved(); - } else { - qCDebug(MAIN) << "homeserver url invalid"; - emit resolveError(tr("homeserver url invalid")); - } - }); + qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() + << "is" << baseUrl.toString(); + setHomeserver(baseUrl); + } + + auto getVersionsJob = callApi(); + + connect(getVersionsJob, &BaseJob::finished, [this, getVersionsJob] { + if (getVersionsJob->status() == BaseJob::Success) { + qCDebug(MAIN) << "homeserver url is valid"; + emit resolved(); + } else { + qCDebug(MAIN) << "homeserver url invalid"; + emit resolveError(tr("homeserver url invalid")); + } }); + }); } void Connection::connectToServer(const QString& user, const QString& password, @@ -372,8 +370,8 @@ void Connection::sync(int timeout) connect(job, &SyncJob::failure, this, [this, job] { d->syncJob = nullptr; if (job->error() == BaseJob::ContentAccessError) { - qCWarning(SYNCJOB) - << "Sync job failed with ContentAccessError - login expired?"; + qCWarning(SYNCJOB) << "Sync job failed with ContentAccessError - " + "login expired?"; emit loginError(job->errorString(), job->rawDataSample()); } else emit syncError(job->errorString(), job->rawDataSample()); @@ -437,7 +435,6 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) visit( *eventPtr, [this](const DirectChatEvent& dce) { - // See // https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events const auto& usersToDCs = dce.usersToDirectChats(); DirectChatsMap remoteRemovals = @@ -492,8 +489,8 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) << QStringList::fromSet(ignoredUsers()).join(','); auto& currentData = d->accountData[accountEvent.matrixType()]; - // A polymorphic event-specific comparison might be a bit more - // efficient; maaybe do it another day + // A polymorphic event-specific comparison might be a bit + // more efficient; maaybe do it another day if (!currentData || currentData->contentJson() != accountEvent.contentJson()) { currentData = std::move(eventPtr); @@ -678,9 +675,9 @@ void Connection::requestDirectChat(const QString& userId) if (auto* u = user(userId)) requestDirectChat(u); else - qCCritical(MAIN) - << "Connection::requestDirectChat: Couldn't get a user object for" - << userId; + qCCritical(MAIN) << "Connection::requestDirectChat: Couldn't get a " + "user object for" + << userId; } void Connection::requestDirectChat(User* u) diff --git a/lib/connection.h b/lib/connection.h index 8d65f0e7..b89c0c65 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -32,13 +32,11 @@ #include -namespace QtOlm -{ +namespace QtOlm { class Account; } -namespace QMatrixClient -{ +namespace QMatrixClient { class Room; class User; class ConnectionData; @@ -93,14 +91,9 @@ static inline user_factory_t defaultUserFactory() * * \sa Connection::callApi */ -enum RunningPolicy -{ - ForegroundRequest = 0x0, - BackgroundRequest = 0x1 -}; +enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; -class Connection : public QObject -{ +class Connection : public QObject { Q_OBJECT Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) @@ -129,8 +122,7 @@ public: using UsersToDevicesToEvents = std::unordered_map>; - enum RoomVisibility - { + enum RoomVisibility { PublishRoom, UnpublishRoom }; // FIXME: Should go inside CreateRoomJob @@ -285,8 +277,7 @@ public: Q_INVOKABLE void getTurnServers(); - struct SupportedRoomVersion - { + struct SupportedRoomVersion { QString id; QString status; diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index c157565f..df4cece2 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -23,11 +23,8 @@ using namespace QMatrixClient; -struct ConnectionData::Private -{ - explicit Private(QUrl url) - : baseUrl(std::move(url)) - {} +struct ConnectionData::Private { + explicit Private(QUrl url) : baseUrl(std::move(url)) {} QUrl baseUrl; QByteArray accessToken; diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 6f9f090c..9b579b1c 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -24,10 +24,8 @@ class QNetworkAccessManager; -namespace QMatrixClient -{ -class ConnectionData -{ +namespace QMatrixClient { +class ConnectionData { public: explicit ConnectionData(QUrl baseUrl); virtual ~ConnectionData(); diff --git a/lib/converters.h b/lib/converters.h index aa07261d..0085fa4b 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -38,11 +38,9 @@ using optional = std::experimental::optional; #endif // Enable std::unordered_map -namespace std -{ +namespace std { template <> -struct hash -{ +struct hash { size_t operator()(const QString& s) const Q_DECL_NOEXCEPT { return qHash(s @@ -57,18 +55,15 @@ struct hash class QVariant; -namespace QMatrixClient -{ +namespace QMatrixClient { template -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); } static void fillFrom(const QJsonObject& jo, T& pod) { pod = T(jo); } }; template -struct JsonConverter -{ +struct JsonConverter { static QJsonObject dump(const T& pod) { QJsonObject jo; @@ -139,52 +134,44 @@ inline void fillFromJson(const QJsonValue& jv, T& pod) // JsonConverter<> specialisations template -struct TrivialJsonDumper -{ +struct TrivialJsonDumper { // Works for: QJsonValue (and all things it can consume), // QJsonObject, QJsonArray static auto dump(const T& val) { return val; } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toBool(); } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toInt(); } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toDouble(); } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return float(jv.toDouble()); } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return qint64(jv.toDouble()); } }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toString(); } }; template <> -struct JsonConverter -{ +struct JsonConverter { static auto dump(const QDateTime& val) = delete; // not provided yet static auto load(const QJsonValue& jv) { @@ -193,8 +180,7 @@ struct JsonConverter }; template <> -struct JsonConverter -{ +struct JsonConverter { static auto dump(const QDate& val) = delete; // not provided yet static auto load(const QJsonValue& jv) { @@ -203,14 +189,12 @@ struct JsonConverter }; template <> -struct JsonConverter : public TrivialJsonDumper -{ +struct JsonConverter : public TrivialJsonDumper { static auto load(const QJsonValue& jv) { return jv.toArray(); } }; template <> -struct JsonConverter -{ +struct JsonConverter { static QString dump(const QByteArray& ba) { return ba.constData(); } static auto load(const QJsonValue& jv) { @@ -219,15 +203,13 @@ struct JsonConverter }; template <> -struct JsonConverter -{ +struct JsonConverter { static QJsonValue dump(const QVariant& v); static QVariant load(const QJsonValue& jv); }; template -struct JsonConverter> -{ +struct JsonConverter> { static QJsonValue dump(const Omittable& from) { return from.omitted() ? QJsonValue() : toJson(from.value()); @@ -241,8 +223,7 @@ struct JsonConverter> }; template -struct JsonArrayConverter -{ +struct JsonArrayConverter { static void dumpTo(QJsonArray& ar, const VectorT& vals) { for (const auto& v : vals) @@ -267,20 +248,17 @@ struct JsonArrayConverter }; template -struct JsonConverter> : public JsonArrayConverter> -{}; +struct JsonConverter> + : public JsonArrayConverter> {}; template -struct JsonConverter> : public JsonArrayConverter> -{}; +struct JsonConverter> : public JsonArrayConverter> {}; template -struct JsonConverter> : public JsonArrayConverter> -{}; +struct JsonConverter> : public JsonArrayConverter> {}; template <> -struct JsonConverter : public JsonConverter> -{ +struct JsonConverter : public JsonConverter> { static auto dump(const QStringList& sl) { return QJsonArray::fromStringList(sl); @@ -288,8 +266,7 @@ struct JsonConverter : public JsonConverter> }; template <> -struct JsonObjectConverter> -{ +struct JsonObjectConverter> { static void dumpTo(QJsonObject& json, const QSet& s) { for (const auto& e : s) @@ -305,8 +282,7 @@ struct JsonObjectConverter> }; template -struct HashMapFromJson -{ +struct HashMapFromJson { static void dumpTo(QJsonObject& json, const HashMapT& hashMap) { for (auto it = hashMap.begin(); it != hashMap.end(); ++it) @@ -322,13 +298,11 @@ struct HashMapFromJson template struct JsonObjectConverter> - : public HashMapFromJson> -{}; + : public HashMapFromJson> {}; template struct JsonObjectConverter> - : public HashMapFromJson> -{}; + : public HashMapFromJson> {}; // We could use std::conditional<> below but QT_VERSION* macros in C++ code // cause (kinda valid but useless and noisy) compiler warnings about @@ -340,16 +314,14 @@ using variant_map_t = QVariantMap; #endif template <> -struct JsonConverter -{ +struct JsonConverter { static QJsonObject dump(const variant_map_t& vh); static QVariantHash load(const QJsonValue& jv); }; // Conditional insertion into a QJsonObject -namespace _impl -{ +namespace _impl { template inline void addTo(QJsonObject& o, const QString& k, ValT&& v) { @@ -384,8 +356,7 @@ namespace _impl // This one is for types that don't have isEmpty() and for all types // when Force is true template - struct AddNode - { + struct AddNode { template static void impl(ContT& container, const QString& key, ForwardedT&& value) @@ -396,8 +367,7 @@ namespace _impl // This one is for types that have isEmpty() when Force is false template - struct AddNode().isEmpty())> - { + struct AddNode().isEmpty())> { template static void impl(ContT& container, const QString& key, ForwardedT&& value) @@ -409,8 +379,7 @@ namespace _impl // This one unfolds Omittable<> (also only when Force is false) template - struct AddNode, false> - { + struct AddNode, false> { template static void impl(ContT& container, const QString& key, const OmittableT& value) diff --git a/lib/e2ee.h b/lib/e2ee.h index d3329def..c85211be 100644 --- a/lib/e2ee.h +++ b/lib/e2ee.h @@ -4,8 +4,7 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { static const auto CiphertextKeyL = "ciphertext"_ls; static const auto SenderKeyKeyL = "sender_key"_ls; static const auto DeviceIdKeyL = "device_id"_ls; diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 46d937b8..15723688 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -16,8 +16,7 @@ using namespace QMatrixClient; using namespace QtOlm; using std::move; -class EncryptionManager::Private -{ +class EncryptionManager::Private { public: explicit Private(const QByteArray& encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold) diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index 02bb882f..79c25a00 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -5,17 +5,14 @@ #include #include -namespace QtOlm -{ +namespace QtOlm { class Account; } -namespace QMatrixClient -{ +namespace QMatrixClient { class Connection; -class EncryptionManager : public QObject -{ +class EncryptionManager : public QObject { Q_OBJECT public: diff --git a/lib/eventitem.h b/lib/eventitem.h index 58f5479c..68d1ae06 100644 --- a/lib/eventitem.h +++ b/lib/eventitem.h @@ -22,12 +22,10 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { class StateEventBase; -class EventStatus -{ +class EventStatus { Q_GADGET public: /** Special marks an event can assume @@ -35,8 +33,7 @@ public: * This is used to hint at a special status of some events in UI. * All values except Redacted and Hidden are mutually exclusive. */ - enum Code - { + enum Code { Normal = 0x0, //< No special designation Submitted = 0x01, //< The event has just been submitted for sending FileUploaded = 0x02, //< The file attached to the event has been @@ -51,11 +48,9 @@ public: Q_FLAG(Status) }; -class EventItemBase -{ +class EventItemBase { public: - explicit EventItemBase(RoomEventPtr&& e) - : evt(std::move(e)) + explicit EventItemBase(RoomEventPtr&& e) : evt(std::move(e)) { Q_ASSERT(evt); } @@ -87,16 +82,14 @@ private: RoomEventPtr evt; }; -class TimelineItem : public EventItemBase -{ +class TimelineItem : public EventItemBase { public: // For compatibility with Qt containers, even though we use // a std:: container now for the room timeline using index_t = int; TimelineItem(RoomEventPtr&& e, index_t number) - : EventItemBase(std::move(e)) - , idx(number) + : EventItemBase(std::move(e)), idx(number) {} index_t index() const { return idx; } @@ -118,8 +111,7 @@ inline const CallEventBase* EventItemBase::viewAs() const return evt->isCallEvent() ? weakPtrCast(evt) : nullptr; } -class PendingEventItem : public EventItemBase -{ +class PendingEventItem : public EventItemBase { Q_GADGET public: using EventItemBase::EventItemBase; diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index abab9867..3f519668 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -24,20 +24,16 @@ #include "event.h" #include "eventcontent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { constexpr const char* FavouriteTag = "m.favourite"; constexpr const char* LowPriorityTag = "m.lowpriority"; -struct TagRecord -{ +struct TagRecord { using order_type = Omittable; order_type order; - TagRecord(order_type order = none) - : order(order) - {} + TagRecord(order_type order = none) : order(order) {} bool operator<(const TagRecord& other) const { @@ -48,8 +44,7 @@ struct TagRecord }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, TagRecord& rec) { // Parse a float both from JSON double and JSON string because @@ -72,26 +67,23 @@ struct JsonObjectConverter using TagsMap = QHash; -#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ - class _Name : public Event \ - { \ - public: \ - using content_type = _ContentType; \ - DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name(QJsonObject obj) \ - : Event(typeId(), std::move(obj)) \ - {} \ - explicit _Name(_ContentType content) \ - : Event(typeId(), matrixTypeId(), \ - QJsonObject { { QStringLiteral(#_ContentKey), \ - toJson(std::move(content)) } }) \ - {} \ - auto _ContentKey() const \ - { \ - return content(#_ContentKey##_ls); \ - } \ - }; \ - REGISTER_EVENT_TYPE(_Name) \ +#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ + class _Name : public Event { \ + public: \ + using content_type = _ContentType; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ + explicit _Name(QJsonObject obj) : Event(typeId(), std::move(obj)) {} \ + explicit _Name(_ContentType content) \ + : Event(typeId(), matrixTypeId(), \ + QJsonObject { { QStringLiteral(#_ContentKey), \ + toJson(std::move(content)) } }) \ + {} \ + auto _ContentKey() const \ + { \ + return content(#_ContentKey##_ls); \ + } \ + }; \ + REGISTER_EVENT_TYPE(_Name) \ // End of macro DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags) diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index 69662eb9..052f732d 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -20,10 +20,8 @@ #include "roomevent.h" -namespace QMatrixClient -{ -class CallAnswerEvent : public CallEventBase -{ +namespace QMatrixClient { +class CallAnswerEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index 1c12b800..2a915a43 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -20,10 +20,8 @@ #include "roomevent.h" -namespace QMatrixClient -{ -class CallCandidatesEvent : public CallEventBase -{ +namespace QMatrixClient { +class CallCandidatesEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index 0a5a3283..97fa2f52 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -20,10 +20,8 @@ #include "roomevent.h" -namespace QMatrixClient -{ -class CallHangupEvent : public CallEventBase -{ +namespace QMatrixClient { +class CallHangupEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index 4334ca5b..9b9d0ae5 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -20,10 +20,8 @@ #include "roomevent.h" -namespace QMatrixClient -{ -class CallInviteEvent : public CallEventBase -{ +namespace QMatrixClient { +class CallInviteEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 6b4a08ee..94857a93 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -20,16 +20,12 @@ #include "event.h" -namespace QMatrixClient -{ -class DirectChatEvent : public Event -{ +namespace QMatrixClient { +class DirectChatEvent : public Event { public: DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) - explicit DirectChatEvent(const QJsonObject& obj) - : Event(typeId(), obj) - {} + explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) {} QMultiHash usersToDirectChats() const; }; diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index 0dbce25c..67298a27 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -3,8 +3,7 @@ #include "e2ee.h" #include "roomevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { class Room; /* * While the specification states: @@ -24,8 +23,7 @@ class Room; * in general. It's possible, because RoomEvent interface is similar to Event's * one and doesn't add new restrictions, just provides additional features. */ -class EncryptedEvent : public RoomEvent -{ +class EncryptedEvent : public RoomEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent) diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index 995c8dad..0c732a51 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -15,11 +15,9 @@ static const std::array encryptionStrings = { { QMatrixClient::MegolmV1AesSha2AlgoKey } }; -namespace QMatrixClient -{ +namespace QMatrixClient { template <> -struct JsonConverter -{ +struct JsonConverter { static EncryptionType load(const QJsonValue& jv) { const auto& encryptionString = jv.toString(); diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index 97119c8d..debabcae 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -21,16 +21,10 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient -{ -class EncryptionEventContent : public EventContent::Base -{ +namespace QMatrixClient { +class EncryptionEventContent : public EventContent::Base { public: - enum EncryptionType : size_t - { - MegolmV1AesSha2 = 0, - Undefined - }; + enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined }; explicit EncryptionEventContent(EncryptionType et = Undefined) : encryption(et) @@ -48,8 +42,7 @@ protected: using EncryptionType = EncryptionEventContent::EncryptionType; -class EncryptionEvent : public StateEvent -{ +class EncryptionEvent : public StateEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 718a6602..694254fe 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -42,9 +42,7 @@ QString EventTypeRegistry::getMatrixType(event_type_t typeId) : QString(); } -Event::Event(Type type, const QJsonObject& json) - : _type(type) - , _json(json) +Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) { if (!json.contains(ContentKeyL) && !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) { diff --git a/lib/events/event.h b/lib/events/event.h index d6525281..686bd8e0 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -25,8 +25,7 @@ # define USE_EVENTTYPE_ALIAS 1 #endif -namespace QMatrixClient -{ +namespace QMatrixClient { // === event_ptr_tt<> and type casting facilities === template @@ -85,8 +84,7 @@ inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content) using event_type_t = size_t; using event_mtype_t = const char*; -class EventTypeRegistry -{ +class EventTypeRegistry { public: ~EventTypeRegistry() = default; @@ -121,8 +119,7 @@ inline event_type_t EventTypeRegistry::initializeTypeId() } template -struct EventTypeTraits -{ +struct EventTypeTraits { static event_type_t id() { static const auto id = EventTypeRegistry::initializeTypeId(); @@ -148,8 +145,7 @@ inline event_ptr_tt makeEvent(ArgTs&&... args) } template -class EventFactory -{ +class EventFactory { public: template static auto addMethod(FnT&& method) @@ -223,8 +219,7 @@ inline auto registerEventType() // === Event === -class Event -{ +class Event { Q_GADGET Q_PROPERTY(Type type READ type CONSTANT) Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) @@ -304,16 +299,14 @@ using Events = EventsArray; // to enable its deserialisation from a /sync and other // polymorphic event arrays #define REGISTER_EVENT_TYPE(_Type) \ - namespace \ - { \ + namespace { \ [[gnu::unused]] static const auto _factoryAdded##_Type = \ registerEventType<_Type>(); \ } \ // End of macro #ifdef USE_EVENTTYPE_ALIAS -namespace EventType -{ +namespace EventType { inline event_type_t logEventType(event_type_t id, const char* idName) { qDebug(EVENTS) << "Using id" << id << "for" << idName; @@ -324,8 +317,7 @@ namespace EventType // This macro provides constants in EventType:: namespace for // back-compatibility with libQMatrixClient 0.3 event type system. # define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ - namespace EventType \ - { \ + namespace EventType { \ [[deprecated("Use is<>(), eventCast<>() or " \ "visit<>()")]] static const auto _Id = \ logEventType(typeId<_Type>(), #_Id); \ diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 2b84c2b7..814f2787 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -70,8 +70,7 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType, const QSize& imageSize, const QString& originalFilename) - : FileInfo(u, fileSize, mimeType, originalFilename) - , imageSize(imageSize) + : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize) {} ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 7a3db1fc..5c0f92d1 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -26,10 +26,8 @@ #include #include -namespace QMatrixClient -{ -namespace EventContent -{ +namespace QMatrixClient { +namespace EventContent { /** * A base class for all content types that can be stored * in a RoomMessageEvent @@ -40,12 +38,9 @@ namespace EventContent * assumed but not required that a content object can also be created * from plain data. */ - class Base - { + class Base { public: - explicit Base(QJsonObject o = {}) - : originalJson(std::move(o)) - {} + explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {} virtual ~Base() = default; // FIXME: make toJson() from converters.* work on base classes @@ -90,8 +85,7 @@ namespace EventContent * * This class is not polymorphic. */ - class FileInfo - { + class FileInfo { public: explicit FileInfo(const QUrl& u, qint64 payloadSize = -1, const QMimeType& mimeType = {}, @@ -131,8 +125,7 @@ namespace EventContent /** * A content info class for image content types: image, thumbnail, video */ - class ImageInfo : public FileInfo - { + class ImageInfo : public FileInfo { public: explicit ImageInfo(const QUrl& u, qint64 fileSize = -1, QMimeType mimeType = {}, const QSize& imageSize = {}, @@ -153,16 +146,11 @@ namespace EventContent * the JSON representation of event content; namely, * "info/thumbnail_url" and "info/thumbnail_info" fields are used. */ - class Thumbnail : public ImageInfo - { + class Thumbnail : public ImageInfo { public: - Thumbnail() - : ImageInfo(QUrl()) - {} // To allow empty thumbnails + Thumbnail() : ImageInfo(QUrl()) {} // To allow empty thumbnails Thumbnail(const QJsonObject& infoJson); - Thumbnail(const ImageInfo& info) - : ImageInfo(info) - {} + Thumbnail(const ImageInfo& info) : ImageInfo(info) {} using ImageInfo::ImageInfo; /** @@ -172,12 +160,9 @@ namespace EventContent void fillInfoJson(QJsonObject* infoJson) const; }; - class TypedBase : public Base - { + class TypedBase : public Base { public: - explicit TypedBase(QJsonObject o = {}) - : Base(std::move(o)) - {} + explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {} virtual QMimeType type() const = 0; virtual const FileInfo* fileInfo() const { return nullptr; } virtual FileInfo* fileInfo() { return nullptr; } @@ -198,8 +183,7 @@ namespace EventContent * \tparam InfoT base info class */ template - class UrlBasedContent : public TypedBase, public InfoT - { + class UrlBasedContent : public TypedBase, public InfoT { public: using InfoT::InfoT; explicit UrlBasedContent(const QJsonObject& json) @@ -227,13 +211,11 @@ namespace EventContent }; template - class UrlWithThumbnailContent : public UrlBasedContent - { + class UrlWithThumbnailContent : public UrlBasedContent { public: using UrlBasedContent::UrlBasedContent; explicit UrlWithThumbnailContent(const QJsonObject& json) - : UrlBasedContent(json) - , thumbnail(InfoT::originalInfoJson) + : UrlBasedContent(json), thumbnail(InfoT::originalInfoJson) { // Another small hack, to simplify making a thumbnail link UrlBasedContent::originalJson.insert("thumbnailMediaId", diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index a203eaa3..9e8bb410 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -20,10 +20,8 @@ #include "stateevent.h" -namespace QMatrixClient -{ -namespace _impl -{ +namespace QMatrixClient { +namespace _impl { template static inline auto loadEvent(const QJsonObject& json, const QString& matrixType) @@ -75,8 +73,7 @@ inline StateEventPtr loadStateEvent(const QString& matrixType, } template -struct JsonConverter> -{ +struct JsonConverter> { static auto load(const QJsonValue& jv) { return loadEvent(jv.toObject()); diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h index d524b549..b1e04561 100644 --- a/lib/events/reactionevent.h +++ b/lib/events/reactionevent.h @@ -20,11 +20,9 @@ #include "roomevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { -struct EventRelation -{ +struct EventRelation { using reltypeid_t = const char*; static constexpr reltypeid_t Reply() { return "m.in_reply_to"; } static constexpr reltypeid_t Annotation() { return "m.annotation"; } @@ -48,14 +46,12 @@ struct EventRelation } }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const EventRelation& pod); static void fillFrom(const QJsonObject& jo, EventRelation& pod); }; -class ReactionEvent : public RoomEvent -{ +class ReactionEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent) @@ -63,9 +59,7 @@ public: : RoomEvent(typeId(), matrixTypeId(), { { QStringLiteral("m.relates_to"), toJson(value) } }) {} - explicit ReactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - {} + explicit ReactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {} EventRelation relation() const { return content(QStringLiteral("m.relates_to")); diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index fcb8431b..4a54b744 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -40,8 +40,7 @@ Example of a Receipt Event: using namespace QMatrixClient; -ReceiptEvent::ReceiptEvent(const QJsonObject& obj) - : Event(typeId(), obj) +ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& contents = contentJson(); _eventsWithReceipts.reserve(contents.size()); diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index e8396670..c32e0543 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -23,22 +23,18 @@ #include #include -namespace QMatrixClient -{ -struct Receipt -{ +namespace QMatrixClient { +struct Receipt { QString userId; QDateTime timestamp; }; -struct ReceiptsForEvent -{ +struct ReceiptsForEvent { QString evtId; QVector receipts; }; using EventsWithReceipts = QVector; -class ReceiptEvent : public Event -{ +class ReceiptEvent : public Event { public: DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) explicit ReceiptEvent(const QJsonObject& obj); diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index a7dd9705..3628fb33 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -20,15 +20,12 @@ #include "roomevent.h" -namespace QMatrixClient -{ -class RedactionEvent : public RoomEvent -{ +namespace QMatrixClient { +class RedactionEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) - explicit RedactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) + explicit RedactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {} QString redactedEvent() const diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index ee460339..16aeb070 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -21,18 +21,15 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient -{ -class RoomAvatarEvent : public StateEvent -{ +namespace QMatrixClient { +class RoomAvatarEvent : public StateEvent { // It's a bit of an overkill to use a full-fledged ImageContent // because in reality m.room.avatar usually only has a single URL, // without a thumbnail. But The Spec says there be thumbnails, and // we follow The Spec. public: DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) - explicit RoomAvatarEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) + explicit RoomAvatarEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) {} QUrl url() const { return content().url; } }; diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index 17b86388..c8ba8c40 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -20,22 +20,17 @@ #include "stateevent.h" -namespace QMatrixClient -{ -class RoomCreateEvent : public StateEventBase -{ +namespace QMatrixClient { +class RoomCreateEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) - explicit RoomCreateEvent() - : StateEventBase(typeId(), matrixTypeId()) - {} + explicit RoomCreateEvent() : StateEventBase(typeId(), matrixTypeId()) {} explicit RoomCreateEvent(const QJsonObject& obj) : StateEventBase(typeId(), obj) {} - struct Predecessor - { + struct Predecessor { QString roomId; QString eventId; }; diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index fb715473..543640ca 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -32,8 +32,7 @@ RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, : Event(type, matrixType, contentJson) {} -RoomEvent::RoomEvent(Type type, const QJsonObject& json) - : Event(type, json) +RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json) { const auto unsignedData = json[UnsignedKeyL].toObject(); const auto redaction = unsignedData[RedactedCauseKeyL]; diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index 8edb397c..155d4600 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -22,13 +22,11 @@ #include -namespace QMatrixClient -{ +namespace QMatrixClient { class RedactionEvent; /** This class corresponds to m.room.* events */ -class RoomEvent : public Event -{ +class RoomEvent : public Event { Q_GADGET Q_PROPERTY(QString id READ id) Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) @@ -93,8 +91,7 @@ using RoomEventPtr = event_ptr_tt; using RoomEvents = EventsArray; using RoomEventsRange = Range; -class CallEventBase : public RoomEvent -{ +class CallEventBase : public RoomEvent { public: CallEventBase(Type type, event_mtype_t matrixType, const QString& callId, int version, const QJsonObject& contentJson = {}); diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index e6292b73..3cbf6685 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -28,11 +28,9 @@ static const std::array membershipStrings = { QStringLiteral("leave"), QStringLiteral("ban") } }; -namespace QMatrixClient -{ +namespace QMatrixClient { template <> -struct JsonConverter -{ +struct JsonConverter { static MembershipType load(const QJsonValue& jv) { const auto& membershipString = jv.toString(); diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index c1015df2..59d59e3a 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -21,13 +21,10 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient -{ -class MemberEventContent : public EventContent::Base -{ +namespace QMatrixClient { +class MemberEventContent : public EventContent::Base { public: - enum MembershipType : size_t - { + enum MembershipType : size_t { Invite = 0, Join, Knock, @@ -36,9 +33,7 @@ public: Undefined }; - explicit MemberEventContent(MembershipType mt = Join) - : membership(mt) - {} + explicit MemberEventContent(MembershipType mt = Join) : membership(mt) {} explicit MemberEventContent(const QJsonObject& json); MembershipType membership; @@ -52,16 +47,14 @@ protected: using MembershipType = MemberEventContent::MembershipType; -class RoomMemberEvent : public StateEvent -{ +class RoomMemberEvent : public StateEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) + explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) {} [[deprecated("Use RoomMemberEvent(userId, contentArgs) " "instead")]] RoomMemberEvent(MemberEventContent&& c) @@ -103,8 +96,7 @@ private: }; template <> -class EventFactory -{ +class EventFactory { public: static event_ptr_tt make(const QJsonObject& json, const QString&) diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index da8d59ca..991931de 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -54,8 +54,7 @@ TypedBase* make(const QJsonObject& json) : nullptr; } -struct MsgTypeDesc -{ +struct MsgTypeDesc { QString matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); @@ -174,8 +173,7 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, {} RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - , _content(nullptr) + : RoomEvent(typeId(), obj), _content(nullptr) { if (isRedacted()) return; @@ -281,8 +279,7 @@ TextContent::TextContent(const QString& text, const QString& contentType, mimeType = QMimeDatabase().mimeTypeForName("text/html"); } -namespace QMatrixClient -{ +namespace QMatrixClient { // Overload the default fromJson<> logic that defined in converters.h // as we want template <> @@ -350,8 +347,7 @@ void TextContent::fillJson(QJsonObject* json) const LocationContent::LocationContent(const QString& geoUri, const Thumbnail& thumbnail) - : geoUri(geoUri) - , thumbnail(thumbnail) + : geoUri(geoUri), thumbnail(thumbnail) {} LocationContent::LocationContent(const QJsonObject& json) diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index 1f1fde41..c7a5cb47 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -23,15 +23,13 @@ class QFileInfo; -namespace QMatrixClient -{ +namespace QMatrixClient { namespace MessageEventContent = EventContent; // Back-compatibility /** * The event class corresponding to m.room.message events */ -class RoomMessageEvent : public RoomEvent -{ +class RoomMessageEvent : public RoomEvent { Q_GADGET Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) Q_PROPERTY(QString plainBody READ plainBody CONSTANT) @@ -40,8 +38,7 @@ class RoomMessageEvent : public RoomEvent public: DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) - enum class MsgType - { + enum class MsgType { Text, Emote, Notice, @@ -96,12 +93,10 @@ REGISTER_EVENT_TYPE(RoomMessageEvent) DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; -namespace EventContent -{ +namespace EventContent { // Additional event content types - struct RelatesTo - { + struct RelatesTo { static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; } static constexpr const char* ReplacementTypeId() { return "m.replace"; } QString type; // The only supported relation so far @@ -118,8 +113,7 @@ namespace EventContent * Available fields: mimeType, body. The body can be either rich text * or plain text, depending on what mimeType specifies. */ - class TextContent : public TypedBase - { + class TextContent : public TypedBase { public: TextContent(const QString& text, const QString& contentType, Omittable relatesTo = none); @@ -148,8 +142,7 @@ namespace EventContent * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent : public TypedBase - { + class LocationContent : public TypedBase { public: LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); explicit LocationContent(const QJsonObject& json); @@ -168,8 +161,7 @@ namespace EventContent * A base class for info types that include duration: audio and video */ template - class PlayableContent : public ContentT - { + class PlayableContent : public ContentT { public: using ContentT::ContentT; PlayableContent(const QJsonObject& json) diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index aa9cb766..95fed998 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -20,16 +20,12 @@ #include "stateevent.h" -namespace QMatrixClient -{ -class RoomTombstoneEvent : public StateEventBase -{ +namespace QMatrixClient { +class RoomTombstoneEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) - explicit RoomTombstoneEvent() - : StateEventBase(typeId(), matrixTypeId()) - {} + explicit RoomTombstoneEvent() : StateEventBase(typeId(), matrixTypeId()) {} explicit RoomTombstoneEvent(const QJsonObject& obj) : StateEventBase(typeId(), obj) {} diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 0078c44d..6dad8020 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -20,25 +20,20 @@ #include "stateevent.h" -namespace QMatrixClient -{ -namespace EventContent -{ +namespace QMatrixClient { +namespace EventContent { template - class SimpleContent - { + class SimpleContent { public: using value_type = T; // The constructor is templated to enable perfect forwarding template SimpleContent(QString keyName, TT&& value) - : value(std::forward(value)) - , key(std::move(keyName)) + : value(std::forward(value)), key(std::move(keyName)) {} SimpleContent(const QJsonObject& json, QString keyName) - : value(fromJson(json[keyName])) - , key(std::move(keyName)) + : value(fromJson(json[keyName])), key(std::move(keyName)) {} QJsonObject toJson() const { @@ -54,14 +49,11 @@ namespace EventContent } // namespace EventContent #define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \ - class _Name : public StateEvent> \ - { \ + class _Name : public StateEvent> { \ public: \ using value_type = content_type::value_type; \ DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name() \ - : _Name(value_type()) \ - {} \ + explicit _Name() : _Name(value_type()) {} \ template \ explicit _Name(T&& value) \ : StateEvent(typeId(), matrixTypeId(), QString(), \ @@ -86,8 +78,7 @@ DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) class RoomAliasesEvent - : public StateEvent> -{ + : public StateEvent> { public: DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent) explicit RoomAliasesEvent(const QJsonObject& obj) diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index d1b742ba..757c94ee 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -20,8 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { /// Make a minimal correct Matrix state event JSON template @@ -34,13 +33,11 @@ inline QJsonObject basicStateEventJson(StrT matrixType, { ContentKey, content } }; } -class StateEventBase : public RoomEvent -{ +class StateEventBase : public RoomEvent { public: using factory_t = EventFactory; - StateEventBase(Type type, const QJsonObject& json) - : RoomEvent(type, json) + StateEventBase(Type type, const QJsonObject& json) : RoomEvent(type, json) {} StateEventBase(Type type, event_mtype_t matrixType, const QString& stateKey = {}, @@ -71,8 +68,7 @@ inline bool is(const Event& e) using StateEventKey = QPair; template -struct Prev -{ +struct Prev { template explicit Prev(const QJsonObject& unsignedJson, ContentParamTs&&... contentParams) @@ -86,8 +82,7 @@ struct Prev }; template -class StateEvent : public StateEventBase -{ +class StateEvent : public StateEventBase { public: using content_type = ContentT; @@ -135,11 +130,9 @@ private: }; } // namespace QMatrixClient -namespace std -{ +namespace std { template <> -struct hash -{ +struct hash { size_t operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT { return qHash(k); diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index 128a206a..ee3d6b67 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -22,8 +22,7 @@ using namespace QMatrixClient; -TypingEvent::TypingEvent(const QJsonObject& obj) - : Event(typeId(), obj) +TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& array = contentJson()["user_ids"_ls].toArray(); for (const auto& user : array) diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index 241359b4..c8170865 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -20,10 +20,8 @@ #include "event.h" -namespace QMatrixClient -{ -class TypingEvent : public Event -{ +namespace QMatrixClient { +class TypingEvent : public Event { public: DEFINE_EVENT_TYPEID("m.typing", TypingEvent) diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index e4a74954..a6471ece 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -32,8 +32,7 @@ using namespace QMatrixClient; -struct NetworkReplyDeleter : public QScopedPointerDeleteLater -{ +struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) { if (reply && reply->isRunning()) @@ -42,8 +41,7 @@ struct NetworkReplyDeleter : public QScopedPointerDeleteLater } }; -class BaseJob::Private -{ +class BaseJob::Private { public: // Using an idiom from clang-tidy: // http://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index f445d087..c85d2d90 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -28,32 +28,22 @@ class QNetworkReply; class QSslError; -namespace QMatrixClient -{ +namespace QMatrixClient { class ConnectionData; -enum class HttpVerb -{ - Get, - Put, - Post, - Delete -}; +enum class HttpVerb { Get, Put, Post, Delete }; -struct JobTimeoutConfig -{ +struct JobTimeoutConfig { int jobTimeout; int nextRetryInterval; }; -class BaseJob : public QObject -{ +class BaseJob : public QObject { Q_OBJECT Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries) public: - enum StatusCode - { + enum StatusCode { NoError = 0 // To be compatible with Qt conventions , Success = 0, @@ -93,8 +83,7 @@ public: * A simple wrapper around QUrlQuery that allows its creation from * a list of string pairs */ - class Query : public QUrlQuery - { + class Query : public QUrlQuery { public: using QUrlQuery::QUrlQuery; Query() = default; @@ -115,16 +104,10 @@ public: * along the lines of StatusCode, with additional values * starting at UserDefinedError */ - class Status - { + class Status { public: - Status(StatusCode c) - : code(c) - {} - Status(int c, QString m) - : code(c) - , message(std::move(m)) - {} + 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) diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 3dff5a68..9722186c 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -6,12 +6,9 @@ using namespace QMatrixClient; -class DownloadFileJob::Private -{ +class DownloadFileJob::Private { public: - Private() - : tempFile(new QTemporaryFile()) - {} + Private() : tempFile(new QTemporaryFile()) {} explicit Private(const QString& localFilename) : targetFile(new QFile(localFilename)) diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index 58858448..ebfe5a0d 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -2,15 +2,10 @@ #include "csapi/content-repo.h" -namespace QMatrixClient -{ -class DownloadFileJob : public GetContentJob -{ +namespace QMatrixClient { +class DownloadFileJob : public GetContentJob { public: - enum - { - FileError = BaseJob::UserDefinedError + 1 - }; + enum { FileError = BaseJob::UserDefinedError + 1 }; using GetContentJob::makeRequestUrl; static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri); diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index eeabe7a9..df0a7f31 100644 --- a/lib/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h @@ -22,10 +22,8 @@ #include -namespace QMatrixClient -{ -class MediaThumbnailJob : public GetContentThumbnailJob -{ +namespace QMatrixClient { +class MediaThumbnailJob : public GetContentThumbnailJob { public: using GetContentThumbnailJob::makeRequestUrl; static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri, diff --git a/lib/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h index d53ae66c..cf482a9a 100644 --- a/lib/jobs/postreadmarkersjob.h +++ b/lib/jobs/postreadmarkersjob.h @@ -24,8 +24,7 @@ using namespace QMatrixClient; -class PostReadMarkersJob : public BaseJob -{ +class PostReadMarkersJob : public BaseJob { public: explicit PostReadMarkersJob(const QString& roomId, const QString& readUpToEventId) diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp index 8248d6b1..6ad7c007 100644 --- a/lib/jobs/requestdata.cpp +++ b/lib/jobs/requestdata.cpp @@ -23,16 +23,10 @@ inline auto fromJson(const JsonDataT& jdata) return fromData(QJsonDocument(jdata).toJson(QJsonDocument::Compact)); } -RequestData::RequestData(const QByteArray& a) - : _source(fromData(a)) -{} +RequestData::RequestData(const QByteArray& a) : _source(fromData(a)) {} -RequestData::RequestData(const QJsonObject& jo) - : _source(fromJson(jo)) -{} +RequestData::RequestData(const QJsonObject& jo) : _source(fromJson(jo)) {} -RequestData::RequestData(const QJsonArray& ja) - : _source(fromJson(ja)) -{} +RequestData::RequestData(const QJsonArray& ja) : _source(fromJson(ja)) {} RequestData::~RequestData() = default; diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h index 974a9ddf..55987a3b 100644 --- a/lib/jobs/requestdata.h +++ b/lib/jobs/requestdata.h @@ -26,23 +26,20 @@ class QJsonArray; class QJsonDocument; class QIODevice; -namespace QMatrixClient -{ +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 -{ +class RequestData { public: RequestData() = default; RequestData(const QByteArray& a); RequestData(const QJsonObject& jo); RequestData(const QJsonArray& ja); - RequestData(QIODevice* source) - : _source(std::unique_ptr(source)) + RequestData(QIODevice* source) : _source(std::unique_ptr(source)) {} RequestData(const RequestData&) = delete; RequestData& operator=(const RequestData&) = delete; diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index e2cec8f7..8f925414 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -22,10 +22,8 @@ #include "../syncdata.h" #include "basejob.h" -namespace QMatrixClient -{ -class SyncJob : public BaseJob -{ +namespace QMatrixClient { +class SyncJob : public BaseJob { public: explicit SyncJob(const QString& since = {}, const QString& filter = {}, int timeout = -1, const QString& presence = {}); diff --git a/lib/joinstate.h b/lib/joinstate.h index e4dc679a..fcf840ae 100644 --- a/lib/joinstate.h +++ b/lib/joinstate.h @@ -22,10 +22,8 @@ #include -namespace QMatrixClient -{ -enum class JoinState : unsigned int -{ +namespace QMatrixClient { +enum class JoinState : unsigned int { Join = 0x1, Invite = 0x2, Leave = 0x4, diff --git a/lib/logging.h b/lib/logging.h index a50c1795..24799752 100644 --- a/lib/logging.h +++ b/lib/logging.h @@ -28,8 +28,7 @@ Q_DECLARE_LOGGING_CATEGORY(EPHEMERAL) Q_DECLARE_LOGGING_CATEGORY(JOBS) Q_DECLARE_LOGGING_CATEGORY(SYNCJOB) -namespace QMatrixClient -{ +namespace QMatrixClient { // QDebug manipulators using QDebugManip = QDebug (*)(QDebug); diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 9ac589b8..7bff654c 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -23,15 +23,13 @@ using namespace QMatrixClient; -class NetworkAccessManager::Private -{ +class NetworkAccessManager::Private { public: QList ignoredSslErrors; }; NetworkAccessManager::NetworkAccessManager(QObject* parent) - : QNetworkAccessManager(parent) - , d(std::make_unique()) + : QNetworkAccessManager(parent), d(std::make_unique()) {} QList NetworkAccessManager::ignoredSslErrors() const diff --git a/lib/networkaccessmanager.h b/lib/networkaccessmanager.h index bf8f0cbc..dfa388f0 100644 --- a/lib/networkaccessmanager.h +++ b/lib/networkaccessmanager.h @@ -22,10 +22,8 @@ #include -namespace QMatrixClient -{ -class NetworkAccessManager : public QNetworkAccessManager -{ +namespace QMatrixClient { +class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: NetworkAccessManager(QObject* parent = nullptr); diff --git a/lib/networksettings.h b/lib/networksettings.h index 0c21a9fe..75bf726d 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -24,10 +24,8 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) -namespace QMatrixClient -{ -class NetworkSettings : public SettingsGroup -{ +namespace QMatrixClient { +class NetworkSettings : public SettingsGroup { Q_OBJECT QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 1b3229d4..94c1ec60 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -22,10 +22,8 @@ #include -namespace QMatrixClient -{ -namespace _impl -{ +namespace QMatrixClient { +namespace _impl { template inline QMetaObject::Connection connectUntil(SenderT* sender, SignalT signal, ContextT* context, @@ -86,12 +84,10 @@ inline auto connectSingleShot(SenderT* sender, SignalT signal, * destruction. */ template -class ConnectionsGuard : public QPointer -{ +class ConnectionsGuard : public QPointer { public: ConnectionsGuard(T* publisher, QObject* subscriber) - : QPointer(publisher) - , subscriber(subscriber) + : QPointer(publisher), subscriber(subscriber) {} ~ConnectionsGuard() { diff --git a/lib/room.cpp b/lib/room.cpp index cf58f3c0..b32d3492 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -78,24 +78,16 @@ using std::move; using std::llround; #endif -enum EventsPlacement : int -{ - Older = -1, - Newer = 1 -}; +enum EventsPlacement : int { Older = -1, Newer = 1 }; -class Room::Private -{ +class Room::Private { public: /// Map of user names to users /** User names potentially duplicate, hence QMultiHash. */ using members_map_t = QMultiHash; Private(Connection* c, QString id_, JoinState initialJoinState) - : q(nullptr) - , connection(c) - , id(move(id_)) - , joinState(initialJoinState) + : q(nullptr), connection(c), id(move(id_)), joinState(initialJoinState) {} Room* q; @@ -144,8 +136,7 @@ public: QPointer eventsHistoryJob; QPointer allMembersJob; - struct FileTransferPrivateInfo - { + struct FileTransferPrivateInfo { FileTransferPrivateInfo() = default; FileTransferPrivateInfo(BaseJob* j, const QString& fileName, bool isUploading = false) @@ -354,8 +345,7 @@ private: decltype(Room::Private::baseState) Room::Private::stubbedState {}; Room::Room(Connection* connection, QString id, JoinState initialJoinState) - : QObject(connection) - , d(new Private(connection, id, initialJoinState)) + : QObject(connection), d(new Private(connection, id, initialJoinState)) { setObjectName(id); // See "Accessing the Public Class" section in @@ -1617,9 +1607,9 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, // Normally in this situation we should instruct // the media server to delete the file; alas, there's no // API specced for that. - qCWarning(MAIN) - << "File uploaded to" << mxcUri - << "but the event referring to it was cancelled"; + qCWarning(MAIN) << "File uploaded to" << mxcUri + << "but the event referring to it was " + "cancelled"; } context->deleteLater(); } @@ -2382,9 +2372,9 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) break; case MembershipType::Join: if (evt.membership() == MembershipType::Invite) - qCWarning(MAIN) - << "Invalid membership change from Join to Invite:" - << evt; + qCWarning(MAIN) << "Invalid membership change from " + "Join to Invite:" + << evt; if (evt.membership() != prevMembership) { disconnect(u, &User::nameAboutToChange, this, nullptr); disconnect(u, &User::nameChanged, this, nullptr); @@ -2564,7 +2554,8 @@ Room::Private::buildShortlist(const ContT& users) const std::partial_sort_copy( users.begin(), users.end(), shortlist.begin(), shortlist.end(), [this](const User* u1, const User* u2) { - // localUser(), if it's in the list, is sorted below all others + // localUser(), if it's in the list, is sorted + // below all others return isLocalUser(u2) || (!isLocalUser(u1) && u1->id() < u2->id()); }); return shortlist; diff --git a/lib/room.h b/lib/room.h index f5433fb6..d6fb8a61 100644 --- a/lib/room.h +++ b/lib/room.h @@ -35,8 +35,7 @@ #include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Event; class Avatar; class SyncRoomData; @@ -52,8 +51,7 @@ class RedactEventJob; * This is specifically tuned to work with QML exposing all traits as * Q_PROPERTY values. */ -class FileTransferInfo -{ +class FileTransferInfo { Q_GADGET Q_PROPERTY(bool isUpload MEMBER isUpload CONSTANT) Q_PROPERTY(bool active READ active CONSTANT) @@ -65,14 +63,7 @@ class FileTransferInfo Q_PROPERTY(QUrl localDir MEMBER localDir CONSTANT) Q_PROPERTY(QUrl localPath MEMBER localPath CONSTANT) public: - enum Status - { - None, - Started, - Completed, - Failed, - Cancelled - }; + enum Status { None, Started, Completed, Failed, Cancelled }; Status status = None; bool isUpload = false; int progress = 0; @@ -86,8 +77,7 @@ public: bool failed() const { return status == Failed; } }; -class Room : public QObject -{ +class Room : public QObject { Q_OBJECT Q_PROPERTY(Connection* connection READ connection CONSTANT) Q_PROPERTY(User* localUser READ localUser CONSTANT) @@ -146,8 +136,7 @@ public: using rev_iter_t = Timeline::const_reverse_iterator; using timeline_iter_t = Timeline::const_iterator; - enum Change : uint - { + enum Change : uint { NoChange = 0x0, NameChange = 0x1, CanonicalAliasChange = 0x2, @@ -663,12 +652,9 @@ private: void setJoinState(JoinState state); }; -class MemberSorter -{ +class MemberSorter { public: - explicit MemberSorter(const Room* r) - : room(r) - {} + explicit MemberSorter(const Room* r) : room(r) {} bool operator()(User* u1, User* u2) const; bool operator()(User* u1, const QString& u2name) const; diff --git a/lib/settings.h b/lib/settings.h index e1ca0866..6747631e 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -24,10 +24,8 @@ class QVariant; -namespace QMatrixClient -{ -class Settings : public QSettings -{ +namespace QMatrixClient { +class Settings : public QSettings { Q_OBJECT public: /** @@ -42,9 +40,7 @@ public: #if defined(_MSC_VER) && _MSC_VER < 1900 // VS 2013 (and probably older) aren't friends with 'using' statements // that involve private constructors - explicit Settings(QObject* parent = 0) - : QSettings(parent) - {} + explicit Settings(QObject* parent = 0) : QSettings(parent) {} #else using QSettings::QSettings; #endif @@ -71,8 +67,7 @@ protected: QSettings legacySettings { legacyOrganizationName, legacyApplicationName }; }; -class SettingsGroup : public Settings -{ +class SettingsGroup : public Settings { public: template explicit SettingsGroup(QString path, ArgTs&&... qsettingsArgs) @@ -121,8 +116,7 @@ private: setValue(QStringLiteral(qsettingname), std::move(newValue)); \ } -class AccountSettings : public SettingsGroup -{ +class AccountSettings : public SettingsGroup { Q_OBJECT Q_PROPERTY(QString userId READ userId CONSTANT) QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) diff --git a/lib/syncdata.h b/lib/syncdata.h index 6932878d..ad9902e4 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -22,8 +22,7 @@ #include "events/stateevent.h" -namespace QMatrixClient -{ +namespace QMatrixClient { /// Room summary, as defined in MSC688 /** * Every member of this structure is an Omittable; as per the MSC, only @@ -32,8 +31,7 @@ namespace QMatrixClient * means that nothing has come from the server; heroes.value().isEmpty() * means a peculiar case of a room with the only member - the current user. */ -struct RoomSummary -{ +struct RoomSummary { Omittable joinedMemberCount; Omittable invitedMemberCount; Omittable heroes; //< mxids of users to take part in the room @@ -48,14 +46,12 @@ struct RoomSummary }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomSummary& rs); static void fillFrom(const QJsonObject& jo, RoomSummary& rs); }; -class SyncRoomData -{ +class SyncRoomData { public: QString roomId; JoinState joinState; @@ -82,8 +78,7 @@ public: // QVector cannot work with non-copiable objects, std::vector can. using SyncDataList = std::vector; -class SyncData -{ +class SyncData { public: SyncData() = default; explicit SyncData(const QString& cacheFileName); diff --git a/lib/user.cpp b/lib/user.cpp index f0216454..0705aee7 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -41,8 +41,7 @@ using namespace QMatrixClient; using namespace std::placeholders; using std::move; -class User::Private -{ +class User::Private { public: static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } @@ -184,8 +183,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, } User::User(QString userId, Connection* connection) - : QObject(connection) - , d(new Private(move(userId), connection)) + : QObject(connection), d(new Private(move(userId), connection)) { setObjectName(userId); } diff --git a/lib/user.h b/lib/user.h index f4d7cff3..779efb34 100644 --- a/lib/user.h +++ b/lib/user.h @@ -23,14 +23,12 @@ #include #include -namespace QMatrixClient -{ +namespace QMatrixClient { class Connection; class Room; class RoomMemberEvent; -class User : public QObject -{ +class User : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id CONSTANT) Q_PROPERTY(bool isGuest READ isGuest CONSTANT) diff --git a/lib/util.cpp b/lib/util.cpp index 9e0807c6..1919e811 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -124,7 +124,8 @@ QString QMatrixClient::serverPart(const QString& mxId) % ServerPartRegEx % ")$"; static QRegularExpression parser( re, - QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits + QRegularExpression::UseUnicodePropertiesOption); // Because Asian + // digits return parser.match(mxId).captured(1); } @@ -148,16 +149,14 @@ void f2(int, QString); static_assert(std::is_same, QString>::value, "Test fn_arg_t<>"); -struct S -{ +struct S { int mf(); }; static_assert(is_callable_v, "Test member function"); static_assert(returns(), "Test returns<> with member function"); -struct Fo -{ +struct Fo { int operator()(); }; static_assert(is_callable_v, "Test is_callable<> with function object"); @@ -165,8 +164,7 @@ static_assert(function_traits::arg_number == 0, "Test function object"); static_assert(std::is_same, int>::value, "Test return type of function object"); -struct Fo1 -{ +struct Fo1 { void operator()(int); }; static_assert(function_traits::arg_number == 1, "Test function object 1"); @@ -182,13 +180,11 @@ static_assert(std::is_same, int>::value, #endif template -struct fn_object -{ +struct fn_object { static int smf(double) { return 0; } }; template <> -struct fn_object -{ +struct fn_object { void operator()(QString); }; static_assert(is_callable_v>, "Test function object"); diff --git a/lib/util.h b/lib/util.h index a29f6253..d055fa46 100644 --- a/lib/util.h +++ b/lib/util.h @@ -63,8 +63,7 @@ static void qAsConst(const T&&) Q_DECL_EQ_DELETE; # define BROKEN_INITIALIZER_LISTS #endif -namespace QMatrixClient -{ +namespace QMatrixClient { // The below enables pretty-printing of enums in logs #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) # define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) @@ -88,8 +87,7 @@ inline auto unique_ptr_cast(PtrT2&& p) return std::unique_ptr(static_cast(p.release())); } -struct NoneTag -{}; +struct NoneTag {}; constexpr NoneTag none {}; /** A crude substitute for `optional` while we're not C++17 @@ -97,27 +95,17 @@ constexpr NoneTag none {}; * Only works with default-constructible types. */ template -class Omittable -{ +class Omittable { static_assert(!std::is_reference::value, "You cannot make an Omittable<> with a reference type"); public: using value_type = std::decay_t; - explicit Omittable() - : Omittable(none) - {} - Omittable(NoneTag) - : _value(value_type()) - , _omitted(true) - {} - Omittable(const value_type& val) - : _value(val) - {} - Omittable(value_type&& val) - : _value(std::move(val)) - {} + explicit Omittable() : Omittable(none) {} + Omittable(NoneTag) : _value(value_type()), _omitted(true) {} + Omittable(const value_type& val) : _value(val) {} + Omittable(value_type&& val) : _value(std::move(val)) {} Omittable& operator=(const value_type& val) { _value = val; @@ -192,8 +180,7 @@ private: bool _omitted = false; }; -namespace _impl -{ +namespace _impl { template struct fn_traits; } @@ -205,13 +192,11 @@ namespace _impl * https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 */ template -struct function_traits : public _impl::fn_traits -{}; +struct function_traits : public _impl::fn_traits {}; // Specialisation for a function template -struct function_traits -{ +struct function_traits { static constexpr auto is_callable = true; using return_type = ReturnT; using arg_types = std::tuple; @@ -219,30 +204,26 @@ struct function_traits static constexpr auto arg_number = std::tuple_size::value; }; -namespace _impl -{ +namespace _impl { template - struct fn_traits - { + struct fn_traits { static constexpr auto is_callable = false; }; template struct fn_traits - : public fn_traits - {}; // A generic function object that has (non-overloaded) operator() + : public fn_traits { + }; // A generic function object that has (non-overloaded) operator() // Specialisation for a member function template struct fn_traits - : function_traits - {}; + : function_traits {}; // Specialisation for a const member function template struct fn_traits - : function_traits - {}; + : function_traits {}; } // namespace _impl template @@ -272,22 +253,15 @@ inline auto operator"" _ls(const char* s, std::size_t size) * are at least ForwardIterators. Inspired by Ranges TS. */ template -class Range -{ +class Range { // Looking forward for Ranges TS to produce something (in C++23?..) using iterator = typename ArrayT::iterator; using const_iterator = typename ArrayT::const_iterator; using size_type = typename ArrayT::size_type; public: - Range(ArrayT& arr) - : from(std::begin(arr)) - , to(std::end(arr)) - {} - Range(iterator from, iterator to) - : from(from) - , to(to) - {} + Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {} + Range(iterator from, iterator to) : from(from), to(to) {} size_type size() const { -- cgit v1.2.3 From 16ed52736f38b7d42fadc5174472a48f6c623c8f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 2 Aug 2019 18:32:38 +0900 Subject: Room/qmc-example: consider fake state event rejection as valid This is an addition in https://github.com/matrix-org/synapse/pull/5805 - Synapse no more tolerates fake state events (which actually obviates the need for this test but fake state events still go through on older Synapses). To allow checking for both cases Room behaviour has been slightly changed (without compat breakage) to make sure the pending event status is set to ReachedServer (and pendingEventChanged() is emitted, if necessary) before merging the pending event into the timeline. --- lib/room.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index b32d3492..95dba857 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1520,6 +1520,7 @@ QString Room::retryMessage(const QString& txnId) " events are likely to be in the timeline after retry"; } it->resetStatus(); + emit pendingEventChanged(int(it - d->unsyncedEvents.begin())); return d->doSendEvent(it->event()); } @@ -2189,6 +2190,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto* nextPendingEvt = nextPending->get(); const auto pendingEvtIdx = int(nextPendingPair.second - unsyncedEvents.begin()); + nextPendingPair.second->setReachedServer(nextPendingEvt->id()); + emit q->pendingEventChanged(pendingEvtIdx); emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); qDebug(EVENTS) << "Merging pending event from transaction" << nextPendingEvt->transactionId() << "into" -- cgit v1.2.3 From 2681303c2aed1bf20e385c41f1382f5aade66e35 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 2 Aug 2019 19:18:51 +0900 Subject: Room: avoid assertion crashes; name variables better --- lib/room.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 95dba857..52f86616 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1469,8 +1469,10 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) return; } - it->setReachedServer(call->eventId()); - emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); + if (it->deliveryStatus() != EventStatus::ReachedServer) { + it->setReachedServer(call->eventId()); + emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); + } }); } else onEventSendingFailure(txnId); @@ -2171,10 +2173,11 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto nextPendingPair = findFirstOf(it, events.end(), unsyncedEvents.begin(), unsyncedEvents.end(), isEchoEvent); - auto nextPending = nextPendingPair.first; + const auto& remoteEcho = nextPendingPair.first; + const auto& localEcho = nextPendingPair.second; - if (it != nextPending) { - RoomEventsRange eventsSpan { it, nextPending }; + if (it != remoteEcho) { + RoomEventsRange eventsSpan { it, remoteEcho }; emit q->aboutToAddNewMessages(eventsSpan); auto insertedSize = moveEventsToTimeline(eventsSpan, Newer); totalInserted += insertedSize; @@ -2183,15 +2186,16 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) emit q->addedMessages(firstInserted->index(), timeline.back().index()); } - if (nextPending == events.end()) + if (remoteEcho == events.end()) break; - it = nextPending + 1; - auto* nextPendingEvt = nextPending->get(); - const auto pendingEvtIdx = - int(nextPendingPair.second - unsyncedEvents.begin()); - nextPendingPair.second->setReachedServer(nextPendingEvt->id()); - emit q->pendingEventChanged(pendingEvtIdx); + it = remoteEcho + 1; + auto* nextPendingEvt = remoteEcho->get(); + const auto pendingEvtIdx = int(localEcho - unsyncedEvents.begin()); + if (localEcho->deliveryStatus() != EventStatus::ReachedServer) { + localEcho->setReachedServer(nextPendingEvt->id()); + emit q->pendingEventChanged(pendingEvtIdx); + } emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); qDebug(EVENTS) << "Merging pending event from transaction" << nextPendingEvt->transactionId() << "into" @@ -2200,13 +2204,12 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (transfer.status != FileTransferInfo::None) fileTransfers.insert(nextPendingEvt->id(), transfer); // After emitting pendingEventAboutToMerge() above we cannot rely - // on the previously obtained nextPendingPair.second staying valid + // on the previously obtained localEcho staying valid // because a signal handler may send another message, thereby altering // unsyncedEvents (see #286). Fortunately, unsyncedEvents only grows at // its back so we can rely on the index staying valid at least. unsyncedEvents.erase(unsyncedEvents.begin() + pendingEvtIdx); - if (auto insertedSize = moveEventsToTimeline({ nextPending, it }, - Newer)) { + if (auto insertedSize = moveEventsToTimeline({ remoteEcho, it }, Newer)) { totalInserted += insertedSize; q->onAddNewTimelineEvents(timeline.cend() - insertedSize); } -- cgit v1.2.3 From 7a5b359b8823646ce97cbaf05c251cb04c291466 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jul 2019 18:16:30 +0900 Subject: Rename zero-impact strings --- lib/connection.cpp | 5 ++--- lib/jobs/basejob.cpp | 4 ++-- lib/settings.h | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 6cd6ad0b..58d2e01a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -95,8 +95,7 @@ public: DirectChatsMap directChats; DirectChatUsersMap directChatUsers; // The below two variables track local changes between sync completions. - // See also: - // https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events + // See https://github.com/quotient-im/libQuotient/wiki/Handling-direct-chat-events DirectChatsMap dcLocalAdditions; DirectChatsMap dcLocalRemovals; std::unordered_map accountData; @@ -435,7 +434,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) visit( *eventPtr, [this](const DirectChatEvent& dce) { - // https://github.com/QMatrixClient/libqmatrixclient/wiki/Handling-direct-chat-events + // https://github.com/quotient-im/libQuotient/wiki/Handling-direct-chat-events const auto& usersToDCs = dce.usersToDirectChats(); DirectChatsMap remoteRemovals = erase_if(d->directChats, [&usersToDCs, this](auto it) { diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index a6471ece..5615736e 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -321,7 +321,7 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const { // QNetworkReply error codes seem to be flawed when it comes to HTTP; - // see, e.g., https://github.com/QMatrixClient/libqmatrixclient/issues/200 + // see, e.g., https://github.com/quotient-im/libQuotient/issues/200 // so check genuine HTTP codes. The below processing is based on // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes const auto httpCodeHeader = @@ -576,7 +576,7 @@ QUrl BaseJob::errorUrl() const { return d->errorUrl; } void BaseJob::setStatus(Status s) { // The crash that led to this code has been reported in - // https://github.com/QMatrixClient/Quaternion/issues/566 - basically, + // https://github.com/quotient-im/Quaternion/issues/566 - basically, // when cleaning up childrent of a deleted Connection, there's a chance // of pending jobs being abandoned, calling setStatus(Abandoned). // There's nothing wrong with this; however, the safety check for diff --git a/lib/settings.h b/lib/settings.h index 6747631e..427f5494 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -140,7 +140,7 @@ public: /** \deprecated \sa setToken */ QString accessToken() const; /** \deprecated Storing accessToken in QSettings is unsafe, - * see QMatrixClient/Quaternion#181 */ + * see quotient-im/Quaternion#181 */ void setAccessToken(const QString& accessToken); Q_INVOKABLE void clearAccessToken(); -- cgit v1.2.3 From 27ca32a1e5a56e09b9cc1d94224d2831004dcf3d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jul 2019 19:32:34 +0900 Subject: Namespace: QMatrixClient -> Quotient (with back comp alias) --- lib/application-service/definitions/location.cpp | 2 +- lib/application-service/definitions/location.h | 4 ++-- lib/application-service/definitions/protocol.cpp | 2 +- lib/application-service/definitions/protocol.h | 4 ++-- lib/application-service/definitions/user.cpp | 2 +- lib/application-service/definitions/user.h | 4 ++-- lib/avatar.cpp | 2 +- lib/avatar.h | 6 ++++-- lib/connection.cpp | 6 +++--- lib/connection.h | 6 +++--- lib/connectiondata.cpp | 2 +- lib/connectiondata.h | 4 ++-- lib/converters.cpp | 2 +- lib/converters.h | 4 ++-- lib/csapi/account-data.cpp | 2 +- lib/csapi/account-data.h | 4 ++-- lib/csapi/admin.cpp | 6 +++--- lib/csapi/admin.h | 4 ++-- lib/csapi/administrative_contact.cpp | 10 +++++----- lib/csapi/administrative_contact.h | 4 ++-- lib/csapi/appservice_room_directory.cpp | 2 +- lib/csapi/appservice_room_directory.h | 4 ++-- lib/csapi/banning.cpp | 2 +- lib/csapi/banning.h | 4 ++-- lib/csapi/capabilities.cpp | 6 +++--- lib/csapi/capabilities.h | 4 ++-- lib/csapi/content-repo.cpp | 2 +- lib/csapi/content-repo.h | 4 ++-- lib/csapi/create_room.cpp | 6 +++--- lib/csapi/create_room.h | 4 ++-- lib/csapi/definitions/auth_data.cpp | 2 +- lib/csapi/definitions/auth_data.h | 4 ++-- lib/csapi/definitions/client_device.cpp | 2 +- lib/csapi/definitions/client_device.h | 4 ++-- lib/csapi/definitions/device_keys.cpp | 2 +- lib/csapi/definitions/device_keys.h | 4 ++-- lib/csapi/definitions/event_filter.cpp | 2 +- lib/csapi/definitions/event_filter.h | 4 ++-- lib/csapi/definitions/public_rooms_response.cpp | 2 +- lib/csapi/definitions/public_rooms_response.h | 4 ++-- lib/csapi/definitions/push_condition.cpp | 2 +- lib/csapi/definitions/push_condition.h | 4 ++-- lib/csapi/definitions/push_rule.cpp | 2 +- lib/csapi/definitions/push_rule.h | 4 ++-- lib/csapi/definitions/push_ruleset.cpp | 2 +- lib/csapi/definitions/push_ruleset.h | 4 ++-- lib/csapi/definitions/room_event_filter.cpp | 2 +- lib/csapi/definitions/room_event_filter.h | 4 ++-- lib/csapi/definitions/sync_filter.cpp | 2 +- lib/csapi/definitions/sync_filter.h | 4 ++-- lib/csapi/definitions/user_identifier.cpp | 2 +- lib/csapi/definitions/user_identifier.h | 4 ++-- lib/csapi/definitions/wellknown/full.cpp | 2 +- lib/csapi/definitions/wellknown/full.h | 4 ++-- lib/csapi/definitions/wellknown/homeserver.cpp | 2 +- lib/csapi/definitions/wellknown/homeserver.h | 4 ++-- lib/csapi/definitions/wellknown/identity_server.cpp | 2 +- lib/csapi/definitions/wellknown/identity_server.h | 4 ++-- lib/csapi/device_management.cpp | 2 +- lib/csapi/device_management.h | 4 ++-- lib/csapi/directory.cpp | 2 +- lib/csapi/directory.h | 4 ++-- lib/csapi/event_context.cpp | 2 +- lib/csapi/event_context.h | 4 ++-- lib/csapi/filter.cpp | 2 +- lib/csapi/filter.h | 4 ++-- lib/csapi/inviting.cpp | 2 +- lib/csapi/inviting.h | 4 ++-- lib/csapi/joining.cpp | 10 +++++----- lib/csapi/joining.h | 4 ++-- lib/csapi/keys.cpp | 6 +++--- lib/csapi/keys.h | 4 ++-- lib/csapi/kicking.cpp | 2 +- lib/csapi/kicking.h | 4 ++-- lib/csapi/leaving.cpp | 2 +- lib/csapi/leaving.h | 4 ++-- lib/csapi/list_joined_rooms.cpp | 2 +- lib/csapi/list_joined_rooms.h | 4 ++-- lib/csapi/list_public_rooms.cpp | 6 +++--- lib/csapi/list_public_rooms.h | 4 ++-- lib/csapi/login.cpp | 6 +++--- lib/csapi/login.h | 4 ++-- lib/csapi/logout.cpp | 2 +- lib/csapi/logout.h | 4 ++-- lib/csapi/message_pagination.cpp | 2 +- lib/csapi/message_pagination.h | 4 ++-- lib/csapi/notifications.cpp | 6 +++--- lib/csapi/notifications.h | 4 ++-- lib/csapi/openid.cpp | 2 +- lib/csapi/openid.h | 4 ++-- lib/csapi/peeking_events.cpp | 2 +- lib/csapi/peeking_events.h | 4 ++-- lib/csapi/presence.cpp | 2 +- lib/csapi/presence.h | 4 ++-- lib/csapi/profile.cpp | 2 +- lib/csapi/profile.h | 4 ++-- lib/csapi/pusher.cpp | 10 +++++----- lib/csapi/pusher.h | 4 ++-- lib/csapi/pushrules.cpp | 2 +- lib/csapi/pushrules.h | 4 ++-- lib/csapi/read_markers.cpp | 2 +- lib/csapi/read_markers.h | 4 ++-- lib/csapi/receipts.cpp | 2 +- lib/csapi/receipts.h | 4 ++-- lib/csapi/redaction.cpp | 2 +- lib/csapi/redaction.h | 4 ++-- lib/csapi/registration.cpp | 2 +- lib/csapi/registration.h | 4 ++-- lib/csapi/report_content.cpp | 2 +- lib/csapi/report_content.h | 4 ++-- lib/csapi/room_send.cpp | 2 +- lib/csapi/room_send.h | 4 ++-- lib/csapi/room_state.cpp | 2 +- lib/csapi/room_state.h | 4 ++-- lib/csapi/room_upgrades.cpp | 2 +- lib/csapi/room_upgrades.h | 4 ++-- lib/csapi/rooms.cpp | 6 +++--- lib/csapi/rooms.h | 4 ++-- lib/csapi/search.cpp | 6 +++--- lib/csapi/search.h | 4 ++-- lib/csapi/sso_login_redirect.cpp | 2 +- lib/csapi/sso_login_redirect.h | 4 ++-- lib/csapi/tags.cpp | 6 +++--- lib/csapi/tags.h | 4 ++-- lib/csapi/third_party_lookup.cpp | 2 +- lib/csapi/third_party_lookup.h | 4 ++-- lib/csapi/third_party_membership.cpp | 2 +- lib/csapi/third_party_membership.h | 4 ++-- lib/csapi/to_device.cpp | 2 +- lib/csapi/to_device.h | 4 ++-- lib/csapi/typing.cpp | 2 +- lib/csapi/typing.h | 4 ++-- lib/csapi/users.cpp | 6 +++--- lib/csapi/users.h | 4 ++-- lib/csapi/versions.cpp | 2 +- lib/csapi/versions.h | 4 ++-- lib/csapi/voip.cpp | 2 +- lib/csapi/voip.h | 4 ++-- lib/csapi/wellknown.cpp | 2 +- lib/csapi/wellknown.h | 4 ++-- lib/csapi/whoami.cpp | 2 +- lib/csapi/whoami.h | 4 ++-- lib/csapi/{{base}}.cpp.mustache | 4 ++-- lib/csapi/{{base}}.h.mustache | 4 ++-- lib/e2ee.h | 4 ++-- lib/encryptionmanager.cpp | 2 +- lib/encryptionmanager.h | 4 ++-- lib/eventitem.cpp | 2 +- lib/eventitem.h | 6 +++--- lib/events/accountdataevents.h | 4 ++-- lib/events/callanswerevent.cpp | 2 +- lib/events/callanswerevent.h | 4 ++-- lib/events/callcandidatesevent.h | 4 ++-- lib/events/callhangupevent.cpp | 2 +- lib/events/callhangupevent.h | 4 ++-- lib/events/callinviteevent.cpp | 2 +- lib/events/callinviteevent.h | 4 ++-- lib/events/directchatevent.cpp | 2 +- lib/events/directchatevent.h | 4 ++-- lib/events/encryptedevent.h | 4 ++-- lib/events/encryptionevent.cpp | 6 +++--- lib/events/encryptionevent.h | 4 ++-- lib/events/event.cpp | 2 +- lib/events/event.h | 10 +++++----- lib/events/eventcontent.cpp | 2 +- lib/events/eventcontent.h | 4 ++-- lib/events/eventloader.h | 4 ++-- lib/events/reactionevent.cpp | 4 ++-- lib/events/reactionevent.h | 4 ++-- lib/events/receiptevent.cpp | 2 +- lib/events/receiptevent.h | 4 ++-- lib/events/redactionevent.h | 4 ++-- lib/events/roomavatarevent.h | 4 ++-- lib/events/roomcreateevent.cpp | 2 +- lib/events/roomcreateevent.h | 4 ++-- lib/events/roomevent.cpp | 4 ++-- lib/events/roomevent.h | 8 ++++---- lib/events/roommemberevent.cpp | 6 +++--- lib/events/roommemberevent.h | 4 ++-- lib/events/roommessageevent.cpp | 6 +++--- lib/events/roommessageevent.h | 4 ++-- lib/events/roomtombstoneevent.cpp | 2 +- lib/events/roomtombstoneevent.h | 4 ++-- lib/events/simplestateevents.h | 6 +++--- lib/events/stateevent.cpp | 2 +- lib/events/stateevent.h | 8 ++++---- lib/events/typingevent.cpp | 2 +- lib/events/typingevent.h | 4 ++-- lib/identity/definitions/request_email_validation.cpp | 2 +- lib/identity/definitions/request_email_validation.h | 4 ++-- lib/identity/definitions/request_msisdn_validation.cpp | 2 +- lib/identity/definitions/request_msisdn_validation.h | 4 ++-- lib/identity/definitions/sid.cpp | 2 +- lib/identity/definitions/sid.h | 4 ++-- lib/jobs/basejob.cpp | 2 +- lib/jobs/basejob.h | 4 ++-- lib/jobs/downloadfilejob.cpp | 2 +- lib/jobs/downloadfilejob.h | 4 ++-- lib/jobs/mediathumbnailjob.cpp | 2 +- lib/jobs/mediathumbnailjob.h | 4 ++-- lib/jobs/postreadmarkersjob.h | 2 +- lib/jobs/requestdata.cpp | 2 +- lib/jobs/requestdata.h | 6 ++++-- lib/jobs/syncjob.cpp | 2 +- lib/jobs/syncjob.h | 4 ++-- lib/joinstate.h | 6 +++--- lib/logging.h | 6 ++++-- lib/networkaccessmanager.cpp | 2 +- lib/networkaccessmanager.h | 4 ++-- lib/networksettings.cpp | 2 +- lib/networksettings.h | 2 +- lib/qt_connection_util.h | 4 ++-- lib/room.cpp | 4 ++-- lib/room.h | 10 +++++----- lib/settings.cpp | 2 +- lib/settings.h | 4 ++-- lib/syncdata.cpp | 4 ++-- lib/syncdata.h | 4 ++-- lib/user.cpp | 2 +- lib/user.h | 6 +++--- lib/util.cpp | 14 +++++++------- lib/util.h | 6 ++++-- 222 files changed, 413 insertions(+), 405 deletions(-) (limited to 'lib') diff --git a/lib/application-service/definitions/location.cpp b/lib/application-service/definitions/location.cpp index 2ab83ae9..0a054029 100644 --- a/lib/application-service/definitions/location.cpp +++ b/lib/application-service/definitions/location.cpp @@ -4,7 +4,7 @@ #include "location.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const ThirdPartyLocation& pod) diff --git a/lib/application-service/definitions/location.h b/lib/application-service/definitions/location.h index caf28615..77512514 100644 --- a/lib/application-service/definitions/location.h +++ b/lib/application-service/definitions/location.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -30,4 +30,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/application-service/definitions/protocol.cpp b/lib/application-service/definitions/protocol.cpp index e87001fb..8c66aa4d 100644 --- a/lib/application-service/definitions/protocol.cpp +++ b/lib/application-service/definitions/protocol.cpp @@ -4,7 +4,7 @@ #include "protocol.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const FieldType& pod) diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h index 0d227851..ab99264f 100644 --- a/lib/application-service/definitions/protocol.h +++ b/lib/application-service/definitions/protocol.h @@ -10,7 +10,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -85,4 +85,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/application-service/definitions/user.cpp b/lib/application-service/definitions/user.cpp index 0f3c3130..17d15a20 100644 --- a/lib/application-service/definitions/user.cpp +++ b/lib/application-service/definitions/user.cpp @@ -4,7 +4,7 @@ #include "user.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const ThirdPartyUser& pod) diff --git a/lib/application-service/definitions/user.h b/lib/application-service/definitions/user.h index 3fd099d0..34c6829c 100644 --- a/lib/application-service/definitions/user.h +++ b/lib/application-service/definitions/user.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -30,4 +30,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/avatar.cpp b/lib/avatar.cpp index 614f008d..cb734984 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -29,7 +29,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; using std::move; class Avatar::Private { diff --git a/lib/avatar.h b/lib/avatar.h index c33e1982..7a566bfa 100644 --- a/lib/avatar.h +++ b/lib/avatar.h @@ -24,7 +24,7 @@ #include #include -namespace QMatrixClient { +namespace Quotient { class Connection; class Avatar { @@ -56,4 +56,6 @@ private: class Private; std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace Quotient +/// \deprecated Use namespace Quotient instead +namespace QMatrixClient = Quotient; \ No newline at end of file diff --git a/lib/connection.cpp b/lib/connection.cpp index 58d2e01a..22db8e3d 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -53,7 +53,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; // This is very much Qt-specific; STL iterators don't have key() and value() template @@ -1349,9 +1349,9 @@ void Connection::setCacheState(bool newValue) } } -bool QMatrixClient::Connection::lazyLoading() const { return d->lazyLoading; } +bool Connection::lazyLoading() const { return d->lazyLoading; } -void QMatrixClient::Connection::setLazyLoading(bool newValue) +void Connection::setLazyLoading(bool newValue) { if (d->lazyLoading != newValue) { d->lazyLoading = newValue; diff --git a/lib/connection.h b/lib/connection.h index b89c0c65..c807b827 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -36,7 +36,7 @@ namespace QtOlm { class Account; } -namespace QMatrixClient { +namespace Quotient { class Room; class User; class ConnectionData; @@ -778,5 +778,5 @@ private: static room_factory_t _roomFactory; static user_factory_t _userFactory; }; -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::Connection*) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::Connection*) diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index df4cece2..486de03d 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -21,7 +21,7 @@ #include "logging.h" #include "networkaccessmanager.h" -using namespace QMatrixClient; +using namespace Quotient; struct ConnectionData::Private { explicit Private(QUrl url) : baseUrl(std::move(url)) {} diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 9b579b1c..80ace08c 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -24,7 +24,7 @@ class QNetworkAccessManager; -namespace QMatrixClient { +namespace Quotient { class ConnectionData { public: explicit ConnectionData(QUrl baseUrl); @@ -50,4 +50,4 @@ private: struct Private; std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/converters.cpp b/lib/converters.cpp index ef58c85e..9f4b9360 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -20,7 +20,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; QJsonValue JsonConverter::dump(const QVariant& v) { diff --git a/lib/converters.h b/lib/converters.h index 0085fa4b..587e4544 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -55,7 +55,7 @@ struct hash { class QVariant; -namespace QMatrixClient { +namespace Quotient { template struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); } @@ -439,4 +439,4 @@ inline void addParam(ContT& container, const QString& key, ValT&& value) _impl::AddNode, Force>::impl(container, key, std::forward(value)); } -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/account-data.cpp b/lib/csapi/account-data.cpp index 7d4f1ad7..2e466fa3 100644 --- a/lib/csapi/account-data.cpp +++ b/lib/csapi/account-data.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index 75bb9ce3..7417da0d 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -122,4 +122,4 @@ public: const QString& roomId, const QString& type); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/admin.cpp b/lib/csapi/admin.cpp index 58334118..d2c20ba8 100644 --- a/lib/csapi/admin.cpp +++ b/lib/csapi/admin.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -46,7 +46,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetWhoIsJob::Private { diff --git a/lib/csapi/admin.h b/lib/csapi/admin.h index bc27c025..472ccf72 100644 --- a/lib/csapi/admin.h +++ b/lib/csapi/admin.h @@ -11,7 +11,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -94,4 +94,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index 067fb68a..32bc8c86 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -29,7 +29,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetAccount3PIDsJob::Private { @@ -67,7 +67,7 @@ BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -82,7 +82,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient static const auto Post3PIDsJobName = QStringLiteral("Post3PIDsJob"); diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index 7f2d0cdc..4ccd7596 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -12,7 +12,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -256,4 +256,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/appservice_room_directory.cpp b/lib/csapi/appservice_room_directory.cpp index 74e037cd..87221aa4 100644 --- a/lib/csapi/appservice_room_directory.cpp +++ b/lib/csapi/appservice_room_directory.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h index d1c3f89f..e19bf320 100644 --- a/lib/csapi/appservice_room_directory.h +++ b/lib/csapi/appservice_room_directory.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -42,4 +42,4 @@ public: const QString& visibility); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/banning.cpp b/lib/csapi/banning.cpp index a46e78f1..eac09d5a 100644 --- a/lib/csapi/banning.cpp +++ b/lib/csapi/banning.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h index 0aa5785b..5df878a8 100644 --- a/lib/csapi/banning.h +++ b/lib/csapi/banning.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -58,4 +58,4 @@ public: explicit UnbanJob(const QString& roomId, const QString& userId); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/capabilities.cpp b/lib/csapi/capabilities.cpp index 9a054fe9..6a544a1e 100644 --- a/lib/csapi/capabilities.cpp +++ b/lib/csapi/capabilities.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -49,7 +49,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetCapabilitiesJob::Private { diff --git a/lib/csapi/capabilities.h b/lib/csapi/capabilities.h index f6e7ad06..b608a2f2 100644 --- a/lib/csapi/capabilities.h +++ b/lib/csapi/capabilities.h @@ -11,7 +11,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -84,4 +84,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index c2720d63..395337f3 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -9,7 +9,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/media/r0"); diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h index 9f267f6c..83069490 100644 --- a/lib/csapi/content-repo.h +++ b/lib/csapi/content-repo.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -282,4 +282,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index e94cb008..68741f13 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -38,7 +38,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class CreateRoomJob::Private { diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index a066a3f3..e7000155 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -11,7 +11,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -235,4 +235,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/auth_data.cpp b/lib/csapi/definitions/auth_data.cpp index 3bb51626..edeb7111 100644 --- a/lib/csapi/definitions/auth_data.cpp +++ b/lib/csapi/definitions/auth_data.cpp @@ -4,7 +4,7 @@ #include "auth_data.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const AuthenticationData& pod) diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index 9e46812c..1aeea6c2 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -9,7 +9,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -34,4 +34,4 @@ struct JsonObjectConverter static void fillFrom(QJsonObject jo, AuthenticationData& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/client_device.cpp b/lib/csapi/definitions/client_device.cpp index c9e6a223..09544138 100644 --- a/lib/csapi/definitions/client_device.cpp +++ b/lib/csapi/definitions/client_device.cpp @@ -4,7 +4,7 @@ #include "client_device.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const Device& pod) { diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index e4accc35..f076c4da 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -34,4 +34,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, Device& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/device_keys.cpp b/lib/csapi/definitions/device_keys.cpp index cc5262b7..0583840d 100644 --- a/lib/csapi/definitions/device_keys.cpp +++ b/lib/csapi/definitions/device_keys.cpp @@ -4,7 +4,7 @@ #include "device_keys.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const DeviceKeys& pod) diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index 6bd96584..d1d8abef 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -41,4 +41,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/event_filter.cpp b/lib/csapi/definitions/event_filter.cpp index 9b2c7a33..b21e08b5 100644 --- a/lib/csapi/definitions/event_filter.cpp +++ b/lib/csapi/definitions/event_filter.cpp @@ -4,7 +4,7 @@ #include "event_filter.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const EventFilter& pod) diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index 9b9b3fa3..b41e2e9e 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -40,4 +40,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, EventFilter& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/public_rooms_response.cpp b/lib/csapi/definitions/public_rooms_response.cpp index d07b1494..b6009718 100644 --- a/lib/csapi/definitions/public_rooms_response.cpp +++ b/lib/csapi/definitions/public_rooms_response.cpp @@ -4,7 +4,7 @@ #include "public_rooms_response.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod) diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index e86e306f..1cb3aad5 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -67,4 +67,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/push_condition.cpp b/lib/csapi/definitions/push_condition.cpp index 5bcb845e..343b4f1a 100644 --- a/lib/csapi/definitions/push_condition.cpp +++ b/lib/csapi/definitions/push_condition.cpp @@ -4,7 +4,7 @@ #include "push_condition.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushCondition& pod) diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index 2c17023e..34a183de 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -36,4 +36,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, PushCondition& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/push_rule.cpp b/lib/csapi/definitions/push_rule.cpp index fc2be2c7..eae7e446 100644 --- a/lib/csapi/definitions/push_rule.cpp +++ b/lib/csapi/definitions/push_rule.cpp @@ -4,7 +4,7 @@ #include "push_rule.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRule& pod) { diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index fe6eb0e6..e64d6ba8 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -12,7 +12,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -43,4 +43,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, PushRule& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/push_ruleset.cpp b/lib/csapi/definitions/push_ruleset.cpp index 6f48d27b..a2db35d9 100644 --- a/lib/csapi/definitions/push_ruleset.cpp +++ b/lib/csapi/definitions/push_ruleset.cpp @@ -4,7 +4,7 @@ #include "push_ruleset.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRuleset& pod) diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index f9aedad8..b6b9670e 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -36,4 +36,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, PushRuleset& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp index bd38ebc7..5613d8d2 100644 --- a/lib/csapi/definitions/room_event_filter.cpp +++ b/lib/csapi/definitions/room_event_filter.cpp @@ -4,7 +4,7 @@ #include "room_event_filter.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const RoomEventFilter& pod) diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 72bf34d3..ae06a615 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -8,7 +8,7 @@ #include "csapi/definitions/event_filter.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -35,4 +35,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/sync_filter.cpp b/lib/csapi/definitions/sync_filter.cpp index 87274a06..15c4bdc1 100644 --- a/lib/csapi/definitions/sync_filter.cpp +++ b/lib/csapi/definitions/sync_filter.cpp @@ -4,7 +4,7 @@ #include "sync_filter.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const StateFilter& pod) diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index 1d6a845c..9ea39a65 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -9,7 +9,7 @@ #include "csapi/definitions/event_filter.h" #include "csapi/definitions/room_event_filter.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -104,4 +104,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, Filter& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/user_identifier.cpp b/lib/csapi/definitions/user_identifier.cpp index 52bb1ae6..9e9b4fe9 100644 --- a/lib/csapi/definitions/user_identifier.cpp +++ b/lib/csapi/definitions/user_identifier.cpp @@ -4,7 +4,7 @@ #include "user_identifier.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const UserIdentifier& pod) diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index 51c47cca..74e6ce2b 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -31,4 +31,4 @@ struct JsonObjectConverter static void fillFrom(QJsonObject jo, UserIdentifier& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/full.cpp b/lib/csapi/definitions/wellknown/full.cpp index 34d3bfbe..595db0e5 100644 --- a/lib/csapi/definitions/wellknown/full.cpp +++ b/lib/csapi/definitions/wellknown/full.cpp @@ -4,7 +4,7 @@ #include "full.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const DiscoveryInformation& pod) diff --git a/lib/csapi/definitions/wellknown/full.h b/lib/csapi/definitions/wellknown/full.h index ddc06653..92c8afff 100644 --- a/lib/csapi/definitions/wellknown/full.h +++ b/lib/csapi/definitions/wellknown/full.h @@ -12,7 +12,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Data structures @@ -39,4 +39,4 @@ struct JsonObjectConverter static void fillFrom(QJsonObject jo, DiscoveryInformation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/homeserver.cpp b/lib/csapi/definitions/wellknown/homeserver.cpp index f5746ede..7b87aa95 100644 --- a/lib/csapi/definitions/wellknown/homeserver.cpp +++ b/lib/csapi/definitions/wellknown/homeserver.cpp @@ -4,7 +4,7 @@ #include "homeserver.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const HomeserverInformation& pod) diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index b73cee17..606df88b 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -25,4 +25,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/identity_server.cpp b/lib/csapi/definitions/wellknown/identity_server.cpp index 922d4665..3b2a5720 100644 --- a/lib/csapi/definitions/wellknown/identity_server.cpp +++ b/lib/csapi/definitions/wellknown/identity_server.cpp @@ -4,7 +4,7 @@ #include "identity_server.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const IdentityServerInformation& pod) diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index a35644fc..b4304ef7 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -25,4 +25,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/device_management.cpp b/lib/csapi/device_management.cpp index 9135c22d..0889089e 100644 --- a/lib/csapi/device_management.cpp +++ b/lib/csapi/device_management.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h index 01838c6f..d380a44f 100644 --- a/lib/csapi/device_management.h +++ b/lib/csapi/device_management.h @@ -13,7 +13,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -145,4 +145,4 @@ public: const Omittable& auth = none); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/directory.cpp b/lib/csapi/directory.cpp index 992d1da5..b2689d1e 100644 --- a/lib/csapi/directory.cpp +++ b/lib/csapi/directory.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0/directory"); diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h index f5331db8..7863aa1a 100644 --- a/lib/csapi/directory.h +++ b/lib/csapi/directory.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -93,4 +93,4 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomAlias); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/event_context.cpp b/lib/csapi/event_context.cpp index 936b2430..d233eedb 100644 --- a/lib/csapi/event_context.cpp +++ b/lib/csapi/event_context.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index ca06f4b9..755fc662 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -9,7 +9,7 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -71,4 +71,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index 79dd5ea5..b4160ba4 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h index 0a5a98ae..5001a492 100644 --- a/lib/csapi/filter.h +++ b/lib/csapi/filter.h @@ -10,7 +10,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -88,4 +88,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/inviting.cpp b/lib/csapi/inviting.cpp index 4ddbe5d0..b60df6f8 100644 --- a/lib/csapi/inviting.cpp +++ b/lib/csapi/inviting.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h index b0911ea8..faf315b1 100644 --- a/lib/csapi/inviting.h +++ b/lib/csapi/inviting.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -44,4 +44,4 @@ public: explicit InviteUserJob(const QString& roomId, const QString& userId); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index cb40cb96..2dd617bb 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -29,7 +29,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class JoinRoomByIdJob::Private { @@ -67,7 +67,7 @@ BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -91,7 +91,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class JoinRoomJob::Private { diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index a96f323d..cf456da9 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -165,4 +165,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index 1752b865..cd33ad19 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); @@ -50,7 +50,7 @@ BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -74,7 +74,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class QueryKeysJob::Private { diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h index f69028fd..27867b8c 100644 --- a/lib/csapi/keys.h +++ b/lib/csapi/keys.h @@ -14,7 +14,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -231,4 +231,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/kicking.cpp b/lib/csapi/kicking.cpp index 05c4c581..ce7fcdad 100644 --- a/lib/csapi/kicking.cpp +++ b/lib/csapi/kicking.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h index 9566a9a4..d51edb47 100644 --- a/lib/csapi/kicking.h +++ b/lib/csapi/kicking.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -39,4 +39,4 @@ public: const QString& reason = {}); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/leaving.cpp b/lib/csapi/leaving.cpp index 325b1e04..abf04888 100644 --- a/lib/csapi/leaving.cpp +++ b/lib/csapi/leaving.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/leaving.h b/lib/csapi/leaving.h index 2ed6c8e7..280545b0 100644 --- a/lib/csapi/leaving.h +++ b/lib/csapi/leaving.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -73,4 +73,4 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index 43c948f7..1260499a 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/list_joined_rooms.h b/lib/csapi/list_joined_rooms.h index 1b64a004..5dab9dfc 100644 --- a/lib/csapi/list_joined_rooms.h +++ b/lib/csapi/list_joined_rooms.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -43,4 +43,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 4d96dac3..0e065440 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); @@ -108,7 +108,7 @@ BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -121,7 +121,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class QueryPublicRoomsJob::Private { diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index da68416d..e68030ad 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -10,7 +10,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -189,4 +189,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/login.cpp b/lib/csapi/login.cpp index 29ee4ab5..02730ff0 100644 --- a/lib/csapi/login.cpp +++ b/lib/csapi/login.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -26,7 +26,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetLoginFlowsJob::Private { diff --git a/lib/csapi/login.h b/lib/csapi/login.h index 3ab0648f..dc944782 100644 --- a/lib/csapi/login.h +++ b/lib/csapi/login.h @@ -13,7 +13,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -143,4 +143,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/logout.cpp b/lib/csapi/logout.cpp index d0bef20e..4b391967 100644 --- a/lib/csapi/logout.cpp +++ b/lib/csapi/logout.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h index c03af180..34d5a441 100644 --- a/lib/csapi/logout.h +++ b/lib/csapi/logout.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -55,4 +55,4 @@ public: static QUrl makeRequestUrl(QUrl baseUrl); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/message_pagination.cpp b/lib/csapi/message_pagination.cpp index 3f09bd85..b612ee91 100644 --- a/lib/csapi/message_pagination.cpp +++ b/lib/csapi/message_pagination.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index 03b3d42a..271e1dd9 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -9,7 +9,7 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -80,4 +80,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index 3a05a0b2..da568a0f 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -31,7 +31,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetNotificationsJob::Private { diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index 4170d539..eeec5d4e 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -13,7 +13,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -94,4 +94,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index 39ba3b9e..8c00df97 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h index 40f1bc40..b2f003e5 100644 --- a/lib/csapi/openid.h +++ b/lib/csapi/openid.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -64,4 +64,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/peeking_events.cpp b/lib/csapi/peeking_events.cpp index 6962d363..bc29b682 100644 --- a/lib/csapi/peeking_events.cpp +++ b/lib/csapi/peeking_events.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index 1fe63c4d..c5cc07f6 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -9,7 +9,7 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -74,4 +74,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index b6e8caf2..0f019026 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index 82c3a300..c5ecb987 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -8,7 +8,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -78,4 +78,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index 630452f6..27168f77 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 95d3ec97..54dc53aa 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -164,4 +164,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index 41a0cffe..90877a95 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -43,7 +43,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetPushersJob::Private { @@ -79,7 +79,7 @@ BaseJob::Status GetPushersJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -92,7 +92,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient static const auto PostPusherJobName = QStringLiteral("PostPusherJob"); diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index a909b9a0..65f8aa15 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -169,4 +169,4 @@ public: Omittable append = none); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index 5842369f..eae5445f 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index 9074addb..cb94fe7b 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -14,7 +14,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -297,4 +297,4 @@ public: const QStringList& actions); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/read_markers.cpp b/lib/csapi/read_markers.cpp index 5ea45f88..a07d09ce 100644 --- a/lib/csapi/read_markers.cpp +++ b/lib/csapi/read_markers.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h index e1fbc263..35c428b2 100644 --- a/lib/csapi/read_markers.h +++ b/lib/csapi/read_markers.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -34,4 +34,4 @@ public: const QString& mRead = {}); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/receipts.cpp b/lib/csapi/receipts.cpp index 28d7032f..f9a45912 100644 --- a/lib/csapi/receipts.cpp +++ b/lib/csapi/receipts.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h index bb037c08..376d4006 100644 --- a/lib/csapi/receipts.h +++ b/lib/csapi/receipts.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -37,4 +37,4 @@ public: const QJsonObject& receipt = {}); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/redaction.cpp b/lib/csapi/redaction.cpp index f944cdd4..d2dbe19b 100644 --- a/lib/csapi/redaction.cpp +++ b/lib/csapi/redaction.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h index c75421cb..42022930 100644 --- a/lib/csapi/redaction.h +++ b/lib/csapi/redaction.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -53,4 +53,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 52b4098d..270011e1 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index 40f1caa6..89aecb80 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -11,7 +11,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -480,4 +480,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/report_content.cpp b/lib/csapi/report_content.cpp index eb62cd12..352f52c9 100644 --- a/lib/csapi/report_content.cpp +++ b/lib/csapi/report_content.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/report_content.h b/lib/csapi/report_content.h index c545d800..c86a4301 100644 --- a/lib/csapi/report_content.h +++ b/lib/csapi/report_content.h @@ -8,7 +8,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -36,4 +36,4 @@ public: int score, const QString& reason); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/room_send.cpp b/lib/csapi/room_send.cpp index a29dd99a..5e970d65 100644 --- a/lib/csapi/room_send.cpp +++ b/lib/csapi/room_send.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h index aa2efd79..110fc83b 100644 --- a/lib/csapi/room_send.h +++ b/lib/csapi/room_send.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -62,4 +62,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/room_state.cpp b/lib/csapi/room_state.cpp index ef4afcd0..bfcd6e17 100644 --- a/lib/csapi/room_state.cpp +++ b/lib/csapi/room_state.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h index 6ddd5594..80619d63 100644 --- a/lib/csapi/room_state.h +++ b/lib/csapi/room_state.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -126,4 +126,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/room_upgrades.cpp b/lib/csapi/room_upgrades.cpp index a72304d1..1d6006ef 100644 --- a/lib/csapi/room_upgrades.cpp +++ b/lib/csapi/room_upgrades.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index 94520aca..5f9262f1 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -41,4 +41,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/rooms.cpp b/lib/csapi/rooms.cpp index 280e8d59..5bfbe44d 100644 --- a/lib/csapi/rooms.cpp +++ b/lib/csapi/rooms.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); @@ -168,7 +168,7 @@ BaseJob::Status GetMembersByRoomJob::parseJson(const QJsonDocument& data) } // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -182,7 +182,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetJoinedMembersByRoomJob::Private { diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 29d7808e..1020fdb1 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -12,7 +12,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -269,4 +269,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/search.cpp b/lib/csapi/search.cpp index ee1fa70c..9619f340 100644 --- a/lib/csapi/search.cpp +++ b/lib/csapi/search.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -144,7 +144,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class SearchJob::Private { diff --git a/lib/csapi/search.h b/lib/csapi/search.h index f965a72a..079ac8e9 100644 --- a/lib/csapi/search.h +++ b/lib/csapi/search.h @@ -16,7 +16,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -200,4 +200,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/sso_login_redirect.cpp b/lib/csapi/sso_login_redirect.cpp index b1dc3674..c2cc81cf 100644 --- a/lib/csapi/sso_login_redirect.cpp +++ b/lib/csapi/sso_login_redirect.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h index af9e7780..b932a15e 100644 --- a/lib/csapi/sso_login_redirect.h +++ b/lib/csapi/sso_login_redirect.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -37,4 +37,4 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& redirectUrl); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/tags.cpp b/lib/csapi/tags.cpp index 13c933e5..3df38074 100644 --- a/lib/csapi/tags.cpp +++ b/lib/csapi/tags.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -26,7 +26,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class GetRoomTagsJob::Private { diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h index dc20cd3d..8ddebd6b 100644 --- a/lib/csapi/tags.h +++ b/lib/csapi/tags.h @@ -11,7 +11,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -123,4 +123,4 @@ public: const QString& roomId, const QString& tag); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/third_party_lookup.cpp b/lib/csapi/third_party_lookup.cpp index 986ead01..678f6b3c 100644 --- a/lib/csapi/third_party_lookup.cpp +++ b/lib/csapi/third_party_lookup.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/third_party_lookup.h b/lib/csapi/third_party_lookup.h index d25c1cf3..63607549 100644 --- a/lib/csapi/third_party_lookup.h +++ b/lib/csapi/third_party_lookup.h @@ -15,7 +15,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -253,4 +253,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/third_party_membership.cpp b/lib/csapi/third_party_membership.cpp index 9fe702aa..7e401163 100644 --- a/lib/csapi/third_party_membership.cpp +++ b/lib/csapi/third_party_membership.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h index 36622c94..bd4c6896 100644 --- a/lib/csapi/third_party_membership.h +++ b/lib/csapi/third_party_membership.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -74,4 +74,4 @@ public: const QString& medium, const QString& address); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/to_device.cpp b/lib/csapi/to_device.cpp index 3fb17109..3f6e8097 100644 --- a/lib/csapi/to_device.cpp +++ b/lib/csapi/to_device.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/to_device.h b/lib/csapi/to_device.h index e0bbbe28..14445439 100644 --- a/lib/csapi/to_device.h +++ b/lib/csapi/to_device.h @@ -9,7 +9,7 @@ #include #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -39,4 +39,4 @@ public: const QHash>& messages = {}); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/typing.cpp b/lib/csapi/typing.cpp index 03499c76..064ebe39 100644 --- a/lib/csapi/typing.cpp +++ b/lib/csapi/typing.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h index 0c3f75a8..350c209d 100644 --- a/lib/csapi/typing.h +++ b/lib/csapi/typing.h @@ -8,7 +8,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -38,4 +38,4 @@ public: bool typing, Omittable timeout = none); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index 39b05a77..77c297dd 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -8,12 +8,12 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace QMatrixClient +namespace Quotient { template <> @@ -28,7 +28,7 @@ struct JsonObjectConverter } }; -} // namespace QMatrixClient +} // namespace Quotient class SearchUserDirectoryJob::Private { diff --git a/lib/csapi/users.h b/lib/csapi/users.h index 2e86c009..d9a16a9d 100644 --- a/lib/csapi/users.h +++ b/lib/csapi/users.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -80,4 +80,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 1d66b94f..9607a1b6 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client"); diff --git a/lib/csapi/versions.h b/lib/csapi/versions.h index 513e7f27..aa8cbac6 100644 --- a/lib/csapi/versions.h +++ b/lib/csapi/versions.h @@ -10,7 +10,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -70,4 +70,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/voip.cpp b/lib/csapi/voip.cpp index 0e83c915..c95afe16 100644 --- a/lib/csapi/voip.cpp +++ b/lib/csapi/voip.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/voip.h b/lib/csapi/voip.h index ab34dcad..38abfa27 100644 --- a/lib/csapi/voip.h +++ b/lib/csapi/voip.h @@ -8,7 +8,7 @@ #include -namespace QMatrixClient +namespace Quotient { // Operations @@ -46,4 +46,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/wellknown.cpp b/lib/csapi/wellknown.cpp index c2bd7822..9a52a2a5 100644 --- a/lib/csapi/wellknown.cpp +++ b/lib/csapi/wellknown.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/.well-known"); diff --git a/lib/csapi/wellknown.h b/lib/csapi/wellknown.h index 66917806..bce67d00 100644 --- a/lib/csapi/wellknown.h +++ b/lib/csapi/wellknown.h @@ -10,7 +10,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -54,4 +54,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index 2ca9c435..fb7f54dc 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -8,7 +8,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); diff --git a/lib/csapi/whoami.h b/lib/csapi/whoami.h index e62b7dad..bbbb3899 100644 --- a/lib/csapi/whoami.h +++ b/lib/csapi/whoami.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -namespace QMatrixClient +namespace Quotient { // Operations @@ -50,4 +50,4 @@ private: QScopedPointer d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache index 8ebac6ef..6cbd1b65 100644 --- a/lib/csapi/{{base}}.cpp.mustache +++ b/lib/csapi/{{base}}.cpp.mustache @@ -8,7 +8,7 @@ {{/producesNonJson?}} #include {{/operations}} -using namespace QMatrixClient; +using namespace Quotient; {{#models.model}} {{#in?}} void JsonObjectConverter<{{qualifiedName}}>::dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) @@ -38,7 +38,7 @@ void JsonObjectConverter<{{qualifiedName}}>::fillFrom({{>maybeCrefJsonObject}} j static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); {{#operation}}{{#models}} // Converters -namespace QMatrixClient +namespace Quotient { {{#model}} template <> struct JsonObjectConverter<{{qualifiedName}}> diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache index 61380ec6..56044e7d 100644 --- a/lib/csapi/{{base}}.h.mustache +++ b/lib/csapi/{{base}}.h.mustache @@ -8,7 +8,7 @@ {{#imports}} #include {{_}}{{/imports}} -namespace QMatrixClient +namespace Quotient { {{#models}} // Data structures @@ -92,4 +92,4 @@ private: }; {{/ operation}} {{/operations}} -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/e2ee.h b/lib/e2ee.h index c85211be..5a9b83c5 100644 --- a/lib/e2ee.h +++ b/lib/e2ee.h @@ -4,7 +4,7 @@ #include -namespace QMatrixClient { +namespace Quotient { static const auto CiphertextKeyL = "ciphertext"_ls; static const auto SenderKeyKeyL = "sender_key"_ls; static const auto DeviceIdKeyL = "device_id"_ls; @@ -28,4 +28,4 @@ static const auto MegolmV1AesSha2AlgoKey = QStringLiteral("m.megolm.v1.aes-sha2"); static const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, MegolmV1AesSha2AlgoKey }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 15723688..22387cf9 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -12,7 +12,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; using namespace QtOlm; using std::move; diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index 79c25a00..b210a85a 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -9,7 +9,7 @@ namespace QtOlm { class Account; } -namespace QMatrixClient { +namespace Quotient { class Connection; class EncryptionManager : public QObject { @@ -35,4 +35,4 @@ private: std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/eventitem.cpp b/lib/eventitem.cpp index de0a5c9f..2e2b11c0 100644 --- a/lib/eventitem.cpp +++ b/lib/eventitem.cpp @@ -21,7 +21,7 @@ #include "events/roomavatarevent.h" #include "events/roommessageevent.h" -using namespace QMatrixClient; +using namespace Quotient; void PendingEventItem::setFileUploaded(const QUrl& remoteUrl) { diff --git a/lib/eventitem.h b/lib/eventitem.h index 68d1ae06..3a7061d3 100644 --- a/lib/eventitem.h +++ b/lib/eventitem.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { class StateEventBase; class EventStatus { @@ -153,5 +153,5 @@ inline QDebug& operator<<(QDebug& d, const TimelineItem& ti) d.nospace() << "(" << ti.index() << "|" << ti->id() << ")"; return d; } -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::EventStatus) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::EventStatus) diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 3f519668..600fa5be 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -24,7 +24,7 @@ #include "event.h" #include "eventcontent.h" -namespace QMatrixClient { +namespace Quotient { constexpr const char* FavouriteTag = "m.favourite"; constexpr const char* LowPriorityTag = "m.lowpriority"; @@ -93,4 +93,4 @@ DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", QSet, DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp index 7ab4a6fb..d6622b30 100644 --- a/lib/events/callanswerevent.cpp +++ b/lib/events/callanswerevent.cpp @@ -44,7 +44,7 @@ m.call.answer } */ -using namespace QMatrixClient; +using namespace Quotient; CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index 052f732d..e01b39db 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class CallAnswerEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) @@ -43,4 +43,4 @@ public: REGISTER_EVENT_TYPE(CallAnswerEvent) DEFINE_EVENTTYPE_ALIAS(CallAnswer, CallAnswerEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index 2a915a43..3d13ba8a 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class CallCandidatesEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) @@ -43,4 +43,4 @@ public: REGISTER_EVENT_TYPE(CallCandidatesEvent) DEFINE_EVENTTYPE_ALIAS(CallCandidates, CallCandidatesEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp index 2a4fd3da..d41849c3 100644 --- a/lib/events/callhangupevent.cpp +++ b/lib/events/callhangupevent.cpp @@ -39,7 +39,7 @@ m.call.hangup } */ -using namespace QMatrixClient; +using namespace Quotient; CallHangupEvent::CallHangupEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index 97fa2f52..d23e29db 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class CallHangupEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) @@ -31,4 +31,4 @@ public: REGISTER_EVENT_TYPE(CallHangupEvent) DEFINE_EVENTTYPE_ALIAS(CallHangup, CallHangupEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/callinviteevent.cpp b/lib/events/callinviteevent.cpp index f565fc3e..54faac8d 100644 --- a/lib/events/callinviteevent.cpp +++ b/lib/events/callinviteevent.cpp @@ -44,7 +44,7 @@ m.call.invite } */ -using namespace QMatrixClient; +using namespace Quotient; CallInviteEvent::CallInviteEvent(const QJsonObject& obj) : CallEventBase(typeId(), obj) diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index 9b9d0ae5..3e39e0ba 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class CallInviteEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) @@ -42,4 +42,4 @@ public: REGISTER_EVENT_TYPE(CallInviteEvent) DEFINE_EVENTTYPE_ALIAS(CallInvite, CallInviteEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 4ba098c2..b4027e16 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -20,7 +20,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; QMultiHash DirectChatEvent::usersToDirectChats() const { diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 94857a93..b4981f2e 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -20,7 +20,7 @@ #include "event.h" -namespace QMatrixClient { +namespace Quotient { class DirectChatEvent : public Event { public: DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) @@ -31,4 +31,4 @@ public: }; REGISTER_EVENT_TYPE(DirectChatEvent) DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index 67298a27..235b2aa4 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -3,7 +3,7 @@ #include "e2ee.h" #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class Room; /* * While the specification states: @@ -63,4 +63,4 @@ public: }; REGISTER_EVENT_TYPE(EncryptedEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index 0c732a51..945e6696 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -12,10 +12,10 @@ #include static const std::array encryptionStrings = { - { QMatrixClient::MegolmV1AesSha2AlgoKey } + { Quotient::MegolmV1AesSha2AlgoKey } }; -namespace QMatrixClient { +namespace Quotient { template <> struct JsonConverter { static EncryptionType load(const QJsonValue& jv) @@ -32,7 +32,7 @@ struct JsonConverter { }; } // namespace QMatrixClient -using namespace QMatrixClient; +using namespace Quotient; EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) : encryption(fromJson(json["algorithm"_ls])) diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index debabcae..68e41719 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -21,7 +21,7 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { class EncryptionEventContent : public EventContent::Base { public: enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined }; @@ -71,4 +71,4 @@ private: REGISTER_EVENT_TYPE(EncryptionEvent) DEFINE_EVENTTYPE_ALIAS(Encryption, EncryptionEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 694254fe..96e33864 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -22,7 +22,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId) { diff --git a/lib/events/event.h b/lib/events/event.h index 686bd8e0..e96d6897 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -25,7 +25,7 @@ # define USE_EVENTTYPE_ALIAS 1 #endif -namespace QMatrixClient { +namespace Quotient { // === event_ptr_tt<> and type casting facilities === template @@ -292,7 +292,7 @@ using Events = EventsArray; // provide matrixTypeId() and typeId(). #define DEFINE_EVENT_TYPEID(_Id, _Type) \ static constexpr event_mtype_t matrixTypeId() { return _Id; } \ - static auto typeId() { return QMatrixClient::typeId<_Type>(); } \ + static auto typeId() { return Quotient::typeId<_Type>(); } \ // End of macro // This macro should be put after an event class definition (in .h or .cpp) @@ -406,6 +406,6 @@ visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, return visit(event, std::forward(visitor2), std::forward(visitors)...); } -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::Event*) -Q_DECLARE_METATYPE(const QMatrixClient::Event*) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::Event*) +Q_DECLARE_METATYPE(const Quotient::Event*) diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index 814f2787..802d8176 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -23,7 +23,7 @@ #include -using namespace QMatrixClient::EventContent; +using namespace Quotient::EventContent; QJsonObject Base::toJson() const { diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 5c0f92d1..c26cb931 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -26,7 +26,7 @@ #include #include -namespace QMatrixClient { +namespace Quotient { namespace EventContent { /** * A base class for all content types that can be stored @@ -275,4 +275,4 @@ namespace EventContent { */ using FileContent = UrlWithThumbnailContent; } // namespace EventContent -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index 9e8bb410..ebb96441 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -20,7 +20,7 @@ #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { namespace _impl { template static inline auto loadEvent(const QJsonObject& json, @@ -83,4 +83,4 @@ struct JsonConverter> { return loadEvent(jd.object()); } }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp index 0a080607..df8910fe 100644 --- a/lib/events/reactionevent.cpp +++ b/lib/events/reactionevent.cpp @@ -20,7 +20,7 @@ using namespace QMatrixClient; -void QMatrixClient::JsonObjectConverter::dumpTo( +void JsonObjectConverter::dumpTo( QJsonObject& jo, const EventRelation& pod) { if (pod.type.isEmpty()) { @@ -33,7 +33,7 @@ void QMatrixClient::JsonObjectConverter::dumpTo( jo.insert(QStringLiteral("key"), pod.key); } -void QMatrixClient::JsonObjectConverter::fillFrom( +void JsonObjectConverter::fillFrom( const QJsonObject& jo, EventRelation& pod) { // The experimental logic for generic relationships (MSC1849) diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h index b1e04561..75c6528c 100644 --- a/lib/events/reactionevent.h +++ b/lib/events/reactionevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { struct EventRelation { using reltypeid_t = const char*; @@ -70,4 +70,4 @@ private: }; REGISTER_EVENT_TYPE(ReactionEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 4a54b744..bf050cb2 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -38,7 +38,7 @@ Example of a Receipt Event: #include "converters.h" #include "logging.h" -using namespace QMatrixClient; +using namespace Quotient; ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) { diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index c32e0543..71cd5de0 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -23,7 +23,7 @@ #include #include -namespace QMatrixClient { +namespace Quotient { struct Receipt { QString userId; QDateTime timestamp; @@ -49,4 +49,4 @@ private: }; REGISTER_EVENT_TYPE(ReceiptEvent) DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index 3628fb33..eac627d5 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { class RedactionEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) @@ -36,4 +36,4 @@ public: }; REGISTER_EVENT_TYPE(RedactionEvent) DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index 16aeb070..109f4014 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -21,7 +21,7 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { class RoomAvatarEvent : public StateEvent { // It's a bit of an overkill to use a full-fledged ImageContent // because in reality m.room.avatar usually only has a single URL, @@ -35,4 +35,4 @@ public: }; REGISTER_EVENT_TYPE(RoomAvatarEvent) DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/roomcreateevent.cpp b/lib/events/roomcreateevent.cpp index cb575f24..c72b5bc2 100644 --- a/lib/events/roomcreateevent.cpp +++ b/lib/events/roomcreateevent.cpp @@ -18,7 +18,7 @@ #include "roomcreateevent.h" -using namespace QMatrixClient; +using namespace Quotient; bool RoomCreateEvent::isFederated() const { diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index c8ba8c40..91aefe9e 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -20,7 +20,7 @@ #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { class RoomCreateEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) @@ -41,4 +41,4 @@ public: bool isUpgrade() const; }; REGISTER_EVENT_TYPE(RoomCreateEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 543640ca..e19c03ce 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -22,7 +22,7 @@ #include "logging.h" #include "redactionevent.h" -using namespace QMatrixClient; +using namespace Quotient; [[gnu::unused]] static auto roomEventTypeInitialised = Event::factory_t::chainFactory(); @@ -48,7 +48,7 @@ QString RoomEvent::id() const { return fullJson()[EventIdKeyL].toString(); } QDateTime RoomEvent::timestamp() const { - return QMatrixClient::fromJson(fullJson()["origin_server_ts"_ls]); + return Quotient::fromJson(fullJson()["origin_server_ts"_ls]); } QString RoomEvent::roomId() const diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index 155d4600..f943bce4 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { class RedactionEvent; /** This class corresponds to m.room.* events */ @@ -102,6 +102,6 @@ public: QString callId() const { return content("call_id"_ls); } int version() const { return content("version"_ls); } }; -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*) -Q_DECLARE_METATYPE(const QMatrixClient::RoomEvent*) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::RoomEvent*) +Q_DECLARE_METATYPE(const Quotient::RoomEvent*) diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 3cbf6685..d0787170 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -28,7 +28,7 @@ static const std::array membershipStrings = { QStringLiteral("leave"), QStringLiteral("ban") } }; -namespace QMatrixClient { +namespace Quotient { template <> struct JsonConverter { static MembershipType load(const QJsonValue& jv) @@ -43,9 +43,9 @@ struct JsonConverter { return MembershipType::Undefined; } }; -} // namespace QMatrixClient +} // namespace Quotient -using namespace QMatrixClient; +using namespace Quotient; MemberEventContent::MemberEventContent(const QJsonObject& json) : membership(fromJson(json["membership"_ls])) diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 59d59e3a..2a16617a 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -21,7 +21,7 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { class MemberEventContent : public EventContent::Base { public: enum MembershipType : size_t { @@ -107,4 +107,4 @@ public: REGISTER_EVENT_TYPE(RoomMemberEvent) DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 991931de..09562d65 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -25,7 +25,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; using namespace EventContent; using MsgType = RoomMessageEvent::MsgType; @@ -279,7 +279,7 @@ TextContent::TextContent(const QString& text, const QString& contentType, mimeType = QMimeDatabase().mimeTypeForName("text/html"); } -namespace QMatrixClient { +namespace Quotient { // Overload the default fromJson<> logic that defined in converters.h // as we want template <> @@ -295,7 +295,7 @@ Omittable fromJson(const QJsonValue& jv) return RelatesTo { jo.value("rel_type"_ls).toString(), jo.value(EventIdKeyL).toString() }; } -} // namespace QMatrixClient +} // namespace Quotient TextContent::TextContent(const QJsonObject& json) : relatesTo(fromJson>(json[RelatesToKeyL])) diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index c7a5cb47..aa515c71 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -23,7 +23,7 @@ class QFileInfo; -namespace QMatrixClient { +namespace Quotient { namespace MessageEventContent = EventContent; // Back-compatibility /** @@ -217,4 +217,4 @@ namespace EventContent { */ using AudioContent = PlayableContent>; } // namespace EventContent -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/roomtombstoneevent.cpp b/lib/events/roomtombstoneevent.cpp index a74bb367..f93eb60d 100644 --- a/lib/events/roomtombstoneevent.cpp +++ b/lib/events/roomtombstoneevent.cpp @@ -18,7 +18,7 @@ #include "roomtombstoneevent.h" -using namespace QMatrixClient; +using namespace Quotient; QString RoomTombstoneEvent::serverMessage() const { diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index 95fed998..2c2f0663 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -20,7 +20,7 @@ #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { class RoomTombstoneEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) @@ -34,4 +34,4 @@ public: QString successorRoomId() const; }; REGISTER_EVENT_TYPE(RoomTombstoneEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 6dad8020..520f6b4b 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -20,7 +20,7 @@ #include "stateevent.h" -namespace QMatrixClient { +namespace Quotient { namespace EventContent { template class SimpleContent { @@ -37,7 +37,7 @@ namespace EventContent { {} QJsonObject toJson() const { - return { { key, QMatrixClient::toJson(value) } }; + return { { key, Quotient::toJson(value) } }; } public: @@ -92,4 +92,4 @@ public: QStringList aliases() const { return content().value; } }; REGISTER_EVENT_TYPE(RoomAliasesEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index bd228abd..c0bb0794 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -18,7 +18,7 @@ #include "stateevent.h" -using namespace QMatrixClient; +using namespace Quotient; // Aside from the normal factory to instantiate StateEventBase inheritors // StateEventBase itself can be instantiated if there's a state_key JSON key diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 757c94ee..74e36e74 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -20,7 +20,7 @@ #include "roomevent.h" -namespace QMatrixClient { +namespace Quotient { /// Make a minimal correct Matrix state event JSON template @@ -128,12 +128,12 @@ private: ContentT _content; std::unique_ptr> _prev; }; -} // namespace QMatrixClient +} // namespace Quotient namespace std { template <> -struct hash { - size_t operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT +struct hash { + size_t operator()(const Quotient::StateEventKey& k) const Q_DECL_NOEXCEPT { return qHash(k); } diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index ee3d6b67..0c5fc6ba 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -20,7 +20,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) { diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index c8170865..d659c597 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -20,7 +20,7 @@ #include "event.h" -namespace QMatrixClient { +namespace Quotient { class TypingEvent : public Event { public: DEFINE_EVENT_TYPEID("m.typing", TypingEvent) @@ -34,4 +34,4 @@ private: }; REGISTER_EVENT_TYPE(TypingEvent) DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/identity/definitions/request_email_validation.cpp b/lib/identity/definitions/request_email_validation.cpp index 131b9488..22cb0072 100644 --- a/lib/identity/definitions/request_email_validation.cpp +++ b/lib/identity/definitions/request_email_validation.cpp @@ -4,7 +4,7 @@ #include "request_email_validation.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const RequestEmailValidation& pod) diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h index 2496d7f5..99487073 100644 --- a/lib/identity/definitions/request_email_validation.h +++ b/lib/identity/definitions/request_email_validation.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -39,4 +39,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/identity/definitions/request_msisdn_validation.cpp b/lib/identity/definitions/request_msisdn_validation.cpp index 0087d202..6024bf61 100644 --- a/lib/identity/definitions/request_msisdn_validation.cpp +++ b/lib/identity/definitions/request_msisdn_validation.cpp @@ -4,7 +4,7 @@ #include "request_msisdn_validation.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const RequestMsisdnValidation& pod) diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h index f8060cfc..ecccf567 100644 --- a/lib/identity/definitions/request_msisdn_validation.h +++ b/lib/identity/definitions/request_msisdn_validation.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -43,4 +43,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/identity/definitions/sid.cpp b/lib/identity/definitions/sid.cpp index cc6973f2..99fe9b59 100644 --- a/lib/identity/definitions/sid.cpp +++ b/lib/identity/definitions/sid.cpp @@ -4,7 +4,7 @@ #include "sid.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo(QJsonObject& jo, const Sid& pod) { diff --git a/lib/identity/definitions/sid.h b/lib/identity/definitions/sid.h index 752d62bb..0f7ce58a 100644 --- a/lib/identity/definitions/sid.h +++ b/lib/identity/definitions/sid.h @@ -6,7 +6,7 @@ #include "converters.h" -namespace QMatrixClient +namespace Quotient { // Data structures @@ -27,4 +27,4 @@ struct JsonObjectConverter static void fillFrom(const QJsonObject& jo, Sid& pod); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 5615736e..ec6b8375 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -30,7 +30,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index c85d2d90..90c20c37 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -28,7 +28,7 @@ class QNetworkReply; class QSslError; -namespace QMatrixClient { +namespace Quotient { class ConnectionData; enum class HttpVerb { Get, Put, Post, Delete }; @@ -364,4 +364,4 @@ inline bool isJobRunning(BaseJob* job) { return job && job->error() == BaseJob::Pending; } -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 9722186c..3a03efde 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -4,7 +4,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; class DownloadFileJob::Private { public: diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index ebfe5a0d..fa697219 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -2,7 +2,7 @@ #include "csapi/content-repo.h" -namespace QMatrixClient { +namespace Quotient { class DownloadFileJob : public GetContentJob { public: enum { FileError = BaseJob::UserDefinedError + 1 }; @@ -24,4 +24,4 @@ private: void beforeAbandon(QNetworkReply*) override; Status parseReply(QNetworkReply*) override; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index db2bbc13..0a346392 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -18,7 +18,7 @@ #include "mediathumbnailjob.h" -using namespace QMatrixClient; +using namespace Quotient; QUrl MediaThumbnailJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri, QSize requestedSize) diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index df0a7f31..75e2e55a 100644 --- a/lib/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { class MediaThumbnailJob : public GetContentThumbnailJob { public: using GetContentThumbnailJob::makeRequestUrl; @@ -42,4 +42,4 @@ protected: private: QImage _thumbnail; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/jobs/postreadmarkersjob.h b/lib/jobs/postreadmarkersjob.h index cf482a9a..5a4d942c 100644 --- a/lib/jobs/postreadmarkersjob.h +++ b/lib/jobs/postreadmarkersjob.h @@ -22,7 +22,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; class PostReadMarkersJob : public BaseJob { public: diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp index 6ad7c007..0c70f085 100644 --- a/lib/jobs/requestdata.cpp +++ b/lib/jobs/requestdata.cpp @@ -6,7 +6,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; auto fromData(const QByteArray& data) { diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h index 55987a3b..020d5ef2 100644 --- a/lib/jobs/requestdata.h +++ b/lib/jobs/requestdata.h @@ -26,7 +26,7 @@ class QJsonArray; class QJsonDocument; class QIODevice; -namespace QMatrixClient { +namespace Quotient { /** * A simple wrapper that represents the request body. * Provides a unified interface to dump an unstructured byte stream @@ -52,4 +52,6 @@ public: private: std::unique_ptr _source; }; -} // namespace QMatrixClient +} // namespace Quotient +/// \deprecated Use namespace Quotient instead +namespace QMatrixClient = Quotient; diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index f660e1b6..cd7709e1 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -18,7 +18,7 @@ #include "syncjob.h" -using namespace QMatrixClient; +using namespace Quotient; static size_t jobId = 0; diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index 8f925414..df419ba8 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -22,7 +22,7 @@ #include "../syncdata.h" #include "basejob.h" -namespace QMatrixClient { +namespace Quotient { class SyncJob : public BaseJob { public: explicit SyncJob(const QString& since = {}, const QString& filter = {}, @@ -38,4 +38,4 @@ protected: private: SyncData d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/joinstate.h b/lib/joinstate.h index fcf840ae..718ae3fd 100644 --- a/lib/joinstate.h +++ b/lib/joinstate.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { enum class JoinState : unsigned int { Join = 0x1, Invite = 0x2, @@ -43,5 +43,5 @@ inline const char* toCString(JoinState js) ++index; return JoinStateStrings[index]; } -} // namespace QMatrixClient -Q_DECLARE_OPERATORS_FOR_FLAGS(QMatrixClient::JoinStates) +} // namespace Quotient +Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::JoinStates) diff --git a/lib/logging.h b/lib/logging.h index 24799752..3d13569a 100644 --- a/lib/logging.h +++ b/lib/logging.h @@ -28,7 +28,7 @@ Q_DECLARE_LOGGING_CATEGORY(EPHEMERAL) Q_DECLARE_LOGGING_CATEGORY(JOBS) Q_DECLARE_LOGGING_CATEGORY(SYNCJOB) -namespace QMatrixClient { +namespace Quotient { // QDebug manipulators using QDebugManip = QDebug (*)(QDebug); @@ -75,7 +75,9 @@ inline qint64 profilerMinNsecs() #endif * 1000; } -} // namespace QMatrixClient +} // namespace Quotient +/// \deprecated Use namespace Quotient instead +namespace QMatrixClient = Quotient; inline QDebug operator<<(QDebug debug_object, const QElapsedTimer& et) { diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 7bff654c..8ee080bf 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -21,7 +21,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; class NetworkAccessManager::Private { public: diff --git a/lib/networkaccessmanager.h b/lib/networkaccessmanager.h index dfa388f0..a678b80f 100644 --- a/lib/networkaccessmanager.h +++ b/lib/networkaccessmanager.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { class NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: @@ -43,4 +43,4 @@ private: class Private; std::unique_ptr d; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/networksettings.cpp b/lib/networksettings.cpp index f5655975..cb071483 100644 --- a/lib/networksettings.cpp +++ b/lib/networksettings.cpp @@ -18,7 +18,7 @@ #include "networksettings.h" -using namespace QMatrixClient; +using namespace Quotient; void NetworkSettings::setupApplicationProxy() const { diff --git a/lib/networksettings.h b/lib/networksettings.h index 75bf726d..a82a44d0 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -24,7 +24,7 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) -namespace QMatrixClient { +namespace Quotient { class NetworkSettings : public SettingsGroup { Q_OBJECT QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 94c1ec60..159e7522 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -22,7 +22,7 @@ #include -namespace QMatrixClient { +namespace Quotient { namespace _impl { template inline QMetaObject::Connection @@ -102,4 +102,4 @@ public: private: QObject* subscriber; }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/room.cpp b/lib/room.cpp index 52f86616..d8fee5aa 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -71,7 +71,7 @@ #include // QtOlm #include // QtOlm -using namespace QMatrixClient; +using namespace Quotient; using namespace std::placeholders; using std::move; #if !(defined __GLIBCXX__ && __GLIBCXX__ <= 20150123) @@ -1085,7 +1085,7 @@ QUrl Room::fileSource(const QString& id) const QString Room::prettyPrint(const QString& plainText) const { - return QMatrixClient::prettyPrint(plainText); + return Quotient::prettyPrint(plainText); } QList Room::usersTyping() const { return d->usersTyping; } diff --git a/lib/room.h b/lib/room.h index d6fb8a61..8448815d 100644 --- a/lib/room.h +++ b/lib/room.h @@ -35,7 +35,7 @@ #include #include -namespace QMatrixClient { +namespace Quotient { class Event; class Avatar; class SyncRoomData; @@ -423,7 +423,7 @@ public: Q_INVOKABLE QUrl fileSource(const QString& id) const; /** Pretty-prints plain text into HTML - * As of now, it's exactly the same as QMatrixClient::prettyPrint(); + * As of now, it's exactly the same as Quotient::prettyPrint(); * in the future, it will also linkify room aliases, mxids etc. * using the room context. */ @@ -668,6 +668,6 @@ public: private: const Room* room; }; -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::FileTransferInfo) -Q_DECLARE_OPERATORS_FOR_FLAGS(QMatrixClient::Room::Changes) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::FileTransferInfo) +Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::Room::Changes) diff --git a/lib/settings.cpp b/lib/settings.cpp index 1278fe33..9c61ab5e 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -4,7 +4,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; QString Settings::legacyOrganizationName {}; QString Settings::legacyApplicationName {}; diff --git a/lib/settings.h b/lib/settings.h index 427f5494..4dcbbea0 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -24,7 +24,7 @@ class QVariant; -namespace QMatrixClient { +namespace Quotient { class Settings : public QSettings { Q_OBJECT public: @@ -148,4 +148,4 @@ public: void setEncryptionAccountPickle(const QByteArray& encryptionAccountPickle); Q_INVOKABLE void clearEncryptionAccountPickle(); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 0c39b438..c784cd7d 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -23,7 +23,7 @@ #include #include -using namespace QMatrixClient; +using namespace Quotient; const QString SyncRoomData::UnreadCountKey = QStringLiteral("x-qmatrixclient.unread_count"); @@ -42,7 +42,7 @@ bool RoomSummary::merge(const RoomSummary& other) | heroes.merge(other.heroes); } -QDebug QMatrixClient::operator<<(QDebug dbg, const RoomSummary& rs) +QDebug Quotient::operator<<(QDebug dbg, const RoomSummary& rs) { QDebugStateSaver _(dbg); QStringList sl; diff --git a/lib/syncdata.h b/lib/syncdata.h index ad9902e4..d55438d7 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -22,7 +22,7 @@ #include "events/stateevent.h" -namespace QMatrixClient { +namespace Quotient { /// Room summary, as defined in MSC688 /** * Every member of this structure is an Omittable; as per the MSC, only @@ -111,4 +111,4 @@ private: static QJsonObject loadJson(const QString& fileName); }; -} // namespace QMatrixClient +} // namespace Quotient diff --git a/lib/user.cpp b/lib/user.cpp index 0705aee7..5dea3942 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -37,7 +37,7 @@ #include -using namespace QMatrixClient; +using namespace Quotient; using namespace std::placeholders; using std::move; diff --git a/lib/user.h b/lib/user.h index 779efb34..c9e3dbc1 100644 --- a/lib/user.h +++ b/lib/user.h @@ -23,7 +23,7 @@ #include #include -namespace QMatrixClient { +namespace Quotient { class Connection; class Room; class RoomMemberEvent; @@ -160,5 +160,5 @@ private: class Private; QScopedPointer d; }; -} // namespace QMatrixClient -Q_DECLARE_METATYPE(QMatrixClient::User*) +} // namespace Quotient +Q_DECLARE_METATYPE(Quotient::User*) diff --git a/lib/util.cpp b/lib/util.cpp index 1919e811..be9656f8 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -32,7 +32,7 @@ static const auto RegExpOptions = | QRegularExpression::UseUnicodePropertiesOption; // Converts all that looks like a URL into HTML links -void QMatrixClient::linkifyUrls(QString& htmlEscapedText) +void Quotient::linkifyUrls(QString& htmlEscapedText) { // Note: outer parentheses are a part of C++ raw string delimiters, not of // the regex (see http://en.cppreference.com/w/cpp/language/string_literal). @@ -70,7 +70,7 @@ void QMatrixClient::linkifyUrls(QString& htmlEscapedText) QStringLiteral(R"(\1\2)")); } -QString QMatrixClient::sanitized(const QString& plainText) +QString Quotient::sanitized(const QString& plainText) { auto text = plainText; text.remove(QChar(0x202e)); // RLO @@ -79,7 +79,7 @@ QString QMatrixClient::sanitized(const QString& plainText) return text; } -QString QMatrixClient::prettyPrint(const QString& plainText) +QString Quotient::prettyPrint(const QString& plainText) { auto pt = plainText.toHtmlEscaped(); linkifyUrls(pt); @@ -88,7 +88,7 @@ QString QMatrixClient::prettyPrint(const QString& plainText) + QStringLiteral(""); } -QString QMatrixClient::cacheLocation(const QString& dirName) +QString Quotient::cacheLocation(const QString& dirName) { const QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) % '/' @@ -99,7 +99,7 @@ QString QMatrixClient::cacheLocation(const QString& dirName) return cachePath; } -qreal QMatrixClient::stringToHueF(const QString& string) +qreal Quotient::stringToHueF(const QString& string) { Q_ASSERT(!string.isEmpty()); QByteArray hash = QCryptographicHash::hash(string.toUtf8(), @@ -118,7 +118,7 @@ static const auto ServerPartRegEx = QStringLiteral( "(?::(\\d{1,5}))?" // Optional port ); -QString QMatrixClient::serverPart(const QString& mxId) +QString Quotient::serverPart(const QString& mxId) { static QString re = "^[@!#$+].+?:(" // Localpart and colon % ServerPartRegEx % ")$"; @@ -135,7 +135,7 @@ QString QMatrixClient::serverPart(const QString& mxId) # pragma clang diagnostic push # pragma ide diagnostic ignored "OCSimplifyInspection" #endif -using namespace QMatrixClient; +using namespace Quotient; int f(); static_assert(std::is_same, int>::value, diff --git a/lib/util.h b/lib/util.h index d055fa46..d94c7321 100644 --- a/lib/util.h +++ b/lib/util.h @@ -63,7 +63,7 @@ static void qAsConst(const T&&) Q_DECL_EQ_DELETE; # define BROKEN_INITIALIZER_LISTS #endif -namespace QMatrixClient { +namespace Quotient { // The below enables pretty-printing of enums in logs #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) # define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) @@ -330,4 +330,6 @@ qreal stringToHueF(const QString& string); /** Extract the serverpart from MXID */ QString serverPart(const QString& mxId); -} // namespace QMatrixClient +} // namespace Quotient +/// \deprecated Use namespace Quotient instead +namespace QMatrixClient = Quotient; \ No newline at end of file -- cgit v1.2.3 From 62674f731b0e46d8a413e26ca1c9da27fd9aae9a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 5 Aug 2019 10:31:56 +0900 Subject: stringToHueF: pick a safer name for the variable std::string is still a thing, after all. --- lib/util.cpp | 6 +++--- lib/util.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index be9656f8..cc18d9ab 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -99,10 +99,10 @@ QString Quotient::cacheLocation(const QString& dirName) return cachePath; } -qreal Quotient::stringToHueF(const QString& string) +qreal Quotient::stringToHueF(const QString& s) { - Q_ASSERT(!string.isEmpty()); - QByteArray hash = QCryptographicHash::hash(string.toUtf8(), + Q_ASSERT(!s.isEmpty()); + QByteArray hash = QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Sha1); QDataStream dataStream(qToLittleEndian(hash).left(2)); dataStream.setByteOrder(QDataStream::LittleEndian); diff --git a/lib/util.h b/lib/util.h index d94c7321..12d3f8ba 100644 --- a/lib/util.h +++ b/lib/util.h @@ -326,7 +326,7 @@ QString cacheLocation(const QString& dirName); * Naming and range are the same as QColor's hueF method: * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision */ -qreal stringToHueF(const QString& string); +qreal stringToHueF(const QString& s); /** Extract the serverpart from MXID */ QString serverPart(const QString& mxId); -- cgit v1.2.3 From 0c425aedbf3729941e40b1606a7c54169b84cf97 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 09:31:59 +0900 Subject: Cleanup [skip ci] --- lib/events/accountdataevents.h | 2 +- lib/events/encryptedevent.cpp | 2 +- lib/events/encryptionevent.cpp | 2 +- lib/events/reactionevent.cpp | 2 +- lib/networksettings.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 600fa5be..68392a32 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -48,7 +48,7 @@ struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, TagRecord& rec) { // Parse a float both from JSON double and JSON string because - // libqmatrixclient previously used to use strings to store order. + // the library previously used to use strings to store order. const auto orderJv = jo.value("order"_ls); if (orderJv.isDouble()) rec.order = fromJson(orderJv); diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp index dac245fa..b5cedc69 100644 --- a/lib/events/encryptedevent.cpp +++ b/lib/events/encryptedevent.cpp @@ -2,7 +2,7 @@ #include "room.h" -using namespace QMatrixClient; +using namespace Quotient; using namespace QtOlm; EncryptedEvent::EncryptedEvent(const QJsonObject& ciphertext, diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index 945e6696..073303b0 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -30,7 +30,7 @@ struct JsonConverter { return EncryptionType::Undefined; } }; -} // namespace QMatrixClient +} // namespace Quotient using namespace Quotient; diff --git a/lib/events/reactionevent.cpp b/lib/events/reactionevent.cpp index df8910fe..003c8ead 100644 --- a/lib/events/reactionevent.cpp +++ b/lib/events/reactionevent.cpp @@ -18,7 +18,7 @@ #include "reactionevent.h" -using namespace QMatrixClient; +using namespace Quotient; void JsonObjectConverter::dumpTo( QJsonObject& jo, const EventRelation& pod) diff --git a/lib/networksettings.h b/lib/networksettings.h index a82a44d0..a6a13f93 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -40,4 +40,4 @@ public: Q_INVOKABLE void setupApplicationProxy() const; }; -} // namespace QMatrixClient +} // namespace Quotient -- cgit v1.2.3 From a1be9751f17041a7f577b04f7545035734a92e22 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 12:30:28 +0900 Subject: SyncData: x-qmatrixclient -> x-quotient --- lib/syncdata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index c784cd7d..5fcc8dda 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -26,7 +26,7 @@ using namespace Quotient; const QString SyncRoomData::UnreadCountKey = - QStringLiteral("x-qmatrixclient.unread_count"); + QStringLiteral("x-quotient.unread_count"); bool RoomSummary::isEmpty() const { -- cgit v1.2.3 From 64d096d3f8cdf93216ab138ea0378ae60a19b7d2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 12:33:52 +0900 Subject: Connection: check for cache_type setting in libQuotient group The fallback to libQMatrixClient still remains. --- lib/connection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 22db8e3d..56613857 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -111,7 +111,8 @@ public: bool cacheState = true; bool cacheToBinary = - SettingsGroup("libqmatrixclient").value("cache_type").toString() + SettingsGroup("libQuotient").get("cache_type", + SettingsGroup("libQMatrixClient").get("cache_type")) != "json"; bool lazyLoading = false; -- cgit v1.2.3 From 9ce16ef3123d5e63b931cafefb7dba4e894fc82b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 13:18:40 +0900 Subject: Drop EventType namespace and DEFINE_EVENTTYPE_ALIAS macro They've been deprecated for almost a year by now. --- lib/events/accountdataevents.h | 3 --- lib/events/callanswerevent.h | 1 - lib/events/callcandidatesevent.h | 1 - lib/events/callhangupevent.h | 1 - lib/events/callinviteevent.h | 1 - lib/events/directchatevent.h | 1 - lib/events/encryptionevent.h | 1 - lib/events/event.h | 22 ---------------------- lib/events/receiptevent.h | 1 - lib/events/redactionevent.h | 1 - lib/events/roomavatarevent.h | 1 - lib/events/roommemberevent.h | 1 - lib/events/roommessageevent.h | 1 - lib/events/simplestateevents.h | 4 ---- lib/events/typingevent.h | 1 - 15 files changed, 41 deletions(-) (limited to 'lib') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 68392a32..a8df08a5 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -90,7 +90,4 @@ DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags) DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id) DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", QSet, ignored_users) - -DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) -DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) } // namespace Quotient diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index e01b39db..2709882b 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -42,5 +42,4 @@ public: }; REGISTER_EVENT_TYPE(CallAnswerEvent) -DEFINE_EVENTTYPE_ALIAS(CallAnswer, CallAnswerEvent) } // namespace Quotient diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index 3d13ba8a..e224f048 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -42,5 +42,4 @@ public: }; REGISTER_EVENT_TYPE(CallCandidatesEvent) -DEFINE_EVENTTYPE_ALIAS(CallCandidates, CallCandidatesEvent) } // namespace Quotient diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index d23e29db..5d73fb62 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -30,5 +30,4 @@ public: }; REGISTER_EVENT_TYPE(CallHangupEvent) -DEFINE_EVENTTYPE_ALIAS(CallHangup, CallHangupEvent) } // namespace Quotient diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index 3e39e0ba..b067a492 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -41,5 +41,4 @@ public: }; REGISTER_EVENT_TYPE(CallInviteEvent) -DEFINE_EVENTTYPE_ALIAS(CallInvite, CallInviteEvent) } // namespace Quotient diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index b4981f2e..bb091c5c 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -30,5 +30,4 @@ public: QMultiHash usersToDirectChats() const; }; REGISTER_EVENT_TYPE(DirectChatEvent) -DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) } // namespace Quotient diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index 68e41719..7bbcc418 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -70,5 +70,4 @@ private: }; REGISTER_EVENT_TYPE(EncryptionEvent) -DEFINE_EVENTTYPE_ALIAS(Encryption, EncryptionEvent) } // namespace Quotient diff --git a/lib/events/event.h b/lib/events/event.h index e96d6897..d9c8e088 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -305,28 +305,6 @@ using Events = EventsArray; } \ // End of macro -#ifdef USE_EVENTTYPE_ALIAS -namespace EventType { - inline event_type_t logEventType(event_type_t id, const char* idName) - { - qDebug(EVENTS) << "Using id" << id << "for" << idName; - return id; - } -} // namespace EventType - -// This macro provides constants in EventType:: namespace for -// back-compatibility with libQMatrixClient 0.3 event type system. -# define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ - namespace EventType { \ - [[deprecated("Use is<>(), eventCast<>() or " \ - "visit<>()")]] static const auto _Id = \ - logEventType(typeId<_Type>(), #_Id); \ - } \ - // End of macro -#else -# define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing -#endif - // === is<>(), eventCast<>() and visit<>() === template diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index 71cd5de0..dd54a476 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -48,5 +48,4 @@ private: EventsWithReceipts _eventsWithReceipts; }; REGISTER_EVENT_TYPE(ReceiptEvent) -DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) } // namespace Quotient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index eac627d5..3b3af18e 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -35,5 +35,4 @@ public: QString reason() const { return contentJson()["reason"_ls].toString(); } }; REGISTER_EVENT_TYPE(RedactionEvent) -DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) } // namespace Quotient diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index 109f4014..4013773c 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -34,5 +34,4 @@ public: QUrl url() const { return content().url; } }; REGISTER_EVENT_TYPE(RoomAvatarEvent) -DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) } // namespace Quotient diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 2a16617a..43aaa8be 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -106,5 +106,4 @@ public: }; REGISTER_EVENT_TYPE(RoomMemberEvent) -DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) } // namespace Quotient diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index aa515c71..d0dbbfb3 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -90,7 +90,6 @@ private: REGISTER_ENUM(MsgType) }; REGISTER_EVENT_TYPE(RoomMessageEvent) -DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; namespace EventContent { diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 520f6b4b..eb1d2f7a 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -69,13 +69,9 @@ namespace EventContent { // End of macro DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) -DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", QString, alias) -DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) -DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) -DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) class RoomAliasesEvent : public StateEvent> { diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index d659c597..1cf4e69d 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -33,5 +33,4 @@ private: QStringList _users; }; REGISTER_EVENT_TYPE(TypingEvent) -DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) } // namespace Quotient -- cgit v1.2.3 From e9ae0d5eaac12a276ea352f1e633fb22cdd46828 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 17:01:41 +0900 Subject: Now that we're C++17, remove old compatibility cruft --- lib/events/encryptionevent.h | 2 +- lib/events/event.h | 23 ++++++++------- lib/events/roommemberevent.h | 2 +- lib/events/roommessageevent.h | 2 +- lib/joinstate.h | 2 +- lib/room.cpp | 10 ------- lib/syncdata.cpp | 2 +- lib/util.h | 65 +++---------------------------------------- 8 files changed, 20 insertions(+), 88 deletions(-) (limited to 'lib') diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index 7bbcc418..cbd3ba4a 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -66,7 +66,7 @@ public: int rotationPeriodMsgs() const { return content().rotationPeriodMsgs; } private: - REGISTER_ENUM(EncryptionType) + Q_ENUM(EncryptionType) }; REGISTER_EVENT_TYPE(EncryptionEvent) diff --git a/lib/events/event.h b/lib/events/event.h index d9c8e088..25362786 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -47,11 +47,10 @@ inline TargetEventT* weakPtrCast(const event_ptr_tt& ptr) /// Re-wrap a smart pointer to base into a smart pointer to derived template -[[deprecated("Consider using eventCast() or visit() " - "instead")]] inline event_ptr_tt -ptrCast(event_ptr_tt&& ptr) +[[deprecated("Consider using eventCast() or visit() instead")]] +inline event_ptr_tt ptrCast(event_ptr_tt&& ptr) { - return unique_ptr_cast(ptr); + return std::unique_ptr(static_cast(ptr.release())); } // === Standard Matrix key names and basicEventJson() === @@ -290,19 +289,19 @@ using Events = EventsArray; // This macro should be used in a public section of an event class to // provide matrixTypeId() and typeId(). -#define DEFINE_EVENT_TYPEID(_Id, _Type) \ - static constexpr event_mtype_t matrixTypeId() { return _Id; } \ - static auto typeId() { return Quotient::typeId<_Type>(); } \ +#define DEFINE_EVENT_TYPEID(_Id, _Type) \ + static constexpr event_mtype_t matrixTypeId() { return _Id; } \ + static auto typeId() { return Quotient::typeId<_Type>(); } \ // End of macro // This macro should be put after an event class definition (in .h or .cpp) // to enable its deserialisation from a /sync and other // polymorphic event arrays -#define REGISTER_EVENT_TYPE(_Type) \ - namespace { \ - [[gnu::unused]] static const auto _factoryAdded##_Type = \ - registerEventType<_Type>(); \ - } \ +#define REGISTER_EVENT_TYPE(_Type) \ + namespace { \ + [[maybe_unused]] static const auto _factoryAdded##_Type = \ + registerEventType<_Type>(); \ + } \ // End of macro // === is<>(), eventCast<>() and visit<>() === diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 43aaa8be..6a34fd7f 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -92,7 +92,7 @@ public: bool isAvatarUpdate() const; private: - REGISTER_ENUM(MembershipType) + Q_ENUM(MembershipType) }; template <> diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index d0dbbfb3..b393382a 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -87,7 +87,7 @@ private: const QString& jsonMsgType, EventContent::TypedBase* content); - REGISTER_ENUM(MsgType) + Q_ENUM(MsgType) }; REGISTER_EVENT_TYPE(RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; diff --git a/lib/joinstate.h b/lib/joinstate.h index 718ae3fd..31c2b6a7 100644 --- a/lib/joinstate.h +++ b/lib/joinstate.h @@ -31,7 +31,7 @@ enum class JoinState : unsigned int { Q_DECLARE_FLAGS(JoinStates, JoinState) -// We cannot use REGISTER_ENUM outside of a Q_OBJECT and besides, we want +// We cannot use Q_ENUM outside of a Q_OBJECT and besides, we want // to use strings that match respective JSON keys. static const std::array JoinStateStrings { { "join", "invite", "leave" } }; diff --git a/lib/room.cpp b/lib/room.cpp index d8fee5aa..f70e0b0e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1050,22 +1050,12 @@ FileTransferInfo Room::fileTransferInfo(const QString& id) const total = INT_MAX; } -#ifdef BROKEN_INITIALIZER_LISTS - FileTransferInfo fti; - fti.status = infoIt->status; - fti.progress = int(progress); - fti.total = int(total); - fti.localDir = QUrl::fromLocalFile(infoIt->localFileInfo.absolutePath()); - fti.localPath = QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()); - return fti; -#else return { infoIt->status, infoIt->isUpload, int(progress), int(total), QUrl::fromLocalFile(infoIt->localFileInfo.absolutePath()), QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()) }; -#endif } QUrl Room::fileSource(const QString& id) const diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 5fcc8dda..5b47b30f 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -92,7 +92,7 @@ SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, switch (joinState) { case JoinState::Join: ephemeral = load(room_, "ephemeral"_ls); - FALLTHROUGH; + [[fallthrough]]; case JoinState::Leave: { accountData = load(room_, "account_data"_ls); timeline = load(room_, "timeline"_ls); diff --git a/lib/util.h b/lib/util.h index 12d3f8ba..7c79804b 100644 --- a/lib/util.h +++ b/lib/util.h @@ -20,73 +20,15 @@ #include -#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) -# include -# include -#endif - #include #include -#if __has_cpp_attribute(fallthrough) -# define FALLTHROUGH [[fallthrough]] -#elif __has_cpp_attribute(clang::fallthrough) -# define FALLTHROUGH [[clang::fallthrough]] -#elif __has_cpp_attribute(gnu::fallthrough) -# define FALLTHROUGH [[gnu::fallthrough]] -#else -# define FALLTHROUGH // -fallthrough -#endif - -// Along the lines of Q_DISABLE_COPY +// Along the lines of Q_DISABLE_COPY - the upstream version comes in Qt 5.13 #define DISABLE_MOVE(_ClassName) \ _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; -#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) -// Copy-pasted from Qt 5.10 -template -Q_DECL_CONSTEXPR typename std::add_const::type& qAsConst(T& t) Q_DECL_NOTHROW -{ - return t; -} -// prevent rvalue arguments: -template -static void qAsConst(const T&&) Q_DECL_EQ_DELETE; -#endif - -// MSVC 2015 and older GCC's don't handle initialisation from initializer lists -// right in the absense of a constructor; MSVC 2015, notably, fails with -// "error C2440: 'return': cannot convert from 'initializer list' to ''" -#if (defined(_MSC_VER) && _MSC_VER < 1910) \ - || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) -# define BROKEN_INITIALIZER_LISTS -#endif - namespace Quotient { -// The below enables pretty-printing of enums in logs -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -# define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) -#else -// Thanks to Olivier for spelling it and for making Q_ENUM to replace it: -// https://woboq.com/blog/q_enum.html -# define REGISTER_ENUM(EnumName) \ - Q_ENUMS(EnumName) \ - friend QDebug operator<<(QDebug dbg, EnumName val) \ - { \ - static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ - return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey( \ - int(val)); \ - } -#endif - -/** static_cast<> for unique_ptr's */ -template -inline auto unique_ptr_cast(PtrT2&& p) -{ - return std::unique_ptr(static_cast(p.release())); -} - struct NoneTag {}; constexpr NoneTag none {}; @@ -185,7 +127,8 @@ namespace _impl { struct fn_traits; } -/** Determine traits of an arbitrary function/lambda/functor +/// Determine traits of an arbitrary function/lambda/functor +/*! * Doesn't work with generic lambdas and function objects that have * operator() overloaded. * \sa @@ -332,4 +275,4 @@ qreal stringToHueF(const QString& s); QString serverPart(const QString& mxId); } // namespace Quotient /// \deprecated Use namespace Quotient instead -namespace QMatrixClient = Quotient; \ No newline at end of file +namespace QMatrixClient = Quotient; -- cgit v1.2.3 From 372a9ed301e52d6daf7ccdb9a9982a67f46486a1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 11 Aug 2019 17:08:20 +0900 Subject: Use [[maybe_unused]] instead of [[gnu::unused]] --- lib/events/roomevent.cpp | 2 +- lib/events/stateevent.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index e19c03ce..971d8597 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -24,7 +24,7 @@ using namespace Quotient; -[[gnu::unused]] static auto roomEventTypeInitialised = +[[maybe_unused]] static auto roomEventTypeInitialised = Event::factory_t::chainFactory(); RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index c0bb0794..5909e8a6 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -23,7 +23,7 @@ using namespace Quotient; // Aside from the normal factory to instantiate StateEventBase inheritors // StateEventBase itself can be instantiated if there's a state_key JSON key // but the event type is unknown. -[[gnu::unused]] static auto stateEventTypeInitialised = +[[maybe_unused]] static auto stateEventTypeInitialised = RoomEvent::factory_t::addMethod( [](const QJsonObject& json, const QString& matrixType) -> StateEventPtr { if (!json.contains(StateKeyKeyL)) -- cgit v1.2.3 From cf19c87f6561afee7aba5591d3c81b7a158449ea Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 12 Aug 2019 15:37:52 +0900 Subject: Make Settings::remove() to clear the legacy; more doc-comments --- lib/settings.cpp | 8 +++++++- lib/settings.h | 43 +++++++++++++++++++++++++++++++++---------- 2 files changed, 40 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/settings.cpp b/lib/settings.cpp index 9c61ab5e..3bf61605 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -18,12 +18,18 @@ void Settings::setLegacyNames(const QString& organizationName, void Settings::setValue(const QString& key, const QVariant& value) { - // qCDebug() << "Setting" << key << "to" << value; QSettings::setValue(key, value); if (legacySettings.contains(key)) legacySettings.remove(key); } +void Settings::remove(const QString& key) +{ + QSettings::remove(key); + if (legacySettings.contains(key)) + legacySettings.remove(key); +} + QVariant Settings::value(const QString& key, const QVariant& defaultValue) const { auto value = QSettings::value(key, legacySettings.value(key, defaultValue)); diff --git a/lib/settings.h b/lib/settings.h index 4dcbbea0..d317ec2a 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -28,27 +28,50 @@ namespace Quotient { class Settings : public QSettings { Q_OBJECT public: - /** + /// Add a legacy organisation/application name to migrate settings from + /*! * Use this function before creating any Settings objects in order - * to setup a read-only location where configuration has previously - * been stored. This will provide an additional fallback in case of - * renaming the organisation/application. + * to set a legacy location where configuration has previously been stored. + * This will provide an additional fallback in case of renaming + * the organisation/application. Values in legacy locations are _removed_ + * when setValue() or remove() is called. */ static void setLegacyNames(const QString& organizationName, const QString& applicationName = {}); -#if defined(_MSC_VER) && _MSC_VER < 1900 - // VS 2013 (and probably older) aren't friends with 'using' statements - // that involve private constructors - explicit Settings(QObject* parent = 0) : QSettings(parent) {} -#else using QSettings::QSettings; -#endif + /// Set the value for a given key + /*! If the key exists in the legacy location, it is removed. */ Q_INVOKABLE void setValue(const QString& key, const QVariant& value); + + /// Remove the value from both the primary and legacy locations + Q_INVOKABLE void remove(const QString& key); + + /// Obtain a value for a given key + /*! + * If the key doesn't exist in the primary settings location, the legacy + * location is checked. If neither location has the key, + * \p defaultValue is returned. + * + * This function returns a QVariant; use get<>() to get the unwrapped + * value if you know the type upfront. + * + * \sa setLegacyNames, get + */ Q_INVOKABLE QVariant value(const QString& key, const QVariant& defaultValue = {}) const; + /// Obtain a value for a given key, coerced to the given type + /*! + * On top of value(), this function unwraps the QVariant and returns + * its contents assuming the type passed as the template parameter. + * If the type is different from the one stored inside the QVariant, + * \p defaultValue is returned. In presence of legacy settings, + * only the first found value is checked; if its type does not match, + * further checks through legacy settings are not performed and + * \p defaultValue is returned. + */ template T get(const QString& key, const T& defaultValue = {}) const { -- cgit v1.2.3 From 1ece121b14ae82f7dd0b893d0dbef3de7c84d303 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 12 Aug 2019 15:47:59 +0900 Subject: e2ee.h: Make constants inline instead of static This changes from internal linkage for every translation unit including e2ee.h to external linkage across translation units but without the need to create a .cpp file just to define and init the constants. --- lib/e2ee.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/e2ee.h b/lib/e2ee.h index 5a9b83c5..f49b9748 100644 --- a/lib/e2ee.h +++ b/lib/e2ee.h @@ -5,27 +5,27 @@ #include namespace Quotient { -static const auto CiphertextKeyL = "ciphertext"_ls; -static const auto SenderKeyKeyL = "sender_key"_ls; -static const auto DeviceIdKeyL = "device_id"_ls; -static const auto SessionIdKeyL = "session_id"_ls; +inline const auto CiphertextKeyL = "ciphertext"_ls; +inline const auto SenderKeyKeyL = "sender_key"_ls; +inline const auto DeviceIdKeyL = "device_id"_ls; +inline const auto SessionIdKeyL = "session_id"_ls; -static const auto AlgorithmKeyL = "algorithm"_ls; -static const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; -static const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; +inline const auto AlgorithmKeyL = "algorithm"_ls; +inline const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; +inline const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; -static const auto AlgorithmKey = QStringLiteral("algorithm"); -static const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); -static const auto RotationPeriodMsgsKey = +inline const auto AlgorithmKey = QStringLiteral("algorithm"); +inline const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); +inline const auto RotationPeriodMsgsKey = QStringLiteral("rotation_period_msgs"); -static const auto Ed25519Key = QStringLiteral("ed25519"); -static const auto Curve25519Key = QStringLiteral("curve25519"); -static const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); -static const auto OlmV1Curve25519AesSha2AlgoKey = +inline const auto Ed25519Key = QStringLiteral("ed25519"); +inline const auto Curve25519Key = QStringLiteral("curve25519"); +inline const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); +inline const auto OlmV1Curve25519AesSha2AlgoKey = QStringLiteral("m.olm.v1.curve25519-aes-sha2"); -static const auto MegolmV1AesSha2AlgoKey = +inline const auto MegolmV1AesSha2AlgoKey = QStringLiteral("m.megolm.v1.aes-sha2"); -static const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, +inline const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, MegolmV1AesSha2AlgoKey }; } // namespace Quotient -- cgit v1.2.3 From fadce11be92abe76cecfe6356b3b38f25dd93e8d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 13 Aug 2019 20:28:56 +0900 Subject: Support for server notices rooms (MSC1452) Closes #326. --- lib/events/accountdataevents.h | 1 + lib/jobs/basejob.cpp | 3 +++ lib/jobs/basejob.h | 1 + lib/room.cpp | 5 +++++ lib/room.h | 2 ++ 5 files changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index a8df08a5..31176766 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -27,6 +27,7 @@ namespace Quotient { constexpr const char* FavouriteTag = "m.favourite"; constexpr const char* LowPriorityTag = "m.lowpriority"; +constexpr const char* ServerNoticeTag = "m.server_notice"; struct TagRecord { using order_type = Omittable; diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index ec6b8375..5930e8b8 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -426,6 +426,9 @@ BaseJob::Status BaseJob::parseError(QNetworkReply* reply, ? tr("Requested room version: %1") .arg(errorJson.value("room_version"_ls).toString()) : errorJson.value("error"_ls).toString() }; + if (errCode == "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM") + return { CannotLeaveRoom, + tr("It's not allowed to leave a server notices room") }; // Not localisable on the client side if (errorJson.contains("error"_ls)) diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 90c20c37..68467d48 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -76,6 +76,7 @@ public: NetworkAuthRequiredError = NetworkAuthRequired, UserConsentRequired, UserConsentRequiredError = UserConsentRequired, + CannotLeaveRoom, UserDefinedError = 256 }; diff --git a/lib/room.cpp b/lib/room.cpp index f70e0b0e..bee9e9cb 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -951,6 +951,11 @@ bool Room::isFavourite() const { return d->tags.contains(FavouriteTag); } bool Room::isLowPriority() const { return d->tags.contains(LowPriorityTag); } +bool Room::isServerNoticeRoom() const +{ + return d->tags.contains(ServerNoticeTag); +} + bool Room::isDirectChat() const { return connection()->isDirectChat(id()); } QList Room::directChatUsers() const diff --git a/lib/room.h b/lib/room.h index 8448815d..2139f28b 100644 --- a/lib/room.h +++ b/lib/room.h @@ -389,6 +389,8 @@ public: bool isFavourite() const; /// Check whether the list of tags has m.lowpriority bool isLowPriority() const; + /// Check whether this room is for server notices (MSC1452) + bool isServerNoticeRoom() const; /// Check whether this room is a direct chat Q_INVOKABLE bool isDirectChat() const; -- cgit v1.2.3 From 8663c2e78407a0c0df872eaf9bb6b41de2fbdc9e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 16 Aug 2019 13:40:09 +0900 Subject: BaseJob: support M_USER_DEACTIVATED error code Closes #344. --- lib/jobs/basejob.cpp | 2 ++ lib/jobs/basejob.h | 1 + 2 files changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 5930e8b8..f3ba00b5 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -429,6 +429,8 @@ BaseJob::Status BaseJob::parseError(QNetworkReply* reply, if (errCode == "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM") return { CannotLeaveRoom, tr("It's not allowed to leave a server notices room") }; + if (errCode == "M_USER_DEACTIVATED") + return { UserDeactivated }; // Not localisable on the client side if (errorJson.contains("error"_ls)) diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 68467d48..fd7beca0 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -77,6 +77,7 @@ public: UserConsentRequired, UserConsentRequiredError = UserConsentRequired, CannotLeaveRoom, + UserDeactivated, UserDefinedError = 256 }; -- cgit v1.2.3 From 5d94e0fa2a3cd1e43b99b0c309bc9e84c6850378 Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Sat, 17 Aug 2019 19:36:38 +0200 Subject: Update the cache for sure if unread counters were changed In Quaternion, I was able to reproduce a process where `d->notificationCount` and `d->highlightCount` were changed in `Room::updateData()` meanwhile `roomChanges` indicated `NoChange` thus the cache became permanently inconsistent with running state. Without deep understanding of `roomChanges` logic, I've applied this straightforward change which made this phenomenon unreproducible. --- lib/room.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index bee9e9cb..031f9467 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1387,15 +1387,18 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; d->unreadMessages = data.unreadCount; + roomChanges |= Change::UnreadNotifsChange; emit unreadMessagesChanged(this); } if (data.highlightCount != d->highlightCount) { d->highlightCount = data.highlightCount; + roomChanges |= Change::UnreadNotifsChange; emit highlightCountChanged(); } if (data.notificationCount != d->notificationCount) { d->notificationCount = data.notificationCount; + roomChanges |= Change::UnreadNotifsChange; emit notificationCountChanged(); } if (roomChanges != Change::NoChange) { -- cgit v1.2.3 From 41a3661f353207be4005a2cd9b0bb7cfa5601a69 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 18 Aug 2019 18:22:15 +0900 Subject: New logging categories: events.state, events.messages, and e2ee --- lib/logging.cpp | 12 +++++------- lib/logging.h | 5 ++++- 2 files changed, 9 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/logging.cpp b/lib/logging.cpp index a7676c97..c346fbf1 100644 --- a/lib/logging.cpp +++ b/lib/logging.cpp @@ -18,17 +18,15 @@ #include "logging.h" -#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) -# define LOGGING_CATEGORY(Name, Id) \ - Q_LOGGING_CATEGORY((Name), (Id), QtInfoMsg) -#else -# define LOGGING_CATEGORY(Name, Id) Q_LOGGING_CATEGORY((Name), (Id)) -#endif +#define LOGGING_CATEGORY(Name, Id) Q_LOGGING_CATEGORY((Name), (Id), QtInfoMsg) // Use LOGGING_CATEGORY instead of Q_LOGGING_CATEGORY in the rest of the code LOGGING_CATEGORY(MAIN, "quotient.main") -LOGGING_CATEGORY(PROFILER, "quotient.profiler") LOGGING_CATEGORY(EVENTS, "quotient.events") +LOGGING_CATEGORY(STATE, "quotient.events.state") +LOGGING_CATEGORY(MESSAGES, "quotient.events.messages") LOGGING_CATEGORY(EPHEMERAL, "quotient.events.ephemeral") +LOGGING_CATEGORY(E2EE, "quotient.e2ee") LOGGING_CATEGORY(JOBS, "quotient.jobs") LOGGING_CATEGORY(SYNCJOB, "quotient.jobs.sync") +LOGGING_CATEGORY(PROFILER, "quotient.profiler") diff --git a/lib/logging.h b/lib/logging.h index 3d13569a..ce4131bb 100644 --- a/lib/logging.h +++ b/lib/logging.h @@ -22,11 +22,14 @@ #include Q_DECLARE_LOGGING_CATEGORY(MAIN) -Q_DECLARE_LOGGING_CATEGORY(PROFILER) +Q_DECLARE_LOGGING_CATEGORY(STATE) +Q_DECLARE_LOGGING_CATEGORY(MESSAGES) Q_DECLARE_LOGGING_CATEGORY(EVENTS) Q_DECLARE_LOGGING_CATEGORY(EPHEMERAL) +Q_DECLARE_LOGGING_CATEGORY(E2EE) Q_DECLARE_LOGGING_CATEGORY(JOBS) Q_DECLARE_LOGGING_CATEGORY(SYNCJOB) +Q_DECLARE_LOGGING_CATEGORY(PROFILER) namespace Quotient { // QDebug manipulators -- cgit v1.2.3 From 7e4e7e9cf10828192482f91748cf29807f101f36 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 18 Aug 2019 19:01:36 +0900 Subject: Room::decryptMessage: fix memory leaks and dangling pointers --- lib/room.cpp | 58 +++++++++++++++++++++++++++------------------------------- 1 file changed, 27 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index bee9e9cb..0c6471e5 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1107,34 +1107,30 @@ bool Room::usesEncryption() const return !d->getCurrentState()->algorithm().isEmpty(); } -const RoomEvent* Room::decryptMessage(EncryptedEvent* encryptedEvent) const +RoomEventPtr Room::decryptMessage(EncryptedEvent* encryptedEvent) { if (encryptedEvent->algorithm() == OlmV1Curve25519AesSha2AlgoKey) { - QString identityKey = connection()->olmAccount()->curve25519IdentityKey(); + QString identityKey = + connection()->olmAccount()->curve25519IdentityKey(); QJsonObject personalCipherObject = encryptedEvent->ciphertext(identityKey); if (personalCipherObject.isEmpty()) { - qCDebug(EVENTS) << "Encrypted event is not for the current device"; - return nullptr; + qCDebug(E2EE) << "Encrypted event is not for the current device"; + return {}; } - return makeEvent( - decryptMessage(personalCipherObject, - encryptedEvent->senderKey().toLatin1())) - .get(); + return makeEvent(decryptMessage( + personalCipherObject, encryptedEvent->senderKey().toLatin1())); } if (encryptedEvent->algorithm() == MegolmV1AesSha2AlgoKey) { - return makeEvent( - decryptMessage(encryptedEvent->ciphertext(), - encryptedEvent->senderKey(), - encryptedEvent->deviceId(), - encryptedEvent->sessionId())) - .get(); + return makeEvent(decryptMessage( + encryptedEvent->ciphertext(), encryptedEvent->senderKey(), + encryptedEvent->deviceId(), encryptedEvent->sessionId())); } - return nullptr; + return {}; } -const QString Room::decryptMessage(QJsonObject personalCipherObject, - QByteArray senderKey) const +QString Room::decryptMessage(QJsonObject personalCipherObject, + QByteArray senderKey) { QString decrypted; @@ -1145,25 +1141,26 @@ const QString Room::decryptMessage(QJsonObject personalCipherObject, int type = personalCipherObject.value(TypeKeyL).toInt(-1); QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1(); - PreKeyMessage* preKeyMessage = new PreKeyMessage(body); - session = new InboundSession(connection()->olmAccount(), preKeyMessage, - senderKey); + PreKeyMessage preKeyMessage { body }; + session = + new InboundSession(connection()->olmAccount(), &preKeyMessage, senderKey, this); if (type == 0) { - if (!session->matches(preKeyMessage, senderKey)) { + if (!session->matches(&preKeyMessage, senderKey)) { connection()->olmAccount()->removeOneTimeKeys(session); } try { - decrypted = session->decrypt(preKeyMessage); + decrypted = session->decrypt(&preKeyMessage); } catch (std::runtime_error& e) { qWarning(EVENTS) << "Decrypt failed:" << e.what(); } - } else if (type == 1) { - Message* message = new Message(body); - if (!session->matches(preKeyMessage, senderKey)) { + } + else if (type == 1) { + Message message { body }; + if (!session->matches(&preKeyMessage, senderKey)) { qWarning(EVENTS) << "Invalid encrypted message"; } try { - decrypted = session->decrypt(message); + decrypted = session->decrypt(&message); } catch (std::runtime_error& e) { qWarning(EVENTS) << "Decrypt failed:" << e.what(); } @@ -1172,16 +1169,15 @@ const QString Room::decryptMessage(QJsonObject personalCipherObject, return decrypted; } -const QString Room::sessionKey(const QString& senderKey, const QString& deviceId, - const QString& sessionId) const +QString Room::sessionKey(const QString& senderKey, const QString& deviceId, + const QString& sessionId) const { // TODO: handling an m.room_key event return ""; } -const QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, - const QString& deviceId, - const QString& sessionId) const +QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, + const QString& deviceId, const QString& sessionId) { QString decrypted; using namespace QtOlm; -- cgit v1.2.3 From a9eef2c6b2f73ebc921b07fe37009bccf31038c8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 18 Aug 2019 19:26:37 +0900 Subject: Room::decryptMessage: now with the header file changes --- lib/room.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 2139f28b..9113654b 100644 --- a/lib/room.h +++ b/lib/room.h @@ -191,14 +191,13 @@ public: memberCount() const; int timelineSize() const; bool usesEncryption() const; - const RoomEvent* decryptMessage(EncryptedEvent* encryptedEvent) const; - const QString decryptMessage(QJsonObject personalCipherObject, - QByteArray senderKey) const; - const QString sessionKey(const QString& senderKey, const QString& deviceId, - const QString& sessionId) const; - const QString decryptMessage(QByteArray cipher, const QString& senderKey, - const QString& deviceId, - const QString& sessionId) const; + RoomEventPtr decryptMessage(EncryptedEvent* encryptedEvent); + QString decryptMessage(QJsonObject personalCipherObject, + QByteArray senderKey); + QString sessionKey(const QString& senderKey, const QString& deviceId, + const QString& sessionId) const; + QString decryptMessage(QByteArray cipher, const QString& senderKey, + const QString& deviceId, const QString& sessionId); int joinedCount() const; int invitedCount() const; int totalMemberCount() const; -- cgit v1.2.3 From aa004bd80f9548c00bd7856c20421f168fadad69 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 18 Aug 2019 19:06:06 +0900 Subject: Room: use new logging categories --- lib/room.cpp | 97 ++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 51 insertions(+), 46 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index f7d3a797..cd7aaac7 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -215,8 +215,8 @@ public: stubbedState.emplace(evtKey, loadStateEvent(EventT::matrixTypeId(), {}, stateKey)); - qCDebug(MAIN) << "A new stub event created for key {" - << evtKey.first << evtKey.second << "}"; + qCDebug(STATE) << "A new stub event created for key {" + << evtKey.first << evtKey.second << "}"; } evt = stubbedState[evtKey].get(); Q_ASSERT(evt); @@ -475,8 +475,8 @@ void Room::setJoinState(JoinState state) if (state == oldState) return; d->joinState = state; - qCDebug(MAIN) << "Room" << id() << "changed state: " << int(oldState) - << "->" << int(state); + qCDebug(STATE) << "Room" << id() << "changed state: " << int(oldState) + << "->" << int(state); emit changed(Change::JoinStateChange); emit joinStateChanged(oldState, state); } @@ -530,12 +530,12 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) unreadMessages = 0; unreadMessages += newUnreadMessages; - qCDebug(MAIN) << "Room" << q->objectName() << "has gained" - << newUnreadMessages << "unread message(s)," - << (q->readMarker() == timeline.crend() - ? "in total at least" - : "in total") - << unreadMessages << "unread message(s)"; + qCDebug(MESSAGES) << "Room" << q->objectName() << "has gained" + << newUnreadMessages << "unread message(s)," + << (q->readMarker() == timeline.crend() + ? "in total at least" + : "in total") + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); } } @@ -577,11 +577,11 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, if (force || unreadMessages != oldUnreadCount) { if (unreadMessages == -1) { - qCDebug(MAIN) + qCDebug(MESSAGES) << "Room" << displayname << "has no more unread messages"; } else - qCDebug(MAIN) << "Room" << displayname << "still has" - << unreadMessages << "unread message(s)"; + qCDebug(MESSAGES) << "Room" << displayname << "still has" + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); changes |= Change::UnreadNotifsChange; } @@ -594,7 +594,7 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) const auto prevMarker = q->readMarker(); auto changes = promoteReadMarker(q->localUser(), upToMarker); if (prevMarker != upToMarker) - qCDebug(MAIN) << "Marked messages as read until" << *q->readMarker(); + qCDebug(MESSAGES) << "Marked messages as read until" << *q->readMarker(); // We shouldn't send read receipts for the local user's own messages - so // search earlier messages for the latest message not from the local user @@ -940,10 +940,10 @@ void Room::Private::setTags(TagsMap newTags) else newTags.insert(checkRes.second, newTags.take(k)); } - } + tags = move(newTags); - qCDebug(MAIN) << "Room" << q->objectName() << "is tagged with" - << q->tagNames().join(QStringLiteral(", ")); + qCDebug(STATE) << "Room" << q->objectName() << "is tagged with" + << q->tagNames().join(QStringLiteral(", ")); emit q->tagsChanged(); } @@ -1381,7 +1381,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { - qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; + qCDebug(MESSAGES) << "Setting unread_count to" << data.unreadCount; d->unreadMessages = data.unreadCount; roomChanges |= Change::UnreadNotifsChange; emit unreadMessagesChanged(this); @@ -1495,12 +1495,13 @@ QString Room::retryMessage(const QString& txnId) if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); if (transferIt->status == FileTransferInfo::Completed) { - qCDebug(MAIN) << "File for transaction" << txnId - << "has already been uploaded, bypassing re-upload"; + qCDebug(MESSAGES) + << "File for transaction" << txnId + << "has already been uploaded, bypassing re-upload"; } else { if (isJobRunning(transferIt->job)) { - qCDebug(MAIN) << "Abandoning the upload job for transaction" - << txnId << "and starting again"; + qCDebug(MESSAGES) << "Abandoning the upload job for transaction" + << txnId << "and starting again"; transferIt->job->abandon(); emit fileTransferFailed(txnId, tr("File upload will be retried")); @@ -1996,15 +1997,15 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->isRedacted() && ti->redactedBecause()->id() == redaction.id()) { - qCDebug(MAIN) << "Redaction" << redaction.id() << "of event" << ti->id() - << "already done, skipping"; + qCDebug(EVENTS) << "Redaction" << redaction.id() << "of event" + << ti->id() << "already done, skipping"; return true; } // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeRedacted(*ti, redaction)); - qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction.id(); + qCDebug(EVENTS) << "Redacted" << oldEvent->id() << "with" << redaction.id(); if (oldEvent->isStateEvent()) { const StateEventKey evtKey { oldEvent->matrixType(), oldEvent->stateKey() }; @@ -2012,7 +2013,7 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) if (currentState.value(evtKey) == oldEvent.get()) { Q_ASSERT(ti.index() >= 0); // Historical states can't be in // currentState - qCDebug(MAIN).nospace() + qCDebug(EVENTS).nospace() << "Redacting state " << oldEvent->matrixType() << "/" << oldEvent->stateKey(); // Retarget the current state to the newly made event. @@ -2066,7 +2067,7 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->replacedBy() == newEvent.id()) { - qCDebug(MAIN) << "Event" << ti->id() << "is already replaced with" + qCDebug(EVENTS) << "Event" << ti->id() << "is already replaced with" << newEvent.id(); return true; } @@ -2074,7 +2075,7 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeReplaced(*ti, newEvent)); - qCDebug(MAIN) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); + qCDebug(EVENTS) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); return true; } @@ -2125,7 +2126,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (targetIt != it) *targetIt = makeRedacted(**targetIt, *r); else - qCDebug(MAIN) + qCDebug(EVENTS) << "Redaction" << r->id() << "ignored: target event" << r->redactedEvent() << "is not found"; // If the target event comes later, it comes already redacted. @@ -2142,9 +2143,10 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (targetIt != it) *targetIt = makeReplaced(**targetIt, *msg); else // FIXME: don't ignore, just show it wherever it arrived - qCDebug(MAIN) << "Replacing event" << msg->id() - << "ignored: replaced event" - << msg->replacedEvent() << "is not found"; + qCDebug(EVENTS) + << "Replacing event" << msg->id() + << "ignored: replaced event" << msg->replacedEvent() + << "is not found"; // Same as with redactions above, the replaced event coming // later will come already with the new content. } @@ -2191,9 +2193,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) emit q->pendingEventChanged(pendingEvtIdx); } emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); - qDebug(EVENTS) << "Merging pending event from transaction" - << nextPendingEvt->transactionId() << "into" - << nextPendingEvt->id(); + qDebug(MESSAGES) << "Merging pending event from transaction" + << nextPendingEvt->transactionId() << "into" + << nextPendingEvt->id(); auto transfer = fileTransfers.take(nextPendingEvt->transactionId()); if (transfer.status != FileTransferInfo::None) fileTransfers.insert(nextPendingEvt->id(), transfer); @@ -2226,8 +2228,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) } } - qCDebug(MAIN) << "Room" << q->objectName() << "received" << totalInserted - << "new events; the last event is now" << timeline.back(); + qCDebug(MESSAGES) << "Room" << q->objectName() << "received" + << totalInserted << "new events; the last event is now" + << timeline.back(); // The first event in the just-added batch (referred to by `from`) // defines whose read marker can possibly be promoted any further over @@ -2238,8 +2241,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto firstWriter = q->user((*from)->senderId()); if (q->readMarker(firstWriter) != timeline.crend()) { roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() - << "to" << *q->readMarker(firstWriter); + qCDebug(MESSAGES) + << "Auto-promoted read marker for" << firstWriter->id() << "to" + << *q->readMarker(firstWriter); } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); @@ -2276,8 +2280,9 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) const auto insertedSize = moveEventsToTimeline(events, Older); const auto from = timeline.crend() - insertedSize; - qCDebug(MAIN) << "Room" << displayname << "received" << insertedSize - << "past events; the oldest event is now" << timeline.front(); + qCDebug(MESSAGES) << "Room" << displayname << "received" << insertedSize + << "past events; the oldest event is now" + << timeline.front(); q->onAddHistoricalTimelineEvents(from); emit q->addedMessages(timeline.front().index(), from->index()); @@ -2319,13 +2324,13 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this,oldStateEvent] (const RoomAliasesEvent& ae) { // clang-format on if (ae.aliases().isEmpty()) { - qDebug(MAIN).noquote() + qDebug(STATE).noquote() << ae.stateKey() << "no more has aliases for room" << objectName(); d->aliasServers.remove(ae.stateKey()); } else { d->aliasServers.insert(ae.stateKey()); - qDebug(MAIN).nospace().noquote() + qDebug(STATE).nospace().noquote() << "New server with aliases for room " << objectName() << ": " << ae.stateKey(); } @@ -2372,7 +2377,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) break; case MembershipType::Join: if (evt.membership() == MembershipType::Invite) - qCWarning(MAIN) << "Invalid membership change from " + qCWarning(STATE) << "Invalid membership change from " "Join to Invite:" << evt; if (evt.membership() != prevMembership) { @@ -2519,7 +2524,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (auto* evt = eventCast(event)) { auto readEventId = evt->event_id(); - qCDebug(MAIN) << "Server-side read marker at" << readEventId; + qCDebug(STATE) << "Server-side read marker at" << readEventId; d->serverReadMarker = readEventId; const auto newMarker = findInTimeline(readEventId); changes |= newMarker != timelineEdge() @@ -2533,7 +2538,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (!currentData || currentData->contentJson() != event->contentJson()) { emit accountDataAboutToChange(event->matrixType()); currentData = move(event); - qCDebug(MAIN) << "Updated account data of type" + qCDebug(STATE) << "Updated account data of type" << currentData->matrixType(); emit accountDataChanged(currentData->matrixType()); return Change::AccountDataChange; -- cgit v1.2.3 From 8f076ef801b9478c9414787252d46703be87953b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 18 Aug 2019 19:30:13 +0900 Subject: room.cpp: applied clang-format with updated penalty values --- lib/room.cpp | 69 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 34 insertions(+), 35 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index cd7aaac7..b0829252 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -555,10 +555,10 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, // Try to auto-promote the read marker over the user's own messages // (switch to direct iterators for that). - auto eagerMarker = find_if(newMarker.base(), timeline.cend(), - [=](const TimelineItem& ti) { - return ti->senderId() != u->id(); - }); + auto eagerMarker = + find_if(newMarker.base(), timeline.cend(), [=](const TimelineItem& ti) { + return ti->senderId() != u->id(); + }); auto changes = setLastReadEvent(u, (*(eagerMarker - 1))->id()); if (isLocalUser(u)) { @@ -932,9 +932,8 @@ void Room::Private::setTags(TagsMap newTags) { emit q->tagsAboutToChange(); const auto keys = newTags.keys(); - for (const auto& k : keys) { - const auto& checkRes = validatedTag(k); - if (checkRes.first) { + for (const auto& k : keys) + if (const auto& checkRes = validatedTag(k); checkRes.first) { if (newTags.contains(checkRes.second)) newTags.remove(k); else @@ -981,13 +980,12 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const Q_ASSERT(event->hasFileContent()); const auto* fileInfo = event->content()->fileInfo(); QString fileName; - if (!fileInfo->originalName.isEmpty()) { + if (!fileInfo->originalName.isEmpty()) fileName = QFileInfo(fileInfo->originalName).fileName(); - } else if (!event->plainBody().isEmpty()) { + else if (!event->plainBody().isEmpty()) { // Having no better options, assume that the body has // the original file URL or at least the file name. - QUrl u { event->plainBody() }; - if (u.isValid()) + if (QUrl u { event->plainBody() }; u.isValid()) fileName = QFileInfo(u.path()).fileName(); } // Check the file name for sanity @@ -995,8 +993,8 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const return "file." % fileInfo->mimeType.preferredSuffix(); if (QSysInfo::productType() == "windows") { - const auto& suffixes = fileInfo->mimeType.suffixes(); - if (!suffixes.isEmpty() + if (const auto& suffixes = fileInfo->mimeType.suffixes(); + suffixes.isEmpty() && std::none_of(suffixes.begin(), suffixes.end(), [&fileName](const QString& s) { return fileName.endsWith(s); @@ -1438,10 +1436,11 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) { const auto txnId = pEvent->transactionId(); // TODO, #133: Enqueue the job rather than immediately trigger it. - if (auto call = connection->callApi( - BackgroundRequest, id, pEvent->matrixType(), txnId, - pEvent->contentJson())) { - Room::connect(call, &BaseJob::started, q, [this, txnId] { + if (auto call = + connection->callApi(BackgroundRequest, id, + pEvent->matrixType(), txnId, + pEvent->contentJson())) { + Room::connect(call, &BaseJob::sentRequest, q, [this, txnId] { auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { qWarning(EVENTS) << "Pending event for transaction" << txnId @@ -1586,8 +1585,8 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, // to enable the preview while the event is pending. uploadFile(txnId, localPath); { - auto&& event = makeEvent(plainText, localFile, - asGenericFile); + auto&& event = + makeEvent(plainText, localFile, asGenericFile); event->setTransactionId(txnId); d->addAsPending(std::move(event)); } @@ -1745,8 +1744,8 @@ void Room::Private::getPreviousContent(int limit) if (isJobRunning(eventsHistoryJob)) return; - eventsHistoryJob = connection->callApi(id, prevBatch, "b", - "", limit); + eventsHistoryJob = + connection->callApi(id, prevBatch, "b", "", limit); emit q->eventsHistoryJobChanged(); connect(eventsHistoryJob, &BaseJob::success, q, [=] { prevBatch = eventsHistoryJob->end(); @@ -1896,10 +1895,10 @@ void Room::Private::dropDuplicateEvents(RoomEvents& events) const // Multiple-remove (by different criteria), single-erase // 1. Check for duplicates against the timeline. - auto dupsBegin = remove_if(events.begin(), events.end(), - [&](const RoomEventPtr& e) { - return eventsIndex.contains(e->id()); - }); + auto dupsBegin = + remove_if(events.begin(), events.end(), [&](const RoomEventPtr& e) { + return eventsIndex.contains(e->id()); + }); // 2. Check for duplicates within the batch if there are still events. for (auto eIt = events.begin(); distance(eIt, dupsBegin) > 1; ++eIt) @@ -2024,8 +2023,8 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) } if (const auto* reaction = eventCast(oldEvent)) { const auto& targetEvtId = reaction->relation().eventId; - const auto lookupKey = qMakePair(targetEvtId, - EventRelation::Annotation()); + const auto lookupKey = + qMakePair(targetEvtId, EventRelation::Annotation()); if (relations.contains(lookupKey)) { relations[lookupKey].removeOne(reaction); } @@ -2166,9 +2165,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto timelineSize = timeline.size(); size_t totalInserted = 0; for (auto it = events.begin(); it != events.end();) { - auto nextPendingPair = findFirstOf(it, events.end(), - unsyncedEvents.begin(), - unsyncedEvents.end(), isEchoEvent); + auto nextPendingPair = + findFirstOf(it, events.end(), unsyncedEvents.begin(), + unsyncedEvents.end(), isEchoEvent); const auto& remoteEcho = nextPendingPair.first; const auto& localEcho = nextPendingPair.second; @@ -2599,11 +2598,11 @@ QString Room::Private::calculateDisplayname() const // "heroes" if available. const bool localUserIsIn = joinState == JoinState::Join; - const bool emptyRoom = membersMap.isEmpty() - || (membersMap.size() == 1 - && isLocalUser(*membersMap.begin())); - const bool nonEmptySummary = !summary.heroes.omitted() - && !summary.heroes->empty(); + const bool emptyRoom = + membersMap.isEmpty() + || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); + const bool nonEmptySummary = + !summary.heroes.omitted() && !summary.heroes->empty(); auto shortlist = nonEmptySummary ? buildShortlist(summary.heroes.value()) : !emptyRoom ? buildShortlist(membersMap) : users_shortlist_t {}; -- cgit v1.2.3 From 25f8622bcdd4322ce9e41c34883cea5ff71a6817 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 19 Aug 2019 08:20:25 +0900 Subject: Room::Private::doSendEvent: fix too early change in BaseJob signals That's a matter of another upcoming commit. --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index b0829252..2e2f73c4 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1440,7 +1440,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) connection->callApi(BackgroundRequest, id, pEvent->matrixType(), txnId, pEvent->contentJson())) { - Room::connect(call, &BaseJob::sentRequest, q, [this, txnId] { + Room::connect(call, &BaseJob::started, q, [this, txnId] { auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { qWarning(EVENTS) << "Pending event for transaction" << txnId -- cgit v1.2.3 From 8629748c5c609882486d25d989becfe0ae2352ed Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 20 Aug 2019 17:59:27 +0900 Subject: Store userId in ConnectionData instead of Connection To collect all connection-identifying information in a single place. --- lib/connection.cpp | 87 ++++++++++++++++++++++++-------------------------- lib/connectiondata.cpp | 11 +++++-- lib/connectiondata.h | 6 +++- 3 files changed, 54 insertions(+), 50 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 56613857..c9623729 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -99,7 +99,6 @@ public: DirectChatsMap dcLocalAdditions; DirectChatsMap dcLocalRemovals; std::unordered_map accountData; - QString userId; int syncLoopTimeout = -1; GetCapabilitiesJob* capabilitiesJob = nullptr; @@ -116,7 +115,7 @@ public: != "json"; bool lazyLoading = false; - void connectWithToken(const QString& user, const QString& accessToken, + void connectWithToken(const QString& userId, const QString& accessToken, const QString& deviceId); void removeRoom(const QString& roomId); @@ -132,7 +131,8 @@ public: void packAndSendAccountData(EventPtr&& event) { const auto eventType = event->matrixType(); - q->callApi(userId, eventType, event->contentJson()); + q->callApi(data->userId(), eventType, + event->contentJson()); accountData[eventType] = std::move(event); emit q->accountDataChanged(eventType); } @@ -159,7 +159,7 @@ Connection::Connection(QObject* parent) : Connection({}, parent) {} Connection::~Connection() { - qCDebug(MAIN) << "deconstructing connection object for" << d->userId; + qCDebug(MAIN) << "deconstructing connection object for" << userId(); stopSync(); } @@ -234,11 +234,11 @@ void Connection::doConnectToServer(const QString& user, const QString& password, const QString& initialDeviceName, const QString& deviceId) { - auto loginJob = callApi( - QStringLiteral("m.login.password"), - UserIdentifier { QStringLiteral("m.id.user"), - { { QStringLiteral("user"), user } } }, - password, /*token*/ "", deviceId, initialDeviceName); + auto loginJob = + callApi(QStringLiteral("m.login.password"), + UserIdentifier { QStringLiteral("m.id.user"), + { { QStringLiteral("user"), user } } }, + password, /*token*/ "", deviceId, initialDeviceName); connect(loginJob, &BaseJob::success, this, [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); @@ -299,11 +299,11 @@ bool Connection::loadingCapabilities() const return d->capabilities.roomVersions.omitted(); } -void Connection::Private::connectWithToken(const QString& user, +void Connection::Private::connectWithToken(const QString& userId, const QString& accessToken, const QString& deviceId) { - userId = user; + data->setUserId(userId); q->user(); // Creates a User object for the local user data->setToken(accessToken.toLatin1()); data->setDeviceId(deviceId); @@ -354,9 +354,9 @@ void Connection::sync(int timeout) Filter filter; filter.room->timeline->limit = 100; filter.room->state->lazyLoadMembers = d->lazyLoading; - auto job = d->syncJob = callApi(BackgroundRequest, - d->data->lastEvent(), filter, - timeout); + auto job = d->syncJob = + callApi(BackgroundRequest, d->data->lastEvent(), filter, + timeout); connect(job, &SyncJob::success, this, [this, job] { onSyncSuccess(job->takeData()); d->syncJob = nullptr; @@ -485,7 +485,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) [this, &eventPtr](const Event& accountEvent) { if (is(accountEvent)) qCDebug(MAIN) - << "Users ignored by" << d->userId << "updated:" + << "Users ignored by" << userId() << "updated:" << QStringList::fromSet(ignoredUsers()).join(','); auto& currentData = d->accountData[accountEvent.matrixType()]; @@ -504,7 +504,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) qDebug(MAIN) << "Sending updated direct chats to the server:" << d->dcLocalRemovals.size() << "removal(s)," << d->dcLocalAdditions.size() << "addition(s)"; - callApi(d->userId, QStringLiteral("m.direct"), + callApi(userId(), QStringLiteral("m.direct"), toJson(d->directChats)); d->dcLocalAdditions.clear(); d->dcLocalRemovals.clear(); @@ -634,8 +634,8 @@ DownloadFileJob* Connection::downloadFile(const QUrl& url, { auto mediaId = url.authority() + url.path(); auto idParts = splitMediaId(mediaId); - auto* job = callApi(idParts.front(), idParts.back(), - localFilename); + auto* job = + callApi(idParts.front(), idParts.back(), localFilename); return job; } @@ -648,7 +648,7 @@ Connection::createRoom(RoomVisibility visibility, const QString& alias, const QVector& invite3pids, const QJsonObject& creationContent) { - invites.removeOne(d->userId); // The creator is by definition in the room + invites.removeOne(userId()); // The creator is by definition in the room auto job = callApi(visibility == PublishRoom ? QStringLiteral("public") : QStringLiteral("private"), @@ -672,12 +672,7 @@ Connection::createRoom(RoomVisibility visibility, const QString& alias, void Connection::requestDirectChat(const QString& userId) { - if (auto* u = user(userId)) - requestDirectChat(u); - else - qCCritical(MAIN) << "Connection::requestDirectChat: Couldn't get a " - "user object for" - << userId; + doInDirectChat(userId, [this](Room* r) { emit directChatAvailable(r); }); } void Connection::requestDirectChat(User* u) @@ -700,7 +695,7 @@ void Connection::doInDirectChat(User* u, const std::function& operation) { Q_ASSERT(u); - const auto& userId = u->id(); + const auto& otherUserId = u->id(); // There can be more than one DC; find the first valid (existing and // not left), and delete inexistent (forgotten?) ones along the way. DirectChatsMap removals; @@ -710,9 +705,9 @@ void Connection::doInDirectChat(User* u, if (auto r = room(roomId, JoinState::Join)) { Q_ASSERT(r->id() == roomId); // A direct chat with yourself should only involve yourself :) - if (userId == d->userId && r->totalMemberCount() > 1) + if (otherUserId == userId() && r->totalMemberCount() > 1) continue; - qCDebug(MAIN) << "Requested direct chat with" << userId + qCDebug(MAIN) << "Requested direct chat with" << otherUserId << "is already available as" << r->id(); operation(r); return; @@ -721,10 +716,10 @@ void Connection::doInDirectChat(User* u, Q_ASSERT(ir->id() == roomId); auto j = joinRoom(ir->id()); connect(j, &BaseJob::success, this, - [this, roomId, userId, operation] { + [this, roomId, otherUserId, operation] { qCDebug(MAIN) << "Joined the already invited direct chat with" - << userId << "as" << roomId; + << otherUserId << "as" << roomId; operation(room(roomId, JoinState::Join)); }); return; @@ -734,7 +729,7 @@ void Connection::doInDirectChat(User* u, if (room(roomId, JoinState::Leave)) continue; - qCWarning(MAIN) << "Direct chat with" << userId << "known as room" + qCWarning(MAIN) << "Direct chat with" << otherUserId << "known as room" << roomId << "is not valid and will be discarded"; // Postpone actual deletion until we finish iterating d->directChats. removals.insert(it.key(), it.value()); @@ -750,9 +745,9 @@ void Connection::doInDirectChat(User* u, emit directChatsListChanged({}, removals); } - auto j = createDirectChat(userId); - connect(j, &BaseJob::success, this, [this, j, userId, operation] { - qCDebug(MAIN) << "Direct chat with" << userId << "has been created as" + auto j = createDirectChat(otherUserId); + connect(j, &BaseJob::success, this, [this, j, otherUserId, operation] { + qCDebug(MAIN) << "Direct chat with" << otherUserId << "has been created as" << j->roomId(); operation(room(j->roomId(), JoinState::Join)); }); @@ -845,7 +840,7 @@ SendMessageJob* Connection::sendMessage(const QString& roomId, QUrl Connection::homeserver() const { return d->data->baseUrl(); } -QString Connection::domain() const { return d->userId.section(':', 1); } +QString Connection::domain() const { return userId().section(':', 1); } Room* Connection::room(const QString& roomId, JoinStates states) const { @@ -905,30 +900,30 @@ Room* Connection::invitation(const QString& roomId) const return d->roomMap.value({ roomId, true }, nullptr); } -User* Connection::user(const QString& userId) +User* Connection::user(const QString& uId) { - if (userId.isEmpty()) + if (uId.isEmpty()) return nullptr; - if (!userId.startsWith('@') || !userId.contains(':')) { - qCCritical(MAIN) << "Malformed userId:" << userId; + if (!uId.startsWith('@') || !uId.contains(':')) { + qCCritical(MAIN) << "Malformed userId:" << uId; return nullptr; } - if (d->userMap.contains(userId)) - return d->userMap.value(userId); - auto* user = userFactory()(this, userId); - d->userMap.insert(userId, user); + if (d->userMap.contains(uId)) + return d->userMap.value(uId); + auto* user = userFactory()(this, uId); + d->userMap.insert(uId, user); emit newUser(user); return user; } const User* Connection::user() const { - return d->userMap.value(d->userId, nullptr); + return d->userMap.value(userId(), nullptr); } -User* Connection::user() { return user(d->userId); } +User* Connection::user() { return user(userId()); } -QString Connection::userId() const { return d->userId; } +QString Connection::userId() const { return d->data->userId(); } QString Connection::deviceId() const { return d->data->deviceId(); } diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index 486de03d..41d97b87 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -29,10 +29,11 @@ struct ConnectionData::Private { QUrl baseUrl; QByteArray accessToken; QString lastEvent; + QString userId; QString deviceId; mutable unsigned int txnCounter = 0; - const qint64 id = QDateTime::currentMSecsSinceEpoch(); + const qint64 txnBase = QDateTime::currentMSecsSinceEpoch(); }; ConnectionData::ConnectionData(QUrl baseUrl) @@ -75,12 +76,15 @@ void ConnectionData::setPort(int port) const QString& ConnectionData::deviceId() const { return d->deviceId; } +const QString& ConnectionData::userId() const { return d->userId; } + void ConnectionData::setDeviceId(const QString& deviceId) { d->deviceId = deviceId; - qCDebug(MAIN) << "updated deviceId to" << d->deviceId; } +void ConnectionData::setUserId(const QString& userId) { d->userId = userId; } + QString ConnectionData::lastEvent() const { return d->lastEvent; } void ConnectionData::setLastEvent(QString identifier) @@ -90,5 +94,6 @@ void ConnectionData::setLastEvent(QString identifier) QByteArray ConnectionData::generateTxnId() const { - return QByteArray::number(d->id) + 'q' + QByteArray::number(++d->txnCounter); + return d->deviceId.toLatin1() + QByteArray::number(d->txnBase) + + QByteArray::number(++d->txnCounter); } diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 80ace08c..561893df 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -33,13 +33,17 @@ public: QByteArray accessToken() const; QUrl baseUrl() const; const QString& deviceId() const; - + const QString& userId() const; QNetworkAccessManager* nam() const; + void setBaseUrl(QUrl baseUrl); void setToken(QByteArray accessToken); + [[deprecated("Use setBaseUrl() instead")]] void setHost(QString host); + [[deprecated("Use setBaseUrl() instead")]] void setPort(int port); void setDeviceId(const QString& deviceId); + void setUserId(const QString& userId); QString lastEvent() const; void setLastEvent(QString identifier); -- cgit v1.2.3 From 92614294a89c1f450b82c2b6e35614cf124dc344 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 20 Aug 2019 18:10:46 +0900 Subject: Connection::run() Finally, clients can pre-create job objects and then separately submit them for execution on a given connection - previously such separation was a privilege of Connection (others had to use Connection::callApi<>, which invoked jobs right away). --- lib/connection.cpp | 11 +++++++++-- lib/connection.h | 20 +++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index c9623729..5f90ed55 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -780,7 +780,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) [this, leaveJob, forgetJob, room] { if (leaveJob->error() == BaseJob::Success || leaveJob->error() == BaseJob::NotFoundError) { - forgetJob->start(connectionData()); + run(forgetJob); // If the matching /sync response hasn't arrived yet, // mark the room for explicit deletion if (room->joinState() != JoinState::Leave) @@ -794,7 +794,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) }); connect(leaveJob, &BaseJob::failure, forgetJob, &BaseJob::abandon); } else - forgetJob->start(connectionData()); + run(forgetJob); connect(forgetJob, &BaseJob::result, this, [this, id, forgetJob] { // Leave room in case of success, or room not known by server if (forgetJob->error() == BaseJob::Success @@ -1355,6 +1355,13 @@ void Connection::setLazyLoading(bool newValue) } } +void Connection::run(BaseJob* job, RunningPolicy runningPolicy) const +{ + connect(job, &BaseJob::failure, this, &Connection::requestFailed); + job->start(d->data.get(), runningPolicy & BackgroundRequest); + d->data->submit(job); +} + void Connection::getTurnServers() { auto job = callApi(); diff --git a/lib/connection.h b/lib/connection.h index c807b827..7e32e5c9 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -37,6 +37,8 @@ class Account; } namespace Quotient { +Q_NAMESPACE + class Room; class User; class ConnectionData; @@ -89,10 +91,12 @@ static inline user_factory_t defaultUserFactory() /** Enumeration with flags defining the network job running policy * So far only background/foreground flags are available. * - * \sa Connection::callApi + * \sa Connection::callApi, Connection::run */ enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; +Q_ENUM_NS(RunningPolicy) + class Connection : public QObject { Q_OBJECT @@ -352,9 +356,12 @@ public: bool lazyLoading() const; void setLazyLoading(bool newValue); - /** Start a job of a specified type with specified arguments and policy + /*! Start a pre-created job object on this connection */ + void run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest) const; + + /*! Start a job of a specified type with specified arguments and policy * - * This is a universal method to start a job of a type passed + * This is a universal method to create and start a job of a type passed * as a template parameter. The policy allows to fine-tune the way * the job is executed - as of this writing it means a choice * between "foreground" and "background". @@ -368,14 +375,13 @@ public: JobT* callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs) const { auto job = new JobT(std::forward(jobArgs)...); - connect(job, &BaseJob::failure, this, &Connection::requestFailed); - job->start(connectionData(), runningPolicy & BackgroundRequest); + run(job, runningPolicy); return job; } - /** Start a job of a specified type with specified arguments + /*! Start a job of a specified type with specified arguments * - * This is an overload that calls the job with "foreground" policy. + * This is an overload that runs the job with "foreground" policy. */ template JobT* callApi(JobArgTs&&... jobArgs) const -- cgit v1.2.3 From ad159b5206de615762e22f95e97ae61400f11761 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 20 Aug 2019 19:56:54 +0900 Subject: BaseJob: Status/StatusCode tweaks, cleanup, mo' comments Notably, recovered Status::fromHttpCode() that was introduced in 5722ceaf4bd10c29f1091e3dc5a87f5650ea8c71 but fell victim of a careless merge (so much for introducing non-topical changes in feature branches). --- lib/jobs/basejob.cpp | 135 ++++++++++++++++++++++++++++----------------------- lib/jobs/basejob.h | 37 ++++++++------ 2 files changed, 97 insertions(+), 75 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index f3ba00b5..621762be 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -77,6 +77,8 @@ public: QByteArray rawResponse; QUrl errorUrl; //< May contain a URL to help with some errors + LoggingCategory logCat = JOBS; + QTimer timer; QTimer retryTimer; @@ -86,7 +88,12 @@ public: int maxRetries = errorStrategy.size(); int retriesTaken = 0; - LoggingCategory logCat = JOBS; + QString urlForLog() const + { + return reply + ? reply->url().toString(QUrl::RemoveQuery) + : makeRequestUrl(connection->baseUrl(), apiEndpoint).toString(); + } }; BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, @@ -112,7 +119,7 @@ BaseJob::~BaseJob() QUrl BaseJob::requestUrl() const { - return d->reply ? d->reply->request().url() : QUrl(); + return d->reply ? d->reply->url() : QUrl(); } bool BaseJob::isBackground() const @@ -193,17 +200,13 @@ void BaseJob::Private::sendRequest(bool inBackground) req.setRawHeader("Authorization", QByteArray("Bearer ") + connection->accessToken()); req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); -#endif req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); -#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) - // some sources claim that there are issues with QT 5.8 req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); -#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)); @@ -318,6 +321,47 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } +BaseJob::Status BaseJob::Status::fromHttpCode(int httpCode, QString msg) +{ + // clang-format off + return { [httpCode]() -> StatusCode { + if (httpCode / 10 == 41) // 41x errors + return httpCode == 410 ? IncorrectRequestError : NotFoundError; + switch (httpCode) { + case 401: case 403: case 407: + return ContentAccessError; + case 404: + return NotFoundError; + case 400: case 405: case 406: case 426: case 428: case 505: + case 494: // Unofficial nginx "Request header too large" + case 497: // Unofficial nginx "HTTP request sent to HTTPS port" + return IncorrectRequestError; + case 429: + return TooManyRequestsError; + case 501: case 510: + return RequestNotImplementedError; + case 511: + return NetworkAuthRequiredError; + default: + return NetworkError; + } + }(), std::move(msg) }; + // clang-format on +} + +QDebug BaseJob::Status::dumpToLog(QDebug dbg) const +{ + QDebugStateSaver _s(dbg); + dbg.noquote().nospace(); + if (auto* const k = QMetaEnum::fromType().valueToKey(code)) { + const QByteArray b = k; + dbg << b.mid(b.lastIndexOf(':')); + } else + dbg << code; + return dbg << ": " << message; + +} + BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const { // QNetworkReply error codes seem to be flawed when it comes to HTTP; @@ -327,62 +371,30 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const const auto httpCodeHeader = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!httpCodeHeader.isValid()) { - qCWarning(d->logCat) << this << "didn't get valid HTTP headers"; + qCWarning(d->logCat) << "No valid HTTP headers from" << d->urlForLog(); return { NetworkError, reply->errorString() }; } - const QString replyState = reply->isRunning() - ? QStringLiteral("(tentative)") - : QStringLiteral("(final)"); - const auto urlString = '|' + d->reply->url().toDisplayString(); const auto httpCode = httpCodeHeader.toInt(); - const auto reason = - reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); if (httpCode / 100 == 2) // 2xx { - qCDebug(d->logCat).noquote().nospace() << this << urlString; - qCDebug(d->logCat).noquote() << " " << httpCode << reason << replyState; + if (reply->isFinished()) + qCInfo(d->logCat) << httpCode << "<-" << d->urlForLog(); if (!checkContentType(reply->rawHeader("Content-Type"), d->expectedContentTypes)) return { UnexpectedResponseTypeWarning, "Unexpected content type of the response" }; return NoError; } + if (reply->isFinished()) + qCWarning(d->logCat) << httpCode << "<-" << d->urlForLog(); - qCWarning(d->logCat).noquote().nospace() << this << urlString; - qCWarning(d->logCat).noquote() << " " << httpCode << reason << replyState; - return { [httpCode]() -> StatusCode { - if (httpCode / 10 == 41) - return httpCode == 410 ? IncorrectRequestError - : NotFoundError; - switch (httpCode) { - case 401: - case 403: - case 407: - return ContentAccessError; - case 404: - return NotFoundError; - case 400: - case 405: - case 406: - case 426: - case 428: - case 505: - case 494: // Unofficial nginx "Request header too large" - case 497: // Unofficial nginx "HTTP request sent to HTTPS port" - return IncorrectRequestError; - case 429: - return TooManyRequestsError; - case 501: - case 510: - return RequestNotImplementedError; - case 511: - return NetworkAuthRequiredError; - default: - return NetworkError; - } - }(), - reply->errorString() }; + auto message = reply->errorString(); + if (message.isEmpty()) + message = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute) + .toString(); + + return Status::fromHttpCode(httpCode, message); } BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) @@ -398,7 +410,7 @@ BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) BaseJob::Status BaseJob::parseJson(const QJsonDocument&) { return Success; } -BaseJob::Status BaseJob::parseError(QNetworkReply* reply, +BaseJob::Status BaseJob::parseError(QNetworkReply* /*reply*/, const QJsonObject& errorJson) { const auto errCode = errorJson.value("errcode"_ls).toString(); @@ -441,7 +453,7 @@ BaseJob::Status BaseJob::parseError(QNetworkReply* reply, void BaseJob::stop() { - // This method is used to semi-finalise the job before retrying; so + // This method is (also) used to semi-finalise the job before retrying; so // stop the timeout timer but keep the retry timer running. d->timer.stop(); if (d->reply) { @@ -449,7 +461,7 @@ void BaseJob::stop() if (d->reply->isRunning()) { qCWarning(d->logCat) << this << "stopped without ready network reply"; - d->reply->abort(); + d->reply->abort(); // Keep the reply object in case clients need it } } else qCWarning(d->logCat) << this << "stopped with empty network reply"; @@ -474,10 +486,10 @@ void BaseJob::finishJob() return; } - // Notify those interested in any completion of the job (including killing) + // Notify those interested in any completion of the job including abandon() emit finished(this); - emit result(this); + emit result(this); // abandon() doesn't emit this if (error()) emit failure(this); else @@ -582,21 +594,22 @@ void BaseJob::setStatus(Status s) { // The crash that led to this code has been reported in // https://github.com/quotient-im/Quaternion/issues/566 - basically, - // when cleaning up childrent of a deleted Connection, there's a chance + // when cleaning up children of a deleted Connection, there's a chance // of pending jobs being abandoned, calling setStatus(Abandoned). // There's nothing wrong with this; however, the safety check for // cleartext access tokens below uses d->connection - which is a dangling // pointer. // To alleviate that, a stricter condition is applied, that for Abandoned // and to-be-Abandoned jobs the status message will be disregarded entirely. - // For 0.6 we might rectify the situation by making d->connection - // a QPointer<> (and derive ConnectionData from QObject, respectively). - if (d->status.code == Abandoned || s.code == Abandoned) - s.message.clear(); - + // We could rectify the situation by making d->connection a QPointer<> + // (and deriving ConnectionData from QObject, respectively) but it's + // a too edge case for the hassle. if (d->status == s) return; + if (d->status.code == Abandoned || s.code == Abandoned) + s.message.clear(); + if (!s.message.isEmpty() && d->connection && !d->connection->accessToken().isEmpty()) s.message.replace(d->connection->accessToken(), "(REDACTED)"); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index fd7beca0..9de7b49d 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -24,6 +24,7 @@ #include #include #include +#include class QNetworkReply; class QSslError; @@ -43,18 +44,23 @@ class BaseJob : public QObject { Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries) public: + /*! The status code of a job + * + * Every job is created in Unprepared status; upon calling prepare() + * from Connection (if things are fine) it go to Pending status. After + * that, the next transition comes after the reply arrives and its contents + * are analysed. At any point in time the job can be abandon()ed, causing + * it to switch to status Abandoned for a brief period before deletion. + */ enum StatusCode { - NoError = 0 // To be compatible with Qt conventions - , Success = 0, + NoError = Success, // To be compatible with Qt conventions Pending = 1, - WarningLevel = 20, + WarningLevel = 20, //< Warnings have codes starting from this UnexpectedResponseType = 21, UnexpectedResponseTypeWarning = UnexpectedResponseType, - Abandoned = 50 //< A tiny period between abandoning and object deletion - , - ErrorLevel = 100 //< Errors have codes starting from this - , + Abandoned = 50, //< A tiny period between abandoning and object deletion + ErrorLevel = 100, //< Errors have codes starting from this NetworkError = 100, Timeout, TimeoutError = Timeout, @@ -64,10 +70,12 @@ public: IncorrectRequestError = IncorrectRequest, IncorrectResponse, IncorrectResponseError = IncorrectResponse, - JsonParseError //< deprecated; Use IncorrectResponse instead + JsonParseError + Q_DECL_ENUMERATOR_DEPRECATED_X("Use IncorrectResponse instead") = IncorrectResponse, TooManyRequests, TooManyRequestsError = TooManyRequests, + RateLimited = TooManyRequests, RequestNotImplemented, RequestNotImplementedError = RequestNotImplemented, UnsupportedRoomVersion, @@ -80,6 +88,7 @@ public: UserDeactivated, UserDefinedError = 256 }; + Q_ENUM(StatusCode) /** * A simple wrapper around QUrlQuery that allows its creation from @@ -97,7 +106,7 @@ public: 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. @@ -106,16 +115,16 @@ public: * along the lines of StatusCode, with additional values * starting at UserDefinedError */ - class Status { - public: + struct Status { Status(StatusCode c) : code(c) {} Status(int c, QString m) : code(c), message(std::move(m)) {} + static Status fromHttpCode(int httpCode, QString msg = {}); bool good() const { return code < ErrorLevel; } - friend QDebug operator<<(QDebug dbg, const Status& s) + QDebug dumpToLog(QDebug dbg) const; + friend QDebug operator<<(const QDebug& dbg, const Status& s) { - QDebugStateSaver _s(dbg); - return dbg.noquote().nospace() << s.code << ": " << s.message; + return s.dumpToLog(dbg); } bool operator==(const Status& other) const -- cgit v1.2.3 From 59c4996a602e9eeae4e3bfc0210ff15f957df38f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 20 Aug 2019 20:09:09 +0900 Subject: BaseJob/ConnectionData: connection-wide rate-limiting As before, completely transparent for clients, driven by 529 errors from the server (but cases of rate limiting are signalled by BaseJob::rateLimited). That brings changes to BaseJob API: timeouts now use int64_t and also can be handled in std::chrono terms; aboutToStart() -> aboutToSendRequest(); started() -> sentRequest(). Closes #292. --- lib/connection.cpp | 2 +- lib/connectiondata.cpp | 70 ++++++++++++++++++- lib/connectiondata.h | 6 ++ lib/jobs/basejob.cpp | 159 ++++++++++++++++++++++++------------------- lib/jobs/basejob.h | 63 +++++++++++------ lib/jobs/downloadfilejob.cpp | 4 +- lib/jobs/downloadfilejob.h | 4 +- lib/room.cpp | 2 +- 8 files changed, 212 insertions(+), 98 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5f90ed55..5ebdcf6c 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1358,7 +1358,7 @@ void Connection::setLazyLoading(bool newValue) void Connection::run(BaseJob* job, RunningPolicy runningPolicy) const { connect(job, &BaseJob::failure, this, &Connection::requestFailed); - job->start(d->data.get(), runningPolicy & BackgroundRequest); + job->prepare(d->data.get(), runningPolicy & BackgroundRequest); d->data->submit(job); } diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index 41d97b87..e241e376 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -20,11 +20,21 @@ #include "logging.h" #include "networkaccessmanager.h" +#include "jobs/basejob.h" + +#include +#include + +#include using namespace Quotient; -struct ConnectionData::Private { - explicit Private(QUrl url) : baseUrl(std::move(url)) {} +class ConnectionData::Private { +public: + explicit Private(QUrl url) : baseUrl(std::move(url)) + { + rateLimiter.setSingleShot(true); + } QUrl baseUrl; QByteArray accessToken; @@ -34,14 +44,68 @@ struct ConnectionData::Private { mutable unsigned int txnCounter = 0; const qint64 txnBase = QDateTime::currentMSecsSinceEpoch(); + + QString id() const { return userId + '/' + deviceId; } + + using job_queue_t = std::queue>; + std::array jobs; // 0 - foreground, 1 - background + QTimer rateLimiter; }; ConnectionData::ConnectionData(QUrl baseUrl) : d(std::make_unique(std::move(baseUrl))) -{} +{ + // Each lambda invocation below takes no more than one job from the + // queues (first foreground, then background) and resumes it; then + // restarts the rate limiter timer with duration 0, effectively yielding + // to the event loop and then resuming until both queues are empty. + QObject::connect(&d->rateLimiter, &QTimer::timeout, [this] { + // TODO: Consider moving out all job->sendRequest() invocations to + // a dedicated thread + d->rateLimiter.setInterval(0); + for (auto& q : d->jobs) + while (!q.empty()) { + auto& job = q.front(); + q.pop(); + if (!job || job->error() == BaseJob::Abandoned) + continue; + if (job->error() != BaseJob::Pending) { + qCCritical(MAIN) + << "Job" << job + << "is in the wrong status:" << job->status(); + Q_ASSERT(false); + job->setStatus(BaseJob::Pending); + } + job->sendRequest(); + d->rateLimiter.start(); + return; + } + qCDebug(MAIN) << d->id() << "job queues are empty"; + }); +} ConnectionData::~ConnectionData() = default; +void ConnectionData::submit(BaseJob* job) +{ + Q_ASSERT(job->error() == BaseJob::Pending); + if (!d->rateLimiter.isActive()) { + job->sendRequest(); + return; + } + d->jobs[size_t(job->isBackground())].emplace(job); + qCDebug(MAIN) << job << "queued," << d->jobs.front().size() << "+" + << d->jobs.back().size() << "total jobs in" << d->id() + << "queues"; +} + +void ConnectionData::limitRate(std::chrono::milliseconds nextCallAfter) +{ + qCDebug(MAIN) << "Jobs for" << (d->userId + "/" + d->deviceId) + << "suspended for" << nextCallAfter.count() << "ms"; + d->rateLimiter.start(nextCallAfter); +} + QByteArray ConnectionData::accessToken() const { return d->accessToken; } QUrl ConnectionData::baseUrl() const { return d->baseUrl; } diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 561893df..5cd7c3c7 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -21,15 +21,21 @@ #include #include +#include class QNetworkAccessManager; namespace Quotient { +class BaseJob; + class ConnectionData { public: explicit ConnectionData(QUrl baseUrl); virtual ~ConnectionData(); + void submit(BaseJob* job); + void limitRate(std::chrono::milliseconds nextCallAfter); + QByteArray accessToken() const; QUrl baseUrl() const; const QString& deviceId() const; diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 621762be..0a17431c 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -31,6 +31,8 @@ #include using namespace Quotient; +using std::chrono::seconds, std::chrono::milliseconds; +using std::chrono_literals::operator""s; struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) @@ -43,6 +45,11 @@ struct NetworkReplyDeleter : public QScopedPointerDeleteLater { class BaseJob::Private { public: + struct JobTimeoutConfig { + seconds jobTimeout; + seconds nextRetryInterval; + }; + // Using an idiom from clang-tidy: // http://clang.llvm.org/extra/clang-tidy/checks/modernize-pass-by-value.html Private(HttpVerb v, QString endpoint, const QUrlQuery& q, Data&& data, @@ -52,12 +59,14 @@ public: , requestQuery(q) , requestData(std::move(data)) , needsToken(nt) - {} + { + timer.setSingleShot(true); + retryTimer.setSingleShot(true); + } - void sendRequest(bool inBackground); - const JobTimeoutConfig& getCurrentTimeoutConfig() const; + void sendRequest(); - const ConnectionData* connection = nullptr; + ConnectionData* connection = nullptr; // Contents for the network request HttpVerb verb; @@ -67,13 +76,15 @@ public: Data requestData; bool needsToken; + bool inBackground = false; + // 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; + QByteArrayList expectedContentTypes { "application/json" }; QScopedPointer reply; - Status status = Pending; + Status status = Unprepared; QByteArray rawResponse; QUrl errorUrl; //< May contain a URL to help with some errors @@ -82,12 +93,18 @@ public: QTimer timer; QTimer retryTimer; - QVector errorStrategy = { { 90, 5 }, - { 90, 10 }, - { 120, 30 } }; - int maxRetries = errorStrategy.size(); + static constexpr std::array errorStrategy { + { { 90s, 5s }, { 90s, 10s }, { 120s, 30s } } + }; + int maxRetries = int(errorStrategy.size()); int retriesTaken = 0; + const JobTimeoutConfig& getCurrentTimeoutConfig() const + { + return errorStrategy[std::min(size_t(retriesTaken), + errorStrategy.size() - 1)]; + } + QString urlForLog() const { return reply @@ -106,9 +123,11 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, : 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); + connect(&d->retryTimer, &QTimer::timeout, this, [this] { + setStatus(Pending); + sendRequest(); + }); } BaseJob::~BaseJob() @@ -124,10 +143,7 @@ QUrl BaseJob::requestUrl() const bool BaseJob::isBackground() const { - return d->reply - && d->reply->request() - .attribute(QNetworkRequest::BackgroundRequestAttribute) - .toBool(); + return d->inBackground; } const QString& BaseJob::apiEndpoint() const { return d->apiEndpoint; } @@ -191,7 +207,7 @@ QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path, return baseUrl; } -void BaseJob::Private::sendRequest(bool inBackground) +void BaseJob::Private::sendRequest() { QNetworkRequest req { makeRequestUrl(connection->baseUrl(), apiEndpoint, requestQuery) }; @@ -223,36 +239,31 @@ void BaseJob::Private::sendRequest(bool inBackground) } } -void BaseJob::beforeStart(const ConnectionData*) {} +void BaseJob::doPrepare() {} -void BaseJob::afterStart(const ConnectionData*, QNetworkReply*) {} +void BaseJob::onSentRequest(QNetworkReply*) {} void BaseJob::beforeAbandon(QNetworkReply*) {} -void BaseJob::start(const ConnectionData* connData, bool inBackground) +void BaseJob::prepare(ConnectionData* connData, bool inBackground) { + d->inBackground = inBackground; d->connection = connData; - d->retryTimer.setSingleShot(true); - connect(&d->retryTimer, &QTimer::timeout, this, - [this, inBackground] { sendRequest(inBackground); }); - - beforeStart(connData); - if (status().good()) - sendRequest(inBackground); - if (status().good()) - afterStart(connData, d->reply.data()); - if (!status().good()) + doPrepare(); + if (status().code != Unprepared && status().code != Pending) QTimer::singleShot(0, this, &BaseJob::finishJob); + setStatus(Pending); } -void BaseJob::sendRequest(bool inBackground) +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(inBackground); + if (status().code == Abandoned) + return; + Q_ASSERT(d->connection && status().code == Pending); + qCDebug(d->logCat) << "Making request to" << d->urlForLog(); + emit aboutToSendRequest(); + d->sendRequest(); + Q_ASSERT(d->reply); connect(d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply); if (d->reply->isRunning()) { connect(d->reply.data(), &QNetworkReply::metaDataChanged, this, @@ -262,10 +273,12 @@ void BaseJob::sendRequest(bool inBackground) connect(d->reply.data(), &QNetworkReply::downloadProgress, this, &BaseJob::downloadProgress); d->timer.start(getCurrentTimeout()); - qCDebug(d->logCat) << this << "request has been sent"; - emit started(); + qCInfo(d->logCat).noquote() << "Request sent to" << d->urlForLog(); + onSentRequest(d->reply.data()); + emit sentRequest(); } else - qCWarning(d->logCat) << this << "request could not start"; + qCWarning(d->logCat).noquote() + << "Request could not start:" << d->urlForLog(); } void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } @@ -286,13 +299,7 @@ void BaseJob::gotReply() parseError(d->reply.data(), QJsonDocument::fromJson(d->rawResponse).object())); } - - if (status().code != TooManyRequestsError) - finishJob(); - else { - stop(); - emit retryScheduled(d->retriesTaken, d->retryTimer.interval()); - } + finishJob(); } bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) @@ -416,14 +423,13 @@ BaseJob::Status BaseJob::parseError(QNetworkReply* /*reply*/, const auto errCode = errorJson.value("errcode"_ls).toString(); if (error() == TooManyRequestsError || errCode == "M_LIMIT_EXCEEDED") { QString msg = tr("Too many requests"); - auto retryInterval = errorJson.value("retry_after_ms"_ls).toInt(-1); - if (retryInterval != -1) - msg += tr(", next retry advised after %1 ms").arg(retryInterval); + int64_t retryAfterMs = errorJson.value("retry_after_ms"_ls).toInt(-1); + if (retryAfterMs >= 0) + msg += tr(", next retry advised after %1 ms").arg(retryAfterMs); else // We still have to figure some reasonable interval - retryInterval = getNextRetryInterval(); + retryAfterMs = getNextRetryMs(); - qCWarning(d->logCat) << this << "will retry in" << retryInterval << "ms"; - d->retryTimer.start(retryInterval); + d->connection->limitRate(milliseconds(retryAfterMs)); return { TooManyRequestsError, msg }; } @@ -470,19 +476,23 @@ void BaseJob::stop() void BaseJob::finishJob() { stop(); - if ((error() == NetworkError || error() == TimeoutError) + if (error() == TooManyRequests) { + emit rateLimited(); + setStatus(Pending); + d->connection->submit(this); + return; + } + if ((error() == NetworkError || error() == Timeout) && d->retriesTaken < d->maxRetries) { - // TODO: The whole retrying thing should be put to ConnectionManager + // TODO: The whole retrying thing should be put to Connection(Manager) // otherwise independently retrying jobs make a bit of notification // storm towards the UI. - const auto retryInterval = error() == TimeoutError - ? 0 - : getNextRetryInterval(); + const seconds retryIn = error() == Timeout ? 0s : getNextRetryInterval(); ++d->retriesTaken; qCWarning(d->logCat).nospace() << this << ": retry #" << d->retriesTaken - << " in " << retryInterval / 1000 << " s"; - d->retryTimer.start(retryInterval); - emit retryScheduled(d->retriesTaken, retryInterval); + << " in " << retryIn.count() << " s"; + d->retryTimer.start(retryIn); + emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); return; } @@ -498,24 +508,35 @@ void BaseJob::finishJob() deleteLater(); } -const JobTimeoutConfig& BaseJob::Private::getCurrentTimeoutConfig() const +seconds BaseJob::getCurrentTimeout() const +{ + return d->getCurrentTimeoutConfig().jobTimeout; +} + +BaseJob::duration_ms_t BaseJob::getCurrentTimeoutMs() const +{ + return milliseconds(getCurrentTimeout()).count(); +} + +seconds BaseJob::getNextRetryInterval() const { - return errorStrategy[std::min(retriesTaken, errorStrategy.size() - 1)]; + return d->getCurrentTimeoutConfig().nextRetryInterval; } -BaseJob::duration_t BaseJob::getCurrentTimeout() const +BaseJob::duration_ms_t BaseJob::getNextRetryMs() const { - return d->getCurrentTimeoutConfig().jobTimeout * 1000; + return milliseconds(getNextRetryInterval()).count(); } -BaseJob::duration_t BaseJob::getNextRetryInterval() const +milliseconds BaseJob::timeToRetry() const { - return d->getCurrentTimeoutConfig().nextRetryInterval * 1000; + return d->retryTimer.isActive() ? d->retryTimer.remainingTimeAsDuration() + : 0s; } -BaseJob::duration_t BaseJob::millisToRetry() const +BaseJob::duration_ms_t BaseJob::millisToRetry() const { - return d->retryTimer.isActive() ? d->retryTimer.remainingTime() : 0; + return timeToRetry().count(); } int BaseJob::maxRetries() const { return d->maxRetries; } diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 9de7b49d..4dc287f8 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -34,11 +34,6 @@ class ConnectionData; enum class HttpVerb { Get, Put, Post, Delete }; -struct JobTimeoutConfig { - int jobTimeout; - int nextRetryInterval; -}; - class BaseJob : public QObject { Q_OBJECT Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) @@ -59,6 +54,7 @@ public: WarningLevel = 20, //< Warnings have codes starting from this UnexpectedResponseType = 21, UnexpectedResponseTypeWarning = UnexpectedResponseType, + Unprepared = 25, //< Initial job state is incomplete, hence warning level Abandoned = 50, //< A tiny period between abandoning and object deletion ErrorLevel = 100, //< Errors have codes starting from this NetworkError = 100, @@ -140,8 +136,6 @@ public: QString message; }; - using duration_t = int; // milliseconds - public: BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, bool needsToken = true); @@ -153,13 +147,16 @@ public: /** Current status of the job */ Status status() const; + /** Short human-friendly message on the job status */ QString statusCaption() const; + /** Get raw response body as received from the server * \param bytesAtMost return this number of leftmost bytes, or -1 * to return the entire response */ QByteArray rawData(int bytesAtMost = -1) const; + /** Get UI-friendly sample of raw data * * This is almost the same as rawData but appends the "truncated" @@ -175,17 +172,24 @@ public: * \sa status */ int error() const; + /** Error-specific message, as returned by the server */ virtual QString errorString() const; + /** A URL to help/clarify the error, if provided by the server */ QUrl errorUrl() 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; + using duration_ms_t = std::chrono::milliseconds::rep; // normally int64_t + + std::chrono::seconds getCurrentTimeout() const; + Q_INVOKABLE duration_ms_t getCurrentTimeoutMs() const; + std::chrono::seconds getNextRetryInterval() const; + Q_INVOKABLE duration_ms_t getNextRetryMs() const; + std::chrono::milliseconds timeToRetry() const; + Q_INVOKABLE duration_ms_t millisToRetry() const; friend QDebug operator<<(QDebug dbg, const BaseJob* j) { @@ -193,7 +197,7 @@ public: } public slots: - void start(const ConnectionData* connData, bool inBackground = false); + void prepare(ConnectionData* connData, bool inBackground); /** * Abandons the result of this job, arrived or unarrived. @@ -206,10 +210,10 @@ public slots: signals: /** The job is about to send a network request */ - void aboutToStart(); + void aboutToSendRequest(); /** The job has sent a network request */ - void started(); + void sentRequest(); /** The job has changed its status */ void statusChanged(Status newStatus); @@ -222,7 +226,14 @@ signals: * @param inMilliseconds the interval after which the next attempt will be * taken */ - void retryScheduled(int nextAttempt, int inMilliseconds); + void retryScheduled(int nextAttempt, duration_ms_t inMilliseconds); + + /** + * The previous network request has been rate-limited; the next attempt + * will be queued and run sometime later. Since other jobs may already + * wait in the queue, it's not possible to predict the wait time. + */ + void rateLimited(); /** * Emitted when the job is finished, in any case. It is used to notify @@ -297,9 +308,20 @@ protected: 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); + /*! Prepares the job for execution + * + * This method is called no more than once per job lifecycle, + * when it's first scheduled for execution; in particular, it is not called + * on retries. + */ + virtual void doPrepare(); + /*! Postprocessing after the network request has been sent + * + * This method is called every time the job receives a running + * QNetworkReply object from NetworkAccessManager - basically, after + * successfully sending a network request (including retries). + */ + virtual void onSentRequest(QNetworkReply*); virtual void beforeAbandon(QNetworkReply*); /** @@ -341,8 +363,7 @@ protected: * @param reply the HTTP reply from the server * @param errorJson the JSON payload describing the error */ - virtual Status parseError(QNetworkReply* reply, - const QJsonObject& errorJson); + virtual Status parseError(QNetworkReply*, const QJsonObject& errorJson); void setStatus(Status s); void setStatus(int code, QString message); @@ -359,10 +380,12 @@ protected slots: void timeout(); private slots: - void sendRequest(bool inBackground); + void sendRequest(); void checkReply(); void gotReply(); + friend class ConnectionData; // to provide access to sendRequest() + private: void stop(); void finishJob(); diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 3a03efde..4e997326 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -39,7 +39,7 @@ QString DownloadFileJob::targetFileName() const return (d->targetFile ? d->targetFile : d->tempFile)->fileName(); } -void DownloadFileJob::beforeStart(const ConnectionData*) +void DownloadFileJob::doPrepare() { if (d->targetFile && !d->targetFile->isReadable() && !d->targetFile->open(QIODevice::WriteOnly)) { @@ -57,7 +57,7 @@ void DownloadFileJob::beforeStart(const ConnectionData*) qCDebug(JOBS) << "Downloading to" << d->tempFile->fileName(); } -void DownloadFileJob::afterStart(const ConnectionData*, QNetworkReply* reply) +void DownloadFileJob::onSentRequest(QNetworkReply* reply) { connect(reply, &QNetworkReply::metaDataChanged, this, [this, reply] { if (!status().good()) diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index fa697219..b7d2d75b 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -19,8 +19,8 @@ private: class Private; QScopedPointer d; - void beforeStart(const ConnectionData*) override; - void afterStart(const ConnectionData*, QNetworkReply* reply) override; + void doPrepare() override; + void onSentRequest(QNetworkReply* reply) override; void beforeAbandon(QNetworkReply*) override; Status parseReply(QNetworkReply*) override; }; diff --git a/lib/room.cpp b/lib/room.cpp index 2e2f73c4..b0829252 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1440,7 +1440,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) connection->callApi(BackgroundRequest, id, pEvent->matrixType(), txnId, pEvent->contentJson())) { - Room::connect(call, &BaseJob::started, q, [this, txnId] { + Room::connect(call, &BaseJob::sentRequest, q, [this, txnId] { auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { qWarning(EVENTS) << "Pending event for transaction" << txnId -- cgit v1.2.3 From e997f214562acf30ae8d4ea7132dde6e583ac6fe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 23 Aug 2019 08:09:28 +0900 Subject: RoomMessageEvent::content(): constify event content --- lib/events/eventcontent.h | 2 ++ lib/events/roommessageevent.h | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index c26cb931..e7656de5 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -25,6 +25,7 @@ #include #include #include +#include namespace Quotient { namespace EventContent { @@ -276,3 +277,4 @@ namespace EventContent { using FileContent = UrlWithThumbnailContent; } // namespace EventContent } // namespace Quotient +Q_DECLARE_METATYPE(const Quotient::EventContent::TypedBase*) diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index b393382a..e95aabfc 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -34,7 +34,7 @@ class RoomMessageEvent : public RoomEvent { Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) Q_PROPERTY(QString plainBody READ plainBody CONSTANT) Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) - Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT) + Q_PROPERTY(const EventContent::TypedBase* content READ content CONSTANT) public: DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) @@ -62,13 +62,13 @@ public: MsgType msgtype() const; QString rawMsgtype() const; QString plainBody() const; - EventContent::TypedBase* content() const { return _content.data(); } + const EventContent::TypedBase* content() const { return _content.data(); } template - void editContent(VisitorT visitor) + void editContent(VisitorT&& visitor) { visitor(*_content); editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(), - content()); + _content.data()); } QMimeType mimeType() const; bool hasTextContent() const; -- cgit v1.2.3 From c3b69fb217c2de0f3b73051b36d4b1e190ef493e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 25 Aug 2019 18:56:03 +0900 Subject: setAvatarForRoom: try recover from otherAvatars inconsistency Hopefully manages cases like #347 in a better way. --- lib/user.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 5dea3942..641f6a6b 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -154,10 +154,11 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, } if (newUrl != mostUsedAvatar.url()) { // Check if the new avatar is about to become most used. - if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size()) { + const auto newUrlUsage = avatarsToRooms.count(newUrl); + if (newUrlUsage >= totalRooms - avatarsToRooms.size()) { QElapsedTimer et; if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { - qCDebug(MAIN) + qCInfo(MAIN) << "Switching the most used avatar of user" << userId << "from" << mostUsedAvatar.url().toDisplayString() << "to" << newUrl.toDisplayString(); @@ -165,7 +166,15 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, } avatarsToRooms.remove(newUrl); auto nextMostUsedIt = otherAvatar(newUrl); - Q_ASSERT(nextMostUsedIt != otherAvatars.end()); + if (nextMostUsedIt == otherAvatars.end()) { + qCCritical(MAIN) + << userId << "doesn't have" << newUrl.toDisplayString() + << "in otherAvatars though it seems to be used in" + << newUrlUsage << "rooms"; + Q_ASSERT(false); + otherAvatars.emplace_back(makeAvatar(newUrl)); + nextMostUsedIt = otherAvatars.end() - 1; + } std::swap(mostUsedAvatar, *nextMostUsedIt); const auto& roomMap = connection->roomMap(); for (const auto* r1 : roomMap) -- cgit v1.2.3 From c27916e7f96860659c5cfd7d311f6b10db3d592f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 25 Aug 2019 18:54:16 +0900 Subject: Room::calculateDisplayname: use local aliases for room naming The Spec now allows it, even if with a disclaimer that it's subject for further removal. --- lib/room.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 2e2f73c4..d47af49f 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2577,7 +2577,7 @@ Room::Private::buildShortlist(const QStringList& userIds) const QString Room::Private::calculateDisplayname() const { - // CS spec, section 11.2.2.5 Calculating the display name for a room + // CS spec, section 13.2.2.5 Calculating the display name for a room // Numbers below refer to respective parts in the spec. // 1. Name (from m.room.name) @@ -2591,12 +2591,18 @@ QString Room::Private::calculateDisplayname() const if (!dispName.isEmpty()) return dispName; - // Using m.room.aliases in naming is explicitly discouraged by the spec + // 3. m.room.aliases - only local aliases, subject for further removal + const auto aliases = q->localAliases(); + if (!aliases.isEmpty()) + return aliases.front(); - // Supplementary code for 3 and 4: build the shortlist of users whose names + // 4. m.heroes and m.room.member + // From here on, we use a more general algorithm than the spec describes + // in order to provide back-compatibility with pre-MSC688 servers. + + // Supplementary code: build the shortlist of users whose names // will be used to construct the room name. Takes into account MSC688's // "heroes" if available. - const bool localUserIsIn = joinState == JoinState::Join; const bool emptyRoom = membersMap.isEmpty() @@ -2607,15 +2613,13 @@ QString Room::Private::calculateDisplayname() const : !emptyRoom ? buildShortlist(membersMap) : users_shortlist_t {}; - // When lazy-loading is on, we can rely on the heroes list. - // If it's off, the below code gathers invited and left members. - // NB: including invitations, if any, into naming is a spec extension. - // This kicks in when there's no lazy loading and it's a room with - // the local user as the only member, with more users invited. + // When the heroes list is there, we can rely on it. If the heroes list is + // missing, the below code gathers invited, or, if there are no invitees, + // left members. if (!shortlist.front() && localUserIsIn) shortlist = buildShortlist(usersInvited); - if (!shortlist.front()) // Still empty shortlist; use left members + if (!shortlist.front()) shortlist = buildShortlist(membersLeft); QStringList names; @@ -2639,7 +2643,7 @@ QString Room::Private::calculateDisplayname() const usersCountExceptLocal - int(shortlist.size())); const auto namesList = QLocale().createSeparatedList(names); - // 3. Room members + // Room members if (!emptyRoom) return namesList; @@ -2647,11 +2651,11 @@ QString Room::Private::calculateDisplayname() const if (!usersInvited.empty()) return tr("Empty room (invited: %1)").arg(namesList); - // 4. Users that previously left the room - if (membersLeft.size() > 0) + // Users that previously left the room + if (!membersLeft.isEmpty()) return tr("Empty room (was: %1)").arg(namesList); - // 5. Fail miserably + // Fail miserably return tr("Empty room (%1)").arg(id); } -- cgit v1.2.3 From d803b04d37e82dd7f8b901d29e04851d97d1f4eb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 21 Aug 2019 15:18:20 +0900 Subject: Tighten the code; add a missing #include MSVC warns on class/struct mismatch and errors on std::array because `` is not indirectly included on that platform. --- lib/connectiondata.cpp | 1 + lib/connectiondata.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index e241e376..a3807fc4 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -25,6 +25,7 @@ #include #include +#include #include using namespace Quotient; diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 5cd7c3c7..b367c977 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -57,7 +57,7 @@ public: QByteArray generateTxnId() const; private: - struct Private; + class Private; std::unique_ptr d; }; } // namespace Quotient -- cgit v1.2.3 From c4a20a6d9a5f6bfbd4315450ccaff8195a3f521a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 21 Aug 2019 08:17:09 +0900 Subject: Don't use enumerator attributes Anything after enumerators is a problem for moc before Qt 5.12; so we can't use enumerator attributes before then. --- lib/jobs/basejob.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 4dc287f8..6c1b802c 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -66,8 +66,7 @@ public: IncorrectRequestError = IncorrectRequest, IncorrectResponse, IncorrectResponseError = IncorrectResponse, - JsonParseError - Q_DECL_ENUMERATOR_DEPRECATED_X("Use IncorrectResponse instead") + JsonParseError //< \deprecated Use IncorrectResponse instead = IncorrectResponse, TooManyRequests, TooManyRequestsError = TooManyRequests, -- cgit v1.2.3 From 97cac4b919735eb59493dd1cd528992ba90e611b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 27 Aug 2019 17:22:22 +0900 Subject: More compliant 'using' for chrono_literals Compilers warn on using 'using ...::operator""s' because they think we're redefining the reserved suffix. --- lib/jobs/basejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 0a17431c..54931c83 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -32,7 +32,7 @@ using namespace Quotient; using std::chrono::seconds, std::chrono::milliseconds; -using std::chrono_literals::operator""s; +using namespace std::chrono_literals; struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) -- cgit v1.2.3 From 944653463fe4134c82d85e2d01e2bc0fa43fd727 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Sep 2019 11:12:37 +0900 Subject: Introduce HashQ<> and UnorderedMap<> Invading into std:: is frowned upon, even though legitimate from the C++ standard perspective. Given that it's possible to pass a hash object to unordered_map, it only takes an alias for std::unordered_map to avoid having to specialize std::hash. And besides, a generic compatibility bridge between qHash and std::hash has been long needed. std::hash in converters.h remains for now; it will be dropped separately when the API files get regenerated to use UnorderedMap. --- lib/connection.cpp | 2 +- lib/connection.h | 2 +- lib/converters.h | 14 ++++---------- lib/events/stateevent.h | 10 ---------- lib/room.cpp | 4 ++-- lib/util.h | 14 ++++++++++++++ 6 files changed, 22 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5ebdcf6c..c3e46356 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -98,7 +98,7 @@ public: // See https://github.com/quotient-im/libQuotient/wiki/Handling-direct-chat-events DirectChatsMap dcLocalAdditions; DirectChatsMap dcLocalRemovals; - std::unordered_map accountData; + UnorderedMap accountData; int syncLoopTimeout = -1; GetCapabilitiesJob* capabilitiesJob = nullptr; diff --git a/lib/connection.h b/lib/connection.h index 7e32e5c9..b4b16679 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -124,7 +124,7 @@ public: using IgnoredUsersList = IgnoredUsersEvent::content_type; using UsersToDevicesToEvents = - std::unordered_map>; + UnorderedMap>; enum RoomVisibility { PublishRoom, diff --git a/lib/converters.h b/lib/converters.h index 587e4544..b753a80b 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -28,16 +28,10 @@ #include #include -#include #include -#if 0 // Waiting for C++17 -# include - -template -using optional = std::experimental::optional; -#endif // Enable std::unordered_map +// REMOVEME in favor of UnorderedMap, once we regenerate API files namespace std { template <> struct hash { @@ -296,9 +290,9 @@ struct HashMapFromJson { } }; -template -struct JsonObjectConverter> - : public HashMapFromJson> {}; +template +struct JsonObjectConverter> + : public HashMapFromJson> {}; template struct JsonObjectConverter> diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 74e36e74..710b4271 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -129,13 +129,3 @@ private: std::unique_ptr> _prev; }; } // namespace Quotient - -namespace std { -template <> -struct hash { - size_t operator()(const Quotient::StateEventKey& k) const Q_DECL_NOEXCEPT - { - return qHash(k); - } -}; -} // namespace std diff --git a/lib/room.cpp b/lib/room.cpp index 2c9fca63..2f697589 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -98,7 +98,7 @@ public: RoomSummary summary = { none, 0, none }; /// The state of the room at timeline position before-0 /// \sa timelineBase - std::unordered_map baseState; + UnorderedMap baseState; /// State event stubs - events without content, just type and state key static decltype(baseState) stubbedState; /// The state of the room at timeline position after-maxTimelineIndex() @@ -131,7 +131,7 @@ public: QHash lastReadEventIds; QString serverReadMarker; TagsMap tags; - std::unordered_map accountData; + UnorderedMap accountData; QString prevBatch; QPointer eventsHistoryJob; QPointer allMembersJob; diff --git a/lib/util.h b/lib/util.h index 7c79804b..788ce5bc 100644 --- a/lib/util.h +++ b/lib/util.h @@ -19,9 +19,11 @@ #pragma once #include +#include #include #include +#include // Along the lines of Q_DISABLE_COPY - the upstream version comes in Qt 5.13 #define DISABLE_MOVE(_ClassName) \ @@ -29,6 +31,18 @@ _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; namespace Quotient { +/// An equivalent of std::hash for QTypes to enable std::unordered_map +template +struct HashQ { + size_t operator()(const T& s) const Q_DECL_NOEXCEPT + { + return qHash(s, uint(qGlobalQHashSeed())); + } +}; +/// A wrapper around std::unordered_map compatible with types that have qHash +template +using UnorderedMap = std::unordered_map>; + struct NoneTag {}; constexpr NoneTag none {}; -- cgit v1.2.3 From c9f99699761e97b16bdb44f584f07baec5488b47 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 24 Sep 2019 19:56:31 +0900 Subject: Room: cleanup --- lib/room.cpp | 35 ++++++++++++++++++----------------- lib/room.h | 2 +- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 2f697589..e2195193 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -327,7 +327,7 @@ public: * content passed in \p newMessage. * \return true if the event has been found and replaced; false otherwise */ - bool processReplacement(const RoomMessageEvent& newMessage); + bool processReplacement(const RoomMessageEvent& newEvent); void setTags(TagsMap newTags); @@ -879,10 +879,10 @@ std::pair validatedTag(QString name) if (name.contains('.')) return { false, name }; - qWarning(MAIN) << "The tag" << name + qCWarning(MAIN) << "The tag" << name << "doesn't follow the CS API conventions"; name.prepend("u."); - qWarning(MAIN) << "Using " << name << "instead"; + qCWarning(MAIN) << "Using " << name << "instead"; return { true, name }; } @@ -916,7 +916,7 @@ void Room::removeTag(const QString& name) } else if (!name.startsWith("u.")) removeTag("u." + name); else - qWarning(MAIN) << "Tag" << name << "on room" << objectName() + qCWarning(MAIN) << "Tag" << name << "on room" << objectName() << "not found, nothing to remove"; } @@ -971,7 +971,7 @@ Room::Private::getEventWithFile(const QString& eventId) const if (event->hasFileContent()) return event; } - qWarning() << "No files to download in event" << eventId; + qCWarning(MAIN) << "No files to download in event" << eventId; return nullptr; } @@ -1014,7 +1014,7 @@ QUrl Room::urlToThumbnail(const QString& eventId) const thumbnail->url, thumbnail->imageSize); } - qDebug() << "Event" << eventId << "has no thumbnail"; + qCDebug(MAIN) << "Event" << eventId << "has no thumbnail"; return {}; } @@ -1149,18 +1149,18 @@ QString Room::decryptMessage(QJsonObject personalCipherObject, try { decrypted = session->decrypt(&preKeyMessage); } catch (std::runtime_error& e) { - qWarning(EVENTS) << "Decrypt failed:" << e.what(); + qCWarning(EVENTS) << "Decrypt failed:" << e.what(); } } else if (type == 1) { Message message { body }; if (!session->matches(&preKeyMessage, senderKey)) { - qWarning(EVENTS) << "Invalid encrypted message"; + qCWarning(EVENTS) << "Invalid encrypted message"; } try { decrypted = session->decrypt(&message); } catch (std::runtime_error& e) { - qWarning(EVENTS) << "Decrypt failed:" << e.what(); + qCWarning(EVENTS) << "Decrypt failed:" << e.what(); } } @@ -1443,11 +1443,12 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) Room::connect(call, &BaseJob::sentRequest, q, [this, txnId] { auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { - qWarning(EVENTS) << "Pending event for transaction" << txnId + qCWarning(EVENTS) << "Pending event for transaction" << txnId << "not found - got synced so soon?"; return; } it->setDeparted(); + qCDebug(EVENTS) << "Event txn" << txnId << "has departed"; emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); }); Room::connect(call, &BaseJob::failure, q, @@ -1457,7 +1458,7 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) emit q->messageSent(txnId, call->eventId()); auto it = q->findPendingEvent(txnId); if (it == unsyncedEvents.end()) { - qDebug(EVENTS) << "Pending event for transaction" << txnId + qCDebug(EVENTS) << "Pending event for transaction" << txnId << "already merged"; return; } @@ -1489,7 +1490,7 @@ QString Room::retryMessage(const QString& txnId) { const auto it = findPendingEvent(txnId); Q_ASSERT(it != d->unsyncedEvents.end()); - qDebug(EVENTS) << "Retrying transaction" << txnId; + qCDebug(EVENTS) << "Retrying transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); @@ -1527,7 +1528,7 @@ void Room::discardMessage(const QString& txnId) return evt->transactionId() == txnId; }); Q_ASSERT(it != d->unsyncedEvents.end()); - qDebug(EVENTS) << "Discarding transaction" << txnId; + qCDebug(EVENTS) << "Discarding transaction" << txnId; const auto& transferIt = d->fileTransfers.find(txnId); if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); @@ -2192,7 +2193,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) emit q->pendingEventChanged(pendingEvtIdx); } emit q->pendingEventAboutToMerge(nextPendingEvt, pendingEvtIdx); - qDebug(MESSAGES) << "Merging pending event from transaction" + qCDebug(MESSAGES) << "Merging pending event from transaction" << nextPendingEvt->transactionId() << "into" << nextPendingEvt->id(); auto transfer = fileTransfers.take(nextPendingEvt->transactionId()); @@ -2323,13 +2324,13 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this,oldStateEvent] (const RoomAliasesEvent& ae) { // clang-format on if (ae.aliases().isEmpty()) { - qDebug(STATE).noquote() + qCDebug(STATE).noquote() << ae.stateKey() << "no more has aliases for room" << objectName(); d->aliasServers.remove(ae.stateKey()); } else { d->aliasServers.insert(ae.stateKey()); - qDebug(STATE).nospace().noquote() + qCDebug(STATE).nospace().noquote() << "New server with aliases for room " << objectName() << ": " << ae.stateKey(); } @@ -2665,7 +2666,7 @@ void Room::Private::updateDisplayname() if (swappedName != displayname) { emit q->displaynameAboutToChange(q); swap(displayname, swappedName); - qDebug(MAIN) << q->objectName() << "has changed display name from" + qCDebug(MAIN) << q->objectName() << "has changed display name from" << swappedName << "to" << displayname; emit q->displaynameChanged(q, swappedName); } diff --git a/lib/room.h b/lib/room.h index 9113654b..ac169550 100644 --- a/lib/room.h +++ b/lib/room.h @@ -557,7 +557,7 @@ signals: * Aside from all changes in the room state * @param changes a set of flags describing what changes occurred * upon the last sync - * \sa StateChange + * \sa Changes */ void changed(Changes changes); /** -- cgit v1.2.3 From 8f1bfa5c5764e399259c13b260661647363cf5d9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 17:46:28 +0900 Subject: Fix formatting weirdness Alas, clang-format is not terrifically good around [[attributes]] --- lib/room.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index ac169550..698f74c8 100644 --- a/lib/room.h +++ b/lib/room.h @@ -187,8 +187,8 @@ public: Q_INVOKABLE QList users() const; QStringList memberNames() const; - [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] int - memberCount() const; + [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] + int memberCount() const; int timelineSize() const; bool usesEncryption() const; RoomEventPtr decryptMessage(EncryptedEvent* encryptedEvent); -- cgit v1.2.3 From be53652eb30edd6e096c172a97609070d1e74746 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 17:50:28 +0900 Subject: Settings: childGroups() should merge legacy and current groups; QMC->QTNT in macros Because otherwise migrating one group to the current location leads to all other groups suddenly disappearing from Settings view. --- lib/networksettings.cpp | 6 +++--- lib/networksettings.h | 6 +++--- lib/settings.cpp | 11 +++++++---- lib/settings.h | 10 +++++----- 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/networksettings.cpp b/lib/networksettings.cpp index cb071483..40ecba11 100644 --- a/lib/networksettings.cpp +++ b/lib/networksettings.cpp @@ -26,9 +26,9 @@ void NetworkSettings::setupApplicationProxy() const { proxyType(), proxyHostName(), proxyPort() }); } -QMC_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, +QTNT_DEFINE_SETTING(NetworkSettings, QNetworkProxy::ProxyType, proxyType, "proxy_type", QNetworkProxy::DefaultProxy, setProxyType) -QMC_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", +QTNT_DEFINE_SETTING(NetworkSettings, QString, proxyHostName, "proxy_hostname", {}, setProxyHostName) -QMC_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, +QTNT_DEFINE_SETTING(NetworkSettings, quint16, proxyPort, "proxy_port", -1, setProxyPort) diff --git a/lib/networksettings.h b/lib/networksettings.h index a6a13f93..2399cf5f 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -27,9 +27,9 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) namespace Quotient { class NetworkSettings : public SettingsGroup { Q_OBJECT - QMC_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) - QMC_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) - QMC_DECLARE_SETTING(quint16, proxyPort, setProxyPort) + QTNT_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) + QTNT_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) + QTNT_DECLARE_SETTING(quint16, proxyPort, setProxyPort) Q_PROPERTY(QString proxyHost READ proxyHostName WRITE setProxyHostName) public: template diff --git a/lib/settings.cpp b/lib/settings.cpp index 3bf61605..0349e699 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -49,7 +49,10 @@ bool Settings::contains(const QString& key) const QStringList Settings::childGroups() const { auto l = QSettings::childGroups(); - return !l.isEmpty() ? l : legacySettings.childGroups(); + for (const auto& g: legacySettings.childGroups()) + if (!l.contains(g)) + l.push_back(g); + return l; } void SettingsGroup::setValue(const QString& key, const QVariant& value) @@ -88,11 +91,11 @@ void SettingsGroup::remove(const QString& key) Settings::remove(fullKey); } -QMC_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", {}, +QTNT_DEFINE_SETTING(AccountSettings, QString, deviceId, "device_id", {}, setDeviceId) -QMC_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, +QTNT_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, setDeviceName) -QMC_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, +QTNT_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) static const auto HomeserverKey = QStringLiteral("homeserver"); diff --git a/lib/settings.h b/lib/settings.h index d317ec2a..79ebba6c 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -119,7 +119,7 @@ private: QString groupPath; }; -#define QMC_DECLARE_SETTING(type, propname, setter) \ +#define QTNT_DECLARE_SETTING(type, propname, setter) \ Q_PROPERTY(type propname READ propname WRITE setter) \ public: \ type propname() const; \ @@ -127,7 +127,7 @@ public: \ \ private: -#define QMC_DEFINE_SETTING(classname, type, propname, qsettingname, \ +#define QTNT_DEFINE_SETTING(classname, type, propname, qsettingname, \ defaultValue, setter) \ type classname::propname() const \ { \ @@ -142,9 +142,9 @@ private: class AccountSettings : public SettingsGroup { Q_OBJECT Q_PROPERTY(QString userId READ userId CONSTANT) - QMC_DECLARE_SETTING(QString, deviceId, setDeviceId) - QMC_DECLARE_SETTING(QString, deviceName, setDeviceName) - QMC_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) + QTNT_DECLARE_SETTING(QString, deviceId, setDeviceId) + QTNT_DECLARE_SETTING(QString, deviceName, setDeviceName) + QTNT_DECLARE_SETTING(bool, keepLoggedIn, setKeepLoggedIn) /** \deprecated \sa setAccessToken */ Q_PROPERTY(QString accessToken READ accessToken WRITE setAccessToken) Q_PROPERTY(QByteArray encryptionAccountPickle READ encryptionAccountPickle -- cgit v1.2.3 From 36589cd6e4c557da7d694c5973546f29a09dee7c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:18:39 +0900 Subject: Make connectSingleShot work wherever QObject::connect works Also: doc-comment connectUntil and unify implementation of both functions. --- lib/qt_connection_util.h | 114 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 28 deletions(-) (limited to 'lib') diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 159e7522..9baf8c69 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -24,10 +24,15 @@ namespace Quotient { namespace _impl { + template + using decorated_slot_tt = + std::function; + template inline QMetaObject::Connection - connectUntil(SenderT* sender, SignalT signal, ContextT* context, - std::function slot, Qt::ConnectionType connType) + connectDecorated(SenderT* sender, SignalT signal, ContextT* context, + decorated_slot_tt decoratedSlot, + Qt::ConnectionType connType) { // See https://bugreports.qt.io/browse/QTBUG-60339 #if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) @@ -36,52 +41,105 @@ namespace _impl { auto pc = std::make_unique(); #endif auto& c = *pc; // Resolve a reference before pc is moved to lambda - c = QObject::connect( - sender, signal, context, - [pc = std::move(pc), slot](ArgTs... args) { + + // Perfect forwarding doesn't work through signal-slot connections - + // arguments are always copied (at best - COWed) to the context of + // the slot. Therefore the slot decorator receives const ArgTs&... + // rather than ArgTs&&... + // TODO: std::bind_front() instead of lambda. + c = QObject::connect(sender, signal, context, + [pc = std::move(pc), + decoratedSlot = std::move(decoratedSlot)](const ArgTs&... args) { Q_ASSERT(*pc); // If it's been triggered, it should exist - if (slot(std::forward(args)...)) - QObject::disconnect(*pc); + decoratedSlot(*pc, args...); }, connType); return c; } + template + inline QMetaObject::Connection + connectUntil(SenderT* sender, SignalT signal, ContextT* context, + std::function functor, + Qt::ConnectionType connType) + { + return connectDecorated(sender, signal, context, + decorated_slot_tt( + [functor = std::move(functor)](QMetaObject::Connection& c, + const ArgTs&... args) { + if (functor(args...)) + QObject::disconnect(c); + }), + connType); + } + template + inline QMetaObject::Connection + connectSingleShot(SenderT* sender, SignalT signal, ContextT* context, + std::function slot, + Qt::ConnectionType connType) + { + return connectDecorated(sender, signal, context, + decorated_slot_tt( + [slot = std::move(slot)](QMetaObject::Connection& c, + const ArgTs&... args) { + QObject::disconnect(c); + slot(args...); + }), + connType); + } } // namespace _impl +/// Create a connection that self-disconnects when its "slot" returns true +/*! A slot accepted by connectUntil() is different from classic Qt slots + * in that its return value must be bool, not void. The slot's return value + * controls whether the connection should be kept; if the slot returns false, + * the connection remains; upon returning true, the slot is disconnected from + * the signal. Because of a different slot signature connectUntil() doesn't + * accept member functions as QObject::connect or Quotient::connectSingleShot + * do; you should pass a lambda or a pre-bound member function to it. + */ template inline auto connectUntil(SenderT* sender, SignalT signal, ContextT* context, const FunctorT& slot, Qt::ConnectionType connType = Qt::AutoConnection) { - return _impl::connectUntil( - sender, signal, context, - typename function_traits::function_type(slot), connType); + return _impl::connectUntil(sender, signal, context, std::function(slot), + connType); } -/** Create a single-shot connection that triggers on the signal and - * then self-disconnects - * - * Only supports DirectConnection type. - */ -template +/// Create a connection that self-disconnects after triggering on the signal +template +inline auto connectSingleShot(SenderT* sender, SignalT signal, + ContextT* context, const FunctorT& slot, + Qt::ConnectionType connType = Qt::AutoConnection) +{ + return _impl::connectSingleShot( + sender, signal, context, std::function(slot), connType); +} + +// Specialisation for usual Qt slots passed as pointers-to-members. +template inline auto connectSingleShot(SenderT* sender, SignalT signal, - ReceiverT* receiver, SlotT slot) + ReceiverT* receiver, + void (SlotObjectT::*slot)(ArgTs...), + Qt::ConnectionType connType = Qt::AutoConnection) { - QMetaObject::Connection connection; - connection = QObject::connect(sender, signal, receiver, slot, - Qt::DirectConnection); - Q_ASSERT(connection); - QObject::connect( - sender, signal, receiver, - [connection] { QObject::disconnect(connection); }, Qt::DirectConnection); - return connection; + // TODO: when switching to C++20, use std::bind_front() instead + return _impl::connectSingleShot(sender, signal, receiver, + std::function( + [receiver, slot](const ArgTs&... args) { + (receiver->*slot)(args...); + }), + connType); } -/** A guard pointer that disconnects an interested object upon destruction - * It's almost QPointer<> except that you have to initialise it with one +/// A guard pointer that disconnects an interested object upon destruction +/*! It's almost QPointer<> except that you have to initialise it with one * more additional parameter - a pointer to a QObject that will be * disconnected from signals of the underlying pointer upon the guard's - * destruction. + * destruction. Note that destructing the guide doesn't destruct either QObject. */ template class ConnectionsGuard : public QPointer { -- cgit v1.2.3 From 72dd1eb7c1986c23a7630205e2f2a0280b7c2a2b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:03:31 +0900 Subject: Simplify visit() signature Checking that BaseEventT descends from Event is really extraneous. --- lib/events/event.h | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/events/event.h b/lib/events/event.h index 25362786..f985ae92 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -334,22 +334,18 @@ inline auto visit(const BaseEventT& event, FnT&& visitor) return visitor(event); } -template -constexpr auto is_event() -{ - return std::is_base_of>::value; -} - -template -constexpr auto needs_cast() -{ - return !std::is_convertible>::value; +namespace _impl { + template + constexpr auto needs_downcast() + { + return !std::is_convertible_v>; + } } // A single type-specific void visitor template -inline std::enable_if_t() && needs_cast() - && std::is_void>::value> +inline std::enable_if_t<_impl::needs_downcast() + && std::is_void_v>> visit(const BaseEventT& event, FnT&& visitor) { using event_type = fn_arg_t; @@ -358,10 +354,9 @@ visit(const BaseEventT& event, FnT&& visitor) } // A single type-specific non-void visitor with an optional default value +// non-voidness is guarded by defaultValue type template -inline std::enable_if_t() && needs_cast(), - fn_return_t> // non-voidness is guarded by - // defaultValue type +inline std::enable_if_t<_impl::needs_downcast(), fn_return_t> visit(const BaseEventT& event, FnT&& visitor, fn_return_t&& defaultValue = {}) { @@ -373,9 +368,8 @@ visit(const BaseEventT& event, FnT&& visitor, // A chain of 2 or more visitors template -inline std::enable_if_t(), fn_return_t> -visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, - FnTs&&... visitors) +inline fn_return_t visit(const BaseEventT& event, FnT1&& visitor1, + FnT2&& visitor2, FnTs&&... visitors) { using event_type1 = fn_arg_t; if (is>(event)) -- cgit v1.2.3 From 534a15d05e0b6e1b44b6387003e1475150e848e8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:01:45 +0900 Subject: function_traits: drop unused pieces is_callable won't ever be needed because std::is_invokable is here; arg_number and returns() didn't find its users; and function_type has been just broken all along for member functions. --- lib/util.cpp | 38 ++------------------------------------ lib/util.h | 22 ++++------------------ 2 files changed, 6 insertions(+), 54 deletions(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index cc18d9ab..041a8aba 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -141,62 +141,28 @@ int f(); static_assert(std::is_same, int>::value, "Test fn_return_t<>"); -void f1(int); -static_assert(function_traits::arg_number == 1, - "Test fn_arg_number"); - -void f2(int, QString); -static_assert(std::is_same, QString>::value, +void f1(int, QString); +static_assert(std::is_same, QString>::value, "Test fn_arg_t<>"); -struct S { - int mf(); -}; -static_assert(is_callable_v, "Test member function"); -static_assert(returns(), - "Test returns<> with member function"); - struct Fo { int operator()(); }; -static_assert(is_callable_v, "Test is_callable<> with function object"); -static_assert(function_traits::arg_number == 0, "Test function object"); static_assert(std::is_same, int>::value, "Test return type of function object"); struct Fo1 { void operator()(int); }; -static_assert(function_traits::arg_number == 1, "Test function object 1"); -static_assert(is_callable_v, "Test is_callable<> with function object 1"); static_assert(std::is_same, int>(), "Test fn_arg_t defaulting to first argument"); #if (!defined(_MSC_VER) || _MSC_VER >= 1910) static auto l = [] { return 1; }; -static_assert(is_callable_v, "Test is_callable_v<> with lambda"); static_assert(std::is_same, int>::value, "Test fn_return_t<> with lambda"); #endif -template -struct fn_object { - static int smf(double) { return 0; } -}; -template <> -struct fn_object { - void operator()(QString); -}; -static_assert(is_callable_v>, "Test function object"); -static_assert(returns>(), - "Test returns<> with function object"); -static_assert(!is_callable_v>, "Test non-function object"); -// FIXME: These two don't work -// static_assert(is_callable_v::smf)>, -// "Test static member function"); -// static_assert(returns::smf)>(), -// "Test returns<> with static member function"); - template QString ft(T&&) { diff --git a/lib/util.h b/lib/util.h index 788ce5bc..4631570f 100644 --- a/lib/util.h +++ b/lib/util.h @@ -154,23 +154,19 @@ struct function_traits : public _impl::fn_traits {}; // Specialisation for a function template struct function_traits { - static constexpr auto is_callable = true; using return_type = ReturnT; using arg_types = std::tuple; - using function_type = std::function; - static constexpr auto arg_number = std::tuple_size::value; }; namespace _impl { template - struct fn_traits { - static constexpr auto is_callable = false; - }; + struct fn_traits; + // Specialisation for function objects with (non-overloaded) operator() + // (this includes non-generic lambdas) template struct fn_traits - : public fn_traits { - }; // A generic function object that has (non-overloaded) operator() + : public fn_traits {}; // Specialisation for a member function template @@ -190,16 +186,6 @@ template using fn_arg_t = std::tuple_element_t::arg_types>; -template -constexpr bool returns() -{ - return std::is_same, R>::value; -} - -// Poor-man's is_invokable -template -constexpr auto is_callable_v = function_traits::is_callable; - inline auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); -- cgit v1.2.3 From f9c0e04259be9fd3be70486bc1eb76bf8f2612fe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:05:17 +0900 Subject: Rename pieces with qmc/qmatrixclient --- lib/jobs/downloadfilejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 4e997326..3e037680 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -12,7 +12,7 @@ public: explicit Private(const QString& localFilename) : targetFile(new QFile(localFilename)) - , tempFile(new QFile(targetFile->fileName() + ".qmcdownload")) + , tempFile(new QFile(targetFile->fileName() + ".qtntdownload")) {} QScopedPointer targetFile; -- cgit v1.2.3 From 7802bcb4dd44c7523cbc687b52dd2e65b900c636 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:11:35 +0900 Subject: BaseJob, urlForLog() -> dumpRequest(): include HTTP verb into log lines --- lib/jobs/basejob.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 54931c83..6a70bc40 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -105,11 +106,17 @@ public: errorStrategy.size() - 1)]; } - QString urlForLog() const + QString dumpRequest() const { - return reply - ? reply->url().toString(QUrl::RemoveQuery) - : makeRequestUrl(connection->baseUrl(), apiEndpoint).toString(); + // Thanks to C++17, std::array's type and bounds are deduced + static const auto verbs = + std::array { QStringLiteral("GET"), QStringLiteral("PUT"), + QStringLiteral("POST"), QStringLiteral("DELETE") }; + const auto verbWord = verbs.at(size_t(verb)); + return verbWord % ' ' + % (reply ? reply->url().toString(QUrl::RemoveQuery) + : makeRequestUrl(connection->baseUrl(), apiEndpoint) + .toString()); } }; @@ -260,7 +267,7 @@ void BaseJob::sendRequest() if (status().code == Abandoned) return; Q_ASSERT(d->connection && status().code == Pending); - qCDebug(d->logCat) << "Making request to" << d->urlForLog(); + qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest(); emit aboutToSendRequest(); d->sendRequest(); Q_ASSERT(d->reply); @@ -273,12 +280,12 @@ void BaseJob::sendRequest() connect(d->reply.data(), &QNetworkReply::downloadProgress, this, &BaseJob::downloadProgress); d->timer.start(getCurrentTimeout()); - qCInfo(d->logCat).noquote() << "Request sent to" << d->urlForLog(); + qCInfo(d->logCat).noquote() << "Sent" << d->dumpRequest(); onSentRequest(d->reply.data()); emit sentRequest(); } else qCWarning(d->logCat).noquote() - << "Request could not start:" << d->urlForLog(); + << "Request could not start:" << d->dumpRequest(); } void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } @@ -378,7 +385,8 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const const auto httpCodeHeader = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!httpCodeHeader.isValid()) { - qCWarning(d->logCat) << "No valid HTTP headers from" << d->urlForLog(); + qCWarning(d->logCat).noquote() + << "No valid HTTP headers from" << d->dumpRequest(); return { NetworkError, reply->errorString() }; } @@ -386,7 +394,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const if (httpCode / 100 == 2) // 2xx { if (reply->isFinished()) - qCInfo(d->logCat) << httpCode << "<-" << d->urlForLog(); + qCInfo(d->logCat).noquote() << httpCode << "<-" << d->dumpRequest(); if (!checkContentType(reply->rawHeader("Content-Type"), d->expectedContentTypes)) return { UnexpectedResponseTypeWarning, @@ -394,7 +402,7 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const return NoError; } if (reply->isFinished()) - qCWarning(d->logCat) << httpCode << "<-" << d->urlForLog(); + qCWarning(d->logCat).noquote() << httpCode << "<-" << d->dumpRequest(); auto message = reply->errorString(); if (message.isEmpty()) -- cgit v1.2.3 From 508f4362b7b5e1863153cca6fe6756adedfa33ee Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 29 Sep 2019 18:20:54 +0900 Subject: Connection: allRooms(), rooms(), roomsCount(); deprecate roomMap() Closes #354. --- lib/connection.cpp | 49 +++++++++++++++++++++++++++++++++++++++---------- lib/connection.h | 29 ++++++++++++++++++++++++++++- lib/user.cpp | 6 ++---- 3 files changed, 69 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index c3e46356..2c5bf574 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -955,6 +955,33 @@ QHash, Room*> Connection::roomMap() const return roomMap; } +QVector Connection::allRooms() const +{ + QVector result; + result.resize(d->roomMap.size()); + std::copy(d->roomMap.cbegin(), d->roomMap.cend(), result.begin()); + return result; +} + +QVector Connection::rooms(JoinStates joinStates) const +{ + QVector result; + for (auto* r: qAsConst(d->roomMap)) + if (joinStates.testFlag(r->joinState())) + result.push_back(r); + return result; +} + +int Connection::roomsCount(JoinStates joinStates) const +{ + // Using int to maintain compatibility with QML + // (consider also that QHash<>::size() returns int anyway). + return int(std::count_if(d->roomMap.begin(), d->roomMap.end(), + [joinStates](Room* r) { + return joinStates.testFlag(r->joinState()); + })); +} + bool Connection::hasAccountData(const QString& type) const { return d->accountData.find(type) != d->accountData.cend(); @@ -1262,18 +1289,20 @@ void Connection::saveState() const { QStringLiteral("minor"), SyncData::cacheVersion().second } } } }; { - QJsonObject rooms; - QJsonObject inviteRooms; - const auto& rs = roomMap(); // Pass on rooms in Leave state - for (const auto* i : rs) - (i->joinState() == JoinState::Invite ? inviteRooms : rooms) - .insert(i->id(), QJsonValue::Null); + QJsonObject roomsJson; + QJsonObject inviteRoomsJson; + for (const auto* r: qAsConst(d->roomMap)) { + if (r->joinState() == JoinState::Leave) + continue; + (r->joinState() == JoinState::Invite ? inviteRoomsJson : roomsJson) + .insert(r->id(), QJsonValue::Null); + } QJsonObject roomObj; - if (!rooms.isEmpty()) - roomObj.insert(QStringLiteral("join"), rooms); - if (!inviteRooms.isEmpty()) - roomObj.insert(QStringLiteral("invite"), inviteRooms); + if (!roomsJson.isEmpty()) + roomObj.insert(QStringLiteral("join"), roomsJson); + if (!inviteRoomsJson.isEmpty()) + roomObj.insert(QStringLiteral("invite"), inviteRoomsJson); rootObj.insert(QStringLiteral("next_batch"), d->data->lastEvent()); rootObj.insert(QStringLiteral("rooms"), roomObj); diff --git a/lib/connection.h b/lib/connection.h index b4b16679..1f1d4cd5 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -135,12 +135,39 @@ public: explicit Connection(const QUrl& server, QObject* parent = nullptr); ~Connection() override; - /** Get all Invited and Joined rooms + /// Get all Invited and Joined rooms + /*! * \return a hashmap from a composite key - room name and whether * it's an Invite rather than Join - to room pointers + * \sa allRooms, rooms, roomsWithTag */ + [[deprecated("Use allRooms(), roomsWithTag() or rooms(joinStates) instead")]] QHash, Room*> roomMap() const; + /// Get all rooms known within this Connection + /*! + * This includes Invite, Join and Leave rooms, in no particular order. + * \note Leave rooms will only show up in the list if they have been left + * in the same running session. The library doesn't cache left rooms + * between runs and it doesn't retrieve the full list of left rooms + * from the server. + * \sa rooms, room, roomsWithTag + */ + Q_INVOKABLE QVector allRooms() const; + + /// Get rooms that have either of the given join state(s) + /*! + * This method returns, in no particular order, rooms which join state + * matches the mask passed in \p joinStates. + * \note Similar to allRooms(), this won't retrieve the full list of + * Leave rooms from the server. + * \sa allRooms, room, roomsWithTag + */ + Q_INVOKABLE QVector rooms(JoinStates joinStates) const; + + /// Get the total number of rooms in the given join state(s) + Q_INVOKABLE int roomsCount(JoinStates joinStates) const; + /** Check whether the account has data of the given type * Direct chats map is not supported by this method _yet_. */ diff --git a/lib/user.cpp b/lib/user.cpp index 641f6a6b..67cd1117 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -111,8 +111,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, et.start(); } - const auto& roomMap = connection->roomMap(); - for (auto* r1 : roomMap) + for (auto* r1: connection->allRooms()) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -176,8 +175,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, nextMostUsedIt = otherAvatars.end() - 1; } std::swap(mostUsedAvatar, *nextMostUsedIt); - const auto& roomMap = connection->roomMap(); - for (const auto* r1 : roomMap) + for (const auto* r1: connection->allRooms()) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); -- cgit v1.2.3 From f71d16b56ab90e494d6a41c276210a4ce593987e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 1 Oct 2019 13:14:30 +0900 Subject: Room::getCurrentState() --- lib/room.cpp | 24 ++++++++++++++++++------ lib/room.h | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index e2195193..aae84416 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -202,25 +202,31 @@ public: void getPreviousContent(int limit = 10); - template - const EventT* getCurrentState(const QString& stateKey = {}) const + const StateEventBase* getCurrentState(const StateEventKey& evtKey) const { - const StateEventKey evtKey { EventT::matrixTypeId(), stateKey }; const auto* evt = currentState.value(evtKey, nullptr); if (!evt) { if (stubbedState.find(evtKey) == stubbedState.end()) { // In the absence of a real event, make a stub as-if an event // with empty content has been received. Event classes should be // prepared for empty/invalid/malicious content anyway. - stubbedState.emplace(evtKey, - loadStateEvent(EventT::matrixTypeId(), {}, - stateKey)); + stubbedState.emplace(evtKey, loadStateEvent(evtKey.first, {}, + evtKey.second)); qCDebug(STATE) << "A new stub event created for key {" << evtKey.first << evtKey.second << "}"; } evt = stubbedState[evtKey].get(); Q_ASSERT(evt); } + Q_ASSERT(evt->matrixType() == evtKey.first + && evt->stateKey() == evtKey.second); + return evt; + } + + template + const EventT* getCurrentState(const QString& stateKey = {}) const + { + const auto* evt = getCurrentState({ EventT::matrixTypeId(), stateKey }); Q_ASSERT(evt->type() == EventT::typeId() && evt->matrixType() == EventT::matrixTypeId()); return static_cast(evt); @@ -1105,6 +1111,12 @@ bool Room::usesEncryption() const return !d->getCurrentState()->algorithm().isEmpty(); } +const StateEventBase* Room::getCurrentState(const QString& evtType, + const QString& stateKey) const +{ + return d->getCurrentState({ evtType, stateKey }); +} + RoomEventPtr Room::decryptMessage(EncryptedEvent* encryptedEvent) { if (encryptedEvent->algorithm() == OlmV1Curve25519AesSha2AlgoKey) { diff --git a/lib/room.h b/lib/room.h index 698f74c8..cded7eb9 100644 --- a/lib/room.h +++ b/lib/room.h @@ -434,6 +434,24 @@ public: Q_INVOKABLE bool supportsCalls() const; + /// Get a state event with the given event type and state key + /*! This method returns a (potentially empty) state event corresponding + * to the pair of event type \p evtType and state key \p stateKey. + */ + Q_INVOKABLE const StateEventBase* + getCurrentState(const QString& evtType, const QString& stateKey = {}) const; + + template + const EvT* getCurrentState(const QString& stateKey = {}) const + { + const auto* evt = + eventCast(getCurrentState(EvT::matrixTypeId(), stateKey)); + Q_ASSERT(evt); + Q_ASSERT(evt->matrixTypeId() == EvT::matrixTypeId() + && evt->stateKey() == stateKey); + return evt; + } + template auto setState(ArgTs&&... args) const { -- cgit v1.2.3 From 1a1093b849df33baa5cee246d71e6ba63730c52f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 2 Oct 2019 09:36:02 +0900 Subject: wrap_in_function() Because Apple stdlib doesn't have std::function deduction guides. --- lib/qt_connection_util.h | 6 +++--- lib/util.h | 18 ++++++++++++++---- 2 files changed, 17 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 9baf8c69..3c81e3ac 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -104,7 +104,7 @@ inline auto connectUntil(SenderT* sender, SignalT signal, ContextT* context, const FunctorT& slot, Qt::ConnectionType connType = Qt::AutoConnection) { - return _impl::connectUntil(sender, signal, context, std::function(slot), + return _impl::connectUntil(sender, signal, context, wrap_in_function(slot), connType); } @@ -115,7 +115,7 @@ inline auto connectSingleShot(SenderT* sender, SignalT signal, Qt::ConnectionType connType = Qt::AutoConnection) { return _impl::connectSingleShot( - sender, signal, context, std::function(slot), connType); + sender, signal, context, wrap_in_function(slot), connType); } // Specialisation for usual Qt slots passed as pointers-to-members. @@ -128,7 +128,7 @@ inline auto connectSingleShot(SenderT* sender, SignalT signal, { // TODO: when switching to C++20, use std::bind_front() instead return _impl::connectSingleShot(sender, signal, receiver, - std::function( + wrap_in_function( [receiver, slot](const ArgTs&... args) { (receiver->*slot)(args...); }), diff --git a/lib/util.h b/lib/util.h index 4631570f..f7a81b2a 100644 --- a/lib/util.h +++ b/lib/util.h @@ -149,19 +149,21 @@ namespace _impl { * https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 */ template -struct function_traits : public _impl::fn_traits {}; +struct function_traits + : public _impl::fn_traits> {}; // Specialisation for a function template struct function_traits { using return_type = ReturnT; using arg_types = std::tuple; + // Doesn't (and there's no plan to make it) work for "classic" + // member functions (i.e. outside of functors). + // See also the comment for wrap_in_function() below + using function_type = std::function; }; namespace _impl { - template - struct fn_traits; - // Specialisation for function objects with (non-overloaded) operator() // (this includes non-generic lambdas) template @@ -186,6 +188,14 @@ template using fn_arg_t = std::tuple_element_t::arg_types>; +// TODO: get rid of it as soon as Apple Clang gets proper deduction guides +// for std::function<> +template +inline auto wrap_in_function(FnT&& f) +{ + return typename function_traits::function_type(std::forward(f)); +} + inline auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); -- cgit v1.2.3 From a2badac44d94294d9404224a55b3193c87cc201d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 2 Oct 2019 11:36:54 +0900 Subject: More fixes for Apple not having deduction guides in stdlib --- lib/jobs/basejob.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 6a70bc40..7336fc47 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -44,6 +44,12 @@ struct NetworkReplyDeleter : public QScopedPointerDeleteLater { } }; +template +constexpr auto make_array(Ts&&... items) +{ + return std::array, sizeof...(Ts)>({items...}); +} + class BaseJob::Private { public: struct JobTimeoutConfig { @@ -108,10 +114,10 @@ public: QString dumpRequest() const { - // Thanks to C++17, std::array's type and bounds are deduced + // FIXME: use std::array {} when Apple stdlib gets deduction guides for it static const auto verbs = - std::array { QStringLiteral("GET"), QStringLiteral("PUT"), - QStringLiteral("POST"), QStringLiteral("DELETE") }; + make_array(QStringLiteral("GET"), QStringLiteral("PUT"), + QStringLiteral("POST"), QStringLiteral("DELETE")); const auto verbWord = verbs.at(size_t(verb)); return verbWord % ' ' % (reply ? reply->url().toString(QUrl::RemoveQuery) -- cgit v1.2.3 From 4623632e14ee9367e5728daf745d5323eb6a51d5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 2 Oct 2019 12:04:32 +0900 Subject: BaseJob::Private: experiment with [[nodiscard]] --- lib/jobs/basejob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 7336fc47..13e65188 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -106,13 +106,13 @@ public: int maxRetries = int(errorStrategy.size()); int retriesTaken = 0; - const JobTimeoutConfig& getCurrentTimeoutConfig() const + [[nodiscard]] const JobTimeoutConfig& getCurrentTimeoutConfig() const { return errorStrategy[std::min(size_t(retriesTaken), errorStrategy.size() - 1)]; } - QString dumpRequest() const + [[nodiscard]] QString dumpRequest() const { // FIXME: use std::array {} when Apple stdlib gets deduction guides for it static const auto verbs = -- cgit v1.2.3 From c201f8a50d155707aabb83850c17a74dd4d571d3 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 2 Oct 2019 01:23:03 -0700 Subject: Fix Room::localAliases() When homeserver's csapi is in a subdomain, e.g. matrix.example.org, the authority of api endpoint url differs from server name, example.org. This causes Room::localAliases() to fail. --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index aae84416..c4cdac04 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -413,7 +413,7 @@ QStringList Room::localAliases() const { return d ->getCurrentState( - connection()->homeserver().authority()) + connection()->domain()) ->aliases(); } -- cgit v1.2.3 From 0a82e68f86a3b4716ebada39c041a6c8673d2999 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 17 Oct 2019 09:59:37 +0900 Subject: Connection::joinRoom: make sure the room object is created early enough All direct slots connected to finished() will run before success() is even emitted; so create the room object in the earliest slot connected to finished(), rather than to success(). --- lib/connection.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 2c5bf574..25f1c3f6 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -534,10 +534,14 @@ JoinRoomJob* Connection::joinRoom(const QString& roomAlias, const QStringList& serverNames) { auto job = callApi(roomAlias, serverNames); - // Upon completion, ensure a room object in Join state is created but only - // if it's not already there due to a sync completing earlier. - connect(job, &JoinRoomJob::success, this, - [this, job] { provideRoom(job->roomId()); }); + // Upon completion, ensure a room object in Join state is created + // (or it might already be there due to a sync completing earlier). + // finished() is used here instead of success() to overtake clients + // that may add their own slots to finished(). + connect(job, &BaseJob::finished, this, [this, job] { + if (job->status().good()) + provideRoom(job->roomId()); + }); return job; } -- cgit v1.2.3 From d59ca7ac194e8d57177afb1ac89603e22b61b4ec Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Oct 2019 08:16:29 +0900 Subject: qmc-example -> quotest, QMCTest -> TestManager Also: some bits of refactoring in the test code to make it more extensible. Closes #352. --- lib/csapi/gtad.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index 301ee0b6..6d4e080f 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -95,9 +95,9 @@ analyzer: - //: { type: "QVector<{{1}}>", imports: } - map: # `additionalProperties` in OpenAPI - RoomState: - type: "std::unordered_map" + type: "UnorderedMap" moveOnly: - imports: + imports: '"util.h"' - /.+/: type: "QHash" imports: -- cgit v1.2.3 From 8b9207d5a04386957d8eab8dd251421eaaa7c0d2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 20 Oct 2019 19:13:56 +0900 Subject: Qualify types in signals and Q_INVOKABLEs Because https://doc.qt.io/qt-5/moc.html#limitations . For direct connections that doesn't matter but it very much does for queued ones. Along with this DirectChatsMap and IgnoredUsersList have been moved from Connection:: to Quotient::. --- lib/connection.cpp | 6 ++-- lib/connection.h | 82 ++++++++++++++++++++++++++++-------------------------- lib/jobs/basejob.h | 19 +++++++------ lib/room.h | 64 ++++++++++++++++++++++-------------------- lib/user.h | 13 +++++---- 5 files changed, 97 insertions(+), 87 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 25f1c3f6..af85d066 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -385,7 +385,7 @@ void Connection::syncLoop(int timeout) syncLoopIteration(); // initial sync to start the loop } -QJsonObject toJson(const Connection::DirectChatsMap& directChats) +QJsonObject toJson(const DirectChatsMap& directChats) { QJsonObject json; for (auto it = directChats.begin(); it != directChats.end();) { @@ -1050,7 +1050,7 @@ QVector Connection::roomsWithTag(const QString& tagName) const return rooms; } -Connection::DirectChatsMap Connection::directChats() const +DirectChatsMap Connection::directChats() const { return d->directChats; } @@ -1117,7 +1117,7 @@ bool Connection::isIgnored(const User* user) const return ignoredUsers().contains(user->id()); } -Connection::IgnoredUsersList Connection::ignoredUsers() const +IgnoredUsersList Connection::ignoredUsers() const { const auto* event = d->unpackAccountData(); return event ? event->ignored_users() : IgnoredUsersList(); diff --git a/lib/connection.h b/lib/connection.h index 1f1d4cd5..e4109fd4 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -97,6 +97,14 @@ enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; Q_ENUM_NS(RunningPolicy) +// Room ids, rather than room pointers, are used in the direct chat +// map types because the library keeps Invite rooms separate from +// rooms in Join and Leave state; and direct chats in account data +// are stored with no regard to their state. +using DirectChatsMap = QMultiHash; +using DirectChatUsersMap = QMultiHash; +using IgnoredUsersList = IgnoredUsersEvent::content_type; + class Connection : public QObject { Q_OBJECT @@ -115,14 +123,6 @@ class Connection : public QObject { lazyLoadingChanged) public: - // Room ids, rather than room pointers, are used in the direct chat - // map types because the library keeps Invite rooms separate from - // rooms in Join and Leave state; and direct chats in account data - // are stored with no regard to their state. - using DirectChatsMap = QMultiHash; - using DirectChatUsersMap = QMultiHash; - using IgnoredUsersList = IgnoredUsersEvent::content_type; - using UsersToDevicesToEvents = UnorderedMap>; @@ -153,7 +153,7 @@ public: * from the server. * \sa rooms, room, roomsWithTag */ - Q_INVOKABLE QVector allRooms() const; + Q_INVOKABLE QVector allRooms() const; /// Get rooms that have either of the given join state(s) /*! @@ -163,10 +163,11 @@ public: * Leave rooms from the server. * \sa allRooms, room, roomsWithTag */ - Q_INVOKABLE QVector rooms(JoinStates joinStates) const; + Q_INVOKABLE QVector + rooms(Quotient::JoinStates joinStates) const; /// Get the total number of rooms in the given join state(s) - Q_INVOKABLE int roomsCount(JoinStates joinStates) const; + Q_INVOKABLE int roomsCount(Quotient::JoinStates joinStates) const; /** Check whether the account has data of the given type * Direct chats map is not supported by this method _yet_. @@ -253,10 +254,10 @@ public: QList directChatUsers(const Room* room) const; /** Check whether a particular user is in the ignore list */ - Q_INVOKABLE bool isIgnored(const User* user) const; + Q_INVOKABLE bool isIgnored(const Quotient::User* user) const; /** Get the whole list of ignored users */ - Q_INVOKABLE IgnoredUsersList ignoredUsers() const; + Q_INVOKABLE Quotient::IgnoredUsersList ignoredUsers() const; /** Add the user to the ignore list * The change signal is emitted synchronously, without waiting @@ -264,14 +265,14 @@ public: * * \sa ignoredUsersListChanged */ - Q_INVOKABLE void addToIgnoredUsers(const User* user); + Q_INVOKABLE void addToIgnoredUsers(const Quotient::User* user); /** Remove the user from the ignore list */ /** Similar to adding, the change signal is emitted synchronously. * * \sa ignoredUsersListChanged */ - Q_INVOKABLE void removeFromIgnoredUsers(const User* user); + Q_INVOKABLE void removeFromIgnoredUsers(const Quotient::User* user); /** Get the full list of users known to this account */ QMap users() const; @@ -281,13 +282,14 @@ public: /** Get the domain name used for ids/aliases on the server */ QString domain() const; /** Find a room by its id and a mask of applicable states */ - Q_INVOKABLE Room* room(const QString& roomId, - JoinStates states = JoinState::Invite - | JoinState::Join) const; + Q_INVOKABLE Quotient::Room* + room(const QString& roomId, + Quotient::JoinStates states = JoinState::Invite | JoinState::Join) const; /** Find a room by its alias and a mask of applicable states */ - Q_INVOKABLE Room* roomByAlias(const QString& roomAlias, - JoinStates states = JoinState::Invite - | JoinState::Join) const; + Q_INVOKABLE Quotient::Room* + roomByAlias(const QString& roomAlias, + Quotient::JoinStates states = JoinState::Invite + | JoinState::Join) const; /** Update the internal map of room aliases to IDs */ /// This is used to maintain the internal index of room aliases. /// It does NOT change aliases on the server, @@ -295,15 +297,15 @@ public: void updateRoomAliases(const QString& roomId, const QString& aliasServer, const QStringList& previousRoomAliases, const QStringList& roomAliases); - Q_INVOKABLE Room* invitation(const QString& roomId) const; - Q_INVOKABLE User* user(const QString& userId); + Q_INVOKABLE Quotient::Room* invitation(const QString& roomId) const; + Q_INVOKABLE Quotient::User* user(const QString& userId); const User* user() const; User* user(); QString userId() const; QString deviceId() const; QByteArray accessToken() const; QtOlm::Account* olmAccount() const; - Q_INVOKABLE SyncJob* syncJob() const; + Q_INVOKABLE Quotient::SyncJob* syncJob() const; Q_INVOKABLE int millisToReconnect() const; Q_INVOKABLE void getTurnServers(); @@ -589,6 +591,7 @@ public slots: /** @deprecated Use callApi() or Room::postReceipt() instead */ virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event) const; + signals: /** * @deprecated @@ -622,7 +625,7 @@ signals: * * @param request - the pointer to the failed job */ - void requestFailed(BaseJob* request); + void requestFailed(Quotient::BaseJob* request); /** A network request (job) failed due to network problems * @@ -640,7 +643,7 @@ signals: void syncDone(); void syncError(QString message, QString details); - void newUser(User* user); + void newUser(Quotient::User* user); /** * \group Signals emitted on room transitions @@ -672,7 +675,7 @@ signals: */ /** A new room object has been created */ - void newRoom(Room* room); + void newRoom(Quotient::Room* room); /** A room invitation is seen for the first time * @@ -680,7 +683,7 @@ signals: * that initial sync will trigger this signal for all rooms in * Invite state. */ - void invitedRoom(Room* room, Room* prev); + void invitedRoom(Quotient::Room* room, Quotient::Room* prev); /** A joined room is seen for the first time * @@ -691,7 +694,7 @@ signals: * this room was in Invite state before, the respective object is * passed in prev (and it will be deleted shortly afterwards). */ - void joinedRoom(Room* room, Room* prev); + void joinedRoom(Quotient::Room* room, Quotient::Room* prev); /** A room has just been left * @@ -702,10 +705,10 @@ signals: * Left rooms upon initial sync (not only those that were left * right before the sync). */ - void leftRoom(Room* room, Room* prev); + void leftRoom(Quotient::Room* room, Quotient::Room* prev); /** The room object is about to be deleted */ - void aboutToDeleteRoom(Room* room); + void aboutToDeleteRoom(Quotient::Room* room); /** The room has just been created by createRoom or requestDirectChat * @@ -716,7 +719,7 @@ signals: * use directChatAvailable signal if you just need to obtain * a direct chat room. */ - void createdRoom(Room* room); + void createdRoom(Quotient::Room* room); /** The first sync for the room has been completed * @@ -726,7 +729,7 @@ signals: * signals (newRoom, joinedRoom etc.) come earlier, when the room * has just been created. */ - void loadedRoomState(Room* room); + void loadedRoomState(Quotient::Room* room); /** Account data (except direct chats) have changed */ void accountDataChanged(QString type); @@ -735,18 +738,18 @@ signals: * This signal is emitted upon any successful outcome from * requestDirectChat. */ - void directChatAvailable(Room* directChat); + void directChatAvailable(Quotient::Room* directChat); /** The list of direct chats has changed * This signal is emitted every time when the mapping of users * to direct chat rooms is changed (because of either local updates * or a different list arrived from the server). */ - void directChatsListChanged(DirectChatsMap additions, - DirectChatsMap removals); + void directChatsListChanged(Quotient::DirectChatsMap additions, + Quotient::DirectChatsMap removals); - void ignoredUsersListChanged(IgnoredUsersList additions, - IgnoredUsersList removals); + void ignoredUsersListChanged(Quotient::IgnoredUsersList additions, + Quotient::IgnoredUsersList removals); void cacheStateChanged(); void lazyLoadingChanged(); @@ -812,4 +815,5 @@ private: static user_factory_t _userFactory; }; } // namespace Quotient -Q_DECLARE_METATYPE(Quotient::Connection*) +Q_DECLARE_METATYPE(Quotient::DirectChatsMap) +Q_DECLARE_METATYPE(Quotient::IgnoredUsersList) diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 6c1b802c..c4da40f5 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -184,11 +184,11 @@ public: using duration_ms_t = std::chrono::milliseconds::rep; // normally int64_t std::chrono::seconds getCurrentTimeout() const; - Q_INVOKABLE duration_ms_t getCurrentTimeoutMs() const; + Q_INVOKABLE Quotient::BaseJob::duration_ms_t getCurrentTimeoutMs() const; std::chrono::seconds getNextRetryInterval() const; - Q_INVOKABLE duration_ms_t getNextRetryMs() const; + Q_INVOKABLE Quotient::BaseJob::duration_ms_t getNextRetryMs() const; std::chrono::milliseconds timeToRetry() const; - Q_INVOKABLE duration_ms_t millisToRetry() const; + Q_INVOKABLE Quotient::BaseJob::duration_ms_t millisToRetry() const; friend QDebug operator<<(QDebug dbg, const BaseJob* j) { @@ -215,7 +215,7 @@ signals: void sentRequest(); /** The job has changed its status */ - void statusChanged(Status newStatus); + void statusChanged(Quotient::BaseJob::Status newStatus); /** * The previous network request has failed; the next attempt will @@ -225,7 +225,8 @@ signals: * @param inMilliseconds the interval after which the next attempt will be * taken */ - void retryScheduled(int nextAttempt, duration_ms_t inMilliseconds); + void retryScheduled(int nextAttempt, + Quotient::BaseJob::duration_ms_t inMilliseconds); /** * The previous network request has been rate-limited; the next attempt @@ -251,7 +252,7 @@ signals: * * @see result, success, failure */ - void finished(BaseJob* job); + void finished(Quotient::BaseJob* job); /** * Emitted when the job is finished (except when abandoned). @@ -262,14 +263,14 @@ signals: * * @see success, failure */ - void result(BaseJob* job); + void result(Quotient::BaseJob* job); /** * Emitted together with result() in case there's no error. * * @see result, failure */ - void success(BaseJob*); + void success(Quotient::BaseJob*); /** * Emitted together with result() if there's an error. @@ -277,7 +278,7 @@ signals: * * @see result, success */ - void failure(BaseJob*); + void failure(Quotient::BaseJob*); void downloadProgress(qint64 bytesReceived, qint64 bytesTotal); void uploadProgress(qint64 bytesSent, qint64 bytesTotal); diff --git a/lib/room.h b/lib/room.h index cded7eb9..80e305f0 100644 --- a/lib/room.h +++ b/lib/room.h @@ -181,11 +181,11 @@ public: QString avatarMediaId() const; QUrl avatarUrl() const; const Avatar& avatarObject() const; - Q_INVOKABLE JoinState joinState() const; - Q_INVOKABLE QList usersTyping() const; + Q_INVOKABLE Quotient::JoinState joinState() const; + Q_INVOKABLE QList usersTyping() const; QList membersLeft() const; - Q_INVOKABLE QList users() const; + Q_INVOKABLE QList users() const; QStringList memberNames() const; [[deprecated("Use joinedCount(), invitedCount(), totalMemberCount()")]] int memberCount() const; @@ -228,7 +228,7 @@ public: * \note The method will return a valid user regardless of * the membership. */ - Q_INVOKABLE User* user(const QString& userId) const; + Q_INVOKABLE Quotient::User* user(const QString& userId) const; /** * \brief Check the join state of a given user in this room @@ -236,16 +236,15 @@ public: * \note Banned and invited users are not tracked for now (Leave * will be returned for them). * - * \return either of Join, Leave, depending on the given - * user's state in the room + * \return Join if the user is a room member; Leave otherwise */ - Q_INVOKABLE JoinState memberJoinState(User* user) const; + Q_INVOKABLE Quotient::JoinState memberJoinState(Quotient::User* user) const; /** * Get a disambiguated name for a given user in * the context of the room */ - Q_INVOKABLE QString roomMembername(const User* u) const; + Q_INVOKABLE QString roomMembername(const Quotient::User* u) const; /** * Get a disambiguated name for a user with this id in * the context of the room @@ -274,9 +273,10 @@ public: Timeline::const_iterator syncEdge() const; /// \deprecated Use historyEdge instead rev_iter_t timelineEdge() const; - Q_INVOKABLE TimelineItem::index_t minTimelineIndex() const; - Q_INVOKABLE TimelineItem::index_t maxTimelineIndex() const; - Q_INVOKABLE bool isValidIndex(TimelineItem::index_t timelineIndex) const; + Q_INVOKABLE Quotient::TimelineItem::index_t minTimelineIndex() const; + Q_INVOKABLE Quotient::TimelineItem::index_t maxTimelineIndex() const; + Q_INVOKABLE bool + isValidIndex(Quotient::TimelineItem::index_t timelineIndex) const; rev_iter_t findInTimeline(TimelineItem::index_t index) const; rev_iter_t findInTimeline(const QString& evtId) const; @@ -414,7 +414,8 @@ public: * the event is even sent), while downloads are using * the normal event id for identifier. */ - Q_INVOKABLE FileTransferInfo fileTransferInfo(const QString& id) const; + Q_INVOKABLE Quotient::FileTransferInfo + fileTransferInfo(const QString& id) const; /// Get the URL to the actual file source in a unified way /*! @@ -438,7 +439,7 @@ public: /*! This method returns a (potentially empty) state event corresponding * to the pair of event type \p evtType and state key \p stateKey. */ - Q_INVOKABLE const StateEventBase* + Q_INVOKABLE const Quotient::StateEventBase* getCurrentState(const QString& evtType, const QString& stateKey = {}) const; template @@ -549,7 +550,8 @@ signals: /// The remote echo has arrived with the sync and will be merged /// with its local counterpart /** NB: Requires a sync loop to be emitted */ - void pendingEventAboutToMerge(RoomEvent* serverEvent, int pendingEventIndex); + void pendingEventAboutToMerge(Quotient::RoomEvent* serverEvent, + int pendingEventIndex); /// The remote and local copies of the event have been merged /** NB: Requires a sync loop to be emitted */ void pendingEventMerged(); @@ -577,21 +579,21 @@ signals: * upon the last sync * \sa Changes */ - void changed(Changes changes); + void changed(Quotient::Room::Changes changes); /** * \brief The room name, the canonical alias or other aliases changed * * Not triggered when display name changes. */ - void namesChanged(Room* room); - void displaynameAboutToChange(Room* room); - void displaynameChanged(Room* room, QString oldName); + void namesChanged(Quotient::Room* room); + void displaynameAboutToChange(Quotient::Room* room); + void displaynameChanged(Quotient::Room* room, QString oldName); void topicChanged(); void avatarChanged(); - void userAdded(User* user); - void userRemoved(User* user); - void memberAboutToRename(User* user, QString newName); - void memberRenamed(User* user); + void userAdded(Quotient::User* user); + void userRemoved(Quotient::User* user); + void memberAboutToRename(Quotient::User* user, QString newName); + void memberRenamed(Quotient::User* user); /// The list of members has changed /** Emitted no more than once per sync, this is a good signal to * for cases when some action should be done upon any change in @@ -605,7 +607,8 @@ signals: void allMembersLoaded(); void encryption(); - void joinStateChanged(JoinState oldState, JoinState newState); + void joinStateChanged(Quotient::JoinState oldState, + Quotient::JoinState newState); void typingChanged(); void highlightCountChanged(); @@ -614,11 +617,11 @@ signals: void displayedChanged(bool displayed); void firstDisplayedEventChanged(); void lastDisplayedEventChanged(); - void lastReadEventChanged(User* user); + void lastReadEventChanged(Quotient::User* user); void readMarkerMoved(QString fromEventId, QString toEventId); - void readMarkerForUserMoved(User* user, QString fromEventId, + void readMarkerForUserMoved(Quotient::User* user, QString fromEventId, QString toEventId); - void unreadMessagesChanged(Room* room); + void unreadMessagesChanged(Quotient::Room* room); void accountDataAboutToChange(QString type); void accountDataChanged(QString type); @@ -626,7 +629,8 @@ signals: void tagsChanged(); void updatedEvent(QString eventId); - void replacedEvent(const RoomEvent* newEvent, const RoomEvent* oldEvent); + void replacedEvent(const Quotient::RoomEvent* newEvent, + const Quotient::RoomEvent* oldEvent); void newFileTransfer(QString id, QUrl localFile); void fileTransferProgress(QString id, qint64 progress, qint64 total); @@ -634,18 +638,18 @@ signals: void fileTransferFailed(QString id, QString errorMessage = {}); void fileTransferCancelled(QString id); - void callEvent(Room* room, const RoomEvent* event); + void callEvent(Quotient::Room* room, const Quotient::RoomEvent* event); /// The room's version stability may have changed void stabilityUpdated(QString recommendedDefault, QStringList stableVersions); /// This room has been upgraded and won't receive updates any more - void upgraded(QString serverMessage, Room* successor); + void upgraded(QString serverMessage, Quotient::Room* successor); /// An attempted room upgrade has failed void upgradeFailed(QString errorMessage); /// The room is about to be deleted - void beforeDestruction(Room*); + void beforeDestruction(Quotient::Room*); protected: virtual Changes processStateEvent(const RoomEvent& e); diff --git a/lib/user.h b/lib/user.h index c9e3dbc1..28ec841b 100644 --- a/lib/user.h +++ b/lib/user.h @@ -107,9 +107,10 @@ public: qreal hueF() const; const Avatar& avatarObject(const Room* room = nullptr) const; - Q_INVOKABLE QImage avatar(int dimension, const Room* room = nullptr); + Q_INVOKABLE QImage avatar(int dimension, + const Quotient::Room* room = nullptr); Q_INVOKABLE QImage avatar(int requestedWidth, int requestedHeight, - const Room* room = nullptr); + const Quotient::Room* room = nullptr); QImage avatar(int width, int height, const Room* room, const Avatar::get_callback_t& callback); @@ -145,9 +146,10 @@ public slots: signals: void nameAboutToChange(QString newName, QString oldName, - const Room* roomContext); - void nameChanged(QString newName, QString oldName, const Room* roomContext); - void avatarChanged(User* user, const Room* roomContext); + const Quotient::Room* roomContext); + void nameChanged(QString newName, QString oldName, + const Quotient::Room* roomContext); + void avatarChanged(Quotient::User* user, const Quotient::Room* roomContext); private slots: void updateName(const QString& newName, const Room* room = nullptr); @@ -161,4 +163,3 @@ private: QScopedPointer d; }; } // namespace Quotient -Q_DECLARE_METATYPE(Quotient::User*) -- cgit v1.2.3 From c8ae34af0c4b28ef5aabf60b97661241860bc200 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 21 Oct 2019 09:41:46 +0900 Subject: ConnectionData: fix read-after-free in clearing the job queue --- lib/connectiondata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index a3807fc4..cbc36420 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -67,7 +67,6 @@ ConnectionData::ConnectionData(QUrl baseUrl) for (auto& q : d->jobs) while (!q.empty()) { auto& job = q.front(); - q.pop(); if (!job || job->error() == BaseJob::Abandoned) continue; if (job->error() != BaseJob::Pending) { @@ -79,6 +78,7 @@ ConnectionData::ConnectionData(QUrl baseUrl) } job->sendRequest(); d->rateLimiter.start(); + q.pop(); return; } qCDebug(MAIN) << d->id() << "job queues are empty"; -- cgit v1.2.3 From 693beec0005bdd98732ad8b4ad760f9de4a9faee Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 15 Oct 2019 19:33:21 +0900 Subject: Connection: make syncLoop() reentrant ...in the sense that you can call it twice and expect the second invocation to be gracefully ignored rather than two loops conflicting with each other. --- lib/connection.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index af85d066..a7b1bee9 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -99,6 +99,7 @@ public: DirectChatsMap dcLocalAdditions; DirectChatsMap dcLocalRemovals; UnorderedMap accountData; + QMetaObject::Connection syncLoopConnection {}; int syncLoopTimeout = -1; GetCapabilitiesJob* capabilitiesJob = nullptr; @@ -380,9 +381,20 @@ void Connection::sync(int timeout) void Connection::syncLoop(int timeout) { - d->syncLoopTimeout = timeout; - connect(this, &Connection::syncDone, this, &Connection::syncLoopIteration); - syncLoopIteration(); // initial sync to start the loop + if (d->syncLoopConnection && d->syncLoopTimeout == timeout) { + qCInfo(MAIN) << "Attempt to run sync loop but there's one already " + "running; nothing will be done"; + return; + } + std::swap(d->syncLoopTimeout, timeout); + if (d->syncLoopConnection) { + qCWarning(MAIN) << "Overriding timeout of the running sync loop from" + << timeout << "to" << d->syncLoopTimeout; + } else { + d->syncLoopConnection = connect(this, &Connection::syncDone, + this, &Connection::syncLoopIteration); + syncLoopIteration(); // initial sync to start the loop + } } QJsonObject toJson(const DirectChatsMap& directChats) @@ -514,8 +526,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) void Connection::stopSync() { // If there's a sync loop, break it - disconnect(this, &Connection::syncDone, this, - &Connection::syncLoopIteration); + disconnect(d->syncLoopConnection); if (d->syncJob) // If there's an ongoing sync job, stop it too { d->syncJob->abandon(); -- cgit v1.2.3 From ff77965cb92e94061345e4e955f1d7289de2597a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 21 Oct 2019 15:58:12 +0900 Subject: Connection: record sync timeout; suspend sync before logout This is mostly internal but clients may see fewer spurious sync failures upon logging out. --- lib/connection.cpp | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index a7b1bee9..3d617733 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -100,7 +100,7 @@ public: DirectChatsMap dcLocalRemovals; UnorderedMap accountData; QMetaObject::Connection syncLoopConnection {}; - int syncLoopTimeout = -1; + int syncTimeout = -1; GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; @@ -260,8 +260,6 @@ void Connection::doConnectToServer(const QString& user, const QString& password, }); } -void Connection::syncLoopIteration() { sync(d->syncLoopTimeout); } - void Connection::connectWithToken(const QString& userId, const QString& accessToken, const QString& deviceId) @@ -336,22 +334,34 @@ void Connection::checkAndConnect(const QString& userId, void Connection::logout() { - auto job = callApi(); - connect(job, &LogoutJob::finished, this, [job, this] { + // If there's an ongoing sync job, stop it but don't break the sync loop yet + const auto syncWasRunning = bool(d->syncJob); + if (syncWasRunning) + { + d->syncJob->abandon(); + d->syncJob = nullptr; + } + const auto* job = callApi(); + connect(job, &LogoutJob::finished, this, [this, job, syncWasRunning] { if (job->status().good() || job->error() == BaseJob::ContentAccessError) { - stopSync(); + if (d->syncLoopConnection) + disconnect(d->syncLoopConnection); d->data->setToken({}); emit stateChanged(); emit loggedOut(); - } + } else if (syncWasRunning) + syncLoopIteration(); // Resume sync loop (or a single sync) }); } void Connection::sync(int timeout) { - if (d->syncJob) + if (d->syncJob) { + qCInfo(MAIN) << d->syncJob << "is already running"; return; + } + d->syncTimeout = timeout; Filter filter; filter.room->timeline->limit = 100; filter.room->state->lazyLoadMembers = d->lazyLoading; @@ -381,22 +391,25 @@ void Connection::sync(int timeout) void Connection::syncLoop(int timeout) { - if (d->syncLoopConnection && d->syncLoopTimeout == timeout) { + if (d->syncLoopConnection && d->syncTimeout == timeout) { qCInfo(MAIN) << "Attempt to run sync loop but there's one already " "running; nothing will be done"; return; } - std::swap(d->syncLoopTimeout, timeout); + std::swap(d->syncTimeout, timeout); if (d->syncLoopConnection) { - qCWarning(MAIN) << "Overriding timeout of the running sync loop from" - << timeout << "to" << d->syncLoopTimeout; + qCInfo(MAIN) << "Timeout for next syncs changed from" + << timeout << "to" << d->syncTimeout; } else { d->syncLoopConnection = connect(this, &Connection::syncDone, - this, &Connection::syncLoopIteration); + this, &Connection::syncLoopIteration, + Qt::QueuedConnection); syncLoopIteration(); // initial sync to start the loop } } +void Connection::syncLoopIteration() { sync(d->syncTimeout); } + QJsonObject toJson(const DirectChatsMap& directChats) { QJsonObject json; -- cgit v1.2.3 From 60bb1cf942ad0815dcf42cbfe8acd1e076d848cf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 29 Oct 2019 22:04:40 +0900 Subject: Derive Omittable<> from std::optional<> That breaks API all over the place but: 1. The fixes are trivial. 2. More of std:: is used instead of home-baking the same stuff. --- lib/connection.cpp | 30 +++--- lib/converters.h | 24 +---- lib/csapi/search.cpp | 191 ------------------------------------- lib/csapi/search.h | 203 ---------------------------------------- lib/events/accountdataevents.h | 10 +- lib/events/roommessageevent.cpp | 23 +++-- lib/events/roommessageevent.h | 2 +- lib/room.cpp | 11 +-- lib/syncdata.cpp | 15 ++- lib/util.h | 108 +++++++-------------- 10 files changed, 81 insertions(+), 536 deletions(-) delete mode 100644 lib/csapi/search.cpp delete mode 100644 lib/csapi/search.h (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 3d617733..47c643b0 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -277,16 +277,16 @@ void Connection::reloadCapabilities() else if (d->capabilitiesJob->error() == BaseJob::IncorrectRequestError) qCDebug(MAIN) << "Server doesn't support /capabilities"; - if (d->capabilities.roomVersions.omitted()) { + if (!d->capabilities.roomVersions) { qCWarning(MAIN) << "Pinning supported room version to 1"; - d->capabilities.roomVersions = { "1", { { "1", "stable" } } }; + d->capabilities.roomVersions.emplace({ "1", { { "1", "stable" } } }); } else { qCDebug(MAIN) << "Room versions:" << defaultRoomVersion() << "is default, full list:" << availableRoomVersions(); } - Q_ASSERT(!d->capabilities.roomVersions.omitted()); + Q_ASSERT(d->capabilities.roomVersions.has_value()); emit capabilitiesLoaded(); - for (auto* r : d->roomMap) + for (auto* r : qAsConst(d->roomMap)) r->checkVersion(); }); } @@ -295,7 +295,7 @@ bool Connection::loadingCapabilities() const { // (Ab)use the fact that room versions cannot be omitted after // the capabilities have been loaded (see reloadCapabilities() above). - return d->capabilities.roomVersions.omitted(); + return !d->capabilities.roomVersions; } void Connection::Private::connectWithToken(const QString& userId, @@ -363,8 +363,8 @@ void Connection::sync(int timeout) d->syncTimeout = timeout; Filter filter; - filter.room->timeline->limit = 100; - filter.room->state->lazyLoadMembers = d->lazyLoading; + filter.room.edit().timeline.edit().limit.emplace(100); + filter.room.edit().state.edit().lazyLoadMembers.emplace(d->lazyLoading); auto job = d->syncJob = callApi(BackgroundRequest, d->data->lastEvent(), filter, timeout); @@ -446,7 +446,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) r->updateData(std::move(roomData), fromCache); if (d->firstTimeRooms.removeOne(r)) { emit loadedRoomState(r); - if (!d->capabilities.roomVersions.omitted()) + if (d->capabilities.roomVersions) r->checkVersion(); // Otherwise, the version will be checked in reloadCapabilities() } @@ -1191,7 +1191,7 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) // and emit a signal. For Invite and Join, there's no such problem. if (room->joinState() == joinState && joinState != JoinState::Leave) return room; - } else if (joinState.omitted()) { + } else if (!joinState) { // No Join and Leave, maybe Invite? room = d->roomMap.value({ id, true }, nullptr); if (room) @@ -1200,9 +1200,7 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) } if (!room) { - room = roomFactory()(this, id, - joinState.omitted() ? JoinState::Join - : joinState.value()); + room = roomFactory()(this, id, joinState.value_or(JoinState::Join)); if (!room) { qCCritical(MAIN) << "Failed to create a room" << id; return nullptr; @@ -1213,7 +1211,7 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) &Connection::aboutToDeleteRoom); emit newRoom(room); } - if (joinState.omitted()) + if (!joinState) return room; if (joinState == JoinState::Invite) { @@ -1431,13 +1429,13 @@ const QString Connection::SupportedRoomVersion::StableTag = QString Connection::defaultRoomVersion() const { - Q_ASSERT(!d->capabilities.roomVersions.omitted()); + Q_ASSERT(d->capabilities.roomVersions.has_value()); return d->capabilities.roomVersions->defaultVersion; } QStringList Connection::stableRoomVersions() const { - Q_ASSERT(!d->capabilities.roomVersions.omitted()); + Q_ASSERT(d->capabilities.roomVersions.has_value()); QStringList l; const auto& allVersions = d->capabilities.roomVersions->available; for (auto it = allVersions.begin(); it != allVersions.end(); ++it) @@ -1457,7 +1455,7 @@ inline bool roomVersionLess(const Connection::SupportedRoomVersion& v1, QVector Connection::availableRoomVersions() const { - Q_ASSERT(!d->capabilities.roomVersions.omitted()); + Q_ASSERT(d->capabilities.roomVersions.has_value()); QVector result; result.reserve(d->capabilities.roomVersions->available.size()); for (auto it = d->capabilities.roomVersions->available.begin(); diff --git a/lib/converters.h b/lib/converters.h index b753a80b..157bff27 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -206,7 +206,7 @@ template struct JsonConverter> { static QJsonValue dump(const Omittable& from) { - return from.omitted() ? QJsonValue() : toJson(from.value()); + return from.has_value() ? toJson(from.value()) : QJsonValue(); } static Omittable load(const QJsonValue& jv) { @@ -378,28 +378,10 @@ namespace _impl { static void impl(ContT& container, const QString& key, const OmittableT& value) { - if (!value.omitted()) - addTo(container, key, value.value()); + if (value) + addTo(container, key, *value); } }; - -#if 0 - // This is a special one that unfolds optional<> - template - struct AddNode, Force> - { - template - static void impl(ContT& container, - const QString& key, const OptionalT& value) - { - if (value) - AddNode::impl(container, key, value.value()); - else if (Force) // Edge case, no value but must put something - AddNode::impl(container, key, QString{}); - } - }; -#endif - } // namespace _impl static constexpr bool IfNotEmpty = false; diff --git a/lib/csapi/search.cpp b/lib/csapi/search.cpp deleted file mode 100644 index 9619f340..00000000 --- a/lib/csapi/search.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "search.h" - -#include "converters.h" - -#include - -using namespace Quotient; - -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient -{ - -template <> -struct JsonObjectConverter -{ - static void dumpTo(QJsonObject& jo, - const SearchJob::IncludeEventContext& pod) - { - addParam(jo, QStringLiteral("before_limit"), - pod.beforeLimit); - addParam(jo, QStringLiteral("after_limit"), pod.afterLimit); - addParam(jo, QStringLiteral("include_profile"), - pod.includeProfile); - } -}; - -template <> -struct JsonObjectConverter -{ - static void dumpTo(QJsonObject& jo, const SearchJob::Group& pod) - { - addParam(jo, QStringLiteral("key"), pod.key); - } -}; - -template <> -struct JsonObjectConverter -{ - static void dumpTo(QJsonObject& jo, const SearchJob::Groupings& pod) - { - addParam(jo, QStringLiteral("group_by"), pod.groupBy); - } -}; - -template <> -struct JsonObjectConverter -{ - static void dumpTo(QJsonObject& jo, const SearchJob::RoomEventsCriteria& pod) - { - addParam<>(jo, QStringLiteral("search_term"), pod.searchTerm); - addParam(jo, QStringLiteral("keys"), pod.keys); - addParam(jo, QStringLiteral("filter"), pod.filter); - addParam(jo, QStringLiteral("order_by"), pod.orderBy); - addParam(jo, QStringLiteral("event_context"), - pod.eventContext); - addParam(jo, QStringLiteral("include_state"), - pod.includeState); - addParam(jo, QStringLiteral("groupings"), pod.groupings); - } -}; - -template <> -struct JsonObjectConverter -{ - static void dumpTo(QJsonObject& jo, const SearchJob::Categories& pod) - { - addParam(jo, QStringLiteral("room_events"), pod.roomEvents); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, SearchJob::UserProfile& result) - { - fromJson(jo.value("displayname"_ls), result.displayname); - fromJson(jo.value("avatar_url"_ls), result.avatarUrl); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, SearchJob::EventContext& result) - { - fromJson(jo.value("start"_ls), result.begin); - fromJson(jo.value("end"_ls), result.end); - fromJson(jo.value("profile_info"_ls), result.profileInfo); - fromJson(jo.value("events_before"_ls), result.eventsBefore); - fromJson(jo.value("events_after"_ls), result.eventsAfter); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, SearchJob::Result& result) - { - fromJson(jo.value("rank"_ls), result.rank); - fromJson(jo.value("result"_ls), result.result); - fromJson(jo.value("context"_ls), result.context); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, SearchJob::GroupValue& result) - { - fromJson(jo.value("next_batch"_ls), result.nextBatch); - fromJson(jo.value("order"_ls), result.order); - fromJson(jo.value("results"_ls), result.results); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, - SearchJob::ResultRoomEvents& result) - { - fromJson(jo.value("count"_ls), result.count); - fromJson(jo.value("highlights"_ls), result.highlights); - fromJson(jo.value("results"_ls), result.results); - fromJson(jo.value("state"_ls), result.state); - fromJson(jo.value("groups"_ls), result.groups); - fromJson(jo.value("next_batch"_ls), result.nextBatch); - } -}; - -template <> -struct JsonObjectConverter -{ - static void fillFrom(const QJsonObject& jo, - SearchJob::ResultCategories& result) - { - fromJson(jo.value("room_events"_ls), result.roomEvents); - } -}; - -} // namespace Quotient - -class SearchJob::Private -{ -public: - ResultCategories searchCategories; -}; - -BaseJob::Query queryToSearch(const QString& nextBatch) -{ - BaseJob::Query _q; - addParam(_q, QStringLiteral("next_batch"), nextBatch); - return _q; -} - -static const auto SearchJobName = QStringLiteral("SearchJob"); - -SearchJob::SearchJob(const Categories& searchCategories, - const QString& nextBatch) - : BaseJob(HttpVerb::Post, SearchJobName, basePath % "/search", - queryToSearch(nextBatch)) - , d(new Private) -{ - QJsonObject _data; - addParam<>(_data, QStringLiteral("search_categories"), searchCategories); - setRequestData(_data); -} - -SearchJob::~SearchJob() = default; - -const SearchJob::ResultCategories& SearchJob::searchCategories() const -{ - return d->searchCategories; -} - -BaseJob::Status SearchJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("search_categories"_ls)) - return { IncorrectResponse, - "The key 'search_categories' not found in the response" }; - fromJson(json.value("search_categories"_ls), d->searchCategories); - - return Success; -} diff --git a/lib/csapi/search.h b/lib/csapi/search.h deleted file mode 100644 index 079ac8e9..00000000 --- a/lib/csapi/search.h +++ /dev/null @@ -1,203 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#pragma once - -#include "converters.h" - -#include "csapi/definitions/room_event_filter.h" - -#include "events/eventloader.h" -#include "jobs/basejob.h" - -#include -#include - -#include - -namespace Quotient -{ - -// Operations - -/// Perform a server-side search. -/*! - * Performs a full text search across different categories. - */ -class SearchJob : public BaseJob -{ -public: - // Inner data structures - - /// Configures whether any context for the eventsreturned are included in - /// the response. - struct IncludeEventContext - { - /// How many events before the result arereturned. By default, this is - /// ``5``. - Omittable beforeLimit; - /// How many events after the result arereturned. By default, this is - /// ``5``. - Omittable afterLimit; - /// Requests that the server returns thehistoric profile information for - /// the usersthat sent the events that were returned.By default, this is - /// ``false``. - Omittable includeProfile; - }; - - /// Configuration for group. - struct Group - { - /// Key that defines the group. - QString key; - }; - - /// Requests that the server partitions the result setbased on the provided - /// list of keys. - struct Groupings - { - /// List of groups to request. - QVector groupBy; - }; - - /// Mapping of category name to search criteria. - struct RoomEventsCriteria - { - /// The string to search events for - QString searchTerm; - /// The keys to search. Defaults to all. - QStringList keys; - /// This takes a `filter`_. - Omittable filter; - /// The order in which to search for results.By default, this is - /// ``"rank"``. - QString orderBy; - /// Configures whether any context for the eventsreturned are included - /// in the response. - Omittable eventContext; - /// Requests the server return the current state foreach room returned. - Omittable includeState; - /// Requests that the server partitions the result setbased on the - /// provided list of keys. - Omittable groupings; - }; - - /// Describes which categories to search in and their criteria. - struct Categories - { - /// Mapping of category name to search criteria. - Omittable roomEvents; - }; - - /// Performs a full text search across different categories. - struct UserProfile - { - /// Performs a full text search across different categories. - QString displayname; - /// Performs a full text search across different categories. - QString avatarUrl; - }; - - /// Context for result, if requested. - struct EventContext - { - /// Pagination token for the start of the chunk - QString begin; - /// Pagination token for the end of the chunk - QString end; - /// The historic profile information of theusers that sent the events - /// returned.The ``string`` key is the user ID for whichthe profile - /// belongs to. - QHash profileInfo; - /// Events just before the result. - RoomEvents eventsBefore; - /// Events just after the result. - RoomEvents eventsAfter; - }; - - /// The result object. - struct Result - { - /// A number that describes how closely this result matches the search. - /// Higher is closer. - Omittable rank; - /// The event that matched. - RoomEventPtr result; - /// Context for result, if requested. - Omittable context; - }; - - /// The results for a particular group value. - struct GroupValue - { - /// Token that can be used to get the next batchof results in the group, - /// by passing as the`next_batch` parameter to the next call. Ifthis - /// field is absent, there are no moreresults in this group. - QString nextBatch; - /// Key that can be used to order differentgroups. - Omittable order; - /// Which results are in this group. - QStringList results; - }; - - /// Mapping of category name to search criteria. - struct ResultRoomEvents - { - /// An approximate count of the total number of results found. - Omittable count; - /// List of words which should be highlighted, useful for stemming which - /// may change the query terms. - QStringList highlights; - /// List of results in the requested order. - std::vector results; - /// The current state for every room in the results.This is included if - /// the request had the``include_state`` key set with a value of - /// ``true``.The ``string`` key is the room ID for which the - /// ``StateEvent`` array belongs to. - std::unordered_map state; - /// Any groups that were requested.The outer ``string`` key is the group - /// key requested (eg: ``room_id``or ``sender``). The inner ``string`` - /// key is the grouped value (eg: a room's ID or a user's ID). - QHash> groups; - /// Token that can be used to get the next batch ofresults, by passing - /// as the `next_batch` parameter tothe next call. If this field is - /// absent, there are nomore results. - QString nextBatch; - }; - - /// Describes which categories to search in and their criteria. - struct ResultCategories - { - /// Mapping of category name to search criteria. - Omittable roomEvents; - }; - - // Construction/destruction - - /*! Perform a server-side search. - * \param searchCategories - * Describes which categories to search in and their criteria. - * \param nextBatch - * The point to return events from. If given, this should be a - * ``next_batch`` result from a previous call to this endpoint. - */ - explicit SearchJob(const Categories& searchCategories, - const QString& nextBatch = {}); - - ~SearchJob() override; - - // Result properties - - /// Describes which categories to search in and their criteria. - const ResultCategories& searchCategories() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; -}; - -} // namespace Quotient diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 31176766..a55016d9 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -1,5 +1,3 @@ -#include - /****************************************************************************** * Copyright (C) 2018 Kitsune Ral * @@ -34,13 +32,13 @@ struct TagRecord { order_type order; - TagRecord(order_type order = none) : order(order) {} + TagRecord(order_type order = none) : order(std::move(order)) {} bool operator<(const TagRecord& other) const { - // Per The Spec, rooms with no order should be after those with order - return !order.omitted() - && (other.order.omitted() || order.value() < other.order.value()); + // Per The Spec, rooms with no order should be after those with order, + // against optional<>::operator<() convention. + return order && (!other.order || *order < *other.order); } }; diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 09562d65..078ae70a 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -95,6 +95,11 @@ MsgType jsonToMsgType(const QString& matrixType) return MsgType::Unknown; } +inline bool isReplacement(const Omittable& rel) +{ + return rel && rel->type == RelatesTo::ReplacementTypeId(); +} + QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, const QString& jsonMsgType, TypedBase* content) @@ -111,6 +116,7 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, // After the above, we know for sure that the content is TextContent // and that its RelatesTo structure is not omitted auto* textContent = static_cast(content); + Q_ASSERT(textContent && textContent->relatesTo.has_value()); if (textContent->relatesTo->type == RelatesTo::ReplacementTypeId()) { auto newContentJson = json.take("m.new_content"_ls).toObject(); newContentJson.insert(BodyKey, plainBody); @@ -243,9 +249,7 @@ QString RoomMessageEvent::replacedEvent() const return {}; const auto& rel = static_cast(content())->relatesTo; - return !rel.omitted() && rel->type == RelatesTo::ReplacementTypeId() - ? rel->eventId - : QString(); + return isReplacement(rel) ? rel->eventId : QString(); } QString rawMsgTypeForMimeType(const QMimeType& mimeType) @@ -269,10 +273,10 @@ QString RoomMessageEvent::rawMsgTypeForFile(const QFileInfo& fi) return rawMsgTypeForMimeType(QMimeDatabase().mimeTypeForFile(fi)); } -TextContent::TextContent(const QString& text, const QString& contentType, +TextContent::TextContent(QString text, const QString& contentType, Omittable relatesTo) : mimeType(QMimeDatabase().mimeTypeForName(contentType)) - , body(text) + , body(std::move(text)) , relatesTo(std::move(relatesTo)) { if (contentType == HtmlContentTypeId) @@ -304,10 +308,9 @@ TextContent::TextContent(const QJsonObject& json) static const auto PlainTextMimeType = db.mimeTypeForName("text/plain"); static const auto HtmlMimeType = db.mimeTypeForName("text/html"); - const auto actualJson = - relatesTo.omitted() || relatesTo->type != RelatesTo::ReplacementTypeId() - ? json - : json.value("m.new_content"_ls).toObject(); + const auto actualJson = isReplacement(relatesTo) + ? json.value("m.new_content"_ls).toObject() + : json; // Special-casing the custom matrix.org's (actually, Riot's) way // of sending HTML messages. if (actualJson["format"_ls].toString() == HtmlContentTypeId) { @@ -331,7 +334,7 @@ void TextContent::fillJson(QJsonObject* json) const json->insert(FormatKey, HtmlContentTypeId); json->insert(FormattedBodyKey, body); } - if (!relatesTo.omitted()) { + if (relatesTo) { json->insert(QStringLiteral("m.relates_to"), QJsonObject { { relatesTo->type, relatesTo->eventId } }); if (relatesTo->type == RelatesTo::ReplacementTypeId()) { diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index e95aabfc..ded5e572 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -114,7 +114,7 @@ namespace EventContent { */ class TextContent : public TypedBase { public: - TextContent(const QString& text, const QString& contentType, + TextContent(QString text, const QString& contentType, Omittable relatesTo = none); explicit TextContent(const QJsonObject& json); diff --git a/lib/room.cpp b/lib/room.cpp index c4cdac04..b34c36ea 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1201,15 +1201,13 @@ QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, int Room::joinedCount() const { - return d->summary.joinedMemberCount.omitted() - ? d->membersMap.size() - : d->summary.joinedMemberCount.value(); + return d->summary.joinedMemberCount.value_or(d->membersMap.size()); } int Room::invitedCount() const { // TODO: Store invited users in Room too - Q_ASSERT(!d->summary.invitedMemberCount.omitted()); + Q_ASSERT(d->summary.invitedMemberCount.has_value()); return d->summary.invitedMemberCount.value(); } @@ -2620,9 +2618,8 @@ QString Room::Private::calculateDisplayname() const const bool emptyRoom = membersMap.isEmpty() || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); - const bool nonEmptySummary = - !summary.heroes.omitted() && !summary.heroes->empty(); - auto shortlist = nonEmptySummary ? buildShortlist(summary.heroes.value()) + const bool nonEmptySummary = summary.heroes && !summary.heroes->empty(); + auto shortlist = nonEmptySummary ? buildShortlist(*summary.heroes) : !emptyRoom ? buildShortlist(membersMap) : users_shortlist_t {}; diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 5b47b30f..89c512a2 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -30,8 +30,7 @@ const QString SyncRoomData::UnreadCountKey = bool RoomSummary::isEmpty() const { - return joinedMemberCount.omitted() && invitedMemberCount.omitted() - && heroes.omitted(); + return !joinedMemberCount && !invitedMemberCount && !heroes; } bool RoomSummary::merge(const RoomSummary& other) @@ -46,12 +45,12 @@ QDebug Quotient::operator<<(QDebug dbg, const RoomSummary& rs) { QDebugStateSaver _(dbg); QStringList sl; - if (!rs.joinedMemberCount.omitted()) - sl << QStringLiteral("joined: %1").arg(rs.joinedMemberCount.value()); - if (!rs.invitedMemberCount.omitted()) - sl << QStringLiteral("invited: %1").arg(rs.invitedMemberCount.value()); - if (!rs.heroes.omitted()) - sl << QStringLiteral("heroes: [%1]").arg(rs.heroes.value().join(',')); + if (rs.joinedMemberCount) + sl << QStringLiteral("joined: %1").arg(*rs.joinedMemberCount); + if (rs.invitedMemberCount) + sl << QStringLiteral("invited: %1").arg(*rs.invitedMemberCount); + if (rs.heroes) + sl << QStringLiteral("heroes: [%1]").arg(rs.heroes->join(',')); dbg.nospace().noquote() << sl.join(QStringLiteral("; ")); return dbg; } diff --git a/lib/util.h b/lib/util.h index f7a81b2a..b7bb8d4c 100644 --- a/lib/util.h +++ b/lib/util.h @@ -24,6 +24,7 @@ #include #include #include +#include // Along the lines of Q_DISABLE_COPY - the upstream version comes in Qt 5.13 #define DISABLE_MOVE(_ClassName) \ @@ -43,68 +44,35 @@ struct HashQ { template using UnorderedMap = std::unordered_map>; -struct NoneTag {}; -constexpr NoneTag none {}; +inline constexpr auto none = std::nullopt; -/** A crude substitute for `optional` while we're not C++17 +/** `std::optional` with tweaks * - * Only works with default-constructible types. + * Due to tweaks, only works with default-constructible types. */ template -class Omittable { +class Omittable : public std::optional { static_assert(!std::is_reference::value, "You cannot make an Omittable<> with a reference type"); public: using value_type = std::decay_t; + static_assert(std::is_default_constructible_v, + "Omittable<> requires a default-constructible type"); - explicit Omittable() : Omittable(none) {} - Omittable(NoneTag) : _value(value_type()), _omitted(true) {} - Omittable(const value_type& val) : _value(val) {} - Omittable(value_type&& val) : _value(std::move(val)) {} - Omittable& operator=(const value_type& val) - { - _value = val; - _omitted = false; - return *this; - } - Omittable& operator=(value_type&& val) - { - // For some reason GCC complains about -Wmaybe-uninitialized - // in the context of using Omittable with converters.h; - // though the logic looks very much benign (GCC bug???) - _value = std::move(val); - _omitted = false; - return *this; - } + using std::optional::optional; - bool operator==(const value_type& rhs) const - { - return !omitted() && value() == rhs; - } - friend bool operator==(const value_type& lhs, - const Omittable& rhs) - { - return rhs == lhs; - } - bool operator!=(const value_type& rhs) const { return !operator==(rhs); } - friend bool operator!=(const value_type& lhs, - const Omittable& rhs) - { - return !(rhs == lhs); - } + // Overload emplace() to allow passing braced-init-lists (the standard + // emplace() does direct-initialisation but not direct-list-initialisation). + using std::optional::emplace; + T& emplace(const T& val) { return std::optional::emplace(val); } + T& emplace(T&& val) { return std::optional::emplace(std::move(val)); } - bool omitted() const { return _omitted; } - const value_type& value() const - { - Q_ASSERT(!_omitted); - return _value; - } - value_type& editValue() + value_type& edit() { - _omitted = false; - return _value; + return this->has_value() ? this->value() : this->emplace(); } + /// Merge the value from another Omittable /// \return true if \p other is not omitted and the value of /// the current Omittable was different (or omitted); @@ -114,26 +82,20 @@ public: auto merge(const Omittable& other) -> std::enable_if_t::value, bool> { - if (other.omitted() || (!_omitted && _value == other.value())) + if (!other || (this->has_value() && **this == *other)) return false; - _omitted = false; - _value = other.value(); + *this = other; return true; } - value_type&& release() - { - _omitted = true; - return std::move(_value); - } - const value_type* operator->() const& { return &value(); } - value_type* operator->() & { return &editValue(); } - const value_type& operator*() const& { return value(); } - value_type& operator*() & { return editValue(); } + // Hide non-const lvalue operator-> and operator* as these are + // a bit too surprising: value() & doesn't lazy-create an object; + // and it's too easy to inadvertently change the underlying value. -private: - T _value; - bool _omitted = false; + const value_type* operator->() const& { return &this->value(); } + value_type* operator->() && { return &this->value(); } + const value_type& operator*() const& { return this->value(); } + value_type& operator*() && { return this->value(); } }; namespace _impl { @@ -213,19 +175,19 @@ class Range { using size_type = typename ArrayT::size_type; public: - Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {} - Range(iterator from, iterator to) : from(from), to(to) {} + constexpr Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {} + constexpr Range(iterator from, iterator to) : from(from), to(to) {} - size_type size() const + constexpr size_type size() const { Q_ASSERT(std::distance(from, to) >= 0); return size_type(std::distance(from, to)); } - bool empty() const { return from == to; } - const_iterator begin() const { return from; } - const_iterator end() const { return to; } - iterator begin() { return from; } - iterator end() { return to; } + constexpr bool empty() const { return from == to; } + constexpr const_iterator begin() const { return from; } + constexpr const_iterator end() const { return to; } + constexpr iterator begin() { return from; } + constexpr iterator end() { return to; } private: iterator from; @@ -239,8 +201,8 @@ private: */ template inline std::pair findFirstOf(InputIt first, InputIt last, - ForwardIt sFirst, - ForwardIt sLast, Pred pred) + ForwardIt sFirst, + ForwardIt sLast, Pred pred) { for (; first != last; ++first) for (auto it = sFirst; it != sLast; ++it) -- cgit v1.2.3 From edbbc2bc77599ead0e14bc08cdddda10d1c5f305 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 31 Oct 2019 12:48:49 +0900 Subject: Omittable: get rid of value() Xcode 10 doesn't have it, and value() is not quite fitting mostly-exceptionless Quotient anyway. --- lib/connection.cpp | 10 +++++----- lib/room.cpp | 2 +- lib/util.h | 28 +++++++++++++++++++--------- 3 files changed, 25 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 47c643b0..998b45d1 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1182,7 +1182,7 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) // TODO: This whole function is a strong case for a RoomManager class. Q_ASSERT_X(!id.isEmpty(), __FUNCTION__, "Empty room id"); - // If joinState.omitted(), all joinState == comparisons below are false. + // If joinState is empty, all joinState == comparisons below are false. const auto roomKey = qMakePair(id, joinState == JoinState::Invite); auto* room = d->roomMap.value(roomKey, nullptr); if (room) { @@ -1214,17 +1214,17 @@ Room* Connection::provideRoom(const QString& id, Omittable joinState) if (!joinState) return room; - if (joinState == JoinState::Invite) { + if (*joinState == JoinState::Invite) { // prev is either Leave or nullptr auto* prev = d->roomMap.value({ id, false }, nullptr); emit invitedRoom(room, prev); } else { - room->setJoinState(joinState.value()); + room->setJoinState(*joinState); // Preempt the Invite room (if any) with a room in Join/Leave state. auto* prevInvite = d->roomMap.take({ id, true }); - if (joinState == JoinState::Join) + if (*joinState == JoinState::Join) emit joinedRoom(room, prevInvite); - else if (joinState == JoinState::Leave) + else if (*joinState == JoinState::Leave) emit leftRoom(room, prevInvite); if (prevInvite) { const auto dcUsers = prevInvite->directChatUsers(); diff --git a/lib/room.cpp b/lib/room.cpp index b34c36ea..0806e30d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1208,7 +1208,7 @@ int Room::invitedCount() const { // TODO: Store invited users in Room too Q_ASSERT(d->summary.invitedMemberCount.has_value()); - return d->summary.invitedMemberCount.value(); + return d->summary.invitedMemberCount.value_or(0); } int Room::totalMemberCount() const { return joinedCount() + invitedCount(); } diff --git a/lib/util.h b/lib/util.h index b7bb8d4c..15c9fec8 100644 --- a/lib/util.h +++ b/lib/util.h @@ -56,6 +56,7 @@ class Omittable : public std::optional { "You cannot make an Omittable<> with a reference type"); public: + using base_type = std::optional; using value_type = std::decay_t; static_assert(std::is_default_constructible_v, "Omittable<> requires a default-constructible type"); @@ -64,13 +65,22 @@ public: // Overload emplace() to allow passing braced-init-lists (the standard // emplace() does direct-initialisation but not direct-list-initialisation). - using std::optional::emplace; - T& emplace(const T& val) { return std::optional::emplace(val); } - T& emplace(T&& val) { return std::optional::emplace(std::move(val)); } - + using base_type::emplace; + T& emplace(const T& val) { return base_type::emplace(val); } + T& emplace(T&& val) { return base_type::emplace(std::move(val)); } + + // use value_or() or check (with operator! or has_value) before accessing + // with operator-> or operator* + // The technical reason is that Xcode 10 has incomplete std::optional + // that has no value(); but using value() may also mean that you rely + // on the optional throwing an exception (which is not assumed practice + // throughout Quotient) or that you spend unnecessary CPU cycles on + // an extraneous has_value() check. + value_type& value() = delete; + const value_type& value() const = delete; value_type& edit() { - return this->has_value() ? this->value() : this->emplace(); + return this->has_value() ? base_type::operator*() : this->emplace(); } /// Merge the value from another Omittable @@ -92,10 +102,10 @@ public: // a bit too surprising: value() & doesn't lazy-create an object; // and it's too easy to inadvertently change the underlying value. - const value_type* operator->() const& { return &this->value(); } - value_type* operator->() && { return &this->value(); } - const value_type& operator*() const& { return this->value(); } - value_type& operator*() && { return this->value(); } + const value_type* operator->() const& { return base_type::operator->(); } + value_type* operator->() && { return base_type::operator->(); } + const value_type& operator*() const& { return base_type::operator*(); } + value_type& operator*() && { return base_type::operator*(); } }; namespace _impl { -- cgit v1.2.3 From ce1de2a78a947423b305289beefc5f904734b234 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 1 Nov 2019 10:25:45 +0900 Subject: Reinstate Omittable<>::omitted (deprecated) To ease on back-compatibility. --- lib/util.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'lib') diff --git a/lib/util.h b/lib/util.h index 15c9fec8..5be62382 100644 --- a/lib/util.h +++ b/lib/util.h @@ -83,6 +83,12 @@ public: return this->has_value() ? base_type::operator*() : this->emplace(); } + [[deprecated("Use '!o' or '!o.has_value()' instead of 'o.omitted()'")]] + bool omitted() const + { + return !this->has_value(); + } + /// Merge the value from another Omittable /// \return true if \p other is not omitted and the value of /// the current Omittable was different (or omitted); -- cgit v1.2.3 From ae5c2aa9acce500e2ad94c6781843c02310dbab3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 1 Nov 2019 20:47:07 +0900 Subject: Omittable: Add direct-list-initialising operator=; document See the change in connection.cpp for the example of usage. Also: removed static_asserts: the first one is provided by std::optional, and the second one is only relevant to edit(). --- lib/connection.cpp | 2 +- lib/util.h | 50 +++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 998b45d1..5ee7e84e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -279,7 +279,7 @@ void Connection::reloadCapabilities() if (!d->capabilities.roomVersions) { qCWarning(MAIN) << "Pinning supported room version to 1"; - d->capabilities.roomVersions.emplace({ "1", { { "1", "stable" } } }); + d->capabilities.roomVersions = { "1", { { "1", "stable" } } }; } else { qCDebug(MAIN) << "Room versions:" << defaultRoomVersion() << "is default, full list:" << availableRoomVersions(); diff --git a/lib/util.h b/lib/util.h index 5be62382..902b4bfc 100644 --- a/lib/util.h +++ b/lib/util.h @@ -44,27 +44,59 @@ struct HashQ { template using UnorderedMap = std::unordered_map>; -inline constexpr auto none = std::nullopt; +constexpr auto none = std::nullopt; /** `std::optional` with tweaks * - * Due to tweaks, only works with default-constructible types. + * The tweaks are: + * - streamlined assignment (operator=)/emplace()ment of values that can be + * used to implicitly construct the underlying type, including + * direct-list-initialisation, e.g.: + * \code + * struct S { int a; char b; } + * Omittable o; + * o = { 1, 'a' }; // std::optional would require o = S { 1, 'a' } + * \endcode + * - entirely deleted value(). The technical reason is that Xcode 10 doesn't + * have it; but besides that, value_or() or (after explicit checking) + * `operator*()`/`operator->()` are better alternatives within Quotient + * that doesn't practice throwing exceptions (as doesn't most of Qt). + * - disabled non-const lvalue operator*() and operator->(), as it's too easy + * to inadvertently cause a value change through them. + * - edit() to provide a safe and explicit lvalue accessor instead of those + * above. Requires the underlying type to be default-constructible. + * Allows chained initialisation of nested Omittables: + * \code + * struct Inner { int member = 10; Omittable innermost; }; + * struct Outer { int anotherMember = 10; Omittable inner; }; + * Omittable o; // = { 10, std::nullopt }; + * o.edit().inner.edit().innermost.emplace(42); + * \endcode + * - merge() - a soft version of operator= that only overwrites its first + * operand with the second one if the second one is not empty. */ template class Omittable : public std::optional { - static_assert(!std::is_reference::value, - "You cannot make an Omittable<> with a reference type"); - public: using base_type = std::optional; using value_type = std::decay_t; - static_assert(std::is_default_constructible_v, - "Omittable<> requires a default-constructible type"); using std::optional::optional; - // Overload emplace() to allow passing braced-init-lists (the standard - // emplace() does direct-initialisation but not direct-list-initialisation). + // Overload emplace() and operator=() to allow passing braced-init-lists + // (the standard emplace() does direct-initialisation but + // not direct-list-initialisation). + using base_type::operator=; + Omittable& operator=(const value_type& v) + { + base_type::operator=(v); + return *this; + } + Omittable& operator=(value_type&& v) + { + base_type::operator=(v); + return *this; + } using base_type::emplace; T& emplace(const T& val) { return base_type::emplace(val); } T& emplace(T&& val) { return base_type::emplace(std::move(val)); } -- cgit v1.2.3 From 2a7871cd9bc67a0bf311766a1edee2ff7a8e5355 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 2 Nov 2019 16:20:57 +0900 Subject: Compatibility with Qt 5.14 --- lib/converters.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/converters.h b/lib/converters.h index 157bff27..075af7ef 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -30,6 +30,7 @@ #include +#if QT_VERSION < QT_VERSION_CHECK(5,14,0) // Enable std::unordered_map // REMOVEME in favor of UnorderedMap, once we regenerate API files namespace std { @@ -37,15 +38,12 @@ template <> struct hash { size_t operator()(const QString& s) const Q_DECL_NOEXCEPT { - return qHash(s -#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) - , - uint(qGlobalQHashSeed()) -#endif + return qHash(s, uint(qGlobalQHashSeed()) ); } }; } // namespace std +#endif class QVariant; -- cgit v1.2.3 From 9d50c475ea60efa472ba6c298fbf23836edcad19 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 10 Nov 2019 17:42:20 +0900 Subject: makeRedacted: fix code formatting --- lib/room.cpp | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 0806e30d..be07ce50 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1934,22 +1934,16 @@ RoomEventPtr makeRedacted(const RoomEvent& target, const RedactionEvent& redaction) { auto originalJson = target.originalJsonObject(); - static const QStringList keepKeys { EventIdKey, - TypeKey, - QStringLiteral("room_id"), - QStringLiteral("sender"), - StateKeyKey, - QStringLiteral("prev_content"), - ContentKey, - QStringLiteral("hashes"), - QStringLiteral("signatures"), - QStringLiteral("depth"), - QStringLiteral("prev_events"), - QStringLiteral("prev_state"), - QStringLiteral("auth_events"), - QStringLiteral("origin"), - QStringLiteral("origin_server_ts"), - QStringLiteral("membership") }; + // clang-format off + static const QStringList keepKeys { EventIdKey, TypeKey, + QStringLiteral("room_id"), QStringLiteral("sender"), StateKeyKey, + QStringLiteral("prev_content"), ContentKey, + QStringLiteral("hashes"), QStringLiteral("signatures"), + QStringLiteral("depth"), QStringLiteral("prev_events"), + QStringLiteral("prev_state"), QStringLiteral("auth_events"), + QStringLiteral("origin"), QStringLiteral("origin_server_ts"), + QStringLiteral("membership") }; + // clang-format on std::vector> keepContentKeysMap { { RoomMemberEvent::typeId(), { QStringLiteral("membership") } }, -- cgit v1.2.3 From fdd094a5ef3dd8a2ef06cfa1282482a60837b317 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 10 Nov 2019 19:20:09 +0900 Subject: Cleanup --- lib/events/roomevent.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 971d8597..4a9a8b5d 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -36,10 +36,8 @@ RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json) { const auto unsignedData = json[UnsignedKeyL].toObject(); const auto redaction = unsignedData[RedactedCauseKeyL]; - if (redaction.isObject()) { + if (redaction.isObject()) _redactedBecause = makeEvent(redaction.toObject()); - return; - } } RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job -- cgit v1.2.3 From 5937127b73a82fc86f6546397373ce9dbaf4e560 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 19 Nov 2019 17:57:41 +0900 Subject: BaseJob: Don't send accessToken if not needed; send again on 401 The first part closes #358; the second part is a workaround for non-standard cases when endpoints without security by the spec turn out to be secured (in particular, the case of authenticating media servers). --- lib/connection.cpp | 9 ++++--- lib/connectiondata.cpp | 12 +++++++++ lib/connectiondata.h | 2 ++ lib/jobs/basejob.cpp | 66 ++++++++++++++++++++++++++++++-------------------- lib/jobs/basejob.h | 8 +++++- 5 files changed, 66 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5ee7e84e..c830557c 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -343,7 +343,8 @@ void Connection::logout() } const auto* job = callApi(); connect(job, &LogoutJob::finished, this, [this, job, syncWasRunning] { - if (job->status().good() || job->error() == BaseJob::ContentAccessError) { + if (job->status().good() || job->error() == BaseJob::Unauthorised + || job->error() == BaseJob::ContentAccessError) { if (d->syncLoopConnection) disconnect(d->syncLoopConnection); d->data->setToken({}); @@ -380,9 +381,9 @@ void Connection::sync(int timeout) }); connect(job, &SyncJob::failure, this, [this, job] { d->syncJob = nullptr; - if (job->error() == BaseJob::ContentAccessError) { - qCWarning(SYNCJOB) << "Sync job failed with ContentAccessError - " - "login expired?"; + if (job->error() == BaseJob::Unauthorised) { + qCWarning(SYNCJOB) + << "Sync job failed with Unauthorised - login expired?"; emit loginError(job->errorString(), job->rawDataSample()); } else emit syncError(job->errorString(), job->rawDataSample()); diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index cbc36420..e2a0f06f 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -42,6 +42,7 @@ public: QString lastEvent; QString userId; QString deviceId; + std::vector needToken; mutable unsigned int txnCounter = 0; const qint64 txnBase = QDateTime::currentMSecsSinceEpoch(); @@ -143,6 +144,12 @@ const QString& ConnectionData::deviceId() const { return d->deviceId; } const QString& ConnectionData::userId() const { return d->userId; } +bool ConnectionData::needsToken(const QString& requestName) const +{ + return std::find(d->needToken.cbegin(), d->needToken.cend(), requestName) + != d->needToken.cend(); +} + void ConnectionData::setDeviceId(const QString& deviceId) { d->deviceId = deviceId; @@ -150,6 +157,11 @@ void ConnectionData::setDeviceId(const QString& deviceId) void ConnectionData::setUserId(const QString& userId) { d->userId = userId; } +void ConnectionData::setNeedsToken(const QString& requestName) +{ + d->needToken.push_back(requestName); +} + QString ConnectionData::lastEvent() const { return d->lastEvent; } void ConnectionData::setLastEvent(QString identifier) diff --git a/lib/connectiondata.h b/lib/connectiondata.h index b367c977..000099d1 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -40,6 +40,7 @@ public: QUrl baseUrl() const; const QString& deviceId() const; const QString& userId() const; + bool needsToken(const QString& requestName) const; QNetworkAccessManager* nam() const; void setBaseUrl(QUrl baseUrl); @@ -50,6 +51,7 @@ public: void setPort(int port); void setDeviceId(const QString& deviceId); void setUserId(const QString& userId); + void setNeedsToken(const QString& requestName); QString lastEvent() const; void setLastEvent(QString identifier); diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 13e65188..7f558685 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -226,8 +226,9 @@ void BaseJob::Private::sendRequest() requestQuery) }; if (!requestHeaders.contains("Content-Type")) req.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - req.setRawHeader("Authorization", - QByteArray("Bearer ") + connection->accessToken()); + if (needsToken) + req.setRawHeader("Authorization", + QByteArray("Bearer ") + connection->accessToken()); req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); @@ -274,6 +275,7 @@ void BaseJob::sendRequest() return; Q_ASSERT(d->connection && status().code == Pending); qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest(); + d->needsToken |= d->connection->needsToken(objectName()); emit aboutToSendRequest(); d->sendRequest(); Q_ASSERT(d->reply); @@ -341,32 +343,32 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } -BaseJob::Status BaseJob::Status::fromHttpCode(int httpCode, QString msg) +BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode) { + if (httpCode / 10 == 41) // 41x errors + return httpCode == 410 ? IncorrectRequestError : NotFoundError; + switch (httpCode) { + case 401: + return Unauthorised; // clang-format off - return { [httpCode]() -> StatusCode { - if (httpCode / 10 == 41) // 41x errors - return httpCode == 410 ? IncorrectRequestError : NotFoundError; - switch (httpCode) { - case 401: case 403: case 407: - return ContentAccessError; - case 404: - return NotFoundError; - case 400: case 405: case 406: case 426: case 428: case 505: - case 494: // Unofficial nginx "Request header too large" - case 497: // Unofficial nginx "HTTP request sent to HTTPS port" - return IncorrectRequestError; - case 429: - return TooManyRequestsError; - case 501: case 510: - return RequestNotImplementedError; - case 511: - return NetworkAuthRequiredError; - default: - return NetworkError; - } - }(), std::move(msg) }; - // clang-format on + case 403: case 407: // clang-format on + return ContentAccessError; + case 404: + return NotFoundError; + // clang-format off + case 400: case 405: case 406: case 426: case 428: case 505: // clang-format on + case 494: // Unofficial nginx "Request header too large" + case 497: // Unofficial nginx "HTTP request sent to HTTPS port" + return IncorrectRequestError; + case 429: + return TooManyRequestsError; + case 501: case 510: + return RequestNotImplementedError; + case 511: + return NetworkAuthRequiredError; + default: + return NetworkError; + } } QDebug BaseJob::Status::dumpToLog(QDebug dbg) const @@ -496,6 +498,16 @@ void BaseJob::finishJob() d->connection->submit(this); return; } + if (error() == Unauthorised && !d->needsToken + && !d->connection->accessToken().isEmpty()) { + // Rerun with access token (extension of the spec while + // https://github.com/matrix-org/matrix-doc/issues/701 is pending) + d->connection->setNeedsToken(objectName()); + qCWarning(d->logCat) << this << "re-running with authentication"; + emit retryScheduled(d->retriesTaken, 0); + setStatus(Pending); + sendRequest(); + } if ((error() == NetworkError || error() == Timeout) && d->retriesTaken < d->maxRetries) { // TODO: The whole retrying thing should be put to Connection(Manager) @@ -596,6 +608,8 @@ QString BaseJob::statusCaption() const return tr("Network problems"); case TimeoutError: return tr("Request timed out"); + case Unauthorised: + return tr("Unauthorised request"); case ContentAccessError: return tr("Access error"); case NotFoundError: diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index c4da40f5..ecbec74a 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -60,6 +60,7 @@ public: NetworkError = 100, Timeout, TimeoutError = Timeout, + Unauthorised, ContentAccessError, NotFoundError, IncorrectRequest, @@ -113,7 +114,12 @@ public: struct Status { Status(StatusCode c) : code(c) {} Status(int c, QString m) : code(c), message(std::move(m)) {} - static Status fromHttpCode(int httpCode, QString msg = {}); + + static StatusCode fromHttpCode(int httpCode); + static Status fromHttpCode(int httpCode, QString msg) + { + return { fromHttpCode(httpCode), std::move(msg) }; + } bool good() const { return code < ErrorLevel; } QDebug dumpToLog(QDebug dbg) const; -- cgit v1.2.3 From 4be8a673b4ad2f1caa0e34b53ed3b12e750cf569 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Sat, 30 Nov 2019 03:13:23 +0300 Subject: Fix room highlighting for names with hashtag Fixes #359 --- lib/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 041a8aba..9f4ac85f 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -56,7 +56,7 @@ void Quotient::linkifyUrls(QString& htmlEscapedText) // https://matrix.org/docs/spec/appendices.html#identifier-grammar static const QRegularExpression MxIdRegExp( QStringLiteral( - R"((^|[^<>/])([!#@][-a-z0-9_=/.]{1,252}:(?:\w|\.|-)+\.\w+(?::\d{1,5})?))"), + R"((^|[^<>/])([!#@][-a-z0-9_=#/.]{1,252}:(?:\w|\.|-)+\.\w+(?::\d{1,5})?))"), RegExpOptions); // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,&," -- cgit v1.2.3 From d18324f1a309bea59a0dc681de820c80bd9086e9 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 6 Dec 2019 14:43:25 +0300 Subject: MSC1954: Remove prev_content from the essential keys list Fixes #318 --- lib/room.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index be07ce50..edf732ff 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1937,7 +1937,6 @@ RoomEventPtr makeRedacted(const RoomEvent& target, // clang-format off static const QStringList keepKeys { EventIdKey, TypeKey, QStringLiteral("room_id"), QStringLiteral("sender"), StateKeyKey, - QStringLiteral("prev_content"), ContentKey, QStringLiteral("hashes"), QStringLiteral("signatures"), QStringLiteral("depth"), QStringLiteral("prev_events"), QStringLiteral("prev_state"), QStringLiteral("auth_events"), -- cgit v1.2.3 From f0d241c6bb8fb3219736ec6ead20564bcea9a991 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 25 Nov 2019 15:20:12 +0900 Subject: Remove extraneous qualification --- lib/room.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 80e305f0..7e419823 100644 --- a/lib/room.h +++ b/lib/room.h @@ -181,7 +181,7 @@ public: QString avatarMediaId() const; QUrl avatarUrl() const; const Avatar& avatarObject() const; - Q_INVOKABLE Quotient::JoinState joinState() const; + Q_INVOKABLE JoinState joinState() const; Q_INVOKABLE QList usersTyping() const; QList membersLeft() const; -- cgit v1.2.3 From e3e94f86edc94b743af913b4b5b8b9af7c446fab Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 8 Dec 2019 21:01:05 +0300 Subject: ConnectionData: stop the timer on destruction ...to prevent from trying to send requests after closing the connection. --- lib/connectiondata.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index e2a0f06f..94cab29b 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -86,7 +86,11 @@ ConnectionData::ConnectionData(QUrl baseUrl) }); } -ConnectionData::~ConnectionData() = default; +ConnectionData::~ConnectionData() +{ + d->rateLimiter.disconnect(); + d->rateLimiter.stop(); +} void ConnectionData::submit(BaseJob* job) { -- cgit v1.2.3 From 77f1ca99ade62565ce30758005b911eb340953e6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 8 Dec 2019 20:19:25 +0300 Subject: Room::addNewMessageEvents: search the whole sync batch for events to redact It seems that sometimes redactions may precede the unredacted events within the same sync batch. --- lib/room.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index be07ce50..198cef0d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2122,12 +2122,10 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // Try to find the target in the timeline, then in the batch. if (processRedaction(*r)) continue; - auto targetIt = std::find_if(events.begin(), it, - [id = r->redactedEvent()]( - const RoomEventPtr& ep) { - return ep->id() == id; - }); - if (targetIt != it) + if (auto targetIt = std::find_if(events.begin(), events.end(), + [id = r->redactedEvent()](const RoomEventPtr& ep) { + return ep->id() == id; + }); targetIt != events.end()) *targetIt = makeRedacted(**targetIt, *r); else qCDebug(EVENTS) -- cgit v1.2.3 From e829f867c14cb51e89e4d10550f2f41329c14ba8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 11:52:26 +0300 Subject: Initialise read marker if none is found in the whole timeline Closes #361. --- lib/room.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 71d76f66..8253ea42 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -515,7 +515,9 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) // that has just arrived. In this case we should recalculate // unreadMessages and might need to promote the read marker further // over local-origin messages. - const auto readMarker = q->readMarker(); + auto readMarker = q->readMarker(); + if (readMarker == timeline.crend() && q->allHistoryLoaded()) + --readMarker; // Read marker not found in the timeline, initialise it if (readMarker >= from && readMarker < to) { promoteReadMarker(q->localUser(), readMarker, true); return; -- cgit v1.2.3 From 105ff3874baf0792616e1b330ce97f0b725adaf5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:14:43 +0300 Subject: Room: more doc-comments --- lib/room.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 7e419823..2c958033 100644 --- a/lib/room.h +++ b/lib/room.h @@ -442,6 +442,10 @@ public: Q_INVOKABLE const Quotient::StateEventBase* getCurrentState(const QString& evtType, const QString& stateKey = {}) const; + /// Get a state event with the given event type and state key + /*! This is a typesafe overload that accepts a C++ event type instead of + * its Matrix name. + */ template const EvT* getCurrentState(const QString& stateKey = {}) const { @@ -453,6 +457,14 @@ public: return evt; } + /// Set a state event of the given type with the given arguments + /*! This typesafe overload attempts to send a state event with the type + * \p EvT and the content defined by \p args. Specifically, the function + * creates a temporary object of type \p EvT passing \p args to + * the constructor, and sends a request to the homeserver using + * the Matrix event type defined by \p EvT and the event content produced + * via EvT::contentJson(). + */ template auto setState(ArgTs&&... args) const { -- cgit v1.2.3 From 1b38ad0ce7ca8b2a5a7e4936824697b84e6b7076 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:32:18 +0300 Subject: Code cleanup --- lib/room.cpp | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 8253ea42..fe50aa9a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2134,25 +2134,23 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) << r->redactedEvent() << "is not found"; // If the target event comes later, it comes already redacted. } - if (auto* msg = eventCast(eptr)) { - if (!msg->replacedEvent().isEmpty()) { - if (processReplacement(*msg)) - continue; - auto targetIt = std::find_if(events.begin(), it, - [id = msg->replacedEvent()]( - const RoomEventPtr& ep) { - return ep->id() == id; - }); - if (targetIt != it) - *targetIt = makeReplaced(**targetIt, *msg); - else // FIXME: don't ignore, just show it wherever it arrived - qCDebug(EVENTS) - << "Replacing event" << msg->id() - << "ignored: replaced event" << msg->replacedEvent() - << "is not found"; - // Same as with redactions above, the replaced event coming - // later will come already with the new content. - } + if (auto* msg = eventCast(eptr); + msg && !msg->replacedEvent().isEmpty()) { + if (processReplacement(*msg)) + continue; + auto targetIt = std::find_if(events.begin(), it, + [id = msg->replacedEvent()](const RoomEventPtr& ep) { + return ep->id() == id; + }); + if (targetIt != it) + *targetIt = makeReplaced(**targetIt, *msg); + else // FIXME: don't ignore, just show it wherever it arrived + qCDebug(EVENTS) + << "Replacing event" << msg->id() + << "ignored: replaced event" << msg->replacedEvent() + << "is not found"; + // Same as with redactions above, the replaced event coming + // later will come already with the new content. } } } @@ -2317,7 +2315,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) || (oldStateEvent->matrixType() == e.matrixType() && oldStateEvent->stateKey() == e.stateKey())); if (!is(e)) // Room member events are too numerous - qCDebug(EVENTS) << "Room state event:" << e; + qCDebug(STATE) << "Room state event:" << e; // clang-format off return visit(e @@ -2327,10 +2325,10 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this,oldStateEvent] (const RoomAliasesEvent& ae) { // clang-format on if (ae.aliases().isEmpty()) { - qCDebug(STATE).noquote() - << ae.stateKey() << "no more has aliases for room" - << objectName(); - d->aliasServers.remove(ae.stateKey()); + if (d->aliasServers.remove(ae.stateKey())) + qCDebug(STATE).noquote() + << ae.stateKey() << "no more has aliases for room" + << objectName(); } else { d->aliasServers.insert(ae.stateKey()); qCDebug(STATE).nospace().noquote() -- cgit v1.2.3 From 608e252bae9cf8cf763e05363bfacf5e1760134f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:19:53 +0300 Subject: RoomEvent::timestamp() -> originTimestamp() The previous name is still available but deprecated. --- lib/events/roomevent.cpp | 2 +- lib/events/roomevent.h | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 4a9a8b5d..a59cd6e0 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -44,7 +44,7 @@ RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job QString RoomEvent::id() const { return fullJson()[EventIdKeyL].toString(); } -QDateTime RoomEvent::timestamp() const +QDateTime RoomEvent::originTimestamp() const { return Quotient::fromJson(fullJson()["origin_server_ts"_ls]); } diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index f943bce4..621652cb 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -46,7 +46,10 @@ public: ~RoomEvent() override; QString id() const; - QDateTime timestamp() const; + QDateTime originTimestamp() const; + [[deprecated("Use originTimestamp()")]] QDateTime timestamp() const { + return originTimestamp(); + } QString roomId() const; QString senderId() const; bool isReplaced() const; -- cgit v1.2.3 From 3e81ba0da47278f383ce8c329010602f84a50482 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:30:36 +0300 Subject: Room::predecessor() and Room::successor() --- lib/room.cpp | 20 ++++++++++++++++++++ lib/room.h | 15 +++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index fe50aa9a..41a8888c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -387,11 +387,31 @@ QString Room::predecessorId() const return d->getCurrentState()->predecessor().roomId; } +Room* Room::predecessor(JoinStates statesFilter) const +{ + if (const auto& predId = predecessorId(); !predId.isEmpty()) + if (auto* r = connection()->room(predId, statesFilter); + r && r->successorId() == id()) + return r; + + return nullptr; +} + QString Room::successorId() const { return d->getCurrentState()->successorRoomId(); } +Room* Room::successor(JoinStates statesFilter) const +{ + if (const auto& succId = successorId(); !succId.isEmpty()) + if (auto* r = connection()->room(succId, statesFilter); + r && r->predecessorId() == id()) + return r; + + return nullptr; +} + const Room::Timeline& Room::messageEvents() const { return d->timeline; } const Room::PendingEvents& Room::pendingEvents() const diff --git a/lib/room.h b/lib/room.h index 2c958033..fa3c6ff3 100644 --- a/lib/room.h +++ b/lib/room.h @@ -167,7 +167,22 @@ public: QString version() const; bool isUnstable() const; QString predecessorId() const; + /// Room predecessor + /** This function validates that the predecessor has a tombstone and + * the tombstone refers to the current room. If that's not the case, + * or if the predecessor is in a join state not matching \p stateFilter, + * the function returns nullptr. + */ + Room* predecessor(JoinStates statesFilter = JoinState::Invite + | JoinState::Join) const; QString successorId() const; + /// Room successor + /** This function validates that the successor room's creation event + * refers to the current room. If that's not the case, or if the successor + * is in a join state not matching \p stateFilter, it returns nullptr. + */ + Room* successor(JoinStates statesFilter = JoinState::Invite + | JoinState::Join) const; QString name() const; /// Room aliases defined on the current user's server /// \sa remoteAliases, setLocalAliases -- cgit v1.2.3 From 4b56d47284500ab61f8e0c5cd7c807c58e1b53cb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:22:37 +0300 Subject: Room::creation() and Room::tombstone() --- lib/room.h | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index fa3c6ff3..10f18742 100644 --- a/lib/room.h +++ b/lib/room.h @@ -27,6 +27,8 @@ #include "events/accountdataevents.h" #include "events/encryptedevent.h" #include "events/roommessageevent.h" +#include "events/roomcreateevent.h" +#include "events/roomtombstoneevent.h" #include #include @@ -303,6 +305,11 @@ public: const RelatedEvents relatedEvents(const RoomEvent& evt, const char* relType) const; + const RoomCreateEvent* creation() const + { return getCurrentState(); } + const RoomTombstoneEvent* tombstone() const + { return getCurrentState(); } + bool displayed() const; /// Mark the room as currently displayed to the user /** -- cgit v1.2.3 From 6b2847de2325f2b818dc336c9339d50de58604ea Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Dec 2019 18:31:59 +0300 Subject: Pass action scope to Room::setTags The tags can now be applied not only to the current room but also propagated to its predecessors and successors. --- lib/room.cpp | 17 ++++++++++++++++- lib/room.h | 18 +++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 41a8888c..60b9a684 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -948,12 +948,27 @@ void Room::removeTag(const QString& name) << "not found, nothing to remove"; } -void Room::setTags(TagsMap newTags) +void Room::setTags(TagsMap newTags, ActionScope applyOn) { + bool propagate = applyOn != ActionScope::ThisRoomOnly; + auto joinStates = + applyOn == ActionScope::WithinSameState ? joinState() : + applyOn == ActionScope::OmitLeftState ? JoinState::Join|JoinState::Invite : + JoinState::Join|JoinState::Invite|JoinState::Leave; + if (propagate) { + for (auto* r = this; (r = r->predecessor(joinStates));) + r->setTags(newTags, ActionScope::ThisRoomOnly); + } + d->setTags(move(newTags)); connection()->callApi( localUser()->id(), id(), TagEvent::matrixTypeId(), TagEvent(d->tags).contentJson()); + + if (propagate) { + for (auto* r = this; (r = r->successor(joinStates));) + r->setTags(newTags, ActionScope::ThisRoomOnly); + } } void Room::Private::setTags(TagsMap newTags) diff --git a/lib/room.h b/lib/room.h index 10f18742..ad19792e 100644 --- a/lib/room.h +++ b/lib/room.h @@ -396,6 +396,19 @@ public: /// Remove a tag from the room Q_INVOKABLE void removeTag(const QString& name); + /// The scope to apply an action on + /*! This enumeration is used to pick a strategy to propagate certain + * actions on the room to its predecessors and successors. + */ + enum ActionScope { + ThisRoomOnly, //< Do not apply to predecessors and successors + WithinSameState, //< Apply to predecessors and successors in the same + //< state as the current one + OmitLeftState, //< Apply to all reachable predecessors and successors + //< except those in Leave state + WholeSequence //< Apply to all reachable predecessors and successors + }; + /** Overwrite the room's tags * This completely replaces the existing room's tags with a set * of new ones and updates the new set on the server. Unlike @@ -403,8 +416,11 @@ public: * immediately, not waiting for confirmation from the server * (because tags are saved in account data rather than in shared * room state). + * \param applyOn setting this to Room::OnAllConversations will set tags + * on this and all _known_ predecessors and successors; + * by default only the current room is changed */ - void setTags(TagsMap newTags); + void setTags(TagsMap newTags, ActionScope applyOn = ThisRoomOnly); /// Check whether the list of tags has m.favourite bool isFavourite() const; -- cgit v1.2.3 From 7b5f65ba64ae4df54919336f4beec19493d1b5ee Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 11 Dec 2019 20:26:39 +0300 Subject: BaseJob::StatusCode: offset first error from ErrorLevel Q_ENUM seems to resolve int to the first enum identifier with that value so NetworkError == ErrorLevel looks confusing in logs. --- lib/jobs/basejob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index ecbec74a..b4c4c136 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -57,7 +57,7 @@ public: Unprepared = 25, //< Initial job state is incomplete, hence warning level Abandoned = 50, //< A tiny period between abandoning and object deletion ErrorLevel = 100, //< Errors have codes starting from this - NetworkError = 100, + NetworkError = 101, Timeout, TimeoutError = Timeout, Unauthorised, -- cgit v1.2.3 From 1ffa6b96adeba8c1a00a0a32112c8b0aaeb2350e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:11:16 +0300 Subject: RequestData: optimise fromData() and preopen the buffer It was just a coincidence that QBuffer allowed reading from it without being isReadable() at the moment of starting a job. --- lib/jobs/requestdata.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/jobs/requestdata.cpp b/lib/jobs/requestdata.cpp index 0c70f085..cec15954 100644 --- a/lib/jobs/requestdata.cpp +++ b/lib/jobs/requestdata.cpp @@ -11,9 +11,8 @@ using namespace Quotient; auto fromData(const QByteArray& data) { auto source = std::make_unique(); - source->open(QIODevice::WriteOnly); - source->write(data); - source->close(); + source->setData(data); + source->open(QIODevice::ReadOnly); return source; } -- cgit v1.2.3 From eabed200f5fe09a37942a52387c0d3f25070b0c3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:34:57 +0300 Subject: Room::fileNameToDownload(): fix a typo in a check on Windows As a result of the typo, the extension was never attached to returned file names if there was none originally. --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 60b9a684..eefccdb5 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1037,7 +1037,7 @@ QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const if (QSysInfo::productType() == "windows") { if (const auto& suffixes = fileInfo->mimeType.suffixes(); - suffixes.isEmpty() + !suffixes.isEmpty() && std::none_of(suffixes.begin(), suffixes.end(), [&fileName](const QString& s) { return fileName.endsWith(s); -- cgit v1.2.3 From ec3ddd6930991b04d13cdb12720f141482f9a6eb Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:37:09 +0300 Subject: Move FileError definition from DownloadFileJob to BaseJob Will use it in BaseJob in a later commit. --- lib/jobs/basejob.h | 1 + lib/jobs/downloadfilejob.h | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index b4c4c136..e708ba8d 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -82,6 +82,7 @@ public: UserConsentRequiredError = UserConsentRequired, CannotLeaveRoom, UserDeactivated, + FileError, UserDefinedError = 256 }; Q_ENUM(StatusCode) diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index b7d2d75b..06dc145c 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -5,8 +5,6 @@ namespace Quotient { class DownloadFileJob : public GetContentJob { public: - enum { FileError = BaseJob::UserDefinedError + 1 }; - using GetContentJob::makeRequestUrl; static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri); -- cgit v1.2.3 From d438c08e11d17dc9c005806a6036e1aeb769c54b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:39:24 +0300 Subject: Connection::uploadFile/Content(): refactoring around QIODevice::open() No more "The file is already open" log messages. --- lib/connection.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index c830557c..999f4382 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -624,12 +624,17 @@ UploadContentJob* Connection::uploadContent(QIODevice* contentSource, const QString& filename, const QString& overrideContentType) const { + Q_ASSERT(contentSource != nullptr); auto contentType = overrideContentType; if (contentType.isEmpty()) { contentType = QMimeDatabase() .mimeTypeForFileNameAndData(filename, contentSource) .name(); - contentSource->open(QIODevice::ReadOnly); + if (!contentSource->open(QIODevice::ReadOnly)) { + qCWarning(MAIN) << "Couldn't open content source" << filename + << "for reading:" << contentSource->errorString(); + return nullptr; + } } return callApi(contentSource, filename, contentType); } @@ -638,11 +643,6 @@ UploadContentJob* Connection::uploadFile(const QString& fileName, const QString& overrideContentType) { auto sourceFile = new QFile(fileName); - if (!sourceFile->open(QIODevice::ReadOnly)) { - qCWarning(MAIN) << "Couldn't open" << sourceFile->fileName() - << "for reading"; - return nullptr; - } return uploadContent(sourceFile, QFileInfo(*sourceFile).fileName(), overrideContentType); } -- cgit v1.2.3 From f6505a541fc0a2e02f9c79496488121a3e46fb01 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:47:07 +0300 Subject: BaseJob: prepare() -> initiate() + refactoring around it * BaseJob::initiate() now calls ConnectionData::submit() without relying on Connection to do that * ConnectionData::submit() is now the only site where a job enters Pending state * No more shortcuts to BaseJob::sendRequest(), even retries are sent through the ConnectionData submission queue * Additional validation in BaseJob::initiate() that the request data device is actually open (because QtNetwork API officially requires that, even if you can get away passing a closed QBuffer to it) --- lib/connection.cpp | 3 +-- lib/connectiondata.cpp | 2 +- lib/jobs/basejob.cpp | 28 +++++++++++++++++++--------- lib/jobs/basejob.h | 2 +- 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 999f4382..c13568f1 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1414,8 +1414,7 @@ void Connection::setLazyLoading(bool newValue) void Connection::run(BaseJob* job, RunningPolicy runningPolicy) const { connect(job, &BaseJob::failure, this, &Connection::requestFailed); - job->prepare(d->data.get(), runningPolicy & BackgroundRequest); - d->data->submit(job); + job->initiate(d->data.get(), runningPolicy & BackgroundRequest); } void Connection::getTurnServers() diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index 94cab29b..753fa3ad 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -94,7 +94,7 @@ ConnectionData::~ConnectionData() void ConnectionData::submit(BaseJob* job) { - Q_ASSERT(job->error() == BaseJob::Pending); + job->setStatus(BaseJob::Pending); if (!d->rateLimiter.isActive()) { job->sendRequest(); return; diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 7f558685..4653e58a 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -138,8 +138,7 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, setObjectName(name); connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout); connect(&d->retryTimer, &QTimer::timeout, this, [this] { - setStatus(Pending); - sendRequest(); + d->connection->submit(this); }); } @@ -259,14 +258,27 @@ void BaseJob::onSentRequest(QNetworkReply*) {} void BaseJob::beforeAbandon(QNetworkReply*) {} -void BaseJob::prepare(ConnectionData* connData, bool inBackground) +void BaseJob::initiate(ConnectionData* connData, bool inBackground) { + Q_ASSERT(connData != nullptr); + d->inBackground = inBackground; d->connection = connData; doPrepare(); - if (status().code != Unprepared && status().code != Pending) + + if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) + && !d->requestData.source()->isReadable()) { + setStatus(FileError, "Request data not ready"); + } + Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this + if (status().code == Unprepared) { + d->connection->submit(this); + } else { + qDebug(d->logCat).noquote() + << "Request failed preparation and won't be sent:" + << d->dumpRequest(); QTimer::singleShot(0, this, &BaseJob::finishJob); - setStatus(Pending); + } } void BaseJob::sendRequest() @@ -292,7 +304,7 @@ void BaseJob::sendRequest() onSentRequest(d->reply.data()); emit sentRequest(); } else - qCWarning(d->logCat).noquote() + qCCritical(d->logCat).noquote() << "Request could not start:" << d->dumpRequest(); } @@ -494,7 +506,6 @@ void BaseJob::finishJob() stop(); if (error() == TooManyRequests) { emit rateLimited(); - setStatus(Pending); d->connection->submit(this); return; } @@ -505,8 +516,7 @@ void BaseJob::finishJob() d->connection->setNeedsToken(objectName()); qCWarning(d->logCat) << this << "re-running with authentication"; emit retryScheduled(d->retriesTaken, 0); - setStatus(Pending); - sendRequest(); + d->connection->submit(this); } if ((error() == NetworkError || error() == Timeout) && d->retriesTaken < d->maxRetries) { diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index e708ba8d..c8046e9e 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -203,7 +203,7 @@ public: } public slots: - void prepare(ConnectionData* connData, bool inBackground); + void initiate(ConnectionData* connData, bool inBackground); /** * Abandons the result of this job, arrived or unarrived. -- cgit v1.2.3 From 7641518deea3591c6903cb5b1b6c7b551c30281b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 19:49:09 +0300 Subject: Room::downloadFile: minor cleanup --- lib/room.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index eefccdb5..69ebd677 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1902,9 +1902,8 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) } auto job = connection()->downloadFile(fileUrl, filePath); if (isJobRunning(job)) { - // If there was a previous transfer (completed or failed), remove it. - d->fileTransfers.remove(eventId); - d->fileTransfers.insert(eventId, { job, job->targetFileName() }); + // If there was a previous transfer (completed or failed), overwrite it. + d->fileTransfers[eventId] = { job, job->targetFileName() }; connect(job, &BaseJob::downloadProgress, this, [this, eventId](qint64 received, qint64 total) { d->fileTransfers[eventId].update(received, total); -- cgit v1.2.3 From 38f45066a2304935550e2c5b6be21a9744f66bf1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Dec 2019 20:57:41 +0300 Subject: BaseJob::initiate(): fix nullptr dereferencing --- lib/jobs/basejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 4653e58a..68adeaf6 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -267,7 +267,7 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) doPrepare(); if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) - && !d->requestData.source()->isReadable()) { + && d->requestData.source() && !d->requestData.source()->isReadable()) { setStatus(FileError, "Request data not ready"); } Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this -- cgit v1.2.3 From 646186d802cfe7213146e4a68f8f926e21931b17 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 13 Dec 2019 14:18:31 +0300 Subject: Room::postFile(): minor refactoring --- lib/room.cpp | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 69ebd677..0a192203 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1628,19 +1628,16 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, QFileInfo localFile { localPath.toLocalFile() }; Q_ASSERT(localFile.isFile()); - const auto txnId = connection()->generateTxnId(); + const auto txnId = + d->addAsPending( + makeEvent(plainText, localFile, asGenericFile)) + ->transactionId(); // Remote URL will only be known after upload; fill in the local path // to enable the preview while the event is pending. uploadFile(txnId, localPath); - { - auto&& event = - makeEvent(plainText, localFile, asGenericFile); - event->setTransactionId(txnId); - d->addAsPending(std::move(event)); - } - auto* context = new QObject(this); - connect(this, &Room::fileTransferCompleted, context, - [context, this, txnId](const QString& id, QUrl, const QUrl& mxcUri) { + // Below, the upload job is used as a context object to clean up connections + connect(this, &Room::fileTransferCompleted, d->fileTransfers[txnId].job, + [this, txnId](const QString& id, QUrl, const QUrl& mxcUri) { if (id == txnId) { auto it = findPendingEvent(txnId); if (it != d->unsyncedEvents.end()) { @@ -1656,11 +1653,10 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, << "but the event referring to it was " "cancelled"; } - context->deleteLater(); } }); - connect(this, &Room::fileTransferCancelled, this, - [context, this, txnId](const QString& id) { + connect(this, &Room::fileTransferCancelled, d->fileTransfers[txnId].job, + [this, txnId](const QString& id) { if (id == txnId) { auto it = findPendingEvent(txnId); if (it != d->unsyncedEvents.end()) { @@ -1670,7 +1666,6 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, d->unsyncedEvents.erase(d->unsyncedEvents.begin() + idx); emit pendingEventDiscarded(); } - context->deleteLater(); } }); @@ -1849,7 +1844,7 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, auto fileName = localFilename.toLocalFile(); auto job = connection()->uploadFile(fileName, overrideContentType); if (isJobRunning(job)) { - d->fileTransfers.insert(id, { job, fileName, true }); + d->fileTransfers[id] = { job, fileName, true }; connect(job, &BaseJob::uploadProgress, this, [this, id](qint64 sent, qint64 total) { d->fileTransfers[id].update(sent, total); -- cgit v1.2.3 From 04960510df6058b4f25d3c41001a519e9fc5ba4a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 13 Dec 2019 14:47:26 +0300 Subject: Room: make downloaded file name building more robust Specifically, handle colons and long file names gracefully. Closes #366. --- lib/room.cpp | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 0a192203..b6925be5 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1005,6 +1005,11 @@ QList Room::directChatUsers() const return connection()->directChatUsers(this); } +QString safeFileName(QString rawName) +{ + return rawName.replace(QRegularExpression("[/\\<>|\"*?:]"), "_"); +} + const RoomMessageEvent* Room::Private::getEventWithFile(const QString& eventId) const { @@ -1020,20 +1025,20 @@ Room::Private::getEventWithFile(const QString& eventId) const QString Room::Private::fileNameToDownload(const RoomMessageEvent* event) const { - Q_ASSERT(event->hasFileContent()); + Q_ASSERT(event && event->hasFileContent()); const auto* fileInfo = event->content()->fileInfo(); QString fileName; if (!fileInfo->originalName.isEmpty()) - fileName = QFileInfo(fileInfo->originalName).fileName(); - else if (!event->plainBody().isEmpty()) { - // Having no better options, assume that the body has - // the original file URL or at least the file name. - if (QUrl u { event->plainBody() }; u.isValid()) - fileName = QFileInfo(u.path()).fileName(); + fileName = QFileInfo(safeFileName(fileInfo->originalName)).fileName(); + else if (QUrl u { event->plainBody() }; u.isValid()) { + qDebug(MAIN) << event->id() + << "has no file name supplied but the event body " + "looks like a URL - using the file name from it"; + fileName = u.fileName(); } - // Check the file name for sanity - if (fileName.isEmpty() || !QTemporaryFile(fileName).open()) - return "file." % fileInfo->mimeType.preferredSuffix(); + if (fileName.isEmpty()) + return safeFileName(fileInfo->mediaId()).replace('.', '-') % '.' + % fileInfo->mimeType.preferredSuffix(); if (QSysInfo::productType() == "windows") { if (const auto& suffixes = fileInfo->mimeType.suffixes(); @@ -1888,12 +1893,15 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) } const auto fileUrl = fileInfo->url; auto filePath = localFilename.toLocalFile(); - if (filePath.isEmpty()) { - // Build our own file path, starting with temp directory and eventId. - filePath = eventId; - filePath = QDir::tempPath() % '/' - % filePath.replace(QRegularExpression("[/\\<>|\"*?:]"), "_") - % '#' % d->fileNameToDownload(event); + if (filePath.isEmpty()) { // Setup default file path + filePath = + fileInfo->url.path().mid(1) % '_' % d->fileNameToDownload(event); + + if (filePath.size() > 200) // If too long, elide in the middle + filePath.replace(128, filePath.size() - 192, "---"); + + filePath = QDir::tempPath() % '/' % filePath; + qDebug(MAIN) << "File path:" << filePath; } auto job = connection()->downloadFile(fileUrl, filePath); if (isJobRunning(job)) { -- cgit v1.2.3 From 92264831077874511341b2dabae255649f741f54 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 23 Dec 2019 21:48:16 +0300 Subject: Connection::forgetRoom: slightly simplify code --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index c13568f1..5bddbb83 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -804,7 +804,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) if (!room) room = d->roomMap.value({ id, true }); if (room && room->joinState() != JoinState::Leave) { - auto leaveJob = room->leaveRoom(); + auto leaveJob = leaveRoom(room); connect(leaveJob, &BaseJob::result, this, [this, leaveJob, forgetJob, room] { if (leaveJob->error() == BaseJob::Success -- cgit v1.2.3 From 79b9f6d928cbd516a610d845891f4054df05e0c4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 23 Dec 2019 21:49:48 +0300 Subject: ConnectionData::submit: when not queuing, still submit asynchronously Otherwise BaseJob::messageSent has every chance to be overlooked by clients who subscribe to it after calling callApi(). --- lib/connectiondata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index 753fa3ad..e806f952 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -96,7 +96,7 @@ void ConnectionData::submit(BaseJob* job) { job->setStatus(BaseJob::Pending); if (!d->rateLimiter.isActive()) { - job->sendRequest(); + QTimer::singleShot(0, job, &BaseJob::sendRequest); return; } d->jobs[size_t(job->isBackground())].emplace(job); -- cgit v1.2.3 From 0373153481e4a08f1dfb194e672188d74ce07d85 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 24 Dec 2019 11:26:11 +0300 Subject: RoomMemberEvent: introduce the reason field (MSC2367) See https://github.com/matrix-org/matrix-doc/pull/2367. Closes #370. --- lib/events/roommemberevent.cpp | 14 ++++++++++---- lib/events/roommemberevent.h | 7 +++++-- 2 files changed, 15 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index d0787170..d4b2be45 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -52,6 +52,7 @@ MemberEventContent::MemberEventContent(const QJsonObject& json) , isDirect(json["is_direct"_ls].toBool()) , displayName(sanitized(json["displayname"_ls].toString())) , avatarUrl(json["avatar_url"_ls].toString()) + , reason(json["reason"_ls].toString()) {} void MemberEventContent::fillJson(QJsonObject* o) const @@ -64,18 +65,23 @@ void MemberEventContent::fillJson(QJsonObject* o) const o->insert(QStringLiteral("displayname"), displayName); if (avatarUrl.isValid()) o->insert(QStringLiteral("avatar_url"), avatarUrl.toString()); + if (!reason.isEmpty()) + o->insert(QStringLiteral("reason"), reason); +} + +bool RoomMemberEvent::changesMembership() const +{ + return !prevContent() || prevContent()->membership != membership(); } bool RoomMemberEvent::isInvite() const { - return membership() == MembershipType::Invite - && (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Invite && changesMembership(); } bool RoomMemberEvent::isJoin() const { - return membership() == MembershipType::Join - && (!prevContent() || prevContent()->membership != membership()); + return membership() == MembershipType::Join && changesMembership(); } bool RoomMemberEvent::isLeave() const diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 6a34fd7f..0ca439e1 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -40,6 +40,7 @@ public: bool isDirect = false; QString displayName; QUrl avatarUrl; + QString reason; protected: void fillJson(QJsonObject* o) const override; @@ -56,8 +57,8 @@ public: explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) {} - [[deprecated("Use RoomMemberEvent(userId, contentArgs) " - "instead")]] RoomMemberEvent(MemberEventContent&& c) + [[deprecated("Use RoomMemberEvent(userId, contentArgs) instead")]] + RoomMemberEvent(MemberEventContent&& c) : StateEvent(typeId(), matrixTypeId(), QString(), c) {} template @@ -85,6 +86,8 @@ public: bool isDirect() const { return content().isDirect; } QString displayName() const { return content().displayName; } QUrl avatarUrl() const { return content().avatarUrl; } + QString reason() const { return content().reason; } + bool changesMembership() const; bool isInvite() const; bool isJoin() const; bool isLeave() const; -- cgit v1.2.3 From 16d6700950f5f0ebd71481efd5e1a24f04e3c651 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sat, 28 Dec 2019 16:12:10 +0800 Subject: isEditing(): fix a bug in replacing events --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index b6925be5..696a5f1b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2137,7 +2137,7 @@ inline bool isEditing(const RoomEventPtr& ep) if (is(*ep)) return true; if (auto* msgEvent = eventCast(ep)) - return msgEvent->replacedEvent().isEmpty(); + return !msgEvent->replacedEvent().isEmpty(); return false; } -- cgit v1.2.3 From e0c01b6fee5027211bd44a39860e40a2b7b226ad Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sun, 29 Dec 2019 21:29:16 +0800 Subject: Add RoomPowerLevelEvent type --- lib/events/roompowerlevelsevent.cpp | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 lib/events/roompowerlevelsevent.cpp (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp new file mode 100644 index 00000000..072f7a95 --- /dev/null +++ b/lib/events/roompowerlevelsevent.cpp @@ -0,0 +1,78 @@ +#include "roompowerlevelsevent.h" + +#include + +using namespace Quotient; + +inline QHash qVariantHashToQHash(const QVariantHash& vHash) { + QHash hash; + + QVariantHash::const_iterator i = vHash.constBegin(); + + while (i != vHash.constEnd()) { + hash.insert(i.key(), i.value().toInt()); + ++i; + } + + return hash; +} + +inline QJsonObject qHashToJson(const QHash& hash) { + QJsonObject json; + + QHash::const_iterator i = hash.constBegin(); + + while (i != hash.constEnd()) { + json.insert(i.key(), i.value()); + ++i; + } + + return json; +} + +PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) : + invite(json["invite"_ls].toInt(50)), + kick(json["kick"_ls].toInt(50)), + ban(json["ban"_ls].toInt(50)), + redact(json["redact"_ls].toInt(50)), + events(qVariantHashToQHash(json["events"_ls].toVariant().toHash())), + eventsDefault(json["events_default"_ls].toInt(0)), + stateDefault(json["state_default"_ls].toInt(0)), + users(qVariantHashToQHash(json["users"_ls].toVariant().toHash())), + usersDefault(json["users_default"_ls].toInt(0)), + notifications(Notifications{json["notifications"_ls]["room"_ls].toInt(50)}) +{ +} + +void PowerLevelsEventContent::fillJson(QJsonObject* o) const { + o->insert(QStringLiteral("invite"), invite); + o->insert(QStringLiteral("kick"), kick); + o->insert(QStringLiteral("ban"), ban); + o->insert(QStringLiteral("redact"), redact); + o->insert(QStringLiteral("events"), qHashToJson(events)); + o->insert(QStringLiteral("events_default"), eventsDefault); + o->insert(QStringLiteral("state_default"), stateDefault); + o->insert(QStringLiteral("users"), qHashToJson(users)); + o->insert(QStringLiteral("users_default"), usersDefault); + o->insert(QStringLiteral("notifications"), QJsonObject{{"room", notifications.room}}); +} + +int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) { + auto e = events(); + + if (e.contains(eventId)) { + return e[eventId]; + } + + return eventsDefault(); +} + +int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) { + auto u = users(); + + if (u.contains(userId)) { + return u[userId]; + } + + return usersDefault(); +} -- cgit v1.2.3 From 52fdd715d605f201331a88ffb1d9caafcf86beb1 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sun, 29 Dec 2019 21:43:35 +0800 Subject: Add missing file --- lib/events/roompowerlevelsevent.h | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 lib/events/roompowerlevelsevent.h (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h new file mode 100644 index 00000000..3a940380 --- /dev/null +++ b/lib/events/roompowerlevelsevent.h @@ -0,0 +1,75 @@ +#pragma once + +#include "eventcontent.h" +#include "stateevent.h" + +namespace Quotient { +class PowerLevelsEventContent : public EventContent::Base { +public: + struct Notifications { + int room; + }; + + explicit PowerLevelsEventContent(const QJsonObject& json); + + int invite; + int kick; + int ban; + + int redact; + + QHash events; + int eventsDefault; + int stateDefault; + + QHash users; + int usersDefault; + + Notifications notifications; + +protected: + void fillJson(QJsonObject* o) const override; +}; + +class RoomPowerLevelsEvent : public StateEvent { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.room.power_levels", RoomPowerLevelsEvent) + + explicit RoomPowerLevelsEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) + {} + + int invite() { return content().invite; } + int kick() { return content().kick; } + int ban() { return content().ban; } + + int redact() { return content().redact; } + + QHash events() { return content().events; } + int eventsDefault() { return content().eventsDefault; } + int stateDefault() { return content().stateDefault; } + + QHash users() { return content().users; } + int usersDefault() { return content().usersDefault; } + + int roomNotification() { return content().notifications.room; } + + int powerLevelForEvent(const QString& eventId); + int powerLevelForUser(const QString& userId); + +private: +}; + +template <> +class EventFactory { +public: + static event_ptr_tt make(const QJsonObject& json, + const QString&) + { + return makeEvent(json); + } +}; + +REGISTER_EVENT_TYPE(RoomPowerLevelsEvent) +} // namespace Quotient -- cgit v1.2.3 From 00921320a407beaae62d01dbcf1550c3334a9258 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Mon, 30 Dec 2019 21:55:01 +0800 Subject: Simplify code --- lib/events/roompowerlevelsevent.cpp | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp index 072f7a95..a6efaa63 100644 --- a/lib/events/roompowerlevelsevent.cpp +++ b/lib/events/roompowerlevelsevent.cpp @@ -4,41 +4,15 @@ using namespace Quotient; -inline QHash qVariantHashToQHash(const QVariantHash& vHash) { - QHash hash; - - QVariantHash::const_iterator i = vHash.constBegin(); - - while (i != vHash.constEnd()) { - hash.insert(i.key(), i.value().toInt()); - ++i; - } - - return hash; -} - -inline QJsonObject qHashToJson(const QHash& hash) { - QJsonObject json; - - QHash::const_iterator i = hash.constBegin(); - - while (i != hash.constEnd()) { - json.insert(i.key(), i.value()); - ++i; - } - - return json; -} - PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) : invite(json["invite"_ls].toInt(50)), kick(json["kick"_ls].toInt(50)), ban(json["ban"_ls].toInt(50)), redact(json["redact"_ls].toInt(50)), - events(qVariantHashToQHash(json["events"_ls].toVariant().toHash())), + events(fromJson>(json["events"_ls])), eventsDefault(json["events_default"_ls].toInt(0)), stateDefault(json["state_default"_ls].toInt(0)), - users(qVariantHashToQHash(json["users"_ls].toVariant().toHash())), + users(fromJson>(json["users"_ls])), usersDefault(json["users_default"_ls].toInt(0)), notifications(Notifications{json["notifications"_ls]["room"_ls].toInt(50)}) { @@ -49,12 +23,12 @@ void PowerLevelsEventContent::fillJson(QJsonObject* o) const { o->insert(QStringLiteral("kick"), kick); o->insert(QStringLiteral("ban"), ban); o->insert(QStringLiteral("redact"), redact); - o->insert(QStringLiteral("events"), qHashToJson(events)); + o->insert(QStringLiteral("events"), Quotient::toJson(events)); o->insert(QStringLiteral("events_default"), eventsDefault); o->insert(QStringLiteral("state_default"), stateDefault); - o->insert(QStringLiteral("users"), qHashToJson(users)); + o->insert(QStringLiteral("users"), Quotient::toJson(users)); o->insert(QStringLiteral("users_default"), usersDefault); - o->insert(QStringLiteral("notifications"), QJsonObject{{"room", notifications.room}}); + o->insert(QStringLiteral("notifications"), {{"room", notifications.room}}); } int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) { -- cgit v1.2.3 From 2a54e66226863bdea4b0828675f38b75e4c7776f Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 1 Jan 2020 14:04:47 +0800 Subject: Mark methods as const. --- lib/events/roompowerlevelsevent.cpp | 4 ++-- lib/events/roompowerlevelsevent.h | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp index a6efaa63..aff64cff 100644 --- a/lib/events/roompowerlevelsevent.cpp +++ b/lib/events/roompowerlevelsevent.cpp @@ -31,7 +31,7 @@ void PowerLevelsEventContent::fillJson(QJsonObject* o) const { o->insert(QStringLiteral("notifications"), {{"room", notifications.room}}); } -int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) { +int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) const { auto e = events(); if (e.contains(eventId)) { @@ -41,7 +41,7 @@ int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) { return eventsDefault(); } -int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) { +int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) const { auto u = users(); if (u.contains(userId)) { diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h index 3a940380..b3405c56 100644 --- a/lib/events/roompowerlevelsevent.h +++ b/lib/events/roompowerlevelsevent.h @@ -40,23 +40,23 @@ public: : StateEvent(typeId(), obj) {} - int invite() { return content().invite; } - int kick() { return content().kick; } - int ban() { return content().ban; } + int invite() const { return content().invite; } + int kick() const { return content().kick; } + int ban() const { return content().ban; } - int redact() { return content().redact; } + int redact() const { return content().redact; } - QHash events() { return content().events; } - int eventsDefault() { return content().eventsDefault; } - int stateDefault() { return content().stateDefault; } + QHash events() const { return content().events; } + int eventsDefault() const { return content().eventsDefault; } + int stateDefault() const { return content().stateDefault; } - QHash users() { return content().users; } - int usersDefault() { return content().usersDefault; } + QHash users() const { return content().users; } + int usersDefault() const { return content().usersDefault; } - int roomNotification() { return content().notifications.room; } + int roomNotification() const { return content().notifications.room; } - int powerLevelForEvent(const QString& eventId); - int powerLevelForUser(const QString& userId); + int powerLevelForEvent(const QString& eventId) const; + int powerLevelForUser(const QString& userId) const; private: }; -- cgit v1.2.3 From d4034fb12b189156e135dcf0fb94960a1d8e9be9 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 1 Jan 2020 14:45:42 +0800 Subject: Add powerLevelForState() --- lib/events/roompowerlevelsevent.cpp | 10 ++++++++++ lib/events/roompowerlevelsevent.h | 1 + 2 files changed, 11 insertions(+) (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp index aff64cff..98588142 100644 --- a/lib/events/roompowerlevelsevent.cpp +++ b/lib/events/roompowerlevelsevent.cpp @@ -41,6 +41,16 @@ int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) const { return eventsDefault(); } +int RoomPowerLevelsEvent::powerLevelForState(const QString &eventId) const { + auto e = events(); + + if (e.contains(eventId)) { + return e[eventId]; + } + + return stateDefault(); +} + int RoomPowerLevelsEvent::powerLevelForUser(const QString &userId) const { auto u = users(); diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h index b3405c56..f0f7207f 100644 --- a/lib/events/roompowerlevelsevent.h +++ b/lib/events/roompowerlevelsevent.h @@ -56,6 +56,7 @@ public: int roomNotification() const { return content().notifications.room; } int powerLevelForEvent(const QString& eventId) const; + int powerLevelForState(const QString& eventId) const; int powerLevelForUser(const QString& userId) const; private: -- cgit v1.2.3 From 285cff6584c48575d4ce49739d7006c21cc91a3f Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 1 Jan 2020 15:44:10 +0800 Subject: Fix linux build --- lib/events/roompowerlevelsevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp index 98588142..c2721595 100644 --- a/lib/events/roompowerlevelsevent.cpp +++ b/lib/events/roompowerlevelsevent.cpp @@ -28,7 +28,7 @@ void PowerLevelsEventContent::fillJson(QJsonObject* o) const { o->insert(QStringLiteral("state_default"), stateDefault); o->insert(QStringLiteral("users"), Quotient::toJson(users)); o->insert(QStringLiteral("users_default"), usersDefault); - o->insert(QStringLiteral("notifications"), {{"room", notifications.room}}); + o->insert(QStringLiteral("notifications"), QJsonObject{{"room", notifications.room}}); } int RoomPowerLevelsEvent::powerLevelForEvent(const QString &eventId) const { -- cgit v1.2.3 From 3accf7dfd82e8e14a7f90a8debee921f2f428679 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Wed, 1 Jan 2020 16:11:13 +0800 Subject: Fix linux build --- lib/events/roompowerlevelsevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roompowerlevelsevent.cpp b/lib/events/roompowerlevelsevent.cpp index c2721595..0a401752 100644 --- a/lib/events/roompowerlevelsevent.cpp +++ b/lib/events/roompowerlevelsevent.cpp @@ -14,7 +14,7 @@ PowerLevelsEventContent::PowerLevelsEventContent(const QJsonObject& json) : stateDefault(json["state_default"_ls].toInt(0)), users(fromJson>(json["users"_ls])), usersDefault(json["users_default"_ls].toInt(0)), - notifications(Notifications{json["notifications"_ls]["room"_ls].toInt(50)}) + notifications(Notifications{json["notifications"_ls].toObject()["room"_ls].toInt(50)}) { } -- cgit v1.2.3 From ac48199de174aa8cca52e703a3e65034fcd5db07 Mon Sep 17 00:00:00 2001 From: Black Hat Date: Sun, 12 Jan 2020 04:29:14 -0800 Subject: Use parsed RoomPowerLevelsEvent in room upgrading --- lib/room.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 696a5f1b..b29f6f48 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -52,6 +52,7 @@ #include "events/roomtombstoneevent.h" #include "events/simplestateevents.h" #include "events/typingevent.h" +#include "events/roompowerlevelsevent.h" #include "jobs/downloadfilejob.h" #include "jobs/mediathumbnailjob.h" #include "jobs/postreadmarkersjob.h" @@ -656,21 +657,12 @@ bool Room::canSwitchVersions() const // TODO, #276: m.room.power_levels const auto* plEvt = - d->currentState.value({ QStringLiteral("m.room.power_levels"), {} }); + d->getCurrentState(); if (!plEvt) return true; - const auto plJson = plEvt->contentJson(); - const auto currentUserLevel = - plJson.value("users"_ls) - .toObject() - .value(localUser()->id()) - .toInt(plJson.value("users_default"_ls).toInt()); - const auto tombstonePowerLevel = - plJson.value("events"_ls) - .toObject() - .value("m.room.tombstone"_ls) - .toInt(plJson.value("state_default"_ls).toInt()); + const auto currentUserLevel = plEvt->powerLevelForUser(localUser()->id()); + const auto tombstonePowerLevel = plEvt->powerLevelForState("m.room.tombstone"_ls); return currentUserLevel >= tombstonePowerLevel; } -- cgit v1.2.3 From 1af2dffb70862a59801a73dacedc695bb062977a Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Fri, 7 Feb 2020 00:04:36 -0500 Subject: Fix build on big-endian systems On little-endian systems, this call to qToLittleEndian(hash) disappears completely. On big-endian systems, it turns into qbswap(hash), and causes a build error. qbswap() isn't defined for QByteArrays, because QByteArray isn't an array containing multi-byte elements. Since each element is a single byte, machine endianness isn't a factor. (If we really wanted to swap the bytes, we'd need to reverse every 4 bytes of the array.) This just drops the call to QToLittleEndian completely. The lines after it converts part of the hash to a QDataStream, which DOES have to worry about endianness, but that code is also specifically calling QDataStream::setByteOrder to specify little-endian. --- lib/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 9f4ac85f..4cbebfe2 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -104,7 +104,7 @@ qreal Quotient::stringToHueF(const QString& s) Q_ASSERT(!s.isEmpty()); QByteArray hash = QCryptographicHash::hash(s.toUtf8(), QCryptographicHash::Sha1); - QDataStream dataStream(qToLittleEndian(hash).left(2)); + QDataStream dataStream(hash.left(2)); dataStream.setByteOrder(QDataStream::LittleEndian); quint16 hashValue; dataStream >> hashValue; -- cgit v1.2.3 From 5072340473b23b77ebb7d4e91f3727ff30950a9a Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Mon, 10 Feb 2020 16:01:08 +0100 Subject: Edit (transmit): fix json struct of `m.new_content` --- lib/events/roommessageevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 078ae70a..9ee476e8 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -120,7 +120,7 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, if (textContent->relatesTo->type == RelatesTo::ReplacementTypeId()) { auto newContentJson = json.take("m.new_content"_ls).toObject(); newContentJson.insert(BodyKey, plainBody); - newContentJson.insert(TypeKey, jsonMsgType); + newContentJson.insert(MsgTypeKeyL, jsonMsgType); json.insert(QStringLiteral("m.new_content"), newContentJson); json[BodyKeyL] = "* " + plainBody; } -- cgit v1.2.3 From e5e9aa06ca9fb936e34d56c47cccb4111f2e80bd Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Mon, 10 Feb 2020 16:03:01 +0100 Subject: Edit (transmit): fix asterisk marking for dumb clients The prepared body got overwritten due to missing return statement. --- lib/events/roommessageevent.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 9ee476e8..7745e6b9 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -122,7 +122,9 @@ QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, newContentJson.insert(BodyKey, plainBody); newContentJson.insert(MsgTypeKeyL, jsonMsgType); json.insert(QStringLiteral("m.new_content"), newContentJson); + json[MsgTypeKeyL] = jsonMsgType; json[BodyKeyL] = "* " + plainBody; + return json; } } } -- cgit v1.2.3 From 1c3373484b96fb8bc855f02caa87bad7567c5e17 Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Mon, 10 Feb 2020 16:24:00 +0100 Subject: TextContent: use MSC-1849 compatible `m.relates_to` field --- lib/events/roommessageevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 7745e6b9..a6620618 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -338,7 +338,7 @@ void TextContent::fillJson(QJsonObject* json) const } if (relatesTo) { json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { relatesTo->type, relatesTo->eventId } }); + QJsonObject { { "rel_type", relatesTo->type }, { "event_id", relatesTo->eventId } }); if (relatesTo->type == RelatesTo::ReplacementTypeId()) { QJsonObject newContentJson; if (mimeType.inherits("text/html")) { -- cgit v1.2.3 From 14df474a5032554faf575f8becbac4c9f2bec3e5 Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Mon, 10 Feb 2020 17:25:42 +0100 Subject: Add EventContent::replacementOf() helper --- lib/events/roommessageevent.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib') diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index ded5e572..2501d097 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -105,6 +105,10 @@ namespace EventContent { { return { RelatesTo::ReplyTypeId(), std::move(eventId) }; } + inline RelatesTo replacementOf(QString eventId) + { + return { RelatesTo::ReplacementTypeId(), std::move(eventId) }; + } /** * Rich text content for m.text, m.emote, m.notice -- cgit v1.2.3 From 4f027cf952e2458d2301236d43b4c9fbcdba345b Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Wed, 15 May 2019 20:51:06 +0200 Subject: RoomMemberEvent: add isRejectedInvite(), isBan(), and isUnban() --- lib/events/roommemberevent.cpp | 20 +++++++++++++++++++- lib/events/roommemberevent.h | 3 +++ 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index d4b2be45..35cbdb3a 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -79,6 +79,12 @@ bool RoomMemberEvent::isInvite() const return membership() == MembershipType::Invite && changesMembership(); } +bool RoomMemberEvent::isRejectedInvite() const +{ + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership == MembershipType::Invite; +} + bool RoomMemberEvent::isJoin() const { return membership() == MembershipType::Join && changesMembership(); @@ -88,7 +94,19 @@ bool RoomMemberEvent::isLeave() const { return membership() == MembershipType::Leave && prevContent() && prevContent()->membership != membership() - && prevContent()->membership != MembershipType::Ban; + && prevContent()->membership != MembershipType::Ban + && prevContent()->membership != MembershipType::Invite; +} + +bool RoomMemberEvent::isBan() const +{ + return membership() == MembershipType::Ban && changesMembership(); +} + +bool RoomMemberEvent::isUnban() const +{ + return membership() == MembershipType::Leave && prevContent() + && prevContent()->membership == MembershipType::Ban; } bool RoomMemberEvent::isRename() const diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 0ca439e1..783b8207 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -88,7 +88,10 @@ public: QUrl avatarUrl() const { return content().avatarUrl; } QString reason() const { return content().reason; } bool changesMembership() const; + bool isBan() const; + bool isUnban() const; bool isInvite() const; + bool isRejectedInvite() const; bool isJoin() const; bool isLeave() const; bool isRename() const; -- cgit v1.2.3 From 2de9cb11b4157eebacc3906df79a8d6d4fb43651 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Wed, 14 Aug 2019 12:43:22 +0300 Subject: E2EE: Fix EncryptionManager initialization place Signed-off-by: Alexey Andreev --- lib/connection.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5bddbb83..75c459d5 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -243,15 +243,6 @@ void Connection::doConnectToServer(const QString& user, const QString& password, connect(loginJob, &BaseJob::success, this, [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); - - AccountSettings accountSettings(loginJob->userId()); - d->encryptionManager.reset( - new EncryptionManager(accountSettings.encryptionAccountPickle())); - if (accountSettings.encryptionAccountPickle().isEmpty()) { - accountSettings.setEncryptionAccountPickle( - d->encryptionManager->olmAccountPickle()); - } - d->encryptionManager->uploadIdentityKeys(this); d->encryptionManager->uploadOneTimeKeys(this); }); @@ -309,6 +300,13 @@ void Connection::Private::connectWithToken(const QString& userId, q->setObjectName(userId % '/' % deviceId); qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() << "by user" << userId << "from device" << deviceId; + AccountSettings accountSettings(userId); + encryptionManager.reset( + new EncryptionManager(accountSettings.encryptionAccountPickle())); + if (accountSettings.encryptionAccountPickle().isEmpty()) { + accountSettings.setEncryptionAccountPickle( + encryptionManager->olmAccountPickle()); + } emit q->stateChanged(); emit q->connected(); q->reloadCapabilities(); -- cgit v1.2.3 From 60c0f079f0366e501de6658b5fb56ec905da0c31 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 13:23:42 +0300 Subject: E2EE: introduce RoomKeyEvent Signed-off-by: Alexey Andreev --- lib/events/roomkeyevent.cpp | 11 +++++++++++ lib/events/roomkeyevent.h | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 lib/events/roomkeyevent.cpp create mode 100644 lib/events/roomkeyevent.h (limited to 'lib') diff --git a/lib/events/roomkeyevent.cpp b/lib/events/roomkeyevent.cpp new file mode 100644 index 00000000..1fb2e9f5 --- /dev/null +++ b/lib/events/roomkeyevent.cpp @@ -0,0 +1,11 @@ +#include "roomkeyevent.h" + +using namespace Quotient; + +RoomKeyEvent::RoomKeyEvent(const QJsonObject &obj) : Event(typeId(), obj) +{ + _algorithm = contentJson()["algorithm"_ls].toString(); + _roomId = contentJson()["room_id"_ls].toString(); + _sessionId = contentJson()["session_id"_ls].toString(); + _sessionKey = contentJson()["session_key"_ls].toString(); +} diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h new file mode 100644 index 00000000..e4bcfd71 --- /dev/null +++ b/lib/events/roomkeyevent.h @@ -0,0 +1,25 @@ +#pragma once + +#include "event.h" + +namespace Quotient { +class RoomKeyEvent : public Event +{ +public: + DEFINE_EVENT_TYPEID("m.room_key", RoomKeyEvent) + + RoomKeyEvent(const QJsonObject& obj); + + const QString algorithm() const { return _algorithm; } + const QString roomId() const { return _roomId; } + const QString sessionId() const { return _sessionId; } + const QString sessionKey() const { return _sessionKey; } + +private: + QString _algorithm; + QString _roomId; + QString _sessionId; + QString _sessionKey; +}; +REGISTER_EVENT_TYPE(RoomKeyEvent) +} // namespace Quotient -- cgit v1.2.3 From 283208f8f891aafaaa0ae573bd8b9fcda783da12 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 17:03:14 +0300 Subject: E2EE: implement SyncData::deviceOneTimeKeysCount Signed-off-by: Alexey Andreev --- lib/syncdata.cpp | 7 +++++++ lib/syncdata.h | 5 +++++ 2 files changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 89c512a2..6e68e2cd 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -178,6 +178,13 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) accountData = load(json, "account_data"_ls); toDeviceEvents = load(json, "to_device"_ls); + auto deviceOneTimeKeysCountVariantHash = + json.value("device_one_time_keys_count"_ls).toObject().toVariantHash(); + for (auto key : deviceOneTimeKeysCountVariantHash.keys()) { + deviceOneTimeKeysCount_.insert( + key, deviceOneTimeKeysCountVariantHash.value(key).toInt()); + } + auto rooms = json.value("rooms"_ls).toObject(); JoinStates::Int ii = 1; // ii is used to make a JoinState value auto totalRooms = 0; diff --git a/lib/syncdata.h b/lib/syncdata.h index d55438d7..6e7183ee 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -92,6 +92,10 @@ public: Events&& takePresenceData(); Events&& takeAccountData(); Events&& takeToDeviceEvents(); + const QHash& deviceOneTimeKeysCount() const + { + return deviceOneTimeKeysCount_; + } SyncDataList&& takeRoomData(); QString nextBatch() const { return nextBatch_; } @@ -108,6 +112,7 @@ private: Events toDeviceEvents; SyncDataList roomData; QStringList unresolvedRoomIds; + QHash deviceOneTimeKeysCount_; static QJsonObject loadJson(const QString& fileName); }; -- cgit v1.2.3 From f341e4a3c60cf4a6f6f4c986f8fe68c82feba1dd Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 17:07:36 +0300 Subject: E2EE: EncryptedEvent constructor debug message Signed-off-by: Alexey Andreev --- lib/events/encryptedevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp index b5cedc69..dccfa540 100644 --- a/lib/events/encryptedevent.cpp +++ b/lib/events/encryptedevent.cpp @@ -28,5 +28,5 @@ EncryptedEvent::EncryptedEvent(QByteArray ciphertext, const QString& senderKey, EncryptedEvent::EncryptedEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) { - qCDebug(EVENTS) << "Encrypted event" << id(); + qCDebug(E2EE) << "Encrypted event from" << senderId(); } -- cgit v1.2.3 From 3c293b287466e8de188fc62e73efc4730ab4dd31 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 17:10:01 +0300 Subject: E2EE: fix olm session decrypt, move to EncryptionManager Signed-off-by: Alexey Andreev --- lib/encryptionmanager.cpp | 175 ++++++++++++++++++++++++++++++++++++++++++---- lib/encryptionmanager.h | 7 ++ 2 files changed, 167 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 22387cf9..e2834c45 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -9,6 +9,10 @@ #include #include // QtOlm +#include // QtOlm +#include // QtOlm +#include // QtOlm +#include // QtOlm #include #include @@ -20,7 +24,8 @@ class EncryptionManager::Private { public: explicit Private(const QByteArray& encryptionAccountPickle, float signedKeysProportion, float oneTimeKeyThreshold) - : signedKeysProportion(move(signedKeysProportion)) + : q(nullptr) + , signedKeysProportion(move(signedKeysProportion)) , oneTimeKeyThreshold(move(oneTimeKeyThreshold)) { Q_ASSERT((0 <= signedKeysProportion) && (signedKeysProportion <= 1)); @@ -44,18 +49,23 @@ public: * until the limit is reached and it starts discarding keys, starting by * the oldest. */ - targetKeysNumber = olmAccount->maxOneTimeKeys(); // 2 // see note below + targetKeysNumber = olmAccount->maxOneTimeKeys() / 2; targetOneTimeKeyCounts = { { SignedCurve25519Key, qRound(signedKeysProportion * targetKeysNumber) }, { Curve25519Key, qRound((1 - signedKeysProportion) * targetKeysNumber) } }; + updateKeysToUpload(); } ~Private() = default; + EncryptionManager* q; + UploadKeysJob* uploadIdentityKeysJob = nullptr; + UploadKeysJob* uploadOneTimeKeysInitJob = nullptr; UploadKeysJob* uploadOneTimeKeysJob = nullptr; + QueryKeysJob* queryKeysJob = nullptr; QScopedPointer olmAccount; @@ -74,6 +84,95 @@ public: } QHash oneTimeKeysToUploadCounts; QHash targetOneTimeKeyCounts; + + // A map from senderKey to InboundSession + QMap sessions; // TODO: cache + void updateDeviceKeys( + const QHash>& + deviceKeys) + { + for (auto userId : deviceKeys.keys()) { + for (auto deviceId : deviceKeys.value(userId).keys()) { + QueryKeysJob::DeviceInformation info = + deviceKeys.value(userId).value(deviceId); + // TODO: ed25519Verify, etc + } + } + } + QString sessionDecrypt(Message* message, const QString& senderKey) + { + QString decrypted; + QList senderSessions = sessions.values(senderKey); + // Try to decrypt message body using one of the known sessions for that + // device + bool sessionsPassed = false; + for (auto senderSession : senderSessions) { + if (senderSession == senderSessions.last()) { + sessionsPassed = true; + } + try { + decrypted = senderSession->decrypt(message); + qCDebug(E2EE) + << "Success decrypting Olm event using existing session" + << senderSession->id(); + break; + } catch (OlmError* e) { + if (message->messageType() == 0) { + PreKeyMessage preKeyMessage = + PreKeyMessage(message->cipherText()); + if (senderSession->matches(&preKeyMessage, senderKey)) { + // We had a matching session for a pre-key message, but + // it didn't work. This means something is wrong, so we + // fail now. + qCDebug(E2EE) + << "Error decrypting pre-key message with existing " + "Olm session" + << senderSession->id() << "reason:" << e->what(); + return QString(); + } + } + // Simply keep trying otherwise + } + } + if (sessionsPassed || senderSessions.empty()) { + if (message->messageType() > 0) { + // Not a pre-key message, we should have had a matching session + if (!sessions.empty()) { + qCDebug(E2EE) << "Error decrypting with existing sessions"; + return QString(); + } + qCDebug(E2EE) << "No existing sessions"; + return QString(); + } + // We have a pre-key message without any matching session, in this + // case we should try to create one. + InboundSession* newSession; + qCDebug(E2EE) << "try to establish new InboundSession with" << senderKey; + PreKeyMessage preKeyMessage = PreKeyMessage(message->cipherText()); + try { + newSession = new InboundSession(olmAccount.data(), + &preKeyMessage, + senderKey.toLatin1(), q); + } catch (OlmError* e) { + qCDebug(E2EE) << "Error decrypting pre-key message when trying " + "to establish a new session:" + << e->what(); + return QString(); + } + qCDebug(E2EE) << "Created new Olm session" << newSession->id(); + try { + decrypted = newSession->decrypt(message); + } catch (OlmError* e) { + qCDebug(E2EE) + << "Error decrypting pre-key message with new session" + << e->what(); + return QString(); + } + olmAccount->removeOneTimeKeys(newSession); + sessions.insert(senderKey, newSession); + } + return decrypted; + } }; EncryptionManager::EncryptionManager(const QByteArray& encryptionAccountPickle, @@ -83,7 +182,9 @@ EncryptionManager::EncryptionManager(const QByteArray& encryptionAccountPickle, , d(std::make_unique(std::move(encryptionAccountPickle), std::move(signedKeysProportion), std::move(oneTimeKeyThreshold))) -{} +{ + d->q = this; +} EncryptionManager::~EncryptionManager() = default; @@ -132,20 +233,19 @@ void EncryptionManager::uploadIdentityKeys(Connection* connection) d->olmAccount->sign(deviceKeysJsonObject) } } } }; + d->uploadIdentityKeysJob = connection->callApi(deviceKeys); connect(d->uploadIdentityKeysJob, &BaseJob::success, this, [this] { d->setOneTimeKeyCounts(d->uploadIdentityKeysJob->oneTimeKeyCounts()); - qDebug() << QString("Uploaded identity keys."); }); - d->uploadIdentityKeysJob = connection->callApi(deviceKeys); } void EncryptionManager::uploadOneTimeKeys(Connection* connection, bool forceUpdate) { if (forceUpdate || d->oneTimeKeyCounts.isEmpty()) { - auto job = connection->callApi(); - connect(job, &BaseJob::success, this, [job, this] { - d->setOneTimeKeyCounts(job->oneTimeKeyCounts()); + d->uploadOneTimeKeysInitJob = connection->callApi(); + connect(d->uploadOneTimeKeysInitJob, &BaseJob::success, this, [this] { + d->setOneTimeKeyCounts(d->uploadIdentityKeysJob->oneTimeKeyCounts()); }); } @@ -170,9 +270,17 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection, if (oneTimeKeysCounter < signedKeysToUploadCount) { QJsonObject message { { QStringLiteral("key"), it.value().toString() } }; - key = d->olmAccount->sign(message); - keyType = SignedCurve25519Key; + QByteArray signedMessage = d->olmAccount->sign(message); + QJsonObject signatures { + { connection->userId(), + QJsonObject { { Ed25519Key + QStringLiteral(":") + + connection->deviceId(), + QString::fromUtf8(signedMessage) } } } + }; + message.insert(QStringLiteral("signatures"), signatures); + key = message; + keyType = SignedCurve25519Key; } else { key = it.value(); keyType = Curve25519Key; @@ -180,13 +288,50 @@ void EncryptionManager::uploadOneTimeKeys(Connection* connection, ++oneTimeKeysCounter; oneTimeKeys.insert(QString("%1:%2").arg(keyType).arg(keyId), key); } - - d->uploadOneTimeKeysJob = connection->callApi(none, - oneTimeKeys); + d->uploadOneTimeKeysJob = + connection->callApi(none, oneTimeKeys); + connect(d->uploadOneTimeKeysJob, &BaseJob::success, this, [this] { + d->setOneTimeKeyCounts(d->uploadOneTimeKeysJob->oneTimeKeyCounts()); + }); d->olmAccount->markKeysAsPublished(); - qDebug() << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.") + qCDebug(E2EE) << QString("Uploaded new one-time keys: %1 signed, %2 unsigned.") .arg(signedKeysToUploadCount) - .arg(unsignedKeysToUploadCount); + .arg(unsignedKeysToUploadCount); +} + +void EncryptionManager::updateOneTimeKeyCounts( + Connection* connection, const QHash& deviceOneTimeKeysCount) +{ + d->oneTimeKeyCounts = deviceOneTimeKeysCount; + if (d->oneTimeKeyShouldUpload()) { + qCDebug(E2EE) << "Uploading new one-time keys."; + uploadOneTimeKeys(connection); + } +} + +void Quotient::EncryptionManager::updateDeviceKeys( + Connection* connection, const QHash& deviceKeys) +{ + d->queryKeysJob = connection->callApi(deviceKeys); + connect(d->queryKeysJob, &BaseJob::success, this, + [this] { d->updateDeviceKeys(d->queryKeysJob->deviceKeys()); }); +} + +QString EncryptionManager::sessionDecryptMessage( + const QJsonObject& personalCipherObject, const QByteArray& senderKey) +{ + QString decrypted; + int type = personalCipherObject.value(TypeKeyL).toInt(-1); + QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1(); + if (type == 0) { + PreKeyMessage preKeyMessage { body }; + decrypted = d->sessionDecrypt(reinterpret_cast(&preKeyMessage), + senderKey); + } else if (type == 1) { + Message message { body }; + decrypted = d->sessionDecrypt(&message, senderKey); + } + return decrypted; } QByteArray EncryptionManager::olmAccountPickle() diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index b210a85a..8f346d37 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -26,6 +26,13 @@ public: void uploadIdentityKeys(Connection* connection); void uploadOneTimeKeys(Connection* connection, bool forceUpdate = false); + void + updateOneTimeKeyCounts(Connection* connection, + const QHash& deviceOneTimeKeysCount); + void updateDeviceKeys(Connection* connection, + const QHash& deviceKeys); + QString sessionDecryptMessage(const QJsonObject& personalCipherObject, + const QByteArray& senderKey); QByteArray olmAccountPickle(); QtOlm::Account* account() const; -- cgit v1.2.3 From 715a0c0aa094eedbe24516e19a7b37dde71ba994 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 17:14:09 +0300 Subject: E2EE: add connection session decrypt, handle to-device and device_one_time_keys_count Signed-off-by: Alexey Andreev --- lib/connection.cpp | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/room.cpp | 5 +++ lib/room.h | 2 + 3 files changed, 119 insertions(+) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 75c459d5..98c8a4bc 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -43,6 +43,8 @@ #include "jobs/mediathumbnailjob.h" #include "jobs/syncjob.h" +#include "account.h" // QtOlm + #include #include #include @@ -148,6 +150,65 @@ public: { return q->stateCacheDir().filePath("state.json"); } + + RoomEventPtr sessionDecryptMessage(const EncryptedEvent& encryptedEvent) + { + if (encryptedEvent.algorithm() != OlmV1Curve25519AesSha2AlgoKey) + { + return {}; + } + QString identityKey = + encryptionManager->account()->curve25519IdentityKey(); + QJsonObject personalCipherObject = + encryptedEvent.ciphertext(identityKey); + if (personalCipherObject.isEmpty()) { + qCDebug(E2EE) << "Encrypted event is not for the current device"; + return {}; + } + QString decrypted = encryptionManager->sessionDecryptMessage( + personalCipherObject, encryptedEvent.senderKey().toLatin1()); + if (decrypted.isEmpty()) { + qCDebug(E2EE) << "Problem with new session from senderKey:" + << encryptedEvent.senderKey() + << encryptionManager->account()->oneTimeKeys(); + return {}; + } + + RoomEventPtr decryptedEvent = makeEvent( + QJsonDocument::fromJson(decrypted.toUtf8()).object()); + + if (decryptedEvent->senderId() != encryptedEvent.senderId()) { + qCDebug(E2EE) << "Found user" << decryptedEvent->senderId() + << "instead of sender" << encryptedEvent.senderId() + << "in Olm plaintext"; + return {}; + } + + // TODO: keys to constants + QJsonObject decryptedEventObject = decryptedEvent->fullJson(); + QString recipient = + decryptedEventObject.value("recipient"_ls).toString(); + if (recipient != data->userId()) { + qCDebug(E2EE) << "Found user" << recipient << "instead of us" + << data->userId() << "in Olm plaintext"; + return {}; + } + QString ourKey = decryptedEventObject.value("recipient_keys"_ls) + .toObject() + .value(Ed25519Key) + .toString(); + if (ourKey + != QString::fromUtf8( + encryptionManager->account()->ed25519IdentityKey())) { + qCDebug(E2EE) << "Found key" << ourKey + << "instead of ours own ed25519 key" + << encryptionManager->account()->ed25519IdentityKey() + << "in Olm plaintext"; + return {}; + } + + return decryptedEvent; + } }; Connection::Connection(const QUrl& server, QObject* parent) @@ -533,6 +594,57 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) d->dcLocalAdditions.clear(); d->dcLocalRemovals.clear(); } + // handling m.room_key to-device encrypted event + for (auto&& toDeviceEvent : data.takeToDeviceEvents()) { + if (toDeviceEvent->type() == EncryptedEvent::typeId()) { + event_ptr_tt encryptedEvent = + makeEvent(toDeviceEvent->fullJson()); + if (encryptedEvent->algorithm() != OlmV1Curve25519AesSha2AlgoKey) { + qCDebug(E2EE) + << "Encrypted event" << encryptedEvent->id() << "algorithm" + << encryptedEvent->algorithm() << "is not supported"; + return; + } + + // TODO: full maintaining of the device keys + // with device_lists sync extention and /keys/query + qCDebug(E2EE) << "Getting device keys for the m.room_key sender:" + << encryptedEvent->senderId(); + // d->encryptionManager->updateDeviceKeys(); + + RoomEventPtr decryptedEvent = + d->sessionDecryptMessage(*encryptedEvent.get()); + // since we are waiting for the RoomKeyEvent: + event_ptr_tt roomKeyEvent = + makeEvent(decryptedEvent->fullJson()); + if (!roomKeyEvent) { + qCDebug(E2EE) << "Failed to decrypt olm event from user" + << encryptedEvent->senderId(); + return; + } + Room* detectedRoom = room(roomKeyEvent->roomId()); + if (!detectedRoom) { + qCDebug(E2EE) + << "Encrypted event room id" << encryptedEvent->roomId() + << "is not found at the connection"; + return; + } + detectedRoom->handleRoomKeyEvent(roomKeyEvent.get(), + encryptedEvent->senderKey()); + } + } + // handling device_one_time_keys_count + auto deviceOneTimeKeysCount = data.deviceOneTimeKeysCount(); + if (!d->encryptionManager) + { + qCDebug(E2EE) << "Encryption manager is not there yet"; + return; + } + if (!deviceOneTimeKeysCount.isEmpty()) + { + d->encryptionManager->updateOneTimeKeyCounts(this, + deviceOneTimeKeysCount); + } } void Connection::stopSync() diff --git a/lib/room.cpp b/lib/room.cpp index b29f6f48..45c1be8b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1233,6 +1233,11 @@ QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, return decrypted; } +void Room::handleRoomKeyEvent(RoomKeyEvent *roomKeyEvent, QString senderKey) +{ + // TODO +} + int Room::joinedCount() const { return d->summary.joinedMemberCount.value_or(d->membersMap.size()); diff --git a/lib/room.h b/lib/room.h index ad19792e..2243ec6b 100644 --- a/lib/room.h +++ b/lib/room.h @@ -26,6 +26,7 @@ #include "events/accountdataevents.h" #include "events/encryptedevent.h" +#include "events/roomkeyevent.h" #include "events/roommessageevent.h" #include "events/roomcreateevent.h" #include "events/roomtombstoneevent.h" @@ -215,6 +216,7 @@ public: const QString& sessionId) const; QString decryptMessage(QByteArray cipher, const QString& senderKey, const QString& deviceId, const QString& sessionId); + void handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey); int joinedCount() const; int invitedCount() const; int totalMemberCount() const; -- cgit v1.2.3 From 6a3cd81b5179a38b5731f09b62f56b24ada7ff0f Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 17:15:44 +0300 Subject: E2EE: implement megolm inbound session decrypt for room Signed-off-by: Alexey Andreev --- lib/room.cpp | 256 ++++++++++++++++++++++++++++++++--------------------------- lib/room.h | 8 +- 2 files changed, 140 insertions(+), 124 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 45c1be8b..11a50dde 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -68,11 +68,13 @@ #include #include #include + +#include // QtOlm +#include // QtOlm #include // QtOlm -#include // QtOlm -#include // QtOlm using namespace Quotient; +using namespace QtOlm; using namespace std::placeholders; using std::move; #if !(defined __GLIBCXX__ && __GLIBCXX__ <= 20150123) @@ -340,6 +342,89 @@ public: QJsonObject toJson() const; + // A map from to + QHash, QPair> + groupSessionIndexRecord; // TODO: cache + // A map from senderKey to a map of sessionId to InboundGroupSession + // Not using QMultiHash, because we want to quickly return + // a number of relations for a given event without enumerating them. + QHash, InboundGroupSession*> groupSessions; // TODO: + // cache + bool addInboundGroupSession(QString senderKey, QString sessionId, + QString sessionKey) + { + if (groupSessions.contains({ senderKey, sessionId })) { + qCDebug(E2EE) << "Inbound Megolm session" << sessionId + << "with senderKey" << senderKey << "already exists"; + return false; + } + + InboundGroupSession* megolmSession; + try { + megolmSession = new InboundGroupSession(sessionKey.toLatin1(), + InboundGroupSession::Init, + q); + } catch (OlmError* e) { + qCDebug(E2EE) << "Unable to create new InboundGroupSession" + << e->what(); + return false; + } + if (megolmSession->id() != sessionId) { + qCDebug(E2EE) << "Session ID mismatch in m.room_key event sent " + "from sender with key" + << senderKey; + return false; + } + groupSessions.insert({ senderKey, sessionId }, megolmSession); + return true; + } + + QString groupSessionDecryptMessage(QByteArray cipher, + const QString& senderKey, + const QString& sessionId, + const QString& eventId, + QDateTime timestamp) + { + std::pair decrypted; + QPair senderSessionPairKey = + qMakePair(senderKey, sessionId); + if (!groupSessions.contains(senderSessionPairKey)) { + qCDebug(E2EE) << "Unable to decrypt event" << eventId + << "The sender's device has not sent us the keys for " + "this message"; + return QString(); + } + InboundGroupSession* senderSession = + groupSessions.value(senderSessionPairKey); + if (!senderSession) { + qCDebug(E2EE) << "Unable to decrypt event" << eventId + << "senderSessionPairKey:" << senderSessionPairKey; + return QString(); + } + try { + decrypted = senderSession->decrypt(cipher); + } catch (OlmError* e) { + qCDebug(E2EE) << "Unable to decrypt event" << eventId + << "with matching megolm session:" << e->what(); + return QString(); + } + QPair properties = groupSessionIndexRecord.value( + qMakePair(senderSession->id(), decrypted.second)); + if (properties.first.isEmpty()) { + groupSessionIndexRecord.insert(qMakePair(senderSession->id(), + decrypted.second), + qMakePair(eventId, timestamp)); + } else { + if ((properties.first != eventId) + || (properties.second != timestamp)) { + qCDebug(E2EE) << "Detected a replay attack on event" << eventId; + return QString(); + } + } + + return decrypted.first; + } + private: using users_shortlist_t = std::array; template @@ -502,8 +587,8 @@ void Room::setJoinState(JoinState state) if (state == oldState) return; d->joinState = state; - qCDebug(STATE) << "Room" << id() << "changed state: " << int(oldState) - << "->" << int(state); + qCDebug(MAIN) << "Room" << id() << "changed state: " << int(oldState) + << "->" << int(state); emit changed(Change::JoinStateChange); emit joinStateChanged(oldState, state); } @@ -559,12 +644,12 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) unreadMessages = 0; unreadMessages += newUnreadMessages; - qCDebug(MESSAGES) << "Room" << q->objectName() << "has gained" - << newUnreadMessages << "unread message(s)," - << (q->readMarker() == timeline.crend() - ? "in total at least" - : "in total") - << unreadMessages << "unread message(s)"; + qCDebug(MAIN) << "Room" << q->objectName() << "has gained" + << newUnreadMessages << "unread message(s)," + << (q->readMarker() == timeline.crend() + ? "in total at least" + : "in total") + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); } } @@ -606,11 +691,11 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, if (force || unreadMessages != oldUnreadCount) { if (unreadMessages == -1) { - qCDebug(MESSAGES) + qCDebug(MAIN) << "Room" << displayname << "has no more unread messages"; } else - qCDebug(MESSAGES) << "Room" << displayname << "still has" - << unreadMessages << "unread message(s)"; + qCDebug(MAIN) << "Room" << displayname << "still has" + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); changes |= Change::UnreadNotifsChange; } @@ -623,7 +708,7 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) const auto prevMarker = q->readMarker(); auto changes = promoteReadMarker(q->localUser(), upToMarker); if (prevMarker != upToMarker) - qCDebug(MESSAGES) << "Marked messages as read until" << *q->readMarker(); + qCDebug(MAIN) << "Marked messages as read until" << *q->readMarker(); // We shouldn't send read receipts for the local user's own messages - so // search earlier messages for the latest message not from the local user @@ -1151,91 +1236,35 @@ const StateEventBase* Room::getCurrentState(const QString& evtType, return d->getCurrentState({ evtType, stateKey }); } -RoomEventPtr Room::decryptMessage(EncryptedEvent* encryptedEvent) +RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) { - if (encryptedEvent->algorithm() == OlmV1Curve25519AesSha2AlgoKey) { - QString identityKey = - connection()->olmAccount()->curve25519IdentityKey(); - QJsonObject personalCipherObject = - encryptedEvent->ciphertext(identityKey); - if (personalCipherObject.isEmpty()) { - qCDebug(E2EE) << "Encrypted event is not for the current device"; + if (encryptedEvent.algorithm() == MegolmV1AesSha2AlgoKey) { + QString decrypted = d->groupSessionDecryptMessage( + encryptedEvent.ciphertext(), encryptedEvent.senderKey(), + encryptedEvent.sessionId(), encryptedEvent.id(), + encryptedEvent.timestamp()); + if (decrypted.isEmpty()) { return {}; } - return makeEvent(decryptMessage( - personalCipherObject, encryptedEvent->senderKey().toLatin1())); - } - if (encryptedEvent->algorithm() == MegolmV1AesSha2AlgoKey) { - return makeEvent(decryptMessage( - encryptedEvent->ciphertext(), encryptedEvent->senderKey(), - encryptedEvent->deviceId(), encryptedEvent->sessionId())); + return makeEvent( + QJsonDocument::fromJson(decrypted.toUtf8()).object()); } + qCDebug(E2EE) << "Algorithm of the encrypted event with id" + << encryptedEvent.id() << "is not for the current device"; return {}; } -QString Room::decryptMessage(QJsonObject personalCipherObject, - QByteArray senderKey) +void Room::handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey) { - QString decrypted; - - using namespace QtOlm; - // TODO: new objects to private fields: - InboundSession* session; - - int type = personalCipherObject.value(TypeKeyL).toInt(-1); - QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1(); - - PreKeyMessage preKeyMessage { body }; - session = - new InboundSession(connection()->olmAccount(), &preKeyMessage, senderKey, this); - if (type == 0) { - if (!session->matches(&preKeyMessage, senderKey)) { - connection()->olmAccount()->removeOneTimeKeys(session); - } - try { - decrypted = session->decrypt(&preKeyMessage); - } catch (std::runtime_error& e) { - qCWarning(EVENTS) << "Decrypt failed:" << e.what(); - } + if (roomKeyEvent->algorithm() != MegolmV1AesSha2AlgoKey) { + qCWarning(E2EE) << "Ignoring unsupported algorithm" + << roomKeyEvent->algorithm() << "in m.room_key event"; } - else if (type == 1) { - Message message { body }; - if (!session->matches(&preKeyMessage, senderKey)) { - qCWarning(EVENTS) << "Invalid encrypted message"; - } - try { - decrypted = session->decrypt(&message); - } catch (std::runtime_error& e) { - qCWarning(EVENTS) << "Decrypt failed:" << e.what(); - } + if (d->addInboundGroupSession(senderKey, roomKeyEvent->sessionId(), + roomKeyEvent->sessionKey())) { + qCDebug(E2EE) << "added new inboundGroupSession:" + << d->groupSessions.count(); } - - return decrypted; -} - -QString Room::sessionKey(const QString& senderKey, const QString& deviceId, - const QString& sessionId) const -{ - // TODO: handling an m.room_key event - return ""; -} - -QString Room::decryptMessage(QByteArray cipher, const QString& senderKey, - const QString& deviceId, const QString& sessionId) -{ - QString decrypted; - using namespace QtOlm; - InboundGroupSession* groupSession; - groupSession = new InboundGroupSession( - sessionKey(senderKey, deviceId, sessionId).toLatin1()); - groupSession->decrypt(cipher); - // TODO: avoid replay attacks - return decrypted; -} - -void Room::handleRoomKeyEvent(RoomKeyEvent *roomKeyEvent, QString senderKey) -{ - // TODO } int Room::joinedCount() const @@ -1428,20 +1457,17 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { - qCDebug(MESSAGES) << "Setting unread_count to" << data.unreadCount; + qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; d->unreadMessages = data.unreadCount; - roomChanges |= Change::UnreadNotifsChange; emit unreadMessagesChanged(this); } if (data.highlightCount != d->highlightCount) { d->highlightCount = data.highlightCount; - roomChanges |= Change::UnreadNotifsChange; emit highlightCountChanged(); } if (data.notificationCount != d->notificationCount) { d->notificationCount = data.notificationCount; - roomChanges |= Change::UnreadNotifsChange; emit notificationCountChanged(); } if (roomChanges != Change::NoChange) { @@ -1544,13 +1570,12 @@ QString Room::retryMessage(const QString& txnId) if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); if (transferIt->status == FileTransferInfo::Completed) { - qCDebug(MESSAGES) - << "File for transaction" << txnId - << "has already been uploaded, bypassing re-upload"; + qCDebug(MAIN) << "File for transaction" << txnId + << "has already been uploaded, bypassing re-upload"; } else { if (isJobRunning(transferIt->job)) { - qCDebug(MESSAGES) << "Abandoning the upload job for transaction" - << txnId << "and starting again"; + qCDebug(MAIN) << "Abandoning the upload job for transaction" + << txnId << "and starting again"; transferIt->job->abandon(); emit fileTransferFailed(txnId, tr("File upload will be retried")); @@ -2036,15 +2061,15 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->isRedacted() && ti->redactedBecause()->id() == redaction.id()) { - qCDebug(EVENTS) << "Redaction" << redaction.id() << "of event" - << ti->id() << "already done, skipping"; + qCDebug(MAIN) << "Redaction" << redaction.id() << "of event" << ti->id() + << "already done, skipping"; return true; } // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeRedacted(*ti, redaction)); - qCDebug(EVENTS) << "Redacted" << oldEvent->id() << "with" << redaction.id(); + qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction.id(); if (oldEvent->isStateEvent()) { const StateEventKey evtKey { oldEvent->matrixType(), oldEvent->stateKey() }; @@ -2052,7 +2077,7 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) if (currentState.value(evtKey) == oldEvent.get()) { Q_ASSERT(ti.index() >= 0); // Historical states can't be in // currentState - qCDebug(EVENTS).nospace() + qCDebug(MAIN).nospace() << "Redacting state " << oldEvent->matrixType() << "/" << oldEvent->stateKey(); // Retarget the current state to the newly made event. @@ -2106,7 +2131,7 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->replacedBy() == newEvent.id()) { - qCDebug(EVENTS) << "Event" << ti->id() << "is already replaced with" + qCDebug(MAIN) << "Event" << ti->id() << "is already replaced with" << newEvent.id(); return true; } @@ -2114,7 +2139,7 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeReplaced(*ti, newEvent)); - qCDebug(EVENTS) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); + qCDebug(MAIN) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); return true; } @@ -2163,7 +2188,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) }); targetIt != events.end()) *targetIt = makeRedacted(**targetIt, *r); else - qCDebug(EVENTS) + qCDebug(MAIN) << "Redaction" << r->id() << "ignored: target event" << r->redactedEvent() << "is not found"; // If the target event comes later, it comes already redacted. @@ -2263,9 +2288,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) } } - qCDebug(MESSAGES) << "Room" << q->objectName() << "received" - << totalInserted << "new events; the last event is now" - << timeline.back(); + qCDebug(MAIN) << "Room" << q->objectName() << "received" << totalInserted + << "new events; the last event is now" << timeline.back(); // The first event in the just-added batch (referred to by `from`) // defines whose read marker can possibly be promoted any further over @@ -2276,9 +2300,8 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto firstWriter = q->user((*from)->senderId()); if (q->readMarker(firstWriter) != timeline.crend()) { roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(MESSAGES) - << "Auto-promoted read marker for" << firstWriter->id() << "to" - << *q->readMarker(firstWriter); + qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() + << "to" << *q->readMarker(firstWriter); } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); @@ -2315,9 +2338,8 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) const auto insertedSize = moveEventsToTimeline(events, Older); const auto from = timeline.crend() - insertedSize; - qCDebug(MESSAGES) << "Room" << displayname << "received" << insertedSize - << "past events; the oldest event is now" - << timeline.front(); + qCDebug(MAIN) << "Room" << displayname << "received" << insertedSize + << "past events; the oldest event is now" << timeline.front(); q->onAddHistoricalTimelineEvents(from); emit q->addedMessages(timeline.front().index(), from->index()); @@ -2412,7 +2434,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) break; case MembershipType::Join: if (evt.membership() == MembershipType::Invite) - qCWarning(STATE) << "Invalid membership change from " + qCWarning(MAIN) << "Invalid membership change from " "Join to Invite:" << evt; if (evt.membership() != prevMembership) { @@ -2559,7 +2581,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (auto* evt = eventCast(event)) { auto readEventId = evt->event_id(); - qCDebug(STATE) << "Server-side read marker at" << readEventId; + qCDebug(MAIN) << "Server-side read marker at" << readEventId; d->serverReadMarker = readEventId; const auto newMarker = findInTimeline(readEventId); changes |= newMarker != timelineEdge() @@ -2573,7 +2595,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (!currentData || currentData->contentJson() != event->contentJson()) { emit accountDataAboutToChange(event->matrixType()); currentData = move(event); - qCDebug(STATE) << "Updated account data of type" + qCDebug(MAIN) << "Updated account data of type" << currentData->matrixType(); emit accountDataChanged(currentData->matrixType()); return Change::AccountDataChange; diff --git a/lib/room.h b/lib/room.h index 2243ec6b..d78a7bfc 100644 --- a/lib/room.h +++ b/lib/room.h @@ -209,13 +209,7 @@ public: int memberCount() const; int timelineSize() const; bool usesEncryption() const; - RoomEventPtr decryptMessage(EncryptedEvent* encryptedEvent); - QString decryptMessage(QJsonObject personalCipherObject, - QByteArray senderKey); - QString sessionKey(const QString& senderKey, const QString& deviceId, - const QString& sessionId) const; - QString decryptMessage(QByteArray cipher, const QString& senderKey, - const QString& deviceId, const QString& sessionId); + RoomEventPtr decryptMessage(const EncryptedEvent& encryptedEvent); void handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey); int joinedCount() const; int invitedCount() const; -- cgit v1.2.3 From 8ff15ee94fc39eef1279d479d12b1cbdec9f4ac5 Mon Sep 17 00:00:00 2001 From: Alexey Andreyev Date: Fri, 23 Aug 2019 18:00:53 +0300 Subject: Room: fix merge regression Signed-off-by: Alexey Andreev --- lib/room.cpp | 90 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 47 insertions(+), 43 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 11a50dde..ecb5a7ad 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -449,7 +449,7 @@ Room::Room(Connection* connection, QString id, JoinState initialJoinState) emit baseStateLoaded(); return this == r; // loadedRoomState fires only once per room }); - qCDebug(MAIN) << "New" << toCString(initialJoinState) << "Room:" << id; + qCDebug(STATE) << "New" << toCString(initialJoinState) << "Room:" << id; } Room::~Room() { delete d; } @@ -587,8 +587,8 @@ void Room::setJoinState(JoinState state) if (state == oldState) return; d->joinState = state; - qCDebug(MAIN) << "Room" << id() << "changed state: " << int(oldState) - << "->" << int(state); + qCDebug(STATE) << "Room" << id() << "changed state: " << int(oldState) + << "->" << int(state); emit changed(Change::JoinStateChange); emit joinStateChanged(oldState, state); } @@ -644,12 +644,12 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) unreadMessages = 0; unreadMessages += newUnreadMessages; - qCDebug(MAIN) << "Room" << q->objectName() << "has gained" - << newUnreadMessages << "unread message(s)," - << (q->readMarker() == timeline.crend() - ? "in total at least" - : "in total") - << unreadMessages << "unread message(s)"; + qCDebug(MESSAGES) << "Room" << q->objectName() << "has gained" + << newUnreadMessages << "unread message(s)," + << (q->readMarker() == timeline.crend() + ? "in total at least" + : "in total") + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); } } @@ -691,11 +691,11 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, if (force || unreadMessages != oldUnreadCount) { if (unreadMessages == -1) { - qCDebug(MAIN) + qCDebug(MESSAGES) << "Room" << displayname << "has no more unread messages"; } else - qCDebug(MAIN) << "Room" << displayname << "still has" - << unreadMessages << "unread message(s)"; + qCDebug(MESSAGES) << "Room" << displayname << "still has" + << unreadMessages << "unread message(s)"; emit q->unreadMessagesChanged(q); changes |= Change::UnreadNotifsChange; } @@ -708,7 +708,7 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) const auto prevMarker = q->readMarker(); auto changes = promoteReadMarker(q->localUser(), upToMarker); if (prevMarker != upToMarker) - qCDebug(MAIN) << "Marked messages as read until" << *q->readMarker(); + qCDebug(MESSAGES) << "Marked messages as read until" << *q->readMarker(); // We shouldn't send read receipts for the local user's own messages - so // search earlier messages for the latest message not from the local user @@ -1287,7 +1287,7 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) { if (!summary.merge(newSummary)) return Change::NoChange; - qCDebug(MAIN).nospace().noquote() + qCDebug(STATE).nospace().noquote() << "Updated room summary for " << q->objectName() << ": " << summary; emit q->memberListChanged(); return Change::SummaryChange; @@ -1457,7 +1457,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) // See https://github.com/quotient-im/libQuotient/wiki/unread_count if (data.unreadCount != -2 && data.unreadCount != d->unreadMessages) { - qCDebug(MAIN) << "Setting unread_count to" << data.unreadCount; + qCDebug(MESSAGES) << "Setting unread_count to" << data.unreadCount; d->unreadMessages = data.unreadCount; emit unreadMessagesChanged(this); } @@ -1570,12 +1570,13 @@ QString Room::retryMessage(const QString& txnId) if (transferIt != d->fileTransfers.end()) { Q_ASSERT(transferIt->isUpload); if (transferIt->status == FileTransferInfo::Completed) { - qCDebug(MAIN) << "File for transaction" << txnId - << "has already been uploaded, bypassing re-upload"; + qCDebug(MESSAGES) + << "File for transaction" << txnId + << "has already been uploaded, bypassing re-upload"; } else { if (isJobRunning(transferIt->job)) { - qCDebug(MAIN) << "Abandoning the upload job for transaction" - << txnId << "and starting again"; + qCDebug(MESSAGES) << "Abandoning the upload job for transaction" + << txnId << "and starting again"; transferIt->job->abandon(); emit fileTransferFailed(txnId, tr("File upload will be retried")); @@ -1767,10 +1768,11 @@ void Room::checkVersion() // or the server capabilities have been loaded. emit stabilityUpdated(defaultVersion, stableVersions); if (!stableVersions.contains(version())) { - qCDebug(MAIN) << this << "version is" << version() - << "which the server doesn't count as stable"; + qCDebug(STATE) << this << "version is" << version() + << "which the server doesn't count as stable"; if (canSwitchVersions()) - qCDebug(MAIN) << "The current user has enough privileges to fix it"; + qCDebug(STATE) + << "The current user has enough privileges to fix it"; } } @@ -2061,15 +2063,15 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->isRedacted() && ti->redactedBecause()->id() == redaction.id()) { - qCDebug(MAIN) << "Redaction" << redaction.id() << "of event" << ti->id() - << "already done, skipping"; + qCDebug(EVENTS) << "Redaction" << redaction.id() << "of event" + << ti->id() << "already done, skipping"; return true; } // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeRedacted(*ti, redaction)); - qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction.id(); + qCDebug(EVENTS) << "Redacted" << oldEvent->id() << "with" << redaction.id(); if (oldEvent->isStateEvent()) { const StateEventKey evtKey { oldEvent->matrixType(), oldEvent->stateKey() }; @@ -2077,7 +2079,7 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) if (currentState.value(evtKey) == oldEvent.get()) { Q_ASSERT(ti.index() >= 0); // Historical states can't be in // currentState - qCDebug(MAIN).nospace() + qCDebug(STATE).nospace() << "Redacting state " << oldEvent->matrixType() << "/" << oldEvent->stateKey(); // Retarget the current state to the newly made event. @@ -2131,15 +2133,15 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) auto& ti = timeline[Timeline::size_type(*pIdx - q->minTimelineIndex())]; if (ti->replacedBy() == newEvent.id()) { - qCDebug(MAIN) << "Event" << ti->id() << "is already replaced with" - << newEvent.id(); + qCDebug(STATE) << "Event" << ti->id() << "is already replaced with" + << newEvent.id(); return true; } // Make a new event from the redacted JSON and put it in the timeline // instead of the redacted one. oldEvent will be deleted on return. auto oldEvent = ti.replaceEvent(makeReplaced(*ti, newEvent)); - qCDebug(MAIN) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); + qCDebug(STATE) << "Replaced" << oldEvent->id() << "with" << newEvent.id(); emit q->replacedEvent(ti.event(), rawPtr(oldEvent)); return true; } @@ -2188,7 +2190,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) }); targetIt != events.end()) *targetIt = makeRedacted(**targetIt, *r); else - qCDebug(MAIN) + qCDebug(STATE) << "Redaction" << r->id() << "ignored: target event" << r->redactedEvent() << "is not found"; // If the target event comes later, it comes already redacted. @@ -2227,10 +2229,10 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) size_t totalInserted = 0; for (auto it = events.begin(); it != events.end();) { auto nextPendingPair = - findFirstOf(it, events.end(), unsyncedEvents.begin(), - unsyncedEvents.end(), isEchoEvent); - const auto& remoteEcho = nextPendingPair.first; - const auto& localEcho = nextPendingPair.second; + findFirstOf(it, events.end(), unsyncedEvents.begin(), + unsyncedEvents.end(), isEchoEvent); + const auto& remoteEcho = nextPendingPair.first; + const auto& localEcho = nextPendingPair.second; if (it != remoteEcho) { RoomEventsRange eventsSpan { it, remoteEcho }; @@ -2288,8 +2290,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) } } - qCDebug(MAIN) << "Room" << q->objectName() << "received" << totalInserted - << "new events; the last event is now" << timeline.back(); + qCDebug(STATE) << "Room" << q->objectName() << "received" + << totalInserted << "new events; the last event is now" + << timeline.back(); // The first event in the just-added batch (referred to by `from`) // defines whose read marker can possibly be promoted any further over @@ -2300,8 +2303,9 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) auto firstWriter = q->user((*from)->senderId()); if (q->readMarker(firstWriter) != timeline.crend()) { roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id() - << "to" << *q->readMarker(firstWriter); + qCDebug(STATE) << "Auto-promoted read marker for" + << firstWriter->id() << "to" + << *q->readMarker(firstWriter); } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); @@ -2338,8 +2342,8 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events) const auto insertedSize = moveEventsToTimeline(events, Older); const auto from = timeline.crend() - insertedSize; - qCDebug(MAIN) << "Room" << displayname << "received" << insertedSize - << "past events; the oldest event is now" << timeline.front(); + qCDebug(STATE) << "Room" << displayname << "received" << insertedSize + << "past events; the oldest event is now" << timeline.front(); q->onAddHistoricalTimelineEvents(from); emit q->addedMessages(timeline.front().index(), from->index()); @@ -2581,7 +2585,7 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (auto* evt = eventCast(event)) { auto readEventId = evt->event_id(); - qCDebug(MAIN) << "Server-side read marker at" << readEventId; + qCDebug(STATE) << "Server-side read marker at" << readEventId; d->serverReadMarker = readEventId; const auto newMarker = findInTimeline(readEventId); changes |= newMarker != timelineEdge() @@ -2595,8 +2599,8 @@ Room::Changes Room::processAccountDataEvent(EventPtr&& event) if (!currentData || currentData->contentJson() != event->contentJson()) { emit accountDataAboutToChange(event->matrixType()); currentData = move(event); - qCDebug(MAIN) << "Updated account data of type" - << currentData->matrixType(); + qCDebug(STATE) << "Updated account data of type" + << currentData->matrixType(); emit accountDataChanged(currentData->matrixType()); return Change::AccountDataChange; } -- cgit v1.2.3 From 42bca67768d6f174ecf125563ab28ef79b994e23 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Sun, 8 Mar 2020 02:43:23 +0530 Subject: fixing msc2432 --- lib/connection.cpp | 10 ++--- lib/connection.h | 2 +- lib/events/roomcanonicalaliasevent.h | 86 ++++++++++++++++++++++++++++++++++++ lib/events/simplestateevents.h | 2 - lib/room.cpp | 48 +++++++++++++++----- 5 files changed, 129 insertions(+), 19 deletions(-) create mode 100644 lib/events/roomcanonicalaliasevent.h (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 5bddbb83..f3d31d2d 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -87,7 +87,7 @@ public: QHash, Room*> roomMap; /// Mapping from serverparts to alias/room id mappings, /// as of the last sync - QHash> roomAliasMap; + QHash roomAliasMap; QVector roomIdsToForget; QVector firstTimeRooms; QVector pendingStateRoomIds; @@ -891,7 +891,7 @@ Room* Connection::room(const QString& roomId, JoinStates states) const Room* Connection::roomByAlias(const QString& roomAlias, JoinStates states) const { - const auto id = d->roomAliasMap.value(serverPart(roomAlias)).value(roomAlias); + const auto id = d->roomAliasMap.value(roomAlias); if (!id.isEmpty()) return room(id, states); @@ -901,17 +901,15 @@ Room* Connection::roomByAlias(const QString& roomAlias, JoinStates states) const } void Connection::updateRoomAliases(const QString& roomId, - const QString& aliasServer, const QStringList& previousRoomAliases, const QStringList& roomAliases) { - auto& aliasMap = d->roomAliasMap[aliasServer]; // Allocate if necessary for (const auto& a : previousRoomAliases) - if (aliasMap.remove(a) == 0) + if (d->roomAliasMap.remove(a) == 0) qCWarning(MAIN) << "Alias" << a << "is not found (already deleted?)"; for (const auto& a : roomAliases) { - auto& mappedId = aliasMap[a]; + auto& mappedId = d->roomAliasMap[a]; if (!mappedId.isEmpty()) { if (mappedId == roomId) qCDebug(MAIN) diff --git a/lib/connection.h b/lib/connection.h index e4109fd4..c7e18c12 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -294,7 +294,7 @@ public: /// This is used to maintain the internal index of room aliases. /// It does NOT change aliases on the server, /// \sa Room::setLocalAliases - void updateRoomAliases(const QString& roomId, const QString& aliasServer, + void updateRoomAliases(const QString& roomId, const QStringList& previousRoomAliases, const QStringList& roomAliases); Q_INVOKABLE Quotient::Room* invitation(const QString& roomId) const; diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h new file mode 100644 index 00000000..3f89f639 --- /dev/null +++ b/lib/events/roomcanonicalaliasevent.h @@ -0,0 +1,86 @@ +// Contains code for RoomCanonicalAlias Event +#pragma once + +#include "stateevent.h" + +namespace Quotient { +namespace EventContent{ + template + class SimpleDualContent { + public: + using first_value_type = T1; + using second_value_type = T2; + + template + SimpleDualContent(QString Key1Name, TT1&& value1, QString Key2Name, + TT2&& value2) + : value1(std::forward(value1)) + , value2(std::forward(value2)) + , key1(std::move(Key1Name)) + , key2(std::move(Key2Name)) + { } + + SimpleDualContent(const QJsonObject& json, QString key1Name, + QString key2Name) + : value1(fromJson(json[key1Name])) + , value2(fromJson(json[key2Name])) + , key1(std::move(key1Name)) + , key2(std::move(key2Name)) + { } + + QJsonObject toJson() const + { + return { { key1, Quotient::toJson(value1) }, + { key2, Quotient::toJson(value2) } }; + } + + public: + T1 value1; + T2 value2; + + protected: + QString key1; + QString key2; + }; +} // namespace EventContent + +class RoomCanonicalAliasEvent + : public StateEvent> { +public: + DEFINE_EVENT_TYPEID("m.room.canonical_alias", RoomCanonicalAliasEvent) + + explicit RoomCanonicalAliasEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj, QStringLiteral("alias"), + QStringLiteral("alt_aliases")) + { } + + RoomCanonicalAliasEvent(const QString& server, const QString& alias, + const QStringList& alt_aliases) + : StateEvent(typeId(), matrixTypeId(), server, QStringLiteral("alias"), + alias, QStringLiteral("alt_aliases"), alt_aliases) + { } + + // For compatibility used at Room::setCanonicalAlias + explicit RoomCanonicalAliasEvent(const QString& value1) + : RoomCanonicalAliasEvent(value1, QStringList()) + { } + + // Because, MSC2432 specifies, that alt_aliases may be present + // without aliases as well + explicit RoomCanonicalAliasEvent(const QStringList& value2) + : RoomCanonicalAliasEvent(QString(), value2) + { } + + template + RoomCanonicalAliasEvent(T1&& value1, T2&& value2) + : StateEvent(typeId(), matrixTypeId(), QString(), + QStringLiteral("alias"), std::forward(value1), + QStringLiteral("alt_aliases"), std::forward(value2)) + { } + + QString alias() const { return content().value1; } + + QStringList alt_aliases() const { return content().value2; } +}; +REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent) +} // namespace Quotient \ No newline at end of file diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index eb1d2f7a..cde5b0fd 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -69,8 +69,6 @@ namespace EventContent { // End of macro DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) -DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", - QString, alias) DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) class RoomAliasesEvent diff --git a/lib/room.cpp b/lib/room.cpp index b29f6f48..475c6570 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -56,6 +56,7 @@ #include "jobs/downloadfilejob.h" #include "jobs/mediathumbnailjob.h" #include "jobs/postreadmarkersjob.h" +#include "events/roomcanonicalaliasevent.h" #include #include @@ -107,6 +108,7 @@ public: QHash currentState; /// Servers with aliases for this room except the one of the local user /// \sa Room::remoteAliases + // This may not be required anymore QSet aliasServers; Timeline timeline; @@ -432,12 +434,13 @@ QString Room::name() const QStringList Room::localAliases() const { - return d - ->getCurrentState( - connection()->domain()) - ->aliases(); + QStringList result(d + ->getCurrentState()->alias()); + result += d->getCurrentState()->alt_aliases(); + return result; } +// Not sure about this function, maybe it is no more required QStringList Room::remoteAliases() const { QStringList result; @@ -1697,8 +1700,7 @@ void Room::setCanonicalAlias(const QString& newAlias) void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(connection()->homeserver().authority(), - aliases); + d->requestSetState(aliases); } void Room::setTopic(const QString& newTopic) @@ -2368,14 +2370,40 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) oldStateEvent ? static_cast(oldStateEvent)->aliases() : QStringList(); - connection()->updateRoomAliases(id(), ae.stateKey(), - previousAliases, ae.aliases()); + connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); return OtherChange; // clang-format off } - , [this] (const RoomCanonicalAliasEvent& evt) { - setObjectName(evt.alias().isEmpty() ? d->id : evt.alias()); + , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { + // clang-format on + setObjectName(cae.alias().isEmpty() ? d->id : cae.alias()); + QString previousCanonicalAlias = + oldStateEvent + ? static_cast(oldStateEvent) + ->alias() + : QString(); + + auto previousAltAliases = + oldStateEvent + ? static_cast(oldStateEvent) + ->alt_aliases() + : QStringList(); + + if (!previousCanonicalAlias.isEmpty()) { + previousAltAliases.push_back(previousCanonicalAlias); + } + + const auto previousAliases = std::move(previousAltAliases); + + auto newAliases = cae.alt_aliases(); + + if (!cae.alias().isEmpty()) { + newAliases.push_front(cae.alias()); + } + + connection()->updateRoomAliases(id(), previousAliases, newAliases); return CanonicalAliasChange; + // clang-format off } , [] (const RoomTopicEvent&) { return TopicChange; -- cgit v1.2.3 From 56d9a0addaabf2cec78e1c82a9846997a3669736 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 25 Feb 2020 20:06:19 +0300 Subject: E2EE: Make building E2EE optional. Contributes to #369 Signed-off-by: Alexey Andreev --- lib/connection.cpp | 25 +++++++++++++++++++++++++ lib/connection.h | 2 ++ lib/encryptionmanager.cpp | 2 ++ lib/encryptionmanager.h | 2 ++ lib/room.cpp | 17 +++++++++++++++++ 5 files changed, 48 insertions(+) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 98c8a4bc..6ad24fba 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -19,7 +19,9 @@ #include "connection.h" #include "connectiondata.h" +#ifdef Quotient_E2EE_ENABLED #include "encryptionmanager.h" +#endif // Quotient_E2EE_ENABLED #include "room.h" #include "settings.h" #include "user.h" @@ -43,7 +45,9 @@ #include "jobs/mediathumbnailjob.h" #include "jobs/syncjob.h" +#ifdef Quotient_E2EE_ENABLED #include "account.h" // QtOlm +#endif // Quotient_E2EE_ENABLED #include #include @@ -107,7 +111,9 @@ public: GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; +#ifdef Quotient_E2EE_ENABLED QScopedPointer encryptionManager; +#endif // Quotient_E2EE_ENABLED SyncJob* syncJob = nullptr; @@ -153,6 +159,10 @@ public: RoomEventPtr sessionDecryptMessage(const EncryptedEvent& encryptedEvent) { +#ifndef Quotient_E2EE_ENABLED + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; + return {}; +#else // Quotient_E2EE_ENABLED if (encryptedEvent.algorithm() != OlmV1Curve25519AesSha2AlgoKey) { return {}; @@ -208,6 +218,7 @@ public: } return decryptedEvent; +#endif // Quotient_E2EE_ENABLED } }; @@ -304,8 +315,12 @@ void Connection::doConnectToServer(const QString& user, const QString& password, connect(loginJob, &BaseJob::success, this, [this, loginJob] { d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); +#ifndef Quotient_E2EE_ENABLED + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; +#else // Quotient_E2EE_ENABLED d->encryptionManager->uploadIdentityKeys(this); d->encryptionManager->uploadOneTimeKeys(this); +#endif // Quotient_E2EE_ENABLED }); connect(loginJob, &BaseJob::failure, this, [this, loginJob] { emit loginError(loginJob->errorString(), loginJob->rawDataSample()); @@ -362,12 +377,16 @@ void Connection::Private::connectWithToken(const QString& userId, qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() << "by user" << userId << "from device" << deviceId; AccountSettings accountSettings(userId); +#ifndef Quotient_E2EE_ENABLED + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; +#else // Quotient_E2EE_ENABLED encryptionManager.reset( new EncryptionManager(accountSettings.encryptionAccountPickle())); if (accountSettings.encryptionAccountPickle().isEmpty()) { accountSettings.setEncryptionAccountPickle( encryptionManager->olmAccountPickle()); } +#endif // Quotient_E2EE_ENABLED emit q->stateChanged(); emit q->connected(); q->reloadCapabilities(); @@ -594,6 +613,9 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) d->dcLocalAdditions.clear(); d->dcLocalRemovals.clear(); } +#ifndef Quotient_E2EE_ENABLED + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; +#else // Quotient_E2EE_ENABLED // handling m.room_key to-device encrypted event for (auto&& toDeviceEvent : data.takeToDeviceEvents()) { if (toDeviceEvent->type() == EncryptedEvent::typeId()) { @@ -645,6 +667,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) d->encryptionManager->updateOneTimeKeyCounts(this, deviceOneTimeKeysCount); } +#endif // Quotient_E2EE_ENABLED } void Connection::stopSync() @@ -1068,10 +1091,12 @@ QString Connection::deviceId() const { return d->data->deviceId(); } QByteArray Connection::accessToken() const { return d->data->accessToken(); } +#ifdef Quotient_E2EE_ENABLED QtOlm::Account* Connection::olmAccount() const { return d->encryptionManager->account(); } +#endif // Quotient_E2EE_ENABLED SyncJob* Connection::syncJob() const { return d->syncJob; } diff --git a/lib/connection.h b/lib/connection.h index e4109fd4..b57f0ca8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -304,7 +304,9 @@ public: QString userId() const; QString deviceId() const; QByteArray accessToken() const; +#ifdef Quotient_E2EE_ENABLED QtOlm::Account* olmAccount() const; +#endif // Quotient_E2EE_ENABLED Q_INVOKABLE Quotient::SyncJob* syncJob() const; Q_INVOKABLE int millisToReconnect() const; diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index e2834c45..0895fae9 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -1,3 +1,4 @@ +#ifdef Quotient_E2EE_ENABLED #include "encryptionmanager.h" #include "connection.h" @@ -366,3 +367,4 @@ bool EncryptionManager::Private::oneTimeKeyShouldUpload() } return false; } +#endif // Quotient_E2EE_ENABLED diff --git a/lib/encryptionmanager.h b/lib/encryptionmanager.h index 8f346d37..5df15e83 100644 --- a/lib/encryptionmanager.h +++ b/lib/encryptionmanager.h @@ -1,3 +1,4 @@ +#ifdef Quotient_E2EE_ENABLED #pragma once #include @@ -43,3 +44,4 @@ private: }; } // namespace Quotient +#endif // Quotient_E2EE_ENABLED diff --git a/lib/room.cpp b/lib/room.cpp index ecb5a7ad..5a966ceb 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -69,9 +69,11 @@ #include #include +#ifdef Quotient_E2EE_ENABLED #include // QtOlm #include // QtOlm #include // QtOlm +#endif // Quotient_E2EE_ENABLED using namespace Quotient; using namespace QtOlm; @@ -342,6 +344,7 @@ public: QJsonObject toJson() const; +#ifdef Quotient_E2EE_ENABLED // A map from to QHash, QPair> groupSessionIndexRecord; // TODO: cache @@ -424,6 +427,7 @@ public: return decrypted.first; } +#endif // Quotient_E2EE_ENABLED private: using users_shortlist_t = std::array; @@ -1238,6 +1242,11 @@ const StateEventBase* Room::getCurrentState(const QString& evtType, RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) { +#ifndef Quotient_E2EE_ENABLED + Q_UNUSED(encryptedEvent); + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; + return {}; +#else // Quotient_E2EE_ENABLED if (encryptedEvent.algorithm() == MegolmV1AesSha2AlgoKey) { QString decrypted = d->groupSessionDecryptMessage( encryptedEvent.ciphertext(), encryptedEvent.senderKey(), @@ -1252,10 +1261,17 @@ RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) qCDebug(E2EE) << "Algorithm of the encrypted event with id" << encryptedEvent.id() << "is not for the current device"; return {}; +#endif // Quotient_E2EE_ENABLED } void Room::handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey) { +#ifndef Quotient_E2EE_ENABLED + Q_UNUSED(roomKeyEvent); + Q_UNUSED(senderKey); + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; + return; +#else // Quotient_E2EE_ENABLED if (roomKeyEvent->algorithm() != MegolmV1AesSha2AlgoKey) { qCWarning(E2EE) << "Ignoring unsupported algorithm" << roomKeyEvent->algorithm() << "in m.room_key event"; @@ -1265,6 +1281,7 @@ void Room::handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey) qCDebug(E2EE) << "added new inboundGroupSession:" << d->groupSessions.count(); } +#endif // Quotient_E2EE_ENABLED } int Room::joinedCount() const -- cgit v1.2.3 From 4102144290312ebed7d4af69dd640835275a9675 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 13 Mar 2020 18:39:59 +0100 Subject: Connection: support getting the list of login flows The flows themselves are not facilitated in any way (yet). --- lib/connection.cpp | 36 +++++++++++++++++++++++++++++++----- lib/connection.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 6ad24fba..1989050e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -30,7 +30,6 @@ #include "csapi/capabilities.h" #include "csapi/joining.h" #include "csapi/leaving.h" -#include "csapi/login.h" #include "csapi/logout.h" #include "csapi/receipts.h" #include "csapi/room_send.h" @@ -111,6 +110,8 @@ public: GetCapabilitiesJob* capabilitiesJob = nullptr; GetCapabilitiesJob::Capabilities capabilities; + QVector loginFlows; + #ifdef Quotient_E2EE_ENABLED QScopedPointer encryptionManager; #endif // Quotient_E2EE_ENABLED @@ -1004,6 +1005,21 @@ QUrl Connection::homeserver() const { return d->data->baseUrl(); } QString Connection::domain() const { return userId().section(':', 1); } +QVector Connection::loginFlows() const +{ + return d->loginFlows; +} + +bool Connection::supportsPasswordAuth() const +{ + return d->loginFlows.contains(LoginFlows::Password); +} + +bool Connection::supportsSso() const +{ + return d->loginFlows.contains(LoginFlows::SSO); +} + Room* Connection::room(const QString& roomId, JoinStates states) const { Room* room = d->roomMap.value({ roomId, false }, nullptr); @@ -1400,11 +1416,21 @@ QByteArray Connection::generateTxnId() const void Connection::setHomeserver(const QUrl& url) { - if (homeserver() == url) - return; + if (homeserver() != url) { + d->data->setBaseUrl(url); + d->loginFlows.clear(); + emit homeserverChanged(homeserver()); + } - d->data->setBaseUrl(url); - emit homeserverChanged(homeserver()); + // Whenever a homeserver is updated, retrieve available login flows from it + auto* j = callApi(BackgroundRequest); + connect(j, &BaseJob::finished, this, [this, j] { + if (j->status().good()) + d->loginFlows = j->flows(); + else + d->loginFlows.clear(); + emit loginFlowsChanged(); + }); } void Connection::saveRoomState(Room* r) const diff --git a/lib/connection.h b/lib/connection.h index b57f0ca8..9d89ca43 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -21,6 +21,7 @@ #include "joinstate.h" #include "qt_connection_util.h" +#include "csapi/login.h" #include "csapi/create_room.h" #include "events/accountdataevents.h" @@ -36,6 +37,8 @@ namespace QtOlm { class Account; } +Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) + namespace Quotient { Q_NAMESPACE @@ -58,6 +61,28 @@ class SendToDeviceJob; class SendMessageJob; class LeaveRoomJob; +// To simplify comparisons of LoginFlows + +inline bool operator==(const GetLoginFlowsJob::LoginFlow& lhs, + const GetLoginFlowsJob::LoginFlow& rhs) +{ + return lhs.type == rhs.type; +} + +inline bool operator!=(const GetLoginFlowsJob::LoginFlow& lhs, + const GetLoginFlowsJob::LoginFlow& rhs) +{ + return !(lhs == rhs); +} + +/// Predefined login flows +struct LoginFlows { + using LoginFlow = GetLoginFlowsJob::LoginFlow; + static inline const LoginFlow Password { "m.login.password" }; + static inline const LoginFlow SSO { "m.login.sso" }; + static inline const LoginFlow Token { "m.login.token" }; +}; + class Connection; using room_factory_t = @@ -117,6 +142,9 @@ class Connection : public QObject { Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged) + Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) + Q_PROPERTY(bool supportsSso READ supportsSso NOTIFY loginFlowsChanged) + Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY @@ -281,6 +309,12 @@ public: QUrl homeserver() const; /** Get the domain name used for ids/aliases on the server */ QString domain() const; + /** Get the list of supported login flows */ + QVector loginFlows() const; + /** Check whether the current homeserver supports password auth */ + bool supportsPasswordAuth() const; + /** Check whether the current homeserver supports SSO */ + bool supportsSso() const; /** Find a room by its id and a mask of applicable states */ Q_INVOKABLE Quotient::Room* room(const QString& roomId, @@ -421,6 +455,18 @@ public: std::forward(jobArgs)...); } + /*! Get a request URL for a job with specified type and arguments + * + * This calls JobT::makeRequestUrl() prepending the connection's homeserver + * to the list of arguments. + */ + template + QUrl getUrlForApi(JobArgTs&&... jobArgs) const + { + return JobT::makeRequestUrl(homeserver(), + std::forward(jobArgs)...); + } + /** Generate a new transaction id. Transaction id's are unique within * a single Connection object */ @@ -609,6 +655,7 @@ signals: void resolveError(QString error); void homeserverChanged(QUrl baseUrl); + void loginFlowsChanged(); void capabilitiesLoaded(); void connected(); -- cgit v1.2.3 From a0008f808859ee491bf0a1bc683748a42c5e209a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 14 Mar 2020 20:28:51 +0100 Subject: Connection: minor cleanup --- lib/connection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 1989050e..b0ab5bfe 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -317,7 +317,7 @@ void Connection::doConnectToServer(const QString& user, const QString& password, d->connectWithToken(loginJob->userId(), loginJob->accessToken(), loginJob->deviceId()); #ifndef Quotient_E2EE_ENABLED - qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED d->encryptionManager->uploadIdentityKeys(this); d->encryptionManager->uploadOneTimeKeys(this); @@ -377,10 +377,10 @@ void Connection::Private::connectWithToken(const QString& userId, q->setObjectName(userId % '/' % deviceId); qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() << "by user" << userId << "from device" << deviceId; - AccountSettings accountSettings(userId); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED + AccountSettings accountSettings(userId); encryptionManager.reset( new EncryptionManager(accountSettings.encryptionAccountPickle())); if (accountSettings.encryptionAccountPickle().isEmpty()) { -- cgit v1.2.3 From d02a510a586db1a89476cd283ea24a281e9bb6af Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 15 Mar 2020 14:53:27 +0100 Subject: Connection: loginWithToken(); connectWithToken() -> assumeIdentity() --- lib/connection.cpp | 88 ++++++++++++++++++++++++++++++++++-------------------- lib/connection.h | 19 ++++++++---- 2 files changed, 69 insertions(+), 38 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index b0ab5bfe..4b4d371a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -125,8 +125,10 @@ public: != "json"; bool lazyLoading = false; - void connectWithToken(const QString& userId, const QString& accessToken, - const QString& deviceId); + template + void loginToServer(LoginArgTs&&... loginArgs); + void assumeIdentity(const QString& userId, const QString& accessToken, + const QString& deviceId); void removeRoom(const QString& roomId); template @@ -296,44 +298,44 @@ void Connection::resolveServer(const QString& mxid) }); } -void Connection::connectToServer(const QString& user, const QString& password, +inline UserIdentifier makeUserIdentifier(const QString& id) +{ + return { QStringLiteral("m.id.user"), { { QStringLiteral("user"), id } } }; +} + +inline UserIdentifier make3rdPartyIdentifier(const QString& medium, + const QString& address) +{ + return { QStringLiteral("m.id.thirdparty"), + { { QStringLiteral("medium"), medium }, + { QStringLiteral("address"), address } } }; +} + +void Connection::connectToServer(const QString& userId, const QString& password, const QString& initialDeviceName, const QString& deviceId) { - checkAndConnect(user, [=] { - doConnectToServer(user, password, initialDeviceName, deviceId); + checkAndConnect(userId, [=] { + d->loginToServer(LoginFlows::Password.type, makeUserIdentifier(userId), + password, /*token*/ "", deviceId, initialDeviceName); }); } -void Connection::doConnectToServer(const QString& user, const QString& password, - const QString& initialDeviceName, - const QString& deviceId) + +void Connection::loginWithToken(const QByteArray& loginToken, + const QString& initialDeviceName, + const QString& deviceId) { - auto loginJob = - callApi(QStringLiteral("m.login.password"), - UserIdentifier { QStringLiteral("m.id.user"), - { { QStringLiteral("user"), user } } }, - password, /*token*/ "", deviceId, initialDeviceName); - connect(loginJob, &BaseJob::success, this, [this, loginJob] { - d->connectWithToken(loginJob->userId(), loginJob->accessToken(), - loginJob->deviceId()); -#ifndef Quotient_E2EE_ENABLED - qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; -#else // Quotient_E2EE_ENABLED - d->encryptionManager->uploadIdentityKeys(this); - d->encryptionManager->uploadOneTimeKeys(this); -#endif // Quotient_E2EE_ENABLED - }); - connect(loginJob, &BaseJob::failure, this, [this, loginJob] { - emit loginError(loginJob->errorString(), loginJob->rawDataSample()); - }); + d->loginToServer(LoginFlows::Token.type, + makeUserIdentifier(/*user is encoded in loginToken*/ {}), + /*password*/ "", loginToken, deviceId, initialDeviceName); } -void Connection::connectWithToken(const QString& userId, - const QString& accessToken, - const QString& deviceId) +void Connection::assumeIdentity(const QString& userId, + const QString& accessToken, + const QString& deviceId) { checkAndConnect(userId, - [=] { d->connectWithToken(userId, accessToken, deviceId); }); + [=] { d->assumeIdentity(userId, accessToken, deviceId); }); } void Connection::reloadCapabilities() @@ -366,9 +368,29 @@ bool Connection::loadingCapabilities() const return !d->capabilities.roomVersions; } -void Connection::Private::connectWithToken(const QString& userId, - const QString& accessToken, - const QString& deviceId) +template +void Connection::Private::loginToServer(LoginArgTs&&... loginArgs) +{ + auto loginJob = + q->callApi(std::forward(loginArgs)...); + connect(loginJob, &BaseJob::success, q, [this, loginJob] { + assumeIdentity(loginJob->userId(), loginJob->accessToken(), + loginJob->deviceId()); +#ifndef Quotient_E2EE_ENABLED + qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; +#else // Quotient_E2EE_ENABLED + encryptionManager->uploadIdentityKeys(this); + encryptionManager->uploadOneTimeKeys(this); +#endif // Quotient_E2EE_ENABLED + }); + connect(loginJob, &BaseJob::failure, q, [this, loginJob] { + emit q->loginError(loginJob->errorString(), loginJob->rawDataSample()); + }); +} + +void Connection::Private::assumeIdentity(const QString& userId, + const QString& accessToken, + const QString& deviceId) { data->setUserId(userId); q->user(); // Creates a User object for the local user diff --git a/lib/connection.h b/lib/connection.h index 9d89ca43..8f2abd0f 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -505,11 +505,23 @@ public slots: /** Determine and set the homeserver from MXID */ void resolveServer(const QString& mxid); - void connectToServer(const QString& user, const QString& password, + void connectToServer(const QString& userId, const QString& password, const QString& initialDeviceName, const QString& deviceId = {}); + void loginWithToken(const QByteArray& loginToken, + const QString& initialDeviceName, + const QString& deviceId = {}); + void assumeIdentity(const QString& userId, const QString& accessToken, + const QString& deviceId); + /*! @deprecated + * Use assumeIdentity() if you have an access token or + * loginWithToken() if you have a login token. + */ void connectWithToken(const QString& userId, const QString& accessToken, - const QString& deviceId); + const QString& deviceId) + { + assumeIdentity(userId, accessToken, deviceId); + } /// Explicitly request capabilities from the server void reloadCapabilities(); @@ -856,9 +868,6 @@ private: * @param connectFn - a function to execute once the HS URL is good */ void checkAndConnect(const QString& userId, std::function connectFn); - void doConnectToServer(const QString& user, const QString& password, - const QString& initialDeviceName, - const QString& deviceId = {}); static room_factory_t _roomFactory; static user_factory_t _userFactory; -- cgit v1.2.3 From 5b7bfc2d559c1707208007f2b7d5c8e898b98f7e Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Tue, 17 Mar 2020 19:57:11 +0100 Subject: MSC1849 compatible edited messages (#373) * Make handling of edited messages MSC1849 compatible Server side aggregation replaces `content` with `new_content` in edited messages. The same must be done at client side on incremental updates to keep timeline consistent. As server side aggregations is in use by default from Synapse 1.2.0 this patch fixes a real issue. --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 5a966ceb..2945111a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2127,7 +2127,7 @@ RoomEventPtr makeReplaced(const RoomEvent& target, const RoomMessageEvent& replacement) { auto originalJson = target.originalJsonObject(); - originalJson[ContentKeyL] = replacement.contentJson(); + originalJson[ContentKeyL] = replacement.contentJson().value("m.new_content"_ls); auto unsignedData = originalJson.take(UnsignedKeyL).toObject(); auto relations = unsignedData.take("m.relations"_ls).toObject(); -- cgit v1.2.3 From 682f96d4af19226e553138b1a86ed9233c35edbd Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Tue, 17 Mar 2020 22:09:26 +0100 Subject: Use constant Co-Authored-By: Kitsune Ral --- lib/events/roommessageevent.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index a6620618..616a034f 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -338,7 +338,7 @@ void TextContent::fillJson(QJsonObject* json) const } if (relatesTo) { json->insert(QStringLiteral("m.relates_to"), - QJsonObject { { "rel_type", relatesTo->type }, { "event_id", relatesTo->eventId } }); + QJsonObject { { "rel_type", relatesTo->type }, { EventIdKey, relatesTo->eventId } }); if (relatesTo->type == RelatesTo::ReplacementTypeId()) { QJsonObject newContentJson; if (mimeType.inherits("text/html")) { -- cgit v1.2.3 From ab3d0263b770e30de673c63740a5c26bcbf33e58 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 18 Mar 2020 09:51:27 +0100 Subject: SsoSession and Connection::prepareForSso() The response in the web browser is quite barebone, just enough to give feedback that things are alright. Closes #386. Closes #388. --- lib/connection.cpp | 6 +++ lib/connection.h | 4 ++ lib/ssosession.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/ssosession.h | 44 +++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 lib/ssosession.cpp create mode 100644 lib/ssosession.h (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 4b4d371a..0e6b1c84 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -321,6 +321,12 @@ void Connection::connectToServer(const QString& userId, const QString& password, }); } +SsoSession* Connection::prepareForSso(const QString& initialDeviceName, + const QString& deviceId) +{ + return new SsoSession(this, initialDeviceName, deviceId); +} + void Connection::loginWithToken(const QByteArray& loginToken, const QString& initialDeviceName, const QString& deviceId) diff --git a/lib/connection.h b/lib/connection.h index 8f2abd0f..9b222ca8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -18,6 +18,7 @@ #pragma once +#include "ssosession.h" #include "joinstate.h" #include "qt_connection_util.h" @@ -467,6 +468,9 @@ public: std::forward(jobArgs)...); } + Q_INVOKABLE SsoSession* prepareForSso(const QString& initialDeviceName, + const QString& deviceId = {}); + /** Generate a new transaction id. Transaction id's are unique within * a single Connection object */ diff --git a/lib/ssosession.cpp b/lib/ssosession.cpp new file mode 100644 index 00000000..0f8f96e1 --- /dev/null +++ b/lib/ssosession.cpp @@ -0,0 +1,127 @@ +#include "ssosession.h" + +#include "connection.h" +#include "csapi/sso_login_redirect.h" + +#include +#include +#include +#include + +using namespace Quotient; + +struct SsoSession::Private { + Private(SsoSession* q, const QString& initialDeviceName = {}, + const QString& deviceId = {}, Connection* connection = nullptr) + : initialDeviceName(initialDeviceName) + , deviceId(deviceId) + , connection(connection) + { + auto* server = new QTcpServer(q); + server->listen(); + // The "/returnToApplication" part is just a hint for the end-user, + // the callback will work without it equally well. + callbackUrl = QStringLiteral("http://localhost:%1/returnToApplication") + .arg(server->serverPort()); + ssoUrl = connection->getUrlForApi(callbackUrl); + + QObject::connect(server, &QTcpServer::newConnection, q, [this, server] { + qCDebug(MAIN) << "SSO callback initiated"; + socket = server->nextPendingConnection(); + server->close(); + QObject::connect(socket, &QTcpSocket::readyRead, socket, [this] { + requestData.append(socket->readAll()); + if (!socket->atEnd() && !requestData.endsWith("\r\n\r\n")) { + qDebug(MAIN) << "Incomplete request, waiting for more data"; + return; + } + processCallback(); + }); + QObject::connect(socket, &QTcpSocket::disconnected, socket, + [this] { socket->deleteLater(); }); + }); + } + void processCallback(); + void sendHttpResponse(const QByteArray& code, const QByteArray& msg); + void onError(const QByteArray& code, const QString& errorMsg); + + QString initialDeviceName; + QString deviceId; + Connection* connection; + QString callbackUrl {}; + QUrl ssoUrl {}; + QTcpSocket* socket = nullptr; + QByteArray requestData {}; +}; + +SsoSession::SsoSession(Connection* connection, const QString& initialDeviceName, + const QString& deviceId) + : QObject(connection) + , d(std::make_unique(this, initialDeviceName, deviceId, connection)) +{ + qCDebug(MAIN) << "SSO session constructed"; +} + +SsoSession::~SsoSession() +{ + qCDebug(MAIN) << "SSO session deconstructed"; +} + +QUrl SsoSession::ssoUrl() const { return d->ssoUrl; } + +QUrl SsoSession::callbackUrl() const { return d->callbackUrl; } + +void SsoSession::Private::processCallback() +{ + // https://matrix.org/docs/guides/sso-for-client-developers + // Inspired by Clementine's src/internet/core/localredirectserver.cpp + // (see at https://github.com/clementine-player/Clementine/) + const auto& requestParts = requestData.split(' '); + if (requestParts.size() < 2 || requestParts[1].isEmpty()) { + onError("400 Bad Request", tr("No login token in SSO callback")); + return; + } + const auto& QueryItemName = QStringLiteral("loginToken"); + QUrlQuery query { QUrl(requestParts[1]).query() }; + if (!query.hasQueryItem(QueryItemName)) { + onError("400 Bad Request", tr("Malformed single sign-on callback")); + } + qCDebug(MAIN) << "Found the token in SSO callback, logging in"; + connection->loginWithToken(query.queryItemValue(QueryItemName).toLatin1(), + initialDeviceName, deviceId); + connect(connection, &Connection::connected, socket, [this] { + const QString msg = + "The application '" % QCoreApplication::applicationName() + % "' has successfully logged in as a user " % connection->userId() + % " with device id " % connection->deviceId() + % ". This window can be closed. Thank you.\r\n"; + sendHttpResponse("200 OK", msg.toHtmlEscaped().toUtf8()); + socket->disconnectFromHost(); + }); + connect(connection, &Connection::loginError, socket, [this] { + onError("401 Unauthorised", tr("Login failed")); + socket->disconnectFromHost(); + }); +} + +void SsoSession::Private::sendHttpResponse(const QByteArray& code, + const QByteArray& msg) +{ + socket->write("HTTP/1.0 "); + socket->write(code); + socket->write("\r\n" + "Content-type: text/html;charset=UTF-8\r\n" + "\r\n\r\n"); + socket->write(msg); + socket->write("\r\n"); +} + +void SsoSession::Private::onError(const QByteArray& code, + const QString& errorMsg) +{ + qCWarning(MAIN).nospace() << errorMsg; + sendHttpResponse(code, "

" + errorMsg.toUtf8() + "

"); + // [kitsune] Yeah, I know, dirty. Maybe the "right" way would be to have + // an intermediate signal but that seems just a fight for purity. + emit connection->loginError(errorMsg, requestData); +} diff --git a/lib/ssosession.h b/lib/ssosession.h new file mode 100644 index 00000000..5845cd4d --- /dev/null +++ b/lib/ssosession.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +#include + +class QTcpServer; +class QTcpSocket; + +namespace Quotient { +class Connection; + +/*! Single sign-on (SSO) session encapsulation + * + * This class is responsible for setting up of a new SSO session, providing + * a URL to be opened (usually, in a web browser) and handling the callback + * response after completing the single sign-on, all the way to actually + * logging the user in. It does NOT open and render the SSO URL, it only does + * the necessary backstage work. + * + * Clients only need to open the URL; the rest is done for them. + * Client code can look something like: + * \code + * QDesktopServices::openUrl( + * connection->prepareForSso(initialDeviceName)->ssoUrl()); + * \endcode + */ +class SsoSession : public QObject { + Q_OBJECT + Q_PROPERTY(QUrl ssoUrl READ ssoUrl CONSTANT) + Q_PROPERTY(QUrl callbackUrl READ callbackUrl CONSTANT) +public: + SsoSession(Connection* connection, const QString& initialDeviceName, + const QString& deviceId = {}); + ~SsoSession() override; + QUrl ssoUrl() const; + QUrl callbackUrl() const; + +private: + class Private; + std::unique_ptr d; +}; +} // namespace Quotient -- cgit v1.2.3 From a682edf6e93f9ad4a087d5ba1374e42b06ec27e9 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 01:11:34 +0530 Subject: Add warranty in lib/events/roomcanonicalaliasevent.h Co-Authored-By: Kitsune Ral --- lib/events/roomcanonicalaliasevent.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 3f89f639..78d541bc 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -1,4 +1,21 @@ -// Contains code for RoomCanonicalAlias Event +/****************************************************************************** + * Copyright (C) 2020 QMatrixClient project + * + * 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 "stateevent.h" @@ -83,4 +100,4 @@ public: QStringList alt_aliases() const { return content().value2; } }; REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent) -} // namespace Quotient \ No newline at end of file +} // namespace Quotient -- cgit v1.2.3 From 99c7e1c5e2a78c6babba50418789bd4c4f0e9db3 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 02:29:45 +0530 Subject: made suggested changes --- lib/events/roomcanonicalaliasevent.h | 72 +++++++++++------------------------- lib/room.cpp | 46 ++++++++++------------- lib/room.h | 3 ++ 3 files changed, 44 insertions(+), 77 deletions(-) (limited to 'lib') diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 78d541bc..012cac75 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -22,82 +22,54 @@ namespace Quotient { namespace EventContent{ - template - class SimpleDualContent { + class AliasesEventContent { + public: - using first_value_type = T1; - using second_value_type = T2; - template - SimpleDualContent(QString Key1Name, TT1&& value1, QString Key2Name, - TT2&& value2) - : value1(std::forward(value1)) - , value2(std::forward(value2)) - , key1(std::move(Key1Name)) - , key2(std::move(Key2Name)) + template + AliasesEventContent(T1&& canonicalAlias, T2&& altAliases) + : canonicalAlias(std::forward(canonicalAlias)) + , altAliases(std::forward(altAliases)) { } - SimpleDualContent(const QJsonObject& json, QString key1Name, - QString key2Name) - : value1(fromJson(json[key1Name])) - , value2(fromJson(json[key2Name])) - , key1(std::move(key1Name)) - , key2(std::move(key2Name)) + AliasesEventContent(const QJsonObject& json) + : canonicalAlias(fromJson(json["alias"])) + , altAliases(fromJson(json["alt_aliases"])) { } QJsonObject toJson() const { - return { { key1, Quotient::toJson(value1) }, - { key2, Quotient::toJson(value2) } }; + return { { "alias", Quotient::toJson(canonicalAlias) }, + { "alt_aliases", Quotient::toJson(altAliases) } }; } - public: - T1 value1; - T2 value2; - - protected: - QString key1; - QString key2; + QString canonicalAlias; + QStringList altAliases; }; } // namespace EventContent class RoomCanonicalAliasEvent - : public StateEvent> { + : public StateEvent { public: DEFINE_EVENT_TYPEID("m.room.canonical_alias", RoomCanonicalAliasEvent) explicit RoomCanonicalAliasEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj, QStringLiteral("alias"), - QStringLiteral("alt_aliases")) - { } - - RoomCanonicalAliasEvent(const QString& server, const QString& alias, - const QStringList& alt_aliases) - : StateEvent(typeId(), matrixTypeId(), server, QStringLiteral("alias"), - alias, QStringLiteral("alt_aliases"), alt_aliases) - { } - - // For compatibility used at Room::setCanonicalAlias - explicit RoomCanonicalAliasEvent(const QString& value1) - : RoomCanonicalAliasEvent(value1, QStringList()) + : StateEvent(typeId(), obj) { } - // Because, MSC2432 specifies, that alt_aliases may be present - // without aliases as well - explicit RoomCanonicalAliasEvent(const QStringList& value2) - : RoomCanonicalAliasEvent(QString(), value2) + RoomCanonicalAliasEvent(const QString& canonicalAlias, const QStringList& altAliases = {}) + : StateEvent(typeId(), matrixTypeId(), QString(), + canonicalAlias, altAliases) { } - template - RoomCanonicalAliasEvent(T1&& value1, T2&& value2) + RoomCanonicalAliasEvent(QString&& canonicalAlias, QStringList&& altAliases = {}) : StateEvent(typeId(), matrixTypeId(), QString(), - QStringLiteral("alias"), std::forward(value1), - QStringLiteral("alt_aliases"), std::forward(value2)) + std::move(canonicalAlias), std::move(altAliases)) { } - QString alias() const { return content().value1; } + QString alias() const { return content().canonicalAlias; } - QStringList alt_aliases() const { return content().value2; } + QStringList altAliases() const { return content().altAliases; } }; REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent) } // namespace Quotient diff --git a/lib/room.cpp b/lib/room.cpp index 475c6570..fa3f10a6 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -108,7 +108,7 @@ public: QHash currentState; /// Servers with aliases for this room except the one of the local user /// \sa Room::remoteAliases - // This may not be required anymore + QSet aliasServers; Timeline timeline; @@ -432,15 +432,20 @@ QString Room::name() const return d->getCurrentState()->name(); } -QStringList Room::localAliases() const +QStringList Room::aliases() const { - QStringList result(d - ->getCurrentState()->alias()); - result += d->getCurrentState()->alt_aliases(); + QStringList result(d->getCurrentState()->alias()); + result += d->getCurrentState()->altAliases(); return result; } -// Not sure about this function, maybe it is no more required +QStringList Room::localAliases() const +{ + return d->getCurrentState( + connection()->domain()) + ->aliases(); +} + QStringList Room::remoteAliases() const { QStringList result; @@ -1693,6 +1698,8 @@ void Room::setName(const QString& newName) d->requestSetState(newName); } +// Change might be required here as well. +// Not sure what will be best void Room::setCanonicalAlias(const QString& newAlias) { d->requestSetState(newAlias); @@ -1700,7 +1707,7 @@ void Room::setCanonicalAlias(const QString& newAlias) void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(aliases); + d->requestSetState(this->canonicalAlias(), aliases); } void Room::setTopic(const QString& newTopic) @@ -2355,23 +2362,8 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) } , [this,oldStateEvent] (const RoomAliasesEvent& ae) { // clang-format on - if (ae.aliases().isEmpty()) { - if (d->aliasServers.remove(ae.stateKey())) - qCDebug(STATE).noquote() - << ae.stateKey() << "no more has aliases for room" - << objectName(); - } else { - d->aliasServers.insert(ae.stateKey()); - qCDebug(STATE).nospace().noquote() - << "New server with aliases for room " << objectName() - << ": " << ae.stateKey(); - } - const auto previousAliases = - oldStateEvent - ? static_cast(oldStateEvent)->aliases() - : QStringList(); - connection()->updateRoomAliases(id(), previousAliases, ae.aliases()); - return OtherChange; + // This event has been removed by MSC-2432 + return NoChange; // clang-format off } , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { @@ -2386,7 +2378,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) auto previousAltAliases = oldStateEvent ? static_cast(oldStateEvent) - ->alt_aliases() + ->altAliases() : QStringList(); if (!previousCanonicalAlias.isEmpty()) { @@ -2395,7 +2387,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) const auto previousAliases = std::move(previousAltAliases); - auto newAliases = cae.alt_aliases(); + auto newAliases = cae.altAliases(); if (!cae.alias().isEmpty()) { newAliases.push_front(cae.alias()); @@ -2651,7 +2643,7 @@ QString Room::Private::calculateDisplayname() const return dispName; // 3. m.room.aliases - only local aliases, subject for further removal - const auto aliases = q->localAliases(); + const auto aliases = q->aliases(); if (!aliases.isEmpty()) return aliases.front(); diff --git a/lib/room.h b/lib/room.h index ad19792e..4d2527f7 100644 --- a/lib/room.h +++ b/lib/room.h @@ -188,11 +188,14 @@ public: QString name() const; /// Room aliases defined on the current user's server /// \sa remoteAliases, setLocalAliases + [[deprecated("Use aliases()")]] QStringList localAliases() const; /// Room aliases defined on other servers /// \sa localAliases + [[deprecated("Use aliases()")]] QStringList remoteAliases() const; QString canonicalAlias() const; + QStringList aliases() const; QString displayName() const; QString topic() const; QString avatarMediaId() const; -- cgit v1.2.3 From a2177b704c06bccc4840607803bca4fbe747830b Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 13:12:42 +0530 Subject: added altAliases() --- lib/room.cpp | 9 +++++++-- lib/room.h | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index fa3f10a6..60b36911 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -439,6 +439,11 @@ QStringList Room::aliases() const return result; } +QStringList Room::altAliases() const +{ + return d->getCurrentState()->altAliases(); +} + QStringList Room::localAliases() const { return d->getCurrentState( @@ -1702,7 +1707,7 @@ void Room::setName(const QString& newName) // Not sure what will be best void Room::setCanonicalAlias(const QString& newAlias) { - d->requestSetState(newAlias); + d->requestSetState(newAlias, this->altAliases()); } void Room::setLocalAliases(const QStringList& aliases) @@ -2394,7 +2399,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) } connection()->updateRoomAliases(id(), previousAliases, newAliases); - return CanonicalAliasChange; + return AliasesChange; // clang-format off } , [] (const RoomTopicEvent&) { diff --git a/lib/room.h b/lib/room.h index 4d2527f7..d5fea94f 100644 --- a/lib/room.h +++ b/lib/room.h @@ -141,7 +141,8 @@ public: enum Change : uint { NoChange = 0x0, NameChange = 0x1, - CanonicalAliasChange = 0x2, + AliasesChange = 0x2, + CanonicalAliasChange = AliasesChange, TopicChange = 0x4, UnreadNotifsChange = 0x8, AvatarChange = 0x10, @@ -195,6 +196,7 @@ public: [[deprecated("Use aliases()")]] QStringList remoteAliases() const; QString canonicalAlias() const; + QStringList altAliases() const; QStringList aliases() const; QString displayName() const; QString topic() const; -- cgit v1.2.3 From 57ed7c21829347bd553a481becc6484164419c18 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 13:28:05 +0530 Subject: dont use this --- lib/room.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 60b36911..77b4a1e5 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1707,12 +1707,12 @@ void Room::setName(const QString& newName) // Not sure what will be best void Room::setCanonicalAlias(const QString& newAlias) { - d->requestSetState(newAlias, this->altAliases()); + d->requestSetState(newAlias, altAliases()); } void Room::setLocalAliases(const QStringList& aliases) { - d->requestSetState(this->canonicalAlias(), aliases); + d->requestSetState(canonicalAlias(), aliases); } void Room::setTopic(const QString& newTopic) -- cgit v1.2.3 From ecdab8ba9d2f411e5cfb049866501f159725a3db Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 15:01:08 +0530 Subject: Make constructor explicit lib/events/roomcanonicalaliasevent.h Co-Authored-By: Kitsune Ral --- lib/events/roomcanonicalaliasevent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 012cac75..0c30db8b 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -57,7 +57,7 @@ public: : StateEvent(typeId(), obj) { } - RoomCanonicalAliasEvent(const QString& canonicalAlias, const QStringList& altAliases = {}) + explicit RoomCanonicalAliasEvent(const QString& canonicalAlias, const QStringList& altAliases = {}) : StateEvent(typeId(), matrixTypeId(), QString(), canonicalAlias, altAliases) { } -- cgit v1.2.3 From 153c1c66c7cc162920e5d4e4d902efcadf015d5b Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 15:01:55 +0530 Subject: Make rvalue constructor explicit Update lib/events/roomcanonicalaliasevent.h Co-Authored-By: Kitsune Ral --- lib/events/roomcanonicalaliasevent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 0c30db8b..311a5959 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -62,7 +62,7 @@ public: canonicalAlias, altAliases) { } - RoomCanonicalAliasEvent(QString&& canonicalAlias, QStringList&& altAliases = {}) + explicit RoomCanonicalAliasEvent(QString&& canonicalAlias, QStringList&& altAliases = {}) : StateEvent(typeId(), matrixTypeId(), QString(), std::move(canonicalAlias), std::move(altAliases)) { } -- cgit v1.2.3 From f9dee85c5f8eb35df14a29d011c3cb84915962a6 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Fri, 20 Mar 2020 15:06:21 +0530 Subject: emit nameChanged() on AliasesChange --- lib/room.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 77b4a1e5..5f96a5f9 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -108,7 +108,6 @@ public: QHash currentState; /// Servers with aliases for this room except the one of the local user /// \sa Room::remoteAliases - QSet aliasServers; Timeline timeline; @@ -1423,7 +1422,7 @@ void Room::updateData(SyncRoomData&& data, bool fromCache) if (roomChanges & TopicChange) emit topicChanged(); - if (roomChanges & NameChange) + if (roomChanges & (NameChange | AliasesChange)) emit namesChanged(this); if (roomChanges & MembersChange) @@ -1703,8 +1702,6 @@ void Room::setName(const QString& newName) d->requestSetState(newName); } -// Change might be required here as well. -// Not sure what will be best void Room::setCanonicalAlias(const QString& newAlias) { d->requestSetState(newAlias, altAliases()); -- cgit v1.2.3 From ed62c822ca200613bde60e48547ba32a0bd21c13 Mon Sep 17 00:00:00 2001 From: Ram Nad Date: Sat, 21 Mar 2020 17:18:40 +0530 Subject: Update aliases code lib/room.cpp Co-Authored-By: Kitsune Ral --- lib/room.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 5f96a5f9..403c024b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -433,9 +433,8 @@ QString Room::name() const QStringList Room::aliases() const { - QStringList result(d->getCurrentState()->alias()); - result += d->getCurrentState()->altAliases(); - return result; + const auto* evt = d->getCurrentState(); + return QStringList(evt->altAliases()) << evt->alias(); } QStringList Room::altAliases() const -- cgit v1.2.3 From 79a3e9e2accd0de0ac8bf35d8b77f36ca211899f Mon Sep 17 00:00:00 2001 From: Black Hat Date: Mon, 23 Mar 2020 22:48:02 -0700 Subject: Expose aliases() and altAliases() as properties --- lib/room.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.h b/lib/room.h index 6f5751f9..4e4d029f 100644 --- a/lib/room.h +++ b/lib/room.h @@ -90,8 +90,8 @@ class Room : public QObject { Q_PROPERTY(QString predecessorId READ predecessorId NOTIFY baseStateLoaded) Q_PROPERTY(QString successorId READ successorId NOTIFY upgraded) Q_PROPERTY(QString name READ name NOTIFY namesChanged) - Q_PROPERTY(QStringList localAliases READ localAliases NOTIFY namesChanged) - Q_PROPERTY(QStringList remoteAliases READ remoteAliases NOTIFY namesChanged) + Q_PROPERTY(QStringList aliases READ aliases NOTIFY namesChanged) + Q_PROPERTY(QStringList altAliases READ altAliases NOTIFY namesChanged) Q_PROPERTY(QString canonicalAlias READ canonicalAlias NOTIFY namesChanged) Q_PROPERTY(QString displayName READ displayName NOTIFY displaynameChanged) Q_PROPERTY(QString topic READ topic NOTIFY topicChanged) -- cgit v1.2.3 From 89573ca2a1c19d79c1417e64613f1a0fa696837c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 24 Mar 2020 13:02:24 +0100 Subject: Connection: fix a deprecation warning --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 7400c82d..ca866429 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -619,7 +619,7 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) if (is(accountEvent)) qCDebug(MAIN) << "Users ignored by" << userId() << "updated:" - << QStringList::fromSet(ignoredUsers()).join(','); + << QStringList(ignoredUsers().values()).join(','); auto& currentData = d->accountData[accountEvent.matrixType()]; // A polymorphic event-specific comparison might be a bit -- cgit v1.2.3 From ef59622e3506c7d234c1fff7dea1aa921f965e25 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Mar 2020 09:25:20 +0100 Subject: Room::aliases(): don't add canonical alias if it's empty Fixes #394. --- lib/room.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 6ac2673e..43a42492 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -523,7 +523,10 @@ QString Room::name() const QStringList Room::aliases() const { const auto* evt = d->getCurrentState(); - return QStringList(evt->altAliases()) << evt->alias(); + auto result = evt->altAliases(); + if (!evt->alias().isEmpty()) + result << evt->alias(); + return result; } QStringList Room::altAliases() const -- cgit v1.2.3 From 3ba2acce470407854bb38b2633675e916a51a904 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Mar 2020 09:28:11 +0100 Subject: AliasesEventContent::toJson(): optimise generated JSON --- lib/events/roomcanonicalaliasevent.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 311a5959..5d680de7 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -37,10 +37,12 @@ namespace EventContent{ , altAliases(fromJson(json["alt_aliases"])) { } - QJsonObject toJson() const + auto toJson() const { - return { { "alias", Quotient::toJson(canonicalAlias) }, - { "alt_aliases", Quotient::toJson(altAliases) } }; + QJsonObject jo; + addParam(jo, QStringLiteral("alias"), canonicalAlias); + addParam(jo, QStringLiteral("alt_aliases"), altAliases); + return jo; } QString canonicalAlias; @@ -68,7 +70,6 @@ public: { } QString alias() const { return content().canonicalAlias; } - QStringList altAliases() const { return content().altAliases; } }; REGISTER_EVENT_TYPE(RoomCanonicalAliasEvent) -- cgit v1.2.3 From ec4110c63443e29c78fdf0f72af08f5395ec48f7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Mar 2020 13:25:09 +0100 Subject: Refactoring around Connection::onSyncSuccess() The method grew large and a bit unwieldy over the years. --- lib/connection.cpp | 195 ++++++++++++++++++++++++-------------------- lib/events/event.h | 11 +++ lib/events/roomkeyevent.cpp | 6 +- lib/events/roomkeyevent.h | 14 +--- lib/room.cpp | 11 +-- lib/room.h | 2 +- 6 files changed, 129 insertions(+), 110 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index ca866429..98515617 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -131,6 +131,11 @@ public: const QString& deviceId); void removeRoom(const QString& roomId); + void consumeRoomData(SyncDataList&& roomDataList, bool fromCache); + void consumeAccountData(Events&& accountDataEvents); + void consumePresenceData(Events&& presenceData); + void consumeToDeviceEvents(Events&& toDeviceEvents); + template EventT* unpackAccountData() const { @@ -534,10 +539,32 @@ QJsonObject toJson(const DirectChatsMap& directChats) void Connection::onSyncSuccess(SyncData&& data, bool fromCache) { d->data->setLastEvent(data.nextBatch()); - for (auto&& roomData : data.takeRoomData()) { - const auto forgetIdx = d->roomIdsToForget.indexOf(roomData.roomId); + d->consumeRoomData(data.takeRoomData(), fromCache); + d->consumeAccountData(data.takeAccountData()); + d->consumePresenceData(data.takePresenceData()); + d->consumeToDeviceEvents(data.takeToDeviceEvents()); +#ifdef Quotient_E2EE_ENABLED + // handling device_one_time_keys_count + if (!d->encryptionManager) + { + qCDebug(E2EE) << "Encryption manager is not there yet, updating " + "one-time key counts will be skipped"; + return; + } + if (const auto deviceOneTimeKeysCount = data.deviceOneTimeKeysCount(); + !deviceOneTimeKeysCount.isEmpty()) + d->encryptionManager->updateOneTimeKeyCounts(this, + deviceOneTimeKeysCount); +#endif // Quotient_E2EE_ENABLED +} + +void Connection::Private::consumeRoomData(SyncDataList&& roomDataList, + bool fromCache) +{ + for (auto&& roomData: roomDataList) { + const auto forgetIdx = roomIdsToForget.indexOf(roomData.roomId); if (forgetIdx != -1) { - d->roomIdsToForget.removeAt(forgetIdx); + roomIdsToForget.removeAt(forgetIdx); if (roomData.joinState == JoinState::Leave) { qDebug(MAIN) << "Room" << roomData.roomId @@ -549,12 +576,12 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) << toCString(roomData.joinState) << "state - suspiciously fast turnaround"; } - if (auto* r = provideRoom(roomData.roomId, roomData.joinState)) { - d->pendingStateRoomIds.removeOne(roomData.roomId); + if (auto* r = q->provideRoom(roomData.roomId, roomData.joinState)) { + pendingStateRoomIds.removeOne(roomData.roomId); r->updateData(std::move(roomData), fromCache); - if (d->firstTimeRooms.removeOne(r)) { - emit loadedRoomState(r); - if (d->capabilities.roomVersions) + if (firstTimeRooms.removeOne(r)) { + emit q->loadedRoomState(r); + if (capabilities.roomVersions) r->checkVersion(); // Otherwise, the version will be checked in reloadCapabilities() } @@ -562,25 +589,28 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) // Let UI update itself after updating each room QCoreApplication::processEvents(); } +} + +void Connection::Private::consumeAccountData(Events&& accountDataEvents) +{ // After running this loop, the account data events not saved in - // d->accountData (see the end of the loop body) are auto-cleaned away - for (auto& eventPtr : data.takeAccountData()) { - visit( - *eventPtr, + // accountData (see the end of the loop body) are auto-cleaned away + for (auto&& eventPtr: accountDataEvents) { + visit(*eventPtr, [this](const DirectChatEvent& dce) { // https://github.com/quotient-im/libQuotient/wiki/Handling-direct-chat-events const auto& usersToDCs = dce.usersToDirectChats(); DirectChatsMap remoteRemovals = - erase_if(d->directChats, [&usersToDCs, this](auto it) { - return !(usersToDCs.contains(it.key()->id(), it.value()) - || d->dcLocalAdditions.contains(it.key(), - it.value())); + erase_if(directChats, [&usersToDCs, this](auto it) { + return !( + usersToDCs.contains(it.key()->id(), it.value()) + || dcLocalAdditions.contains(it.key(), it.value())); }); - erase_if(d->directChatUsers, [&remoteRemovals](auto it) { + erase_if(directChatUsers, [&remoteRemovals](auto it) { return remoteRemovals.contains(it.value(), it.key()); }); // Remove from dcLocalRemovals what the server already has. - erase_if(d->dcLocalRemovals, [&remoteRemovals](auto it) { + erase_if(dcLocalRemovals, [&remoteRemovals](auto it) { return remoteRemovals.contains(it.key(), it.value()); }); if (MAIN().isDebugEnabled()) @@ -593,13 +623,13 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) DirectChatsMap remoteAdditions; for (auto it = usersToDCs.begin(); it != usersToDCs.end(); ++it) { - if (auto* u = user(it.key())) { - if (!d->directChats.contains(u, it.value()) - && !d->dcLocalRemovals.contains(u, it.value())) { - Q_ASSERT(!d->directChatUsers.contains(it.value(), u)); + if (auto* u = q->user(it.key())) { + if (!directChats.contains(u, it.value()) + && !dcLocalRemovals.contains(u, it.value())) { + Q_ASSERT(!directChatUsers.contains(it.value(), u)); remoteAdditions.insert(u, it.value()); - d->directChats.insert(u, it.value()); - d->directChatUsers.insert(it.value(), u); + directChats.insert(u, it.value()); + directChatUsers.insert(it.value(), u); qCDebug(MAIN) << "Marked room" << it.value() << "as a direct chat with" << u->id(); } @@ -608,20 +638,21 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) << "Couldn't get a user object for" << it.key(); } // Remove from dcLocalAdditions what the server already has. - erase_if(d->dcLocalAdditions, [&remoteAdditions](auto it) { + erase_if(dcLocalAdditions, [&remoteAdditions](auto it) { return remoteAdditions.contains(it.key(), it.value()); }); if (!remoteAdditions.isEmpty() || !remoteRemovals.isEmpty()) - emit directChatsListChanged(remoteAdditions, remoteRemovals); + emit q->directChatsListChanged(remoteAdditions, + remoteRemovals); }, // catch-all, passing eventPtr for a possible take-over [this, &eventPtr](const Event& accountEvent) { if (is(accountEvent)) qCDebug(MAIN) - << "Users ignored by" << userId() << "updated:" - << QStringList(ignoredUsers().values()).join(','); + << "Users ignored by" << data->userId() << "updated:" + << QStringList(q->ignoredUsers().values()).join(','); - auto& currentData = d->accountData[accountEvent.matrixType()]; + auto& currentData = accountData[accountEvent.matrixType()]; // A polymorphic event-specific comparison might be a bit // more efficient; maaybe do it another day if (!currentData @@ -629,74 +660,58 @@ void Connection::onSyncSuccess(SyncData&& data, bool fromCache) currentData = std::move(eventPtr); qCDebug(MAIN) << "Updated account data of type" << currentData->matrixType(); - emit accountDataChanged(currentData->matrixType()); + emit q->accountDataChanged(currentData->matrixType()); } }); } - if (!d->dcLocalAdditions.isEmpty() || !d->dcLocalRemovals.isEmpty()) { + if (!dcLocalAdditions.isEmpty() || !dcLocalRemovals.isEmpty()) { qDebug(MAIN) << "Sending updated direct chats to the server:" - << d->dcLocalRemovals.size() << "removal(s)," - << d->dcLocalAdditions.size() << "addition(s)"; - callApi(userId(), QStringLiteral("m.direct"), - toJson(d->directChats)); - d->dcLocalAdditions.clear(); - d->dcLocalRemovals.clear(); + << dcLocalRemovals.size() << "removal(s)," + << dcLocalAdditions.size() << "addition(s)"; + q->callApi(data->userId(), QStringLiteral("m.direct"), + toJson(directChats)); + dcLocalAdditions.clear(); + dcLocalRemovals.clear(); } -#ifndef Quotient_E2EE_ENABLED - qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; -#else // Quotient_E2EE_ENABLED - // handling m.room_key to-device encrypted event - for (auto&& toDeviceEvent : data.takeToDeviceEvents()) { - if (toDeviceEvent->type() == EncryptedEvent::typeId()) { - event_ptr_tt encryptedEvent = - makeEvent(toDeviceEvent->fullJson()); - if (encryptedEvent->algorithm() != OlmV1Curve25519AesSha2AlgoKey) { - qCDebug(E2EE) - << "Encrypted event" << encryptedEvent->id() << "algorithm" - << encryptedEvent->algorithm() << "is not supported"; - return; - } +} - // TODO: full maintaining of the device keys - // with device_lists sync extention and /keys/query - qCDebug(E2EE) << "Getting device keys for the m.room_key sender:" - << encryptedEvent->senderId(); - // d->encryptionManager->updateDeviceKeys(); - - RoomEventPtr decryptedEvent = - d->sessionDecryptMessage(*encryptedEvent.get()); - // since we are waiting for the RoomKeyEvent: - event_ptr_tt roomKeyEvent = - makeEvent(decryptedEvent->fullJson()); - if (!roomKeyEvent) { - qCDebug(E2EE) << "Failed to decrypt olm event from user" - << encryptedEvent->senderId(); - return; - } - Room* detectedRoom = room(roomKeyEvent->roomId()); - if (!detectedRoom) { - qCDebug(E2EE) - << "Encrypted event room id" << encryptedEvent->roomId() - << "is not found at the connection"; - return; - } - detectedRoom->handleRoomKeyEvent(roomKeyEvent.get(), - encryptedEvent->senderKey()); +void Connection::Private::consumePresenceData(Events&& presenceData) +{ + // To be implemented +} + +void Connection::Private::consumeToDeviceEvents(Events&& toDeviceEvents) +{ +#ifdef Quotient_E2EE_ENABLED + // handling m.room_key to-device encrypted event + visitEach(toDeviceEvents, [this](const EncryptedEvent& ee) { + if (ee.algorithm() != OlmV1Curve25519AesSha2AlgoKey) { + qCDebug(E2EE) << "Encrypted event" << ee.id() << "algorithm" + << ee.algorithm() << "is not supported"; + return; } - } - // handling device_one_time_keys_count - auto deviceOneTimeKeysCount = data.deviceOneTimeKeysCount(); - if (!d->encryptionManager) - { - qCDebug(E2EE) << "Encryption manager is not there yet"; - return; - } - if (!deviceOneTimeKeysCount.isEmpty()) - { - d->encryptionManager->updateOneTimeKeyCounts(this, - deviceOneTimeKeysCount); - } -#endif // Quotient_E2EE_ENABLED + + // TODO: full maintaining of the device keys + // with device_lists sync extention and /keys/query + qCDebug(E2EE) << "Getting device keys for the m.room_key sender:" + << ee.senderId(); + // encryptionManager->updateDeviceKeys(); + + visit(*sessionDecryptMessage(ee), + [this, senderKey = ee.senderKey()](const RoomKeyEvent& roomKeyEvent) { + if (auto* detectedRoom = q->room(roomKeyEvent.roomId())) + detectedRoom->handleRoomKeyEvent(roomKeyEvent, senderKey); + else + qCDebug(E2EE) + << "Encrypted event room id" << roomKeyEvent.roomId() + << "is not found at the connection" << q->objectName(); + }, + [](const Event& evt) { + qCDebug(E2EE) << "Skipping encrypted to_device event, type" + << evt.matrixType(); + }); + }); +#endif } void Connection::stopSync() diff --git a/lib/events/event.h b/lib/events/event.h index f985ae92..6c8961ad 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -377,6 +377,17 @@ inline fn_return_t visit(const BaseEventT& event, FnT1&& visitor1, return visit(event, std::forward(visitor2), std::forward(visitors)...); } + +// A facility overload that calls void-returning visit() on each event +// over a range of event pointers +template +inline auto visitEach(RangeT&& events, FnTs&&... visitors) + -> std::enable_if_t, Event>> +{ + for (auto&& evtPtr: events) + visit(*evtPtr, std::forward(visitors)...); +} } // namespace Quotient Q_DECLARE_METATYPE(Quotient::Event*) Q_DECLARE_METATYPE(const Quotient::Event*) diff --git a/lib/events/roomkeyevent.cpp b/lib/events/roomkeyevent.cpp index 1fb2e9f5..66580430 100644 --- a/lib/events/roomkeyevent.cpp +++ b/lib/events/roomkeyevent.cpp @@ -4,8 +4,6 @@ using namespace Quotient; RoomKeyEvent::RoomKeyEvent(const QJsonObject &obj) : Event(typeId(), obj) { - _algorithm = contentJson()["algorithm"_ls].toString(); - _roomId = contentJson()["room_id"_ls].toString(); - _sessionId = contentJson()["session_id"_ls].toString(); - _sessionKey = contentJson()["session_key"_ls].toString(); + if (roomId().isEmpty()) + qCWarning(E2EE) << "Room key event has empty room id"; } diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h index e4bcfd71..679cbf7c 100644 --- a/lib/events/roomkeyevent.h +++ b/lib/events/roomkeyevent.h @@ -10,16 +10,10 @@ public: RoomKeyEvent(const QJsonObject& obj); - const QString algorithm() const { return _algorithm; } - const QString roomId() const { return _roomId; } - const QString sessionId() const { return _sessionId; } - const QString sessionKey() const { return _sessionKey; } - -private: - QString _algorithm; - QString _roomId; - QString _sessionId; - QString _sessionKey; + QString algorithm() const { return content("algorithm"_ls); } + QString roomId() const { return content("room_id"_ls); } + QString sessionId() const { return content("session_id"_ls); } + QString sessionKey() const { return content("session_key"_ls); } }; REGISTER_EVENT_TYPE(RoomKeyEvent) } // namespace Quotient diff --git a/lib/room.cpp b/lib/room.cpp index 43a42492..16f81813 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1278,7 +1278,8 @@ RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) #endif // Quotient_E2EE_ENABLED } -void Room::handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey) +void Room::handleRoomKeyEvent(const RoomKeyEvent& roomKeyEvent, + const QString& senderKey) { #ifndef Quotient_E2EE_ENABLED Q_UNUSED(roomKeyEvent); @@ -1286,12 +1287,12 @@ void Room::handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey) qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; return; #else // Quotient_E2EE_ENABLED - if (roomKeyEvent->algorithm() != MegolmV1AesSha2AlgoKey) { + if (roomKeyEvent.algorithm() != MegolmV1AesSha2AlgoKey) { qCWarning(E2EE) << "Ignoring unsupported algorithm" - << roomKeyEvent->algorithm() << "in m.room_key event"; + << roomKeyEvent.algorithm() << "in m.room_key event"; } - if (d->addInboundGroupSession(senderKey, roomKeyEvent->sessionId(), - roomKeyEvent->sessionKey())) { + if (d->addInboundGroupSession(senderKey, roomKeyEvent.sessionId(), + roomKeyEvent.sessionKey())) { qCDebug(E2EE) << "added new inboundGroupSession:" << d->groupSessions.count(); } diff --git a/lib/room.h b/lib/room.h index 4e4d029f..a5b933c2 100644 --- a/lib/room.h +++ b/lib/room.h @@ -215,7 +215,7 @@ public: int timelineSize() const; bool usesEncryption() const; RoomEventPtr decryptMessage(const EncryptedEvent& encryptedEvent); - void handleRoomKeyEvent(RoomKeyEvent* roomKeyEvent, QString senderKey); + void handleRoomKeyEvent(const RoomKeyEvent& roomKeyEvent, const QString& senderKey); int joinedCount() const; int invitedCount() const; int totalMemberCount() const; -- cgit v1.2.3 From a81590c9e3b26f76d02d498802a44719c116fa13 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 27 Mar 2020 11:25:59 +0100 Subject: Fix FTBFS with Quotient_ENABLE_E2EE --- lib/connection.cpp | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 98515617..021ff5dd 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -165,25 +165,24 @@ public: return q->stateCacheDir().filePath("state.json"); } - RoomEventPtr sessionDecryptMessage(const EncryptedEvent& encryptedEvent) + EventPtr sessionDecryptMessage(const EncryptedEvent& encryptedEvent) { #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; return {}; #else // Quotient_E2EE_ENABLED if (encryptedEvent.algorithm() != OlmV1Curve25519AesSha2AlgoKey) - { return {}; - } - QString identityKey = + + const auto identityKey = encryptionManager->account()->curve25519IdentityKey(); - QJsonObject personalCipherObject = + const auto personalCipherObject = encryptedEvent.ciphertext(identityKey); if (personalCipherObject.isEmpty()) { qCDebug(E2EE) << "Encrypted event is not for the current device"; return {}; } - QString decrypted = encryptionManager->sessionDecryptMessage( + const auto decrypted = encryptionManager->sessionDecryptMessage( personalCipherObject, encryptedEvent.senderKey().toLatin1()); if (decrypted.isEmpty()) { qCDebug(E2EE) << "Problem with new session from senderKey:" @@ -192,29 +191,29 @@ public: return {}; } - RoomEventPtr decryptedEvent = makeEvent( - QJsonDocument::fromJson(decrypted.toUtf8()).object()); + auto&& decryptedEvent = + fromJson(QJsonDocument::fromJson(decrypted.toUtf8())); - if (decryptedEvent->senderId() != encryptedEvent.senderId()) { - qCDebug(E2EE) << "Found user" << decryptedEvent->senderId() + if (auto sender = decryptedEvent->fullJson()["sender"_ls].toString(); + sender != encryptedEvent.senderId()) { + qCWarning(E2EE) << "Found user" << sender << "instead of sender" << encryptedEvent.senderId() << "in Olm plaintext"; return {}; } // TODO: keys to constants - QJsonObject decryptedEventObject = decryptedEvent->fullJson(); - QString recipient = + const auto decryptedEventObject = decryptedEvent->fullJson(); + const auto recipient = decryptedEventObject.value("recipient"_ls).toString(); if (recipient != data->userId()) { qCDebug(E2EE) << "Found user" << recipient << "instead of us" << data->userId() << "in Olm plaintext"; return {}; } - QString ourKey = decryptedEventObject.value("recipient_keys"_ls) - .toObject() - .value(Ed25519Key) - .toString(); + const auto ourKey = + decryptedEventObject.value("recipient_keys"_ls).toObject() + .value(Ed25519Key).toString(); if (ourKey != QString::fromUtf8( encryptionManager->account()->ed25519IdentityKey())) { @@ -225,7 +224,7 @@ public: return {}; } - return decryptedEvent; + return std::move(decryptedEvent); #endif // Quotient_E2EE_ENABLED } }; @@ -390,8 +389,8 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs) #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED - encryptionManager->uploadIdentityKeys(this); - encryptionManager->uploadOneTimeKeys(this); + encryptionManager->uploadIdentityKeys(q); + encryptionManager->uploadOneTimeKeys(q); #endif // Quotient_E2EE_ENABLED }); connect(loginJob, &BaseJob::failure, q, [this, loginJob] { -- cgit v1.2.3 From ecc31fc7a615faa516a03c07ebd96d0d2494c315 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 30 Mar 2020 19:12:06 +0200 Subject: BaseJob: shutdown timers on abandoning and destruction A part of the fix for #398. --- lib/jobs/basejob.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 68adeaf6..8b63f7e8 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -145,6 +145,7 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, BaseJob::~BaseJob() { stop(); + d->retryTimer.stop(); // See #398 qCDebug(d->logCat) << this << "destroyed"; } @@ -686,6 +687,8 @@ void BaseJob::setStatus(int code, QString message) void BaseJob::abandon() { beforeAbandon(d->reply ? d->reply.data() : nullptr); + d->timer.stop(); + d->retryTimer.stop(); // In case abandon() was called between retries setStatus(Abandoned); if (d->reply) d->reply->disconnect(this); -- cgit v1.2.3 From d38516e4e123474876e4fc7f850fe321ec5ccb54 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 30 Mar 2020 19:12:40 +0200 Subject: BaseJob: check the connection even harder --- lib/jobs/basejob.cpp | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 8b63f7e8..65668521 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -234,6 +234,7 @@ void BaseJob::Private::sendRequest() req.setMaximumRedirectsAllowed(10); req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) req.setRawHeader(it.key(), it.value()); @@ -261,25 +262,31 @@ void BaseJob::beforeAbandon(QNetworkReply*) {} void BaseJob::initiate(ConnectionData* connData, bool inBackground) { - Q_ASSERT(connData != nullptr); + if (connData && connData->baseUrl().isValid()) { + d->inBackground = inBackground; + d->connection = connData; + doPrepare(); - d->inBackground = inBackground; - d->connection = connData; - doPrepare(); - - if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) - && d->requestData.source() && !d->requestData.source()->isReadable()) { - setStatus(FileError, "Request data not ready"); - } - Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this - if (status().code == Unprepared) { - d->connection->submit(this); - } else { - qDebug(d->logCat).noquote() + if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) + && d->requestData.source() && !d->requestData.source()->isReadable()) { + setStatus(FileError, "Request data not ready"); + } + Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this + if (status().code == Unprepared) { + d->connection->submit(this); + return; + } + qCWarning(d->logCat).noquote() << "Request failed preparation and won't be sent:" << d->dumpRequest(); - QTimer::singleShot(0, this, &BaseJob::finishJob); + } else { + qCCritical(d->logCat) + << "Developers, ensure the Connection is valid before using it"; + Q_ASSERT(false); + setStatus(IncorrectRequestError, tr("Invalid server connection")); } + // The status is no good, finalise + QTimer::singleShot(0, this, &BaseJob::finishJob); } void BaseJob::sendRequest() @@ -338,7 +345,7 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) // ignore possible appendixes of the content type const auto ctype = type.split(';').front(); - for (const auto& pattern : patterns) { + for (const auto& pattern: patterns) { if (pattern.startsWith('*') || ctype == pattern) // Fast lane return true; -- cgit v1.2.3 From 0a7008bfd345afb083d8ac8c0c6cf21f879aedc6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 30 Mar 2020 19:16:11 +0200 Subject: Connection::resolveServer(): refactor Also: use 4-arg connect() to make sure lambdas are disconnected if the connection is gone. --- lib/connection.cpp | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 021ff5dd..6babcf27 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -253,51 +253,48 @@ void Connection::resolveServer(const QString& mxid) return; } - setHomeserver(maybeBaseUrl); - auto domain = maybeBaseUrl.host(); qCDebug(MAIN) << "Finding the server" << domain; + d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file auto getWellKnownJob = callApi(); - connect( - getWellKnownJob, &BaseJob::finished, + connect(getWellKnownJob, &BaseJob::finished, this, [this, getWellKnownJob, maybeBaseUrl] { - if (getWellKnownJob->status() == BaseJob::NotFoundError) - qCDebug(MAIN) << "No .well-known file, IGNORE"; - else { + if (getWellKnownJob->status() != BaseJob::NotFoundError) { if (getWellKnownJob->status() != BaseJob::Success) { - qCDebug(MAIN) + qCWarning(MAIN) << "Fetching .well-known file failed, FAIL_PROMPT"; - emit resolveError(tr("Fetching .well-known file failed")); + emit resolveError(tr("Failed resolving the homeserver")); return; } - QUrl baseUrl(getWellKnownJob->data().homeserver.baseUrl); + QUrl baseUrl { getWellKnownJob->data().homeserver.baseUrl }; if (baseUrl.isEmpty()) { - qCDebug(MAIN) << "base_url not provided, FAIL_PROMPT"; - emit resolveError(tr("base_url not provided")); + qCWarning(MAIN) << "base_url not provided, FAIL_PROMPT"; + emit resolveError( + tr("The homeserver base URL is not provided")); return; } if (!baseUrl.isValid()) { - qCDebug(MAIN) << "base_url invalid, FAIL_ERROR"; - emit resolveError(tr("base_url invalid")); + qCWarning(MAIN) << "base_url invalid, FAIL_ERROR"; + emit resolveError(tr("The homeserver base URL is invalid")); return; } - - qCDebug(MAIN) << ".well-known for" << maybeBaseUrl.host() - << "is" << baseUrl.toString(); + qCInfo(MAIN) << ".well-known URL for" << maybeBaseUrl.host() + << "is" << baseUrl.authority(); setHomeserver(baseUrl); + } else { + qCInfo(MAIN) << "No .well-known file, using" << maybeBaseUrl + << "for base URL"; + setHomeserver(maybeBaseUrl); } auto getVersionsJob = callApi(); - - connect(getVersionsJob, &BaseJob::finished, [this, getVersionsJob] { - if (getVersionsJob->status() == BaseJob::Success) { - qCDebug(MAIN) << "homeserver url is valid"; - emit resolved(); - } else { - qCDebug(MAIN) << "homeserver url invalid"; - emit resolveError(tr("homeserver url invalid")); - } + connect(getVersionsJob, &BaseJob::success, this, + &Connection::resolved); + connect(getVersionsJob, &BaseJob::failure, this, [this] { + qCWarning(MAIN) << "Homeserver base URL invalid"; + emit resolveError(tr("The homeserver base URL " + "doesn't seem to work")); }); }); } -- cgit v1.2.3 From 86b29318385edf07ecbaca975e5ab90f11304d1a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 30 Mar 2020 21:57:40 +0200 Subject: Reparent all jobs to their connections (Mostly) fixes #397. Fixes #398. If there's a need to reparent the job to some other object, it should now be done after callApi()/run() call (not that there were many touch points before that moment, anyway). Collateral damage: job-starting methods are no more const (but that didn't belong them anyway, too). --- lib/connection.cpp | 23 ++++++++++++----------- lib/connection.h | 38 ++++++++++++++++++-------------------- 2 files changed, 30 insertions(+), 31 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 6babcf27..1c63adeb 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -723,7 +723,7 @@ void Connection::stopSync() QString Connection::nextBatchToken() const { return d->data->lastEvent(); } -PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const +PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) { return callApi(room->id(), "m.read", event->id()); } @@ -773,7 +773,7 @@ inline auto splitMediaId(const QString& mediaId) MediaThumbnailJob* Connection::getThumbnail(const QString& mediaId, QSize requestedSize, - RunningPolicy policy) const + RunningPolicy policy) { auto idParts = splitMediaId(mediaId); return callApi(policy, idParts.front(), idParts.back(), @@ -781,21 +781,21 @@ MediaThumbnailJob* Connection::getThumbnail(const QString& mediaId, } MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, QSize requestedSize, - RunningPolicy policy) const + RunningPolicy policy) { return getThumbnail(url.authority() + url.path(), requestedSize, policy); } MediaThumbnailJob* Connection::getThumbnail(const QUrl& url, int requestedWidth, int requestedHeight, - RunningPolicy policy) const + RunningPolicy policy) { return getThumbnail(url, QSize(requestedWidth, requestedHeight), policy); } UploadContentJob* Connection::uploadContent(QIODevice* contentSource, const QString& filename, - const QString& overrideContentType) const + const QString& overrideContentType) { Q_ASSERT(contentSource != nullptr); auto contentType = overrideContentType; @@ -820,19 +820,19 @@ UploadContentJob* Connection::uploadFile(const QString& fileName, overrideContentType); } -GetContentJob* Connection::getContent(const QString& mediaId) const +GetContentJob* Connection::getContent(const QString& mediaId) { auto idParts = splitMediaId(mediaId); return callApi(idParts.front(), idParts.back()); } -GetContentJob* Connection::getContent(const QUrl& url) const +GetContentJob* Connection::getContent(const QUrl& url) { return getContent(url.authority() + url.path()); } DownloadFileJob* Connection::downloadFile(const QUrl& url, - const QString& localFilename) const + const QString& localFilename) { auto mediaId = url.authority() + url.path(); auto idParts = splitMediaId(mediaId); @@ -1011,7 +1011,7 @@ ForgetRoomJob* Connection::forgetRoom(const QString& id) SendToDeviceJob* Connection::sendToDevices(const QString& eventType, - const UsersToDevicesToEvents& eventsMap) const + const UsersToDevicesToEvents& eventsMap) { QHash> json; json.reserve(int(eventsMap.size())); @@ -1032,7 +1032,7 @@ Connection::sendToDevices(const QString& eventType, } SendMessageJob* Connection::sendMessage(const QString& roomId, - const RoomEvent& event) const + const RoomEvent& event) { const auto txnId = event.transactionId().isEmpty() ? generateTxnId() : event.transactionId(); @@ -1609,8 +1609,9 @@ void Connection::setLazyLoading(bool newValue) } } -void Connection::run(BaseJob* job, RunningPolicy runningPolicy) const +void Connection::run(BaseJob* job, RunningPolicy runningPolicy) { + job->setParent(this); // Protects from #397, #398 connect(job, &BaseJob::failure, this, &Connection::requestFailed); job->initiate(d->data.get(), runningPolicy & BackgroundRequest); } diff --git a/lib/connection.h b/lib/connection.h index 350571f1..b0263732 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -423,7 +423,7 @@ public: void setLazyLoading(bool newValue); /*! Start a pre-created job object on this connection */ - void run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest) const; + void run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest); /*! Start a job of a specified type with specified arguments and policy * @@ -438,7 +438,7 @@ public: * \sa BaseJob::isBackground. QNetworkRequest::BackgroundRequestAttribute */ template - JobT* callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs) const + JobT* callApi(RunningPolicy runningPolicy, JobArgTs&&... jobArgs) { auto job = new JobT(std::forward(jobArgs)...); run(job, runningPolicy); @@ -450,7 +450,7 @@ public: * This is an overload that runs the job with "foreground" policy. */ template - JobT* callApi(JobArgTs&&... jobArgs) const + JobT* callApi(JobArgTs&&... jobArgs) { return callApi(ForegroundRequest, std::forward(jobArgs)...); @@ -544,25 +544,24 @@ public slots: virtual MediaThumbnailJob* getThumbnail(const QString& mediaId, QSize requestedSize, - RunningPolicy policy = BackgroundRequest) const; - MediaThumbnailJob* - getThumbnail(const QUrl& url, QSize requestedSize, - RunningPolicy policy = BackgroundRequest) const; - MediaThumbnailJob* - getThumbnail(const QUrl& url, int requestedWidth, int requestedHeight, - RunningPolicy policy = BackgroundRequest) const; + RunningPolicy policy = BackgroundRequest); + MediaThumbnailJob* getThumbnail(const QUrl& url, QSize requestedSize, + RunningPolicy policy = BackgroundRequest); + MediaThumbnailJob* getThumbnail(const QUrl& url, int requestedWidth, + int requestedHeight, + RunningPolicy policy = BackgroundRequest); // QIODevice* should already be open - UploadContentJob* - uploadContent(QIODevice* contentSource, const QString& filename = {}, - const QString& overrideContentType = {}) const; + UploadContentJob* uploadContent(QIODevice* contentSource, + const QString& filename = {}, + const QString& overrideContentType = {}); UploadContentJob* uploadFile(const QString& fileName, const QString& overrideContentType = {}); - GetContentJob* getContent(const QString& mediaId) const; - GetContentJob* getContent(const QUrl& url) const; + GetContentJob* getContent(const QString& mediaId); + GetContentJob* getContent(const QUrl& url); // If localFilename is empty, a temporary file will be created DownloadFileJob* downloadFile(const QUrl& url, - const QString& localFilename = {}) const; + const QString& localFilename = {}); /** * \brief Create a room (generic method) @@ -641,11 +640,10 @@ public slots: ForgetRoomJob* forgetRoom(const QString& id); SendToDeviceJob* sendToDevices(const QString& eventType, - const UsersToDevicesToEvents& eventsMap) const; + const UsersToDevicesToEvents& eventsMap); /** \deprecated This method is experimental and may be removed any time */ - SendMessageJob* sendMessage(const QString& roomId, - const RoomEvent& event) const; + SendMessageJob* sendMessage(const QString& roomId, const RoomEvent& event); /** \deprecated Do not use this directly, use Room::leaveRoom() instead */ virtual LeaveRoomJob* leaveRoom(Room* room); @@ -654,7 +652,7 @@ public slots: /** @deprecated Use callApi() or Room::postReceipt() instead */ - virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event) const; + virtual PostReceiptJob* postReceipt(Room* room, RoomEvent* event); signals: /** -- cgit v1.2.3 From 3d04593f513916ee6e4fcb1ec2d18afa7b76590a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 5 Apr 2020 15:48:31 +0200 Subject: RequestData: create empty QByteArray by default May fix #380. Also: remove explicit copying disablers, the unique_ptr<> member disables copying just fine. --- lib/jobs/requestdata.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h index 020d5ef2..9cb5ecaf 100644 --- a/lib/jobs/requestdata.h +++ b/lib/jobs/requestdata.h @@ -18,9 +18,10 @@ #pragma once +#include + #include -class QByteArray; class QJsonObject; class QJsonArray; class QJsonDocument; @@ -35,14 +36,11 @@ namespace Quotient { */ class RequestData { public: - RequestData() = default; - RequestData(const QByteArray& a); + RequestData(const QByteArray& a = {}); RequestData(const QJsonObject& jo); RequestData(const QJsonArray& ja); RequestData(QIODevice* source) : _source(std::unique_ptr(source)) {} - RequestData(const RequestData&) = delete; - RequestData& operator=(const RequestData&) = delete; RequestData(RequestData&&) = default; RequestData& operator=(RequestData&&) = default; ~RequestData(); -- cgit v1.2.3 From ab9996fc62767d61da779d869e68bfc966ffce8f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 6 Apr 2020 20:53:26 +0200 Subject: CMakeLists: install header files to a subdirectory This is a Quotient part of #328 fix; QtOlm part is pending. --- lib/jobs/basejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 65668521..4ede073f 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -232,7 +232,7 @@ void BaseJob::Private::sendRequest() req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); +// req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) -- cgit v1.2.3 From a8572345e7a30c96b8ede47e95af65ff2cdef86c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 6 Apr 2020 21:20:32 +0200 Subject: Revert changes accidentally sneaked in with the previous commit --- lib/jobs/basejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 4ede073f..65668521 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -232,7 +232,7 @@ void BaseJob::Private::sendRequest() req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); -// req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) -- cgit v1.2.3 From fc2614d88ccc3c938ea69fd6a8f4978bc9663e16 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 6 Apr 2020 21:58:26 +0200 Subject: BaseJob: disable pipelining Longer running on RHEL/CentOS 8 leads to crashes that no more occur with disabled pipelining. --- lib/jobs/basejob.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 65668521..dfc3d3dd 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -232,7 +232,9 @@ void BaseJob::Private::sendRequest() req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + // Pipelining doesn't fly quite well with SSL, occasionally crashing at + // what seems like an attempt to write to a closed channel. +// req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) -- cgit v1.2.3 From 254c971106b8cc5603caea123803a70bc4bf2e6e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 7 Apr 2020 16:11:27 +0200 Subject: Room::addNewMessageEvents: fix an assertion failure on empty sender When a message is redacted it has no sender. If it happens to be in the bulkhead, Quotient tries to promote a read marker over it and fails on being unable to resolve the author. --- lib/room.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 16f81813..5d4e2141 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2309,7 +2309,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (q->supportsCalls()) for (auto it = from; it != timeline.cend(); ++it) - if (auto* evt = it->viewAs()) + if (const auto* evt = it->viewAs()) emit q->callEvent(q, evt); if (totalInserted > 0) { @@ -2331,12 +2331,15 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) // read receipts from the server (or, for the local user, // markMessagesAsRead() invocation) to promote their read markers over // the new message events. - auto firstWriter = q->user((*from)->senderId()); - if (q->readMarker(firstWriter) != timeline.crend()) { - roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(STATE) << "Auto-promoted read marker for" - << firstWriter->id() << "to" - << *q->readMarker(firstWriter); + if (const auto senderId = (*from)->senderId(); !senderId.isEmpty()) { + auto* const firstWriter = q->user(senderId); + if (q->readMarker(firstWriter) != timeline.crend()) { + roomChanges |= + promoteReadMarker(firstWriter, rev_iter_t(from) - 1); + qCDebug(STATE) + << "Auto-promoted read marker for" << senderId + << "to" << *q->readMarker(firstWriter); + } } updateUnreadCount(timeline.crbegin(), rev_iter_t(from)); -- cgit v1.2.3 From 160b11bdfe32c9983fe5f34eafa783148b2decbe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 7 Apr 2020 16:14:21 +0200 Subject: BaseJob: don't finish and re-try at the same time Due to a missing return statement, a retry with auth case led to the job being finished and pending at the same time, with no good consequences. --- lib/jobs/basejob.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index dfc3d3dd..7cddc343 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -527,6 +527,7 @@ void BaseJob::finishJob() qCWarning(d->logCat) << this << "re-running with authentication"; emit retryScheduled(d->retriesTaken, 0); d->connection->submit(this); + return; } if ((error() == NetworkError || error() == Timeout) && d->retriesTaken < d->maxRetries) { @@ -542,6 +543,8 @@ void BaseJob::finishJob() return; } + Q_ASSERT(status().code != Pending); + // Notify those interested in any completion of the job including abandon() emit finished(this); -- cgit v1.2.3 From 803f5d21935fe159a87864f10a52707eaf93fe5f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 8 Apr 2020 18:35:35 +0200 Subject: EventStatus::Replaced This can be handy to pass the replaced status to QML. --- lib/eventitem.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/eventitem.h b/lib/eventitem.h index 3a7061d3..7b2c3c44 100644 --- a/lib/eventitem.h +++ b/lib/eventitem.h @@ -42,7 +42,8 @@ public: ReachedServer = 0x04, //< The server has received the event SendingFailed = 0x05, //< The server could not receive the event Redacted = 0x08, //< The event has been redacted - Hidden = 0x10, //< The event should not be shown in the timeline + Replaced = 0x10, //< The event has been replaced + Hidden = 0x100, //< The event should not be shown in the timeline }; Q_DECLARE_FLAGS(Status, Code) Q_FLAG(Status) -- cgit v1.2.3 From feb5e82fbe96269f1f7683470b02fbc9c70acaa0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 12 Apr 2020 10:01:18 +0200 Subject: Room::canSwitchVersions(): refactor and make Q_INVOKABLE --- lib/room.cpp | 16 +++++++--------- lib/room.h | 6 +++--- 2 files changed, 10 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 5d4e2141..dc65ebfc 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -758,15 +758,13 @@ bool Room::canSwitchVersions() const if (!successorId().isEmpty()) return false; // No one can upgrade a room that's already upgraded - // TODO, #276: m.room.power_levels - const auto* plEvt = - d->getCurrentState(); - if (!plEvt) - return true; - - const auto currentUserLevel = plEvt->powerLevelForUser(localUser()->id()); - const auto tombstonePowerLevel = plEvt->powerLevelForState("m.room.tombstone"_ls); - return currentUserLevel >= tombstonePowerLevel; + if (const auto* plEvt = d->getCurrentState()) { + const auto currentUserLevel = plEvt->powerLevelForUser(localUser()->id()); + const auto tombstonePowerLevel = + plEvt->powerLevelForState("m.room.tombstone"_ls); + return currentUserLevel >= tombstonePowerLevel; + } + return true; } bool Room::hasUnreadMessages() const { return unreadCount() >= 0; } diff --git a/lib/room.h b/lib/room.h index a5b933c2..66a8df7a 100644 --- a/lib/room.h +++ b/lib/room.h @@ -474,6 +474,9 @@ public: Q_INVOKABLE bool supportsCalls() const; + /// Whether the current user is allowed to upgrade the room + Q_INVOKABLE bool canSwitchVersions() const; + /// Get a state event with the given event type and state key /*! This method returns a (potentially empty) state event corresponding * to the pair of event type \p evtType and state key \p stateKey. @@ -566,9 +569,6 @@ public slots: /// Mark all messages in the room as read void markAllMessagesAsRead(); - /// Whether the current user is allowed to upgrade the room - bool canSwitchVersions() const; - /// Switch the room's version (aka upgrade) void switchVersion(QString newVersion); -- cgit v1.2.3 From 0c96ae749c749a5a36e9f28a91bb69f7702d94c5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 12 Apr 2020 12:15:08 +0200 Subject: Room::safeMemberName() Pulled from Quaternion code. --- lib/room.cpp | 5 +++++ lib/room.h | 7 +++++++ 2 files changed, 12 insertions(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index dc65ebfc..ffe78a3a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1449,6 +1449,11 @@ QString Room::roomMembername(const QString& userId) const return roomMembername(user(userId)); } +QString Room::safeMemberName(const QString& userId) const +{ + return sanitized(roomMembername(userId)); +} + void Room::updateData(SyncRoomData&& data, bool fromCache) { if (d->prevBatch.isEmpty()) diff --git a/lib/room.h b/lib/room.h index 66a8df7a..1ddff517 100644 --- a/lib/room.h +++ b/lib/room.h @@ -269,6 +269,13 @@ public: */ Q_INVOKABLE QString roomMembername(const QString& userId) const; + /** Get a display-safe member name in the context of this room + * + * Display-safe means HTML-safe + without RLO/LRO markers + * (see https://github.com/quotient-im/Quaternion/issues/545). + */ + Q_INVOKABLE QString safeMemberName(const QString& userId) const; + const Timeline& messageEvents() const; const PendingEvents& pendingEvents() const; -- cgit v1.2.3 From e8be883d1c05bd82040e59c4744362509bd24222 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 14 Apr 2020 06:33:41 +0200 Subject: Room: emit updatedEvent() on un-reaction --- lib/room.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index ffe78a3a..c9363a00 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2128,6 +2128,7 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) qMakePair(targetEvtId, EventRelation::Annotation()); if (relations.contains(lookupKey)) { relations[lookupKey].removeOne(reaction); + emit q->updatedEvent(targetEvtId); } } q->onRedaction(*oldEvent, *ti); -- cgit v1.2.3 From 04a277dbb6c15e79be8623f3c36edd465d4b80a7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 14 Apr 2020 07:14:28 +0200 Subject: Settings: use UTF-8 encoding Fixes #403. --- lib/settings.cpp | 5 +++++ lib/settings.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/settings.cpp b/lib/settings.cpp index 0349e699..5413693d 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -16,6 +16,11 @@ void Settings::setLegacyNames(const QString& organizationName, legacyApplicationName = applicationName; } +Settings::Settings(QObject* parent) : QSettings(parent) +{ + setIniCodec("UTF-8"); +} + void Settings::setValue(const QString& key, const QVariant& value) { QSettings::setValue(key, value); diff --git a/lib/settings.h b/lib/settings.h index 79ebba6c..5a097285 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -39,7 +39,7 @@ public: static void setLegacyNames(const QString& organizationName, const QString& applicationName = {}); - using QSettings::QSettings; + explicit Settings(QObject* parent = nullptr); /// Set the value for a given key /*! If the key exists in the legacy location, it is removed. */ -- cgit v1.2.3 From 4088ab4572c5b7cde603aeb1a89bc4515833beaf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 14 Apr 2020 14:21:26 +0200 Subject: BaseJob::makeRequestUrl(): even more tolerance to slash separators The code is really defensive now, making sure there's exactly one slash between the base path and the endpoint. It's still very conservative about the path composition otherwise (no normalisation etc.). --- lib/connection.cpp | 2 +- lib/jobs/basejob.cpp | 7 +++++-- lib/jobs/basejob.h | 6 +++--- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 1c63adeb..41ecee67 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -280,7 +280,7 @@ void Connection::resolveServer(const QString& mxid) return; } qCInfo(MAIN) << ".well-known URL for" << maybeBaseUrl.host() - << "is" << baseUrl.authority(); + << "is" << baseUrl.toString(); setHomeserver(baseUrl); } else { qCInfo(MAIN) << "No .well-known file, using" << maybeBaseUrl diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 7cddc343..8e1a63aa 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -212,8 +212,11 @@ QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path, const QUrlQuery& query) { auto pathBase = baseUrl.path(); - if (!pathBase.endsWith('/') && !path.startsWith('/')) - pathBase.push_back('/'); + // QUrl::adjusted(QUrl::StripTrailingSlashes) doesn't help with root '/' + while (pathBase.endsWith('/')) + pathBase.chop(1); + if (!path.startsWith('/')) // Normally API files do start with '/' + pathBase.push_back('/'); // so this shouldn't be needed these days baseUrl.setPath(pathBase + path, QUrl::TolerantMode); baseUrl.setQuery(query); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index c8046e9e..2049f59c 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -308,9 +308,9 @@ protected: 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. + * + * The function ensures exactly one '/' between the path component of + * \p baseUrl and \p path. The query component of \p baseUrl is ignored. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& path, const QUrlQuery& query = {}); -- cgit v1.2.3 From 7ecc82079f85a7fa29f122fd2fd78401af0ee799 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 15 Apr 2020 09:27:41 +0200 Subject: Connection: connectToServer -> loginWithPassword connectToServer() is left for compatibility but deprecated. --- lib/connection.cpp | 7 ++++--- lib/connection.h | 15 +++++++++++---- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 41ecee67..229a6564 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -312,9 +312,10 @@ inline UserIdentifier make3rdPartyIdentifier(const QString& medium, { QStringLiteral("address"), address } } }; } -void Connection::connectToServer(const QString& userId, const QString& password, - const QString& initialDeviceName, - const QString& deviceId) +void Connection::loginWithPassword(const QString& userId, + const QString& password, + const QString& initialDeviceName, + const QString& deviceId) { checkAndConnect(userId, [=] { d->loginToServer(LoginFlows::Password.type, makeUserIdentifier(userId), diff --git a/lib/connection.h b/lib/connection.h index b0263732..32d6df79 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -509,15 +509,22 @@ public slots: /** Determine and set the homeserver from MXID */ void resolveServer(const QString& mxid); - void connectToServer(const QString& userId, const QString& password, - const QString& initialDeviceName, - const QString& deviceId = {}); + void loginWithPassword(const QString& userId, const QString& password, + const QString& initialDeviceName, + const QString& deviceId = {}); void loginWithToken(const QByteArray& loginToken, const QString& initialDeviceName, const QString& deviceId = {}); void assumeIdentity(const QString& userId, const QString& accessToken, const QString& deviceId); - /*! @deprecated + /*! \deprecated Use loginWithPassword instead */ + void connectToServer(const QString& userId, const QString& password, + const QString& initialDeviceName, + const QString& deviceId = {}) + { + loginWithPassword(userId, password, initialDeviceName, deviceId); + } + /*! \deprecated * Use assumeIdentity() if you have an access token or * loginWithToken() if you have a login token. */ -- cgit v1.2.3 From b19a7c8b9786ad506ab208da4f0f5e76ff7daa74 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 15 Apr 2020 11:02:03 +0200 Subject: Connection: cleanup and reformat --- lib/connection.cpp | 4 ++-- lib/connection.h | 14 +++++--------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 229a6564..db61986a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -334,8 +334,8 @@ void Connection::loginWithToken(const QByteArray& loginToken, const QString& deviceId) { d->loginToServer(LoginFlows::Token.type, - makeUserIdentifier(/*user is encoded in loginToken*/ {}), - /*password*/ "", loginToken, deviceId, initialDeviceName); + none /*user is encoded in loginToken*/, "" /*password*/, + loginToken, deviceId, initialDeviceName); } void Connection::assumeIdentity(const QString& userId, diff --git a/lib/connection.h b/lib/connection.h index 32d6df79..c908b58b 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -138,18 +138,14 @@ class Connection : public QObject { Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged) Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) - Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY - capabilitiesLoaded) - Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY - homeserverChanged) + Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded) + Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged) - Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) + Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) Q_PROPERTY(bool supportsSso READ supportsSso NOTIFY loginFlowsChanged) Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged) - Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY - cacheStateChanged) - Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY - lazyLoadingChanged) + Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) + Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) public: using UsersToDevicesToEvents = -- cgit v1.2.3 From 753c281f4eb055d684e63472468fd091f2166526 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 15 Apr 2020 13:05:16 +0200 Subject: Connection: track resolving and login flows jobs; isUsable() This is to prevent the jobs from several resolveServer() / setHomeserver() invocations running in parallel. --- lib/connection.cpp | 98 ++++++++++++++++++++++++++++++------------------------ lib/connection.h | 3 ++ 2 files changed, 58 insertions(+), 43 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index db61986a..9f4f7082 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -116,6 +116,9 @@ public: QScopedPointer encryptionManager; #endif // Quotient_E2EE_ENABLED + QPointer resolverJob = nullptr; + QPointer loginFlowsJob = nullptr; + SyncJob* syncJob = nullptr; bool cacheState = true; @@ -245,6 +248,9 @@ Connection::~Connection() void Connection::resolveServer(const QString& mxid) { + if (isJobRunning(d->resolverJob)) + d->resolverJob->abandon(); + auto maybeBaseUrl = QUrl::fromUserInput(serverPart(mxid)); maybeBaseUrl.setScheme("https"); // Instead of the Qt-default "http" if (maybeBaseUrl.isEmpty() || !maybeBaseUrl.isValid()) { @@ -257,46 +263,43 @@ void Connection::resolveServer(const QString& mxid) qCDebug(MAIN) << "Finding the server" << domain; d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file - auto getWellKnownJob = callApi(); - connect(getWellKnownJob, &BaseJob::finished, this, - [this, getWellKnownJob, maybeBaseUrl] { - if (getWellKnownJob->status() != BaseJob::NotFoundError) { - if (getWellKnownJob->status() != BaseJob::Success) { - qCWarning(MAIN) - << "Fetching .well-known file failed, FAIL_PROMPT"; - emit resolveError(tr("Failed resolving the homeserver")); - return; - } - QUrl baseUrl { getWellKnownJob->data().homeserver.baseUrl }; - if (baseUrl.isEmpty()) { - qCWarning(MAIN) << "base_url not provided, FAIL_PROMPT"; - emit resolveError( - tr("The homeserver base URL is not provided")); - return; - } - if (!baseUrl.isValid()) { - qCWarning(MAIN) << "base_url invalid, FAIL_ERROR"; - emit resolveError(tr("The homeserver base URL is invalid")); - return; - } - qCInfo(MAIN) << ".well-known URL for" << maybeBaseUrl.host() - << "is" << baseUrl.toString(); - setHomeserver(baseUrl); - } else { - qCInfo(MAIN) << "No .well-known file, using" << maybeBaseUrl - << "for base URL"; - setHomeserver(maybeBaseUrl); + d->resolverJob = callApi(); + connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl] { + if (d->resolverJob->status() != BaseJob::NotFoundError) { + if (d->resolverJob->status() != BaseJob::Success) { + qCWarning(MAIN) + << "Fetching .well-known file failed, FAIL_PROMPT"; + emit resolveError(tr("Failed resolving the homeserver")); + return; } - - auto getVersionsJob = callApi(); - connect(getVersionsJob, &BaseJob::success, this, - &Connection::resolved); - connect(getVersionsJob, &BaseJob::failure, this, [this] { - qCWarning(MAIN) << "Homeserver base URL invalid"; - emit resolveError(tr("The homeserver base URL " - "doesn't seem to work")); - }); + QUrl baseUrl { d->resolverJob->data().homeserver.baseUrl }; + if (baseUrl.isEmpty()) { + qCWarning(MAIN) << "base_url not provided, FAIL_PROMPT"; + emit resolveError( + tr("The homeserver base URL is not provided")); + return; + } + if (!baseUrl.isValid()) { + qCWarning(MAIN) << "base_url invalid, FAIL_ERROR"; + emit resolveError(tr("The homeserver base URL is invalid")); + return; + } + qCInfo(MAIN) << ".well-known URL for" << maybeBaseUrl.host() << "is" + << baseUrl.toString(); + setHomeserver(baseUrl); + } else { + qCInfo(MAIN) << "No .well-known file, using" << maybeBaseUrl + << "for base URL"; + setHomeserver(maybeBaseUrl); + } + connect(d->loginFlowsJob, &BaseJob::success, this, + &Connection::resolved); + connect(d->loginFlowsJob, &BaseJob::failure, this, [this] { + qCWarning(MAIN) << "Homeserver base URL sanity check failed"; + emit resolveError( + tr("The homeserver base URL doesn't seem to work")); }); + }); } inline UserIdentifier makeUserIdentifier(const QString& id) @@ -1045,6 +1048,8 @@ QUrl Connection::homeserver() const { return d->data->baseUrl(); } QString Connection::domain() const { return userId().section(':', 1); } +bool Connection::isUsable() const { return !loginFlows().isEmpty(); } + QVector Connection::loginFlows() const { return d->loginFlows; @@ -1454,17 +1459,24 @@ QByteArray Connection::generateTxnId() const void Connection::setHomeserver(const QUrl& url) { + if (isJobRunning(d->resolverJob)) + d->resolverJob->abandon(); + d->resolverJob = nullptr; + if (isJobRunning(d->loginFlowsJob)) + d->loginFlowsJob->abandon(); + d->loginFlowsJob = nullptr; + d->loginFlows.clear(); + if (homeserver() != url) { d->data->setBaseUrl(url); - d->loginFlows.clear(); emit homeserverChanged(homeserver()); } // Whenever a homeserver is updated, retrieve available login flows from it - auto* j = callApi(BackgroundRequest); - connect(j, &BaseJob::finished, this, [this, j] { - if (j->status().good()) - d->loginFlows = j->flows(); + d->loginFlowsJob = callApi(BackgroundRequest); + connect(d->loginFlowsJob, &BaseJob::finished, this, [this] { + if (d->loginFlowsJob->status().good()) + d->loginFlows = d->loginFlowsJob->flows(); else d->loginFlows.clear(); emit loginFlowsChanged(); diff --git a/lib/connection.h b/lib/connection.h index c908b58b..7998afa8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -142,6 +142,7 @@ class Connection : public QObject { Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged) Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) + Q_PROPERTY(bool isUsable READ isUsable NOTIFY loginFlowsChanged) Q_PROPERTY(bool supportsSso READ supportsSso NOTIFY loginFlowsChanged) Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) @@ -306,6 +307,8 @@ public: QUrl homeserver() const; /** Get the domain name used for ids/aliases on the server */ QString domain() const; + /** Whether the configured homeserver is known to be reachable and working */ + bool isUsable() const; /** Get the list of supported login flows */ QVector loginFlows() const; /** Check whether the current homeserver supports password auth */ -- cgit v1.2.3 From 44ea26d3c32dd2f4c4aa53b85ef7ec75fb354cb3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 17 Apr 2020 07:39:10 +0200 Subject: gtad.yaml, *.mustache: upgrade to GTAD 0.7 beta That means much more readable templates thanks to GTAD 0.7 in turn using the upgrade Mustache engine. --- lib/csapi/gtad.yaml | 12 +++ lib/csapi/{{base}}.cpp.mustache | 200 +++++++++++++++++++++++----------------- lib/csapi/{{base}}.h.mustache | 124 +++++++++++++++---------- 3 files changed, 200 insertions(+), 136 deletions(-) (limited to 'lib') diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index 6d4e080f..086bd4f7 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -145,6 +145,18 @@ mustache: joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' + # Doc-comment blocks. Comment indent is managed by clang-format + # (without clang-format there'd have to be a separate partial definition + # for each indent...) + + # For structures that are not supposed to have a summary (e.g., JSON schema) + docCommentShort: |- + {{#description}} + /// {{_}}{{/description}} + docCommentSummary: |- + {{#summary}} \brief {{summary}} + *{{/summary}} + templates: - "{{base}}.h.mustache" - "{{base}}.cpp.mustache" diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache index 6cbd1b65..b3bd4de9 100644 --- a/lib/csapi/{{base}}.cpp.mustache +++ b/lib/csapi/{{base}}.cpp.mustache @@ -3,74 +3,84 @@ {{^models}} #include "converters.h"{{/models}} {{#operations}} -{{#producesNonJson?}} + {{#producesNonJson?}} #include -{{/producesNonJson?}} + {{/producesNonJson?}} #include {{/operations}} + using namespace Quotient; + {{#models.model}} {{#in?}} -void JsonObjectConverter<{{qualifiedName}}>::dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) -{ -{{#propertyMap -}} fillJson(jo, pod.{{nameCamelCase}}); -{{/propertyMap}}{{#parents -}} fillJson<{{name}}>(jo, pod); -{{/parents}}{{#vars -}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); -{{/vars}} +void JsonObjectConverter<{{qualifiedName}}>::dumpTo( + QJsonObject& jo, const {{qualifiedName}}& pod) +{ {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} } - {{/in?}}{{#out?}} -void JsonObjectConverter<{{qualifiedName}}>::fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) -{ -{{#parents -}} fillFromJson<{{qualifiedName}}>(jo, result); -{{/parents}}{{#vars -}} fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); -{{/vars}}{{#propertyMap -}} fromJson(jo, result.{{nameCamelCase}}); -{{/propertyMap}} + {{/in?}} + + {{#out?}} +void JsonObjectConverter<{{qualifiedName}}>::fillFrom( + {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) +{ {{#parents}} + fillFromJson<{{qualifiedName}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} } {{/out?}} + {{/models.model}} {{#operations}} + static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); {{#operation}}{{#models}} + // Converters -namespace Quotient -{ +namespace Quotient { {{#model}} -template <> struct JsonObjectConverter<{{qualifiedName}}> -{ - {{#in? -}} static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) - { -{{#propertyMap -}} fillJson(jo, pod.{{nameCamelCase}}); -{{/propertyMap}}{{#parents -}} fillJson<{{name}}>(jo, pod); -{{/parents}}{{#vars -}} addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); -{{/vars}} + +template <> struct JsonObjectConverter<{{qualifiedName}}> { + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) + { {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} } - {{/in?}}{{#out? -}} static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) - { -{{#parents -}} fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); -{{/parents}}{{#vars -}} fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); -{{/vars}}{{#propertyMap -}} fromJson(jo, result.{{nameCamelCase}}); -{{/propertyMap}} + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) + { {{#parents}} + fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), + result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} } {{/out?}} }; {{/model}} -} // namespace QMatrixClient + +} // namespace Quotient {{/models}} {{#responses}}{{#normalResponse?}}{{#allProperties?}} + class {{camelCaseOperationId}}Job::Private { public:{{#allProperties}} @@ -78,76 +88,94 @@ class {{camelCaseOperationId}}Job::Private }; {{/allProperties?}}{{/normalResponse?}}{{/responses}} {{#queryParams?}} -BaseJob::Query queryTo{{camelCaseOperationId}}({{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) + +BaseJob::Query queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) { BaseJob::Query _q;{{#queryParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, + QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} return _q; } {{/queryParams?}} {{^bodyParams}} -QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) + +QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) { return BaseJob::makeRequestUrl(std::move(baseUrl), basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); -} - {{/bodyParams}} -static const auto {{camelCaseOperationId}}JobName = QStringLiteral("{{camelCaseOperationId}}Job"); - -{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDef}}{{/allParams}}) - : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, {{camelCaseOperationId}}JobName, - basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, - queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}{{#skipAuth}}{{#queryParams?}}, - {}{{/queryParams?}}, false{{/skipAuth}}){{#responses}}{{#normalResponse?}}{{#allProperties?}} - , d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} -{ -{{#headerParams?}}{{#headerParams -}} setRequestHeader("{{baseName}}", {{paramName}}.toLatin1());{{/headerParams}} -{{/headerParams?}}{{#bodyParams? -}}{{#inlineBody -}} setRequestData(Data({{#consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson? - }}{{^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson?}})); -{{/inlineBody}}{{^inlineBody -}} QJsonObject _data;{{#bodyParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, QStringLiteral("{{baseName}}"), {{paramName}});{{/bodyParams}} +} {{/bodyParams}} + +{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( + {{#allParams}}{{>joinedParamDef}}{{/allParams}}) + : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, + QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} + basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} + {{#queryParams? + }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) + {{/queryParams? + }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) + {{#responses}}{{#normalResponse?}}{{#allProperties? + }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} +{ {{#headerParams}} + setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); + {{/headerParams}}{{#bodyParams?}} + {{#inlineBody}} + setRequestData(Data({{!avoid extra linebreaks}}{{ + #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ + ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? + }})); + {{/inlineBody}}{{^inlineBody}} + QJsonObject _data; + {{#bodyParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, + QStringLiteral("{{baseName}}"), {{paramName}}); + {{/bodyParams}} setRequestData(_data); -{{/inlineBody}}{{/bodyParams? -}}{{#producesNonJson? -}} setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); -{{/producesNonJson?}} + {{/inlineBody}} + {{/bodyParams?}}{{#producesNonJson?}} + setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); + {{/producesNonJson?}} } {{#responses}}{{#normalResponse?}}{{#allProperties?}} + {{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; {{#allProperties}} -{{>qualifiedMaybeCrefType}} {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} + +{{>qualifiedMaybeCrefType}} + {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} { return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; } {{/allProperties}} + {{#producesNonJson?}} BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) { - {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}");{{! We don't check for required headers yet }} + {{! We don't check for required headers yet }} + {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} return Success; } {{/producesNonJson?}}{{^producesNonJson?}} BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) { -{{#inlineResponse -}} fromJson(data, d->{{paramName}}); -{{/inlineResponse}}{{^inlineResponse -}} auto json = data.object(); -{{# properties}}{{#required? -}} if (!json.contains("{{baseName}}"_ls)) + {{#inlineResponse}} + fromJson(data, d->{{paramName}}); + {{/inlineResponse}}{{^inlineResponse}} + auto json = data.object(); + {{#properties}}{{#required?}} + if (!json.contains("{{baseName}}"_ls)) return { IncorrectResponse, "The key '{{baseName}}' not found in the response" }; -{{/required? -}} fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); -{{/ properties}} -{{/inlineResponse -}} return Success; + {{/required?}} + fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); + {{/properties}} + {{/inlineResponse}} + + return Success; } {{/producesNonJson?}} {{/allProperties?}}{{/normalResponse?}}{{/responses}} diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache index 56044e7d..404aafe8 100644 --- a/lib/csapi/{{base}}.h.mustache +++ b/lib/csapi/{{base}}.h.mustache @@ -8,88 +8,112 @@ {{#imports}} #include {{_}}{{/imports}} -namespace Quotient -{ +namespace Quotient { {{#models}} + // Data structures -{{# model}} -{{#description}}/// {{_}}{{/description}} + + {{#model}} +{{>docCommentShort}} struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} -{ -{{# vars}}{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} +{ {{#vars}} + + {{>docCommentShort}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/ vars}} -{{# propertyMap}} -{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{/vars}}{{#propertyMap}} + + {{>docCommentShort}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/ propertyMap}} + {{/propertyMap}} }; template <> struct JsonObjectConverter<{{name}}> { - {{#in?}}static void dumpTo(QJsonObject& jo, const {{name}}& pod);{{/in?}} - {{#out?}}static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod);{{/out?}}}; -{{/ model}} + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{name}}& pod); + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); + {{/out?}} +}; + {{/model}} {{/models}} -{{#operations}}// Operations -{{# operation}} -{{#summary}}/// {{summary}}{{/summary}} -{{#description?}}/*!{{#description}} +{{#operations}} + +// Operations + {{#operation}} + +/*!{{>docCommentSummary}}{{#description}} * {{_}}{{/description}} - */{{/description?}} -class {{camelCaseOperationId}}Job : public BaseJob -{ -public:{{#models}} + */ +class {{camelCaseOperationId}}Job : public BaseJob { +public: {{#models}} // Inner data structures -{{# model}} -{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} - struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} + {{#model}} + + {{>docCommentShort}} + struct {{name}}{{#parents?}} : + {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} { -{{# vars}}{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{#vars}} + {{>docCommentShort}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/ vars}} -{{# propertyMap}} -{{#description?}} /// {{#description}}{{_}}{{/description}}{{/description?}} + {{/vars}} + {{#propertyMap}} + {{>docCommentShort}} {{>maybeOmittableType}} {{nameCamelCase}}; -{{/ propertyMap}} + {{/propertyMap}} }; -{{/ model}} + {{/model}} + // Construction/destruction -{{/ models}}{{#allParams?}} - /*! {{summary}}{{#allParams}} + + {{/models}} + {{^allParams?}} + {{#summary}} + /// {{summary}} + {{/summary}} + {{/allParams?}}{{#allParams?}} + /*!{{>docCommentSummary}} + {{#allParams}} * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}}{{/allParams}} - */{{/allParams?}} + * {{_}}{{/description}} + {{/allParams}} + */ + {{/allParams?}} explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); -{{^ bodyParams}} + {{^bodyParams}} - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * {{camelCaseOperationId}}Job is necessary but the job - * itself isn't. + * This function can be used when a URL for {{camelCaseOperationId}}Job + * is necessary but the job itself isn't. */ - static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); -{{/ bodyParams}} -{{# responses}}{{#normalResponse?}}{{#allProperties?}} + static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); + {{/bodyParams}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} ~{{camelCaseOperationId}}Job() override; // Result properties -{{#allProperties}}{{#description}} - /// {{_}}{{/description}} - {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}};{{/allProperties}} + {{#allProperties}} -protected:{{#producesNonJson?}} + {{>docCommentShort}} + {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; + {{/allProperties}} + +protected: {{#producesNonJson?}} Status parseReply(QNetworkReply* reply) override; -{{/producesNonJson?}}{{^producesNonJson?}} + {{/producesNonJson?}}{{^producesNonJson?}} Status parseJson(const QJsonDocument& data) override; -{{/producesNonJson?}} + {{/producesNonJson?}} private: class Private; QScopedPointer d; -{{/ allProperties?}}{{/normalResponse?}}{{/responses}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} }; -{{/ operation}} + {{/operation}} {{/operations}} + } // namespace Quotient -- cgit v1.2.3 From 7036ed0dcb137cb5cbb6b426dd338c5e2e4c6424 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 17 Apr 2020 07:42:13 +0200 Subject: Regenerate API files using new GTAD and refreshed templates No functional changes. --- lib/application-service/definitions/location.h | 11 +- lib/application-service/definitions/protocol.h | 61 ++++---- lib/application-service/definitions/user.h | 11 +- lib/csapi/account-data.cpp | 18 +-- lib/csapi/account-data.h | 57 ++++---- lib/csapi/admin.cpp | 19 +-- lib/csapi/admin.h | 55 +++---- lib/csapi/administrative_contact.cpp | 48 ++---- lib/csapi/administrative_contact.h | 101 +++++++------ lib/csapi/appservice_room_directory.cpp | 6 +- lib/csapi/appservice_room_directory.h | 15 +- lib/csapi/banning.cpp | 9 +- lib/csapi/banning.h | 23 ++- lib/csapi/capabilities.cpp | 20 +-- lib/csapi/capabilities.h | 37 ++--- lib/csapi/content-repo.cpp | 49 ++----- lib/csapi/content-repo.h | 112 +++++++------- lib/csapi/create_room.cpp | 17 +-- lib/csapi/create_room.h | 161 ++++++++++++--------- lib/csapi/definitions/auth_data.h | 10 +- lib/csapi/definitions/client_device.h | 23 +-- lib/csapi/definitions/device_keys.h | 35 +++-- lib/csapi/definitions/event_filter.h | 13 +- lib/csapi/definitions/public_rooms_response.h | 47 +++--- lib/csapi/definitions/push_condition.h | 32 ++-- lib/csapi/definitions/push_rule.h | 22 +-- lib/csapi/definitions/push_ruleset.h | 10 +- lib/csapi/definitions/room_event_filter.h | 11 +- lib/csapi/definitions/sync_filter.h | 69 +++++---- lib/csapi/definitions/user_identifier.h | 9 +- lib/csapi/definitions/wellknown/full.h | 20 ++- lib/csapi/definitions/wellknown/homeserver.h | 9 +- lib/csapi/definitions/wellknown/identity_server.h | 9 +- lib/csapi/device_management.cpp | 30 ++-- lib/csapi/device_management.h | 67 ++++----- lib/csapi/directory.cpp | 16 +- lib/csapi/directory.h | 50 +++---- lib/csapi/event_context.cpp | 7 +- lib/csapi/event_context.h | 26 ++-- lib/csapi/filter.cpp | 15 +- lib/csapi/filter.h | 32 ++-- lib/csapi/inviting.cpp | 4 +- lib/csapi/inviting.h | 13 +- lib/csapi/joining.cpp | 29 ++-- lib/csapi/joining.h | 54 +++---- lib/csapi/keys.cpp | 42 ++---- lib/csapi/keys.h | 70 ++++----- lib/csapi/kicking.cpp | 4 +- lib/csapi/kicking.h | 13 +- lib/csapi/leaving.cpp | 8 +- lib/csapi/leaving.h | 37 +++-- lib/csapi/list_joined_rooms.cpp | 8 +- lib/csapi/list_joined_rooms.h | 19 +-- lib/csapi/list_public_rooms.cpp | 37 ++--- lib/csapi/list_public_rooms.h | 66 ++++----- lib/csapi/login.cpp | 22 +-- lib/csapi/login.h | 41 +++--- lib/csapi/logout.cpp | 9 +- lib/csapi/logout.h | 33 ++--- lib/csapi/message_pagination.cpp | 14 +- lib/csapi/message_pagination.h | 23 ++- lib/csapi/notifications.cpp | 14 +- lib/csapi/notifications.h | 41 +++--- lib/csapi/openid.cpp | 8 +- lib/csapi/openid.h | 16 +- lib/csapi/peeking_events.cpp | 9 +- lib/csapi/peeking_events.h | 23 ++- lib/csapi/presence.cpp | 11 +- lib/csapi/presence.h | 34 ++--- lib/csapi/profile.cpp | 39 ++--- lib/csapi/profile.h | 78 +++++----- lib/csapi/pusher.cpp | 28 ++-- lib/csapi/pusher.h | 99 ++++++------- lib/csapi/pushrules.cpp | 50 ++----- lib/csapi/pushrules.h | 120 +++++++-------- lib/csapi/read_markers.cpp | 4 +- lib/csapi/read_markers.h | 13 +- lib/csapi/receipts.cpp | 4 +- lib/csapi/receipts.h | 13 +- lib/csapi/redaction.cpp | 7 +- lib/csapi/redaction.h | 13 +- lib/csapi/registration.cpp | 64 +++----- lib/csapi/registration.h | 129 +++++++++-------- lib/csapi/report_content.cpp | 4 +- lib/csapi/report_content.h | 13 +- lib/csapi/room_send.cpp | 7 +- lib/csapi/room_send.h | 13 +- lib/csapi/room_state.cpp | 15 +- lib/csapi/room_state.h | 23 ++- lib/csapi/room_upgrades.cpp | 7 +- lib/csapi/room_upgrades.h | 13 +- lib/csapi/rooms.cpp | 60 +++----- lib/csapi/rooms.h | 112 +++++++------- lib/csapi/sso_login_redirect.cpp | 5 +- lib/csapi/sso_login_redirect.h | 20 ++- lib/csapi/tags.cpp | 21 +-- lib/csapi/tags.h | 56 ++++--- lib/csapi/third_party_lookup.cpp | 56 +++---- lib/csapi/third_party_lookup.h | 115 +++++++-------- lib/csapi/third_party_membership.cpp | 4 +- lib/csapi/third_party_membership.h | 13 +- lib/csapi/to_device.cpp | 4 +- lib/csapi/to_device.h | 13 +- lib/csapi/typing.cpp | 4 +- lib/csapi/typing.h | 13 +- lib/csapi/users.cpp | 14 +- lib/csapi/users.h | 36 ++--- lib/csapi/versions.cpp | 8 +- lib/csapi/versions.h | 20 ++- lib/csapi/voip.cpp | 9 +- lib/csapi/voip.h | 19 +-- lib/csapi/wellknown.cpp | 10 +- lib/csapi/wellknown.h | 19 +-- lib/csapi/whoami.cpp | 8 +- lib/csapi/whoami.h | 19 +-- .../definitions/request_email_validation.h | 37 ++--- .../definitions/request_msisdn_validation.h | 43 +++--- lib/identity/definitions/sid.h | 17 +-- 118 files changed, 1613 insertions(+), 1992 deletions(-) (limited to 'lib') diff --git a/lib/application-service/definitions/location.h b/lib/application-service/definitions/location.h index 77512514..e4c2d096 100644 --- a/lib/application-service/definitions/location.h +++ b/lib/application-service/definitions/location.h @@ -8,24 +8,23 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures -struct ThirdPartyLocation -{ +struct ThirdPartyLocation { /// An alias for a matrix room. QString alias; + /// The protocol ID that the third party location is a part of. QString protocol; + /// Information used to identify this third party location. QJsonObject fields; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod); }; diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h index ab99264f..ac1e50e2 100644 --- a/lib/application-service/definitions/protocol.h +++ b/lib/application-service/definitions/protocol.h @@ -10,77 +10,80 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Data structures /// Definition of valid values for a field. -struct FieldType -{ +struct FieldType { /// A regular expression for validation of a field's value. This may be - /// relativelycoarse to verify the value as the application service - /// providing this protocolmay apply additional validation or filtering. + /// relatively coarse to verify the value as the application service + /// providing this protocol may apply additional validation or filtering. QString regexp; + /// An placeholder serving as a valid example of the field value. QString placeholder; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const FieldType& pod); static void fillFrom(const QJsonObject& jo, FieldType& pod); }; -struct ProtocolInstance -{ +struct ProtocolInstance { /// A human-readable description for the protocol, such as the name. QString desc; + /// An optional content URI representing the protocol. Overrides the one - /// providedat the higher level Protocol object. + /// provided at the higher level Protocol object. QString icon; + /// Preset values for ``fields`` the client may use to search by. QJsonObject fields; + /// A unique identifier across all instances. QString networkId; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod); static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod); }; -struct ThirdPartyProtocol -{ - /// Fields which may be used to identify a third party user. These should - /// beordered to suggest the way that entities may be grouped, where - /// highergroupings are ordered first. For example, the name of a network - /// should besearched before the nickname of a user. +struct ThirdPartyProtocol { + /// Fields which may be used to identify a third party user. These should be + /// ordered to suggest the way that entities may be grouped, where higher + /// groupings are ordered first. For example, the name of a network should + /// be searched before the nickname of a user. QStringList userFields; + /// Fields which may be used to identify a third party location. These - /// should beordered to suggest the way that entities may be grouped, where - /// highergroupings are ordered first. For example, the name of a network - /// should besearched before the name of a channel. + /// should be ordered to suggest the way that entities may be grouped, where + /// higher groupings are ordered first. For example, the name of a network + /// should be searched before the name of a channel. QStringList locationFields; + /// A content URI representing an icon for the third party protocol. QString icon; + /// The type definitions for the fields defined in the ``user_fields`` and /// ``location_fields``. Each entry in those arrays MUST have an entry here. - /// The``string`` key for this object is field name itself.May be an empty - /// object if no fields are defined. + /// The + /// ``string`` key for this object is field name itself. + /// + /// May be an empty object if no fields are defined. QHash fieldTypes; - /// A list of objects representing independent instances of - /// configuration.For example, multiple networks on IRC if multiple are - /// provided by thesame application service. + + /// A list of objects representing independent instances of configuration. + /// For example, multiple networks on IRC if multiple are provided by the + /// same application service. QVector instances; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod); }; diff --git a/lib/application-service/definitions/user.h b/lib/application-service/definitions/user.h index 34c6829c..0d1984b6 100644 --- a/lib/application-service/definitions/user.h +++ b/lib/application-service/definitions/user.h @@ -8,24 +8,23 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures -struct ThirdPartyUser -{ +struct ThirdPartyUser { /// A Matrix User ID represting a third party user. QString userid; + /// The protocol ID that the third party location is a part of. QString protocol; + /// Information used to identify this third party location. QJsonObject fields; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const ThirdPartyUser& pod); static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod); }; diff --git a/lib/csapi/account-data.cpp b/lib/csapi/account-data.cpp index 2e466fa3..b7825718 100644 --- a/lib/csapi/account-data.cpp +++ b/lib/csapi/account-data.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SetAccountDataJobName = QStringLiteral("SetAccountDataJob"); - SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content) - : BaseJob(HttpVerb::Put, SetAccountDataJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetAccountDataJob"), basePath % "/user/" % userId % "/account_data/" % type) { setRequestData(Data(toJson(content))); @@ -30,21 +28,16 @@ QUrl GetAccountDataJob::makeRequestUrl(QUrl baseUrl, const QString& userId, % "/account_data/" % type); } -static const auto GetAccountDataJobName = QStringLiteral("GetAccountDataJob"); - GetAccountDataJob::GetAccountDataJob(const QString& userId, const QString& type) - : BaseJob(HttpVerb::Get, GetAccountDataJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetAccountDataJob"), basePath % "/user/" % userId % "/account_data/" % type) {} -static const auto SetAccountDataPerRoomJobName = - QStringLiteral("SetAccountDataPerRoomJob"); - SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type, const QJsonObject& content) - : BaseJob(HttpVerb::Put, SetAccountDataPerRoomJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetAccountDataPerRoomJob"), basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type) { @@ -61,13 +54,10 @@ QUrl GetAccountDataPerRoomJob::makeRequestUrl(QUrl baseUrl, % roomId % "/account_data/" % type); } -static const auto GetAccountDataPerRoomJobName = - QStringLiteral("GetAccountDataPerRoomJob"); - GetAccountDataPerRoomJob::GetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type) - : BaseJob(HttpVerb::Get, GetAccountDataPerRoomJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetAccountDataPerRoomJob"), basePath % "/user/" % userId % "/rooms/" % roomId % "/account_data/" % type) {} diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index 7417da0d..a8dba5c0 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -8,21 +8,20 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Set some account_data for the user. -/*! +/*! \brief Set some account_data for the user. + * * Set some account_data for the client. This config is only visible to the user * that set the account_data. The config will be synced to clients in the * top-level ``account_data``. */ -class SetAccountDataJob : public BaseJob -{ +class SetAccountDataJob : public BaseJob { public: - /*! Set some account_data for the user. + /*! \brief Set some account_data for the user. + * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. @@ -36,15 +35,15 @@ public: const QJsonObject& content = {}); }; -/// Get some account_data for the user. -/*! +/*! \brief Get some account_data for the user. + * * Get some account_data for the client. This config is only visible to the user * that set the account_data. */ -class GetAccountDataJob : public BaseJob -{ +class GetAccountDataJob : public BaseJob { public: - /*! Get some account_data for the user. + /*! \brief Get some account_data for the user. + * * \param userId * The ID of the user to get account_data for. The access token must be * authorized to make requests for this user ID. @@ -54,26 +53,25 @@ public: */ explicit GetAccountDataJob(const QString& userId, const QString& type); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetAccountDataJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetAccountDataJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& type); }; -/// Set some account_data for the user. -/*! +/*! \brief Set some account_data for the user. + * * Set some account_data for the client on a given room. This config is only * visible to the user that set the account_data. The config will be synced to * clients in the per-room ``account_data``. */ -class SetAccountDataPerRoomJob : public BaseJob -{ +class SetAccountDataPerRoomJob : public BaseJob { public: - /*! Set some account_data for the user. + /*! \brief Set some account_data for the user. + * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. @@ -90,15 +88,15 @@ public: const QJsonObject& content = {}); }; -/// Get some account_data for the user. -/*! +/*! \brief Get some account_data for the user. + * * Get some account_data for the client on a given room. This config is only * visible to the user that set the account_data. */ -class GetAccountDataPerRoomJob : public BaseJob -{ +class GetAccountDataPerRoomJob : public BaseJob { public: - /*! Get some account_data for the user. + /*! \brief Get some account_data for the user. + * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. @@ -112,11 +110,10 @@ public: const QString& roomId, const QString& type); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetAccountDataPerRoomJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetAccountDataPerRoomJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& type); diff --git a/lib/csapi/admin.cpp b/lib/csapi/admin.cpp index d2c20ba8..14173e94 100644 --- a/lib/csapi/admin.cpp +++ b/lib/csapi/admin.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetWhoIsJob::ConnectionInfo& result) { @@ -29,8 +27,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetWhoIsJob::SessionInfo& result) { fromJson(jo.value("connections"_ls), result.connections); @@ -38,8 +35,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetWhoIsJob::DeviceInfo& result) { fromJson(jo.value("sessions"_ls), result.sessions); @@ -48,8 +44,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetWhoIsJob::Private -{ +class GetWhoIsJob::Private { public: QString userId; QHash devices; @@ -61,10 +56,8 @@ QUrl GetWhoIsJob::makeRequestUrl(QUrl baseUrl, const QString& userId) basePath % "/admin/whois/" % userId); } -static const auto GetWhoIsJobName = QStringLiteral("GetWhoIsJob"); - GetWhoIsJob::GetWhoIsJob(const QString& userId) - : BaseJob(HttpVerb::Get, GetWhoIsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetWhoIsJob"), basePath % "/admin/whois/" % userId) , d(new Private) {} diff --git a/lib/csapi/admin.h b/lib/csapi/admin.h index 472ccf72..75ae1eb0 100644 --- a/lib/csapi/admin.h +++ b/lib/csapi/admin.h @@ -11,29 +11,28 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets information about a particular user. -/*! +/*! \brief Gets information about a particular user. + * * Gets information about a particular user. * * This API may be restricted to only be called by the user being looked * up, or by a server admin. Server-local administrator privileges are not * specified in this document. */ -class GetWhoIsJob : public BaseJob -{ +class GetWhoIsJob : public BaseJob { public: // Inner data structures - /// Gets information about a particular user.This API may be restricted to - /// only be called by the user being lookedup, or by a server admin. - /// Server-local administrator privileges are notspecified in this document. - struct ConnectionInfo - { + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being looked + /// up, or by a server admin. Server-local administrator privileges are not + /// specified in this document. + struct ConnectionInfo { /// Most recently seen IP address of the session. QString ip; /// Unix timestamp that the session was last active. @@ -42,20 +41,22 @@ public: QString userAgent; }; - /// Gets information about a particular user.This API may be restricted to - /// only be called by the user being lookedup, or by a server admin. - /// Server-local administrator privileges are notspecified in this document. - struct SessionInfo - { + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being looked + /// up, or by a server admin. Server-local administrator privileges are not + /// specified in this document. + struct SessionInfo { /// Information particular connections in the session. QVector connections; }; - /// Gets information about a particular user.This API may be restricted to - /// only be called by the user being lookedup, or by a server admin. - /// Server-local administrator privileges are notspecified in this document. - struct DeviceInfo - { + /// Gets information about a particular user. + /// + /// This API may be restricted to only be called by the user being looked + /// up, or by a server admin. Server-local administrator privileges are not + /// specified in this document. + struct DeviceInfo { /// A user's sessions (i.e. what they did with an access token from one /// login). QVector sessions; @@ -63,26 +64,26 @@ public: // Construction/destruction - /*! Gets information about a particular user. + /*! \brief Gets information about a particular user. + * * \param userId * The user to look up. */ explicit GetWhoIsJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetWhoIsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetWhoIsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetWhoIsJob() override; // Result properties /// The Matrix user ID of the user. const QString& userId() const; + /// Each key is an identitfier for one of the user's devices. const QHash& devices() const; diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index 32bc8c86..36d93e73 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetAccount3PIDsJob::ThirdPartyIdentifier& result) { @@ -31,8 +29,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetAccount3PIDsJob::Private -{ +class GetAccount3PIDsJob::Private { public: QVector threepids; }; @@ -43,10 +40,9 @@ QUrl GetAccount3PIDsJob::makeRequestUrl(QUrl baseUrl) basePath % "/account/3pid"); } -static const auto GetAccount3PIDsJobName = QStringLiteral("GetAccount3PIDsJob"); - GetAccount3PIDsJob::GetAccount3PIDsJob() - : BaseJob(HttpVerb::Get, GetAccount3PIDsJobName, basePath % "/account/3pid") + : BaseJob(HttpVerb::Get, QStringLiteral("GetAccount3PIDsJob"), + basePath % "/account/3pid") , d(new Private) {} @@ -67,12 +63,10 @@ BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data) } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Post3PIDsJob::ThreePidCredentials& pod) { @@ -84,11 +78,10 @@ struct JsonObjectConverter } // namespace Quotient -static const auto Post3PIDsJobName = QStringLiteral("Post3PIDsJob"); - Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, Omittable bind) - : BaseJob(HttpVerb::Post, Post3PIDsJobName, basePath % "/account/3pid") + : BaseJob(HttpVerb::Post, QStringLiteral("Post3PIDsJob"), + basePath % "/account/3pid") { QJsonObject _data; addParam<>(_data, QStringLiteral("three_pid_creds"), threePidCreds); @@ -96,12 +89,9 @@ Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, setRequestData(_data); } -static const auto Delete3pidFromAccountJobName = - QStringLiteral("Delete3pidFromAccountJob"); - Delete3pidFromAccountJob::Delete3pidFromAccountJob(const QString& medium, const QString& address) - : BaseJob(HttpVerb::Post, Delete3pidFromAccountJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("Delete3pidFromAccountJob"), basePath % "/account/3pid/delete") { QJsonObject _data; @@ -110,19 +100,15 @@ Delete3pidFromAccountJob::Delete3pidFromAccountJob(const QString& medium, setRequestData(_data); } -class RequestTokenTo3PIDEmailJob::Private -{ +class RequestTokenTo3PIDEmailJob::Private { public: Sid data; }; -static const auto RequestTokenTo3PIDEmailJobName = - QStringLiteral("RequestTokenTo3PIDEmailJob"); - RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob( const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenTo3PIDEmailJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenTo3PIDEmailJob"), basePath % "/account/3pid/email/requestToken", false) , d(new Private) { @@ -142,23 +128,20 @@ const Sid& RequestTokenTo3PIDEmailJob::data() const { return d->data; } BaseJob::Status RequestTokenTo3PIDEmailJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class RequestTokenTo3PIDMSISDNJob::Private -{ +class RequestTokenTo3PIDMSISDNJob::Private { public: Sid data; }; -static const auto RequestTokenTo3PIDMSISDNJobName = - QStringLiteral("RequestTokenTo3PIDMSISDNJob"); - RequestTokenTo3PIDMSISDNJob::RequestTokenTo3PIDMSISDNJob( const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenTo3PIDMSISDNJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenTo3PIDMSISDNJob"), basePath % "/account/3pid/msisdn/requestToken", false) , d(new Private) { @@ -179,5 +162,6 @@ const Sid& RequestTokenTo3PIDMSISDNJob::data() const { return d->data; } BaseJob::Status RequestTokenTo3PIDMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index 4ccd7596..af98fe9c 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -12,13 +12,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets a list of a user's third party identifiers. -/*! +/*! \brief Gets a list of a user's third party identifiers. + * * Gets a list of the third party identifiers that the homeserver has * associated with the user's account. * @@ -28,25 +27,25 @@ namespace Quotient * Identifiers in this list may be used by the homeserver as, for example, * identifiers that it will accept to reset the user's account password. */ -class GetAccount3PIDsJob : public BaseJob -{ +class GetAccount3PIDsJob : public BaseJob { public: // Inner data structures - /// Gets a list of the third party identifiers that the homeserver - /// hasassociated with the user's account.This is *not* the same as the list - /// of third party identifiers bound tothe user's Matrix ID in identity - /// servers.Identifiers in this list may be used by the homeserver as, for - /// example,identifiers that it will accept to reset the user's account - /// password. - struct ThirdPartyIdentifier - { + /// Gets a list of the third party identifiers that the homeserver has + /// associated with the user's account. + /// + /// This is *not* the same as the list of third party identifiers bound to + /// the user's Matrix ID in identity servers. + /// + /// Identifiers in this list may be used by the homeserver as, for example, + /// identifiers that it will accept to reset the user's account password. + struct ThirdPartyIdentifier { /// The medium of the third party identifier. QString medium; /// The third party identifier address. QString address; - /// The timestamp, in milliseconds, when the identifier wasvalidated by - /// the identity server. + /// The timestamp, in milliseconds, when the identifier was + /// validated by the identity server. qint64 validatedAt; /// The timestamp, in milliseconds, when the homeserver associated the /// third party identifier with the user. @@ -55,16 +54,15 @@ public: // Construction/destruction + /// Gets a list of a user's third party identifiers. explicit GetAccount3PIDsJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetAccount3PIDsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetAccount3PIDsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetAccount3PIDsJob() override; // Result properties @@ -87,18 +85,16 @@ private: QScopedPointer d; }; -/// Adds contact information to the user's account. -/*! +/*! \brief Adds contact information to the user's account. + * * Adds contact information to the user's account. */ -class Post3PIDsJob : public BaseJob -{ +class Post3PIDsJob : public BaseJob { public: // Inner data structures /// The third party credentials to associate with the account. - struct ThreePidCredentials - { + struct ThreePidCredentials { /// The client secret used in the session with the identity server. QString clientSecret; /// The identity server to use. @@ -109,7 +105,8 @@ public: // Construction/destruction - /*! Adds contact information to the user's account. + /*! \brief Adds contact information to the user's account. + * * \param threePidCreds * The third party credentials to associate with the account. * \param bind @@ -121,15 +118,15 @@ public: Omittable bind = none); }; -/// Deletes a third party identifier from the user's account -/*! +/*! \brief Deletes a third party identifier from the user's account + * * Removes a third party identifier from the user's account. This might not * cause an unbind of the identifier from the identity server. */ -class Delete3pidFromAccountJob : public BaseJob -{ +class Delete3pidFromAccountJob : public BaseJob { public: - /*! Deletes a third party identifier from the user's account + /*! \brief Deletes a third party identifier from the user's account + * * \param medium * The medium of the third party identifier being removed. * \param address @@ -139,9 +136,9 @@ public: const QString& address); }; -/// Begins the validation process for an email address for association with the -/// user's account. -/*! +/*! \brief Begins the validation process for an email address for association + * with the user's account. + * * Proxies the Identity Service API ``validate/email/requestToken``, but * first checks that the given email address is **not** already associated * with an account on this homeserver. This API should be used to request @@ -149,13 +146,14 @@ public: * parameters and response are identical to that of the * |/register/email/requestToken|_ endpoint. */ -class RequestTokenTo3PIDEmailJob : public BaseJob -{ +class RequestTokenTo3PIDEmailJob : public BaseJob { public: - /*! Begins the validation process for an email address for association with - * the user's account. \param clientSecret A unique string generated by the - * client, and used to identify the validation attempt. It must be a string - * consisting of the characters + /*! \brief Begins the validation process for an email address for + * association with the user's account. + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param email @@ -195,9 +193,9 @@ private: QScopedPointer d; }; -/// Begins the validation process for a phone number for association with the -/// user's account. -/*! +/*! \brief Begins the validation process for a phone number for association with + * the user's account. + * * Proxies the Identity Service API ``validate/msisdn/requestToken``, but * first checks that the given phone number is **not** already associated * with an account on this homeserver. This API should be used to request @@ -205,13 +203,14 @@ private: * parameters and response are identical to that of the * |/register/msisdn/requestToken|_ endpoint. */ -class RequestTokenTo3PIDMSISDNJob : public BaseJob -{ +class RequestTokenTo3PIDMSISDNJob : public BaseJob { public: - /*! Begins the validation process for a phone number for association with - * the user's account. \param clientSecret A unique string generated by the - * client, and used to identify the validation attempt. It must be a string - * consisting of the characters + /*! \brief Begins the validation process for a phone number for association + * with the user's account. + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param country diff --git a/lib/csapi/appservice_room_directory.cpp b/lib/csapi/appservice_room_directory.cpp index 87221aa4..bfcab2a7 100644 --- a/lib/csapi/appservice_room_directory.cpp +++ b/lib/csapi/appservice_room_directory.cpp @@ -12,12 +12,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto UpdateAppserviceRoomDirectoryVsibilityJobName = - QStringLiteral("UpdateAppserviceRoomDirectoryVsibilityJob"); - UpdateAppserviceRoomDirectoryVsibilityJob::UpdateAppserviceRoomDirectoryVsibilityJob( const QString& networkId, const QString& roomId, const QString& visibility) - : BaseJob(HttpVerb::Put, UpdateAppserviceRoomDirectoryVsibilityJobName, + : BaseJob(HttpVerb::Put, + QStringLiteral("UpdateAppserviceRoomDirectoryVsibilityJob"), basePath % "/directory/list/appservice/" % networkId % "/" % roomId) { QJsonObject _data; diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h index e19bf320..f0ad78b0 100644 --- a/lib/csapi/appservice_room_directory.h +++ b/lib/csapi/appservice_room_directory.h @@ -6,13 +6,13 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Updates a room's visibility in the application service's room directory. -/*! +/*! \brief Updates a room's visibility in the application service's room + * directory. + * * Updates the visibility of a given room on the application service's room * directory. * @@ -23,10 +23,11 @@ namespace Quotient * (``as_token``) instead of a typical client's access_token. This API cannot be * invoked by users who are not identified as application services. */ -class UpdateAppserviceRoomDirectoryVsibilityJob : public BaseJob -{ +class UpdateAppserviceRoomDirectoryVsibilityJob : public BaseJob { public: - /*! Updates a room's visibility in the application service's room directory. + /*! \brief Updates a room's visibility in the application service's room + * directory. + * * \param networkId * The protocol (network) ID to update the room list for. This would * have been provided by the application service as being listed as diff --git a/lib/csapi/banning.cpp b/lib/csapi/banning.cpp index eac09d5a..6ac9b3ba 100644 --- a/lib/csapi/banning.cpp +++ b/lib/csapi/banning.cpp @@ -12,11 +12,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto BanJobName = QStringLiteral("BanJob"); - BanJob::BanJob(const QString& roomId, const QString& userId, const QString& reason) - : BaseJob(HttpVerb::Post, BanJobName, basePath % "/rooms/" % roomId % "/ban") + : BaseJob(HttpVerb::Post, QStringLiteral("BanJob"), + basePath % "/rooms/" % roomId % "/ban") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); @@ -24,10 +23,8 @@ BanJob::BanJob(const QString& roomId, const QString& userId, setRequestData(_data); } -static const auto UnbanJobName = QStringLiteral("UnbanJob"); - UnbanJob::UnbanJob(const QString& roomId, const QString& userId) - : BaseJob(HttpVerb::Post, UnbanJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("UnbanJob"), basePath % "/rooms/" % roomId % "/unban") { QJsonObject _data; diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h index 5df878a8..6bf782e7 100644 --- a/lib/csapi/banning.h +++ b/lib/csapi/banning.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Ban a user in the room. -/*! +/*! \brief Ban a user in the room. + * * Ban a user in the room. If the user is currently in the room, also kick them. * * When a user is banned from a room, they may not join it or be invited to it @@ -21,10 +20,10 @@ namespace Quotient * The caller must have the required power level in order to perform this * operation. */ -class BanJob : public BaseJob -{ +class BanJob : public BaseJob { public: - /*! Ban a user in the room. + /*! \brief Ban a user in the room. + * * \param roomId * The room identifier (not alias) from which the user should be banned. * \param userId @@ -37,8 +36,8 @@ public: const QString& reason = {}); }; -/// Unban a user from the room. -/*! +/*! \brief Unban a user from the room. + * * Unban a user from the room. This allows them to be invited to the room, * and join if they would otherwise be allowed to join according to its join * rules. @@ -46,10 +45,10 @@ public: * The caller must have the required power level in order to perform this * operation. */ -class UnbanJob : public BaseJob -{ +class UnbanJob : public BaseJob { public: - /*! Unban a user from the room. + /*! \brief Unban a user from the room. + * * \param roomId * The room identifier (not alias) from which the user should be unbanned. * \param userId diff --git a/lib/csapi/capabilities.cpp b/lib/csapi/capabilities.cpp index 6a544a1e..8c7ed141 100644 --- a/lib/csapi/capabilities.cpp +++ b/lib/csapi/capabilities.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetCapabilitiesJob::ChangePasswordCapability& result) { @@ -27,8 +25,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetCapabilitiesJob::RoomVersionsCapability& result) { @@ -38,8 +35,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(QJsonObject jo, GetCapabilitiesJob::Capabilities& result) { @@ -51,8 +47,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetCapabilitiesJob::Private -{ +class GetCapabilitiesJob::Private { public: Capabilities capabilities; }; @@ -63,10 +58,9 @@ QUrl GetCapabilitiesJob::makeRequestUrl(QUrl baseUrl) basePath % "/capabilities"); } -static const auto GetCapabilitiesJobName = QStringLiteral("GetCapabilitiesJob"); - GetCapabilitiesJob::GetCapabilitiesJob() - : BaseJob(HttpVerb::Get, GetCapabilitiesJobName, basePath % "/capabilities") + : BaseJob(HttpVerb::Get, QStringLiteral("GetCapabilitiesJob"), + basePath % "/capabilities") , d(new Private) {} diff --git a/lib/csapi/capabilities.h b/lib/csapi/capabilities.h index b608a2f2..9f46ab2e 100644 --- a/lib/csapi/capabilities.h +++ b/lib/csapi/capabilities.h @@ -11,63 +11,56 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets information about the server's capabilities. -/*! +/*! \brief Gets information about the server's capabilities. + * * Gets information about the server's supported feature set * and other relevant capabilities. */ -class GetCapabilitiesJob : public BaseJob -{ +class GetCapabilitiesJob : public BaseJob { public: // Inner data structures /// Capability to indicate if the user can change their password. - struct ChangePasswordCapability - { + struct ChangePasswordCapability { /// True if the user can change their password, false otherwise. bool enabled; }; /// The room versions the server supports. - struct RoomVersionsCapability - { + struct RoomVersionsCapability { /// The default room version the server is using for new rooms. QString defaultVersion; /// A detailed description of the room versions the server supports. QHash available; }; - /// The custom capabilities the server supports, using theJava package - /// naming convention. - struct Capabilities - { + /// The custom capabilities the server supports, using the + /// Java package naming convention. + struct Capabilities { /// Capability to indicate if the user can change their password. Omittable changePassword; /// The room versions the server supports. Omittable roomVersions; - - /// The custom capabilities the server supports, using theJava package - /// naming convention. + /// The custom capabilities the server supports, using the + /// Java package naming convention. QHash additionalProperties; }; // Construction/destruction + /// Gets information about the server's capabilities. explicit GetCapabilitiesJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetCapabilitiesJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetCapabilitiesJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetCapabilitiesJob() override; // Result properties diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index 395337f3..d811426d 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -13,8 +13,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/media/r0"); -class UploadContentJob::Private -{ +class UploadContentJob::Private { public: QString contentUri; }; @@ -26,12 +25,10 @@ BaseJob::Query queryToUploadContent(const QString& filename) return _q; } -static const auto UploadContentJobName = QStringLiteral("UploadContentJob"); - UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, const QString& contentType) - : BaseJob(HttpVerb::Post, UploadContentJobName, basePath % "/upload", - queryToUploadContent(filename)) + : BaseJob(HttpVerb::Post, QStringLiteral("UploadContentJob"), + basePath % "/upload", queryToUploadContent(filename)) , d(new Private) { setRequestHeader("Content-Type", contentType.toLatin1()); @@ -53,8 +50,7 @@ BaseJob::Status UploadContentJob::parseJson(const QJsonDocument& data) return Success; } -class GetContentJob::Private -{ +class GetContentJob::Private { public: QString contentType; QString contentDisposition; @@ -77,11 +73,9 @@ QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, queryToGetContent(allowRemote)); } -static const auto GetContentJobName = QStringLiteral("GetContentJob"); - GetContentJob::GetContentJob(const QString& serverName, const QString& mediaId, bool allowRemote) - : BaseJob(HttpVerb::Get, GetContentJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetContentJob"), basePath % "/download/" % serverName % "/" % mediaId, queryToGetContent(allowRemote), {}, false) , d(new Private) @@ -108,8 +102,7 @@ BaseJob::Status GetContentJob::parseReply(QNetworkReply* reply) return Success; } -class GetContentOverrideNameJob::Private -{ +class GetContentOverrideNameJob::Private { public: QString contentType; QString contentDisposition; @@ -135,14 +128,11 @@ QUrl GetContentOverrideNameJob::makeRequestUrl(QUrl baseUrl, queryToGetContentOverrideName(allowRemote)); } -static const auto GetContentOverrideNameJobName = - QStringLiteral("GetContentOverrideNameJob"); - GetContentOverrideNameJob::GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, const QString& fileName, bool allowRemote) - : BaseJob(HttpVerb::Get, GetContentOverrideNameJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetContentOverrideNameJob"), basePath % "/download/" % serverName % "/" % mediaId % "/" % fileName, queryToGetContentOverrideName(allowRemote), {}, false) @@ -173,8 +163,7 @@ BaseJob::Status GetContentOverrideNameJob::parseReply(QNetworkReply* reply) return Success; } -class GetContentThumbnailJob::Private -{ +class GetContentThumbnailJob::Private { public: QString contentType; QIODevice* data; @@ -204,14 +193,11 @@ QUrl GetContentThumbnailJob::makeRequestUrl(QUrl baseUrl, queryToGetContentThumbnail(width, height, method, allowRemote)); } -static const auto GetContentThumbnailJobName = - QStringLiteral("GetContentThumbnailJob"); - GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, int height, const QString& method, bool allowRemote) - : BaseJob(HttpVerb::Get, GetContentThumbnailJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetContentThumbnailJob"), basePath % "/thumbnail/" % serverName % "/" % mediaId, queryToGetContentThumbnail(width, height, method, allowRemote), {}, false) @@ -236,8 +222,7 @@ BaseJob::Status GetContentThumbnailJob::parseReply(QNetworkReply* reply) return Success; } -class GetUrlPreviewJob::Private -{ +class GetUrlPreviewJob::Private { public: Omittable matrixImageSize; QString ogImage; @@ -258,11 +243,9 @@ QUrl GetUrlPreviewJob::makeRequestUrl(QUrl baseUrl, const QString& url, queryToGetUrlPreview(url, ts)); } -static const auto GetUrlPreviewJobName = QStringLiteral("GetUrlPreviewJob"); - GetUrlPreviewJob::GetUrlPreviewJob(const QString& url, Omittable ts) - : BaseJob(HttpVerb::Get, GetUrlPreviewJobName, basePath % "/preview_url", - queryToGetUrlPreview(url, ts)) + : BaseJob(HttpVerb::Get, QStringLiteral("GetUrlPreviewJob"), + basePath % "/preview_url", queryToGetUrlPreview(url, ts)) , d(new Private) {} @@ -284,8 +267,7 @@ BaseJob::Status GetUrlPreviewJob::parseJson(const QJsonDocument& data) return Success; } -class GetConfigJob::Private -{ +class GetConfigJob::Private { public: Omittable uploadSize; }; @@ -295,10 +277,9 @@ QUrl GetConfigJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/config"); } -static const auto GetConfigJobName = QStringLiteral("GetConfigJob"); - GetConfigJob::GetConfigJob() - : BaseJob(HttpVerb::Get, GetConfigJobName, basePath % "/config") + : BaseJob(HttpVerb::Get, QStringLiteral("GetConfigJob"), + basePath % "/config") , d(new Private) {} diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h index 83069490..6263ef0b 100644 --- a/lib/csapi/content-repo.h +++ b/lib/csapi/content-repo.h @@ -10,17 +10,17 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Upload some content to the content repository. - -class UploadContentJob : public BaseJob -{ +/*! \brief Upload some content to the content repository. + * + */ +class UploadContentJob : public BaseJob { public: - /*! Upload some content to the content repository. + /*! \brief Upload some content to the content repository. + * * \param content * \param filename * The name of the file being uploaded @@ -45,12 +45,13 @@ private: QScopedPointer d; }; -/// Download content from the content repository. - -class GetContentJob : public BaseJob -{ +/*! \brief Download content from the content repository. + * + */ +class GetContentJob : public BaseJob { public: - /*! Download content from the content repository. + /*! \brief Download content from the content repository. + * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) * \param mediaId @@ -63,23 +64,23 @@ public: explicit GetContentJob(const QString& serverName, const QString& mediaId, bool allowRemote = true); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetContentJob is necessary but the job - * itself isn't. + * 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, bool allowRemote = true); - ~GetContentJob() override; // Result properties /// The content type of the file that was previously uploaded. const QString& contentType() const; + /// The name of the file that was previously uploaded, if set. const QString& contentDisposition() const; + /// The content that was previously uploaded. QIODevice* data() const; @@ -91,12 +92,13 @@ private: QScopedPointer d; }; -/// Download content from the content repository as a given filename. - -class GetContentOverrideNameJob : public BaseJob -{ +/*! \brief Download content from the content repository as a given filename. + * + */ +class GetContentOverrideNameJob : public BaseJob { public: - /*! Download content from the content repository as a given filename. + /*! \brief Download content from the content repository as a given filename. + * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) * \param mediaId @@ -113,24 +115,24 @@ public: const QString& fileName, bool allowRemote = true); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetContentOverrideNameJob is necessary but the job - * itself isn't. + * 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, bool allowRemote = true); - ~GetContentOverrideNameJob() override; // Result properties /// The content type of the file that was previously uploaded. const QString& contentType() const; + /// The name of file given in the request const QString& contentDisposition() const; + /// The content that was previously uploaded. QIODevice* data() const; @@ -142,12 +144,13 @@ private: QScopedPointer d; }; -/// Download a thumbnail of the content from the content repository. - -class GetContentThumbnailJob : public BaseJob -{ +/*! \brief Download a thumbnail of the content from the content repository. + * + */ +class GetContentThumbnailJob : public BaseJob { public: - /*! Download a thumbnail of the content from the content repository. + /*! \brief Download a thumbnail of the content from the content repository. + * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) * \param mediaId @@ -170,23 +173,22 @@ public: int height, const QString& method = {}, bool allowRemote = true); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetContentThumbnailJob is necessary but the job - * itself isn't. + * 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 = {}, bool allowRemote = true); - ~GetContentThumbnailJob() override; // Result properties /// The content type of the thumbnail. const QString& contentType() const; + /// A thumbnail of the requested content. QIODevice* data() const; @@ -198,12 +200,13 @@ private: QScopedPointer d; }; -/// Get information about a URL for a client - -class GetUrlPreviewJob : public BaseJob -{ +/*! \brief Get information about a URL for a client + * + */ +class GetUrlPreviewJob : public BaseJob { public: - /*! Get information about a URL for a client + /*! \brief Get information about a URL for a client + * * \param url * The URL to get a preview of * \param ts @@ -213,21 +216,20 @@ public: */ explicit GetUrlPreviewJob(const QString& url, Omittable ts = none); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetUrlPreviewJob is necessary but the job - * itself isn't. + * 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, Omittable ts = none); - ~GetUrlPreviewJob() override; // Result properties /// The byte-size of the image. Omitted if there is no image attached. Omittable matrixImageSize() const; + /// An MXC URI to the image. Omitted if there is no image. const QString& ogImage() const; @@ -239,8 +241,8 @@ private: QScopedPointer d; }; -/// Get the configuration for the content repository. -/*! +/*! \brief Get the configuration for the content repository. + * * This endpoint allows clients to retrieve the configuration of the content * repository, such as upload limitations. * Clients SHOULD use this as a guide when using content repository endpoints. @@ -252,19 +254,17 @@ private: * content repository APIs, for example, proxies may enforce a lower upload size * limit than is advertised by the server on this endpoint. */ -class GetConfigJob : public BaseJob -{ +class GetConfigJob : public BaseJob { public: + /// Get the configuration for the content repository. explicit GetConfigJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetConfigJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetConfigJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetConfigJob() override; // Result properties diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index 68741f13..ec2a1855 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const CreateRoomJob::Invite3pid& pod) { addParam<>(jo, QStringLiteral("id_server"), pod.idServer); @@ -28,8 +26,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const CreateRoomJob::StateEvent& pod) { addParam<>(jo, QStringLiteral("type"), pod.type); @@ -40,14 +37,11 @@ struct JsonObjectConverter } // namespace Quotient -class CreateRoomJob::Private -{ +class CreateRoomJob::Private { public: QString roomId; }; -static const auto CreateRoomJobName = QStringLiteral("CreateRoomJob"); - CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QStringList& invite, @@ -57,7 +51,8 @@ CreateRoomJob::CreateRoomJob(const QString& visibility, const QVector& initialState, const QString& preset, Omittable isDirect, const QJsonObject& powerLevelContentOverride) - : BaseJob(HttpVerb::Post, CreateRoomJobName, basePath % "/createRoom") + : BaseJob(HttpVerb::Post, QStringLiteral("CreateRoomJob"), + basePath % "/createRoom") , d(new Private) { QJsonObject _data; diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index e7000155..f2bd9333 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -11,13 +11,12 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Create a new room -/*! +/*! \brief Create a new room + * * Create a new room with various configuration options. * * The server MUST apply the normal state resolution rules when creating @@ -60,40 +59,55 @@ namespace Quotient * requesting user as the creator, alongside other keys provided in the * ``creation_content``. */ -class CreateRoomJob : public BaseJob -{ +class CreateRoomJob : public BaseJob { public: // Inner data structures - /// Create a new room with various configuration options.The server MUST - /// apply the normal state resolution rules when creatingthe new room, - /// including checking power levels for each event. It MUSTapply the events - /// implied by the request in the following order:0. A default - /// ``m.room.power_levels`` event, giving the room creator (and not other - /// members) permission to send state events. Overridden by the - /// ``power_level_content_override`` parameter.1. Events set by the - /// ``preset``. Currently these are the ``m.room.join_rules``, - /// ``m.room.history_visibility``, and ``m.room.guest_access`` state - /// events.2. Events listed in ``initial_state``, in the order that they are - /// listed.3. Events implied by ``name`` and ``topic`` (``m.room.name`` and - /// ``m.room.topic`` state events).4. Invite events implied by ``invite`` - /// and ``invite_3pid`` (``m.room.member`` with ``membership: invite`` and - /// ``m.room.third_party_invite``).The available presets do the following - /// with respect to room state:======================== ============== - /// ====================== ================ ========= Preset - /// ``join_rules`` ``history_visibility`` ``guest_access`` - /// Other======================== ============== ====================== - /// ================ =========``private_chat`` ``invite`` - /// ``shared`` ``can_join````trusted_private_chat`` ``invite`` - /// ``shared`` ``can_join`` All invitees are given the - /// same power level as the room creator.``public_chat`` ``public`` - /// ``shared`` ``forbidden``======================== - /// ============== ====================== ================ =========The - /// server will create a ``m.room.create`` event in the room with - /// therequesting user as the creator, alongside other keys provided in - /// the``creation_content``. - struct Invite3pid - { + /// Create a new room with various configuration options. + /// + /// The server MUST apply the normal state resolution rules when creating + /// the new room, including checking power levels for each event. It MUST + /// apply the events implied by the request in the following order: + /// + /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// (and not other members) permission to send state events. Overridden + /// by the ``power_level_content_override`` parameter. + /// + /// 1. Events set by the ``preset``. Currently these are the + /// ``m.room.join_rules``, + /// ``m.room.history_visibility``, and ``m.room.guest_access`` state + /// events. + /// + /// 2. Events listed in ``initial_state``, in the order that they are + /// listed. + /// + /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// ``m.room.topic`` + /// state events). + /// + /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// (``m.room.member`` with + /// ``membership: invite`` and ``m.room.third_party_invite``). + /// + /// The available presets do the following with respect to room state: + /// + /// ======================== ============== ====================== + /// ================ ========= + /// Preset ``join_rules`` ``history_visibility`` + /// ``guest_access`` Other + /// ======================== ============== ====================== + /// ================ ========= + /// ``private_chat`` ``invite`` ``shared`` ``can_join`` + /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All + /// invitees are given the same power level as the room creator. + /// ``public_chat`` ``public`` ``shared`` ``forbidden`` + /// ======================== ============== ====================== + /// ================ ========= + /// + /// The server will create a ``m.room.create`` event in the room with the + /// requesting user as the creator, alongside other keys provided in the + /// ``creation_content``. + struct Invite3pid { /// The hostname+port of the identity server which should be used for /// third party identifier lookups. QString idServer; @@ -104,35 +118,51 @@ public: QString address; }; - /// Create a new room with various configuration options.The server MUST - /// apply the normal state resolution rules when creatingthe new room, - /// including checking power levels for each event. It MUSTapply the events - /// implied by the request in the following order:0. A default - /// ``m.room.power_levels`` event, giving the room creator (and not other - /// members) permission to send state events. Overridden by the - /// ``power_level_content_override`` parameter.1. Events set by the - /// ``preset``. Currently these are the ``m.room.join_rules``, - /// ``m.room.history_visibility``, and ``m.room.guest_access`` state - /// events.2. Events listed in ``initial_state``, in the order that they are - /// listed.3. Events implied by ``name`` and ``topic`` (``m.room.name`` and - /// ``m.room.topic`` state events).4. Invite events implied by ``invite`` - /// and ``invite_3pid`` (``m.room.member`` with ``membership: invite`` and - /// ``m.room.third_party_invite``).The available presets do the following - /// with respect to room state:======================== ============== - /// ====================== ================ ========= Preset - /// ``join_rules`` ``history_visibility`` ``guest_access`` - /// Other======================== ============== ====================== - /// ================ =========``private_chat`` ``invite`` - /// ``shared`` ``can_join````trusted_private_chat`` ``invite`` - /// ``shared`` ``can_join`` All invitees are given the - /// same power level as the room creator.``public_chat`` ``public`` - /// ``shared`` ``forbidden``======================== - /// ============== ====================== ================ =========The - /// server will create a ``m.room.create`` event in the room with - /// therequesting user as the creator, alongside other keys provided in - /// the``creation_content``. - struct StateEvent - { + /// Create a new room with various configuration options. + /// + /// The server MUST apply the normal state resolution rules when creating + /// the new room, including checking power levels for each event. It MUST + /// apply the events implied by the request in the following order: + /// + /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// (and not other members) permission to send state events. Overridden + /// by the ``power_level_content_override`` parameter. + /// + /// 1. Events set by the ``preset``. Currently these are the + /// ``m.room.join_rules``, + /// ``m.room.history_visibility``, and ``m.room.guest_access`` state + /// events. + /// + /// 2. Events listed in ``initial_state``, in the order that they are + /// listed. + /// + /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// ``m.room.topic`` + /// state events). + /// + /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// (``m.room.member`` with + /// ``membership: invite`` and ``m.room.third_party_invite``). + /// + /// The available presets do the following with respect to room state: + /// + /// ======================== ============== ====================== + /// ================ ========= + /// Preset ``join_rules`` ``history_visibility`` + /// ``guest_access`` Other + /// ======================== ============== ====================== + /// ================ ========= + /// ``private_chat`` ``invite`` ``shared`` ``can_join`` + /// ``trusted_private_chat`` ``invite`` ``shared`` ``can_join`` All + /// invitees are given the same power level as the room creator. + /// ``public_chat`` ``public`` ``shared`` ``forbidden`` + /// ======================== ============== ====================== + /// ================ ========= + /// + /// The server will create a ``m.room.create`` event in the room with the + /// requesting user as the creator, alongside other keys provided in the + /// ``creation_content``. + struct StateEvent { /// The type of event to send. QString type; /// The state_key of the state event. Defaults to an empty string. @@ -143,7 +173,8 @@ public: // Construction/destruction - /*! Create a new room + /*! \brief Create a new room + * * \param visibility * A ``public`` visibility indicates that the room will be shown * in the published room list. A ``private`` visibility will hide diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index 1aeea6c2..e564f7f3 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -9,17 +9,16 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Data structures /// Used by clients to submit authentication information to the /// interactive-authentication API -struct AuthenticationData -{ +struct AuthenticationData { /// The login type that the client is attempting to complete. QString type; + /// The value of the session key given by the homeserver. QString session; @@ -28,8 +27,7 @@ struct AuthenticationData }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const AuthenticationData& pod); static void fillFrom(QJsonObject jo, AuthenticationData& pod); }; diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index f076c4da..2cf75950 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -6,30 +6,31 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures /// A client device -struct Device -{ +struct Device { /// Identifier of this device. QString deviceId; - /// Display name set by the user for this device. Absent if no name has - /// beenset. + + /// Display name set by the user for this device. Absent if no name has been + /// set. QString displayName; + /// The IP address where this device was last seen. (May be a few minutes - /// outof date, for efficiency reasons). + /// out of date, for efficiency reasons). QString lastSeenIp; - /// The timestamp (in milliseconds since the unix epoch) when this deviceswas - /// last seen. (May be a few minutes out of date, for efficiencyreasons). + + /// The timestamp (in milliseconds since the unix epoch) when this devices + /// was last seen. (May be a few minutes out of date, for efficiency + /// reasons). Omittable lastSeenTs; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Device& pod); static void fillFrom(const QJsonObject& jo, Device& pod); }; diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index d1d8abef..a067b4f3 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -8,35 +8,38 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures /// Device identity keys -struct DeviceKeys -{ - /// The ID of the user the device belongs to. Must match the user ID - /// usedwhen logging in. +struct DeviceKeys { + /// The ID of the user the device belongs to. Must match the user ID used + /// when logging in. QString userId; - /// The ID of the device these keys belong to. Must match the device ID - /// usedwhen logging in. + + /// The ID of the device these keys belong to. Must match the device ID used + /// when logging in. QString deviceId; + /// The encryption algorithms supported by this device. QStringList algorithms; - /// Public identity keys. The names of the properties should be in theformat - /// ``:``. The keys themselves should beencoded as - /// specified by the key algorithm. + + /// Public identity keys. The names of the properties should be in the + /// format ``:``. The keys themselves should be + /// encoded as specified by the key algorithm. QHash keys; - /// Signatures for the device key object. A map from user ID, to a map - /// from``:`` to the signature.The signature is - /// calculated using the process described at `SigningJSON`_. + + /// Signatures for the device key object. A map from user ID, to a map from + /// ``:`` to the signature. + /// + /// The signature is calculated using the process described at `Signing + /// JSON`_. QHash> signatures; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DeviceKeys& pod); static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); }; diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index b41e2e9e..3958b125 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -6,27 +6,29 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct EventFilter -{ +struct EventFilter { /// The maximum number of events to return. Omittable limit; + /// A list of sender IDs to exclude. If this list is absent then no senders /// are excluded. A matching sender will be excluded even if it is listed in /// the ``'senders'`` filter. QStringList notSenders; + /// A list of event types to exclude. If this list is absent then no event /// types are excluded. A matching type will be excluded even if it is /// listed in the ``'types'`` filter. A '*' can be used as a wildcard to /// match any sequence of characters. QStringList notTypes; + /// A list of senders IDs to include. If this list is absent then all /// senders are included. QStringList senders; + /// A list of event types to include. If this list is absent then all event /// types are included. A ``'*'`` can be used as a wildcard to match any /// sequence of characters. @@ -34,8 +36,7 @@ struct EventFilter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const EventFilter& pod); static void fillFrom(const QJsonObject& jo, EventFilter& pod); }; diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index 1cb3aad5..36aa52b9 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -8,61 +8,68 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures -struct PublicRoomsChunk -{ +struct PublicRoomsChunk { /// Aliases of the room. May be empty. QStringList aliases; + /// The canonical alias of the room, if any. QString canonicalAlias; + /// The name of the room, if any. QString name; + /// The number of members joined to the room. int numJoinedMembers; + /// The ID of the room. QString roomId; + /// The topic of the room, if any. QString topic; + /// Whether the room may be viewed by guest users without joining. bool worldReadable; - /// Whether guest users may join the room and participate in it.If they can, - /// they will be subject to ordinary power levelrules like any other user. + + /// Whether guest users may join the room and participate in it. + /// If they can, they will be subject to ordinary power level + /// rules like any other user. bool guestCanJoin; + /// The URL for the room's avatar, if one is set. QString avatarUrl; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod); static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod); }; - /// A list of the rooms on the server. -struct PublicRoomsResponse -{ +struct PublicRoomsResponse { /// A paginated chunk of public rooms. QVector chunk; - /// A pagination token for the response. The absence of this tokenmeans - /// there are no more results to fetch and the client shouldstop paginating. + + /// A pagination token for the response. The absence of this token + /// means there are no more results to fetch and the client should + /// stop paginating. QString nextBatch; - /// A pagination token that allows fetching previous results. Theabsence of - /// this token means there are no results before thisbatch, i.e. this is the - /// first batch. + + /// A pagination token that allows fetching previous results. The + /// absence of this token means there are no results before this + /// batch, i.e. this is the first batch. QString prevBatch; - /// An estimate on the total number of public rooms, if theserver has an - /// estimate. + + /// An estimate on the total number of public rooms, if the + /// server has an estimate. Omittable totalRoomCountEstimate; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod); static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); }; diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index 34a183de..189153b3 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -6,32 +6,32 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct PushCondition -{ - +struct PushCondition { QString kind; - /// Required for ``event_match`` conditions. The dot-separated field of - /// theevent to match. + + /// Required for ``event_match`` conditions. The dot-separated field of the + /// event to match. QString key; - /// Required for ``event_match`` conditions. The glob-style pattern tomatch - /// against. Patterns with no special glob characters should betreated as - /// having asterisks prepended and appended when testing thecondition. + + /// Required for ``event_match`` conditions. The glob-style pattern to + /// match against. Patterns with no special glob characters should be + /// treated as having asterisks prepended and appended when testing the + /// condition. QString pattern; - /// Required for ``room_member_count`` conditions. A decimal integeroptionally - /// prefixed by one of, ==, <, >, >= or <=. A prefix of < matchesrooms where - /// the member count is strictly less than the given number andso forth. If - /// no prefix is present, this parameter defaults to ==. + + /// Required for ``room_member_count`` conditions. A decimal integer + /// optionally prefixed by one of, ==, <, >, >= or <=. A prefix of < matches + /// rooms where the member count is strictly less than the given number and + /// so forth. If no prefix is present, this parameter defaults to ==. QString is; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushCondition& pod); static void fillFrom(const QJsonObject& jo, PushCondition& pod); }; diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index e64d6ba8..c09d063f 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -12,33 +12,35 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Data structures -struct PushRule -{ +struct PushRule { /// The actions to perform when this rule is matched. QVector actions; + /// Whether this is a default rule, or has been set explicitly. bool isDefault; + /// Whether the push rule is enabled or not. bool enabled; + /// The ID of this rule. QString ruleId; + /// The conditions that must hold true for an event in order for a rule to - /// beapplied to an event. A rule with no conditions always matches. - /// Onlyapplicable to ``underride`` and ``override`` rules. + /// be applied to an event. A rule with no conditions always matches. Only + /// applicable to ``underride`` and ``override`` rules. QVector conditions; - /// The glob-style pattern to match against. Only applicable to - /// ``content``rules. + + /// The glob-style pattern to match against. Only applicable to ``content`` + /// rules. QString pattern; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRule& pod); static void fillFrom(const QJsonObject& jo, PushRule& pod); }; diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index b6b9670e..98a21408 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -10,14 +10,11 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures -struct PushRuleset -{ - +struct PushRuleset { QVector content; QVector override; @@ -30,8 +27,7 @@ struct PushRuleset }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PushRuleset& pod); static void fillFrom(const QJsonObject& jo, PushRuleset& pod); }; diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index ae06a615..756f9ada 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -8,20 +8,20 @@ #include "csapi/definitions/event_filter.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct RoomEventFilter : EventFilter -{ +struct RoomEventFilter : EventFilter { /// A list of room IDs to exclude. If this list is absent then no rooms are /// excluded. A matching room will be excluded even if it is listed in the /// ``'rooms'`` filter. QStringList notRooms; + /// A list of room IDs to include. If this list is absent then all rooms are /// included. QStringList rooms; + /// If ``true``, includes only events with a ``url`` key in their content. /// If ``false``, excludes those events. If omitted, ``url`` key is not /// considered for filtering. @@ -29,8 +29,7 @@ struct RoomEventFilter : EventFilter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod); static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); }; diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index 9ea39a65..ad8d055d 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -9,97 +9,102 @@ #include "csapi/definitions/event_filter.h" #include "csapi/definitions/room_event_filter.h" -namespace Quotient -{ +namespace Quotient { // Data structures /// The state events to include for rooms. -struct StateFilter : RoomEventFilter -{ - /// If ``true``, the only ``m.room.member`` events returned inthe ``state`` - /// section of the ``/sync`` response are thosewhich are definitely necessary - /// for a client to displaythe ``sender`` of the timeline events in that - /// response.If ``false``, ``m.room.member`` events are not filtered.By - /// default, servers should suppress duplicate redundantlazy-loaded - /// ``m.room.member`` events from being sent to a givenclient across multiple - /// calls to ``/sync``, given that most clientscache membership events (see - /// ``include_redundant_members``to change this behaviour). +struct StateFilter : RoomEventFilter { + /// If ``true``, the only ``m.room.member`` events returned in + /// the ``state`` section of the ``/sync`` response are those + /// which are definitely necessary for a client to display + /// the ``sender`` of the timeline events in that response. + /// If ``false``, ``m.room.member`` events are not filtered. + /// By default, servers should suppress duplicate redundant + /// lazy-loaded ``m.room.member`` events from being sent to a given + /// client across multiple calls to ``/sync``, given that most clients + /// cache membership events (see ``include_redundant_members`` + /// to change this behaviour). Omittable lazyLoadMembers; - /// If ``true``, the ``state`` section of the ``/sync`` response willalways - /// contain the ``m.room.member`` events required to displaythe ``sender`` - /// of the timeline events in that response, assuming``lazy_load_members`` - /// is enabled. This means that redundantduplicate member events may be - /// returned across multiple calls to``/sync``. This is useful for naive - /// clients who never trackmembership data. If ``false``, duplicate - /// ``m.room.member`` eventsmay be suppressed by the server across multiple - /// calls to ``/sync``.If ``lazy_load_members`` is ``false`` this field is - /// ignored. + + /// If ``true``, the ``state`` section of the ``/sync`` response will + /// always contain the ``m.room.member`` events required to display + /// the ``sender`` of the timeline events in that response, assuming + /// ``lazy_load_members`` is enabled. This means that redundant + /// duplicate member events may be returned across multiple calls to + /// ``/sync``. This is useful for naive clients who never track + /// membership data. If ``false``, duplicate ``m.room.member`` events + /// may be suppressed by the server across multiple calls to ``/sync``. + /// If ``lazy_load_members`` is ``false`` this field is ignored. Omittable includeRedundantMembers; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const StateFilter& pod); static void fillFrom(const QJsonObject& jo, StateFilter& pod); }; - /// Filters to be applied to room data. -struct RoomFilter -{ +struct RoomFilter { /// A list of room IDs to exclude. If this list is absent then no rooms are /// excluded. A matching room will be excluded even if it is listed in the /// ``'rooms'`` filter. This filter is applied before the filters in /// ``ephemeral``, ``state``, ``timeline`` or ``account_data`` QStringList notRooms; + /// A list of room IDs to include. If this list is absent then all rooms are /// included. This filter is applied before the filters in ``ephemeral``, /// ``state``, ``timeline`` or ``account_data`` QStringList rooms; + /// The events that aren't recorded in the room history, e.g. typing and /// receipts, to include for rooms. Omittable ephemeral; + /// Include rooms that the user has left in the sync, default false Omittable includeLeave; + /// The state events to include for rooms. Omittable state; + /// The message and state update events to include for rooms. Omittable timeline; + /// The per user account data to include for rooms. Omittable accountData; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RoomFilter& pod); static void fillFrom(const QJsonObject& jo, RoomFilter& pod); }; -struct Filter -{ +struct Filter { /// List of event fields to include. If this list is absent then all fields /// are included. The entries may include '.' charaters to indicate /// sub-fields. So ['content.body'] will include the 'body' field of the /// 'content' object. A literal '.' character in a field name may be escaped /// using a '\\'. A server may include more fields than were requested. QStringList eventFields; + /// The format to use for events. 'client' will return the events in a /// format suitable for clients. 'federation' will return the raw event as /// receieved over federation. The default is 'client'. QString eventFormat; + /// The presence updates to include. Omittable presence; + /// The user account data that isn't associated with rooms to include. Omittable accountData; + /// Filters to be applied to room data. Omittable room; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Filter& pod); static void fillFrom(const QJsonObject& jo, Filter& pod); }; diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index 74e6ce2b..72a81677 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -8,14 +8,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Data structures /// Identification information for a user -struct UserIdentifier -{ +struct UserIdentifier { /// The type of identification. See `Identifier types`_ for supported /// values and additional property descriptions. QString type; @@ -25,8 +23,7 @@ struct UserIdentifier }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const UserIdentifier& pod); static void fillFrom(QJsonObject jo, UserIdentifier& pod); }; diff --git a/lib/csapi/definitions/wellknown/full.h b/lib/csapi/definitions/wellknown/full.h index 92c8afff..88d7da79 100644 --- a/lib/csapi/definitions/wellknown/full.h +++ b/lib/csapi/definitions/wellknown/full.h @@ -12,20 +12,19 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Data structures -/// Used by clients to determine the homeserver, identity server, and other/// +/// Used by clients to determine the homeserver, identity server, and other /// optional components they should be interacting with. -struct DiscoveryInformation -{ - /// Used by clients to determine the homeserver, identity server, and - /// otheroptional components they should be interacting with. +struct DiscoveryInformation { + /// Used by clients to determine the homeserver, identity server, and other + /// optional components they should be interacting with. HomeserverInformation homeserver; - /// Used by clients to determine the homeserver, identity server, and - /// otheroptional components they should be interacting with. + + /// Used by clients to determine the homeserver, identity server, and other + /// optional components they should be interacting with. Omittable identityServer; /// Application-dependent keys using Java package naming convention. @@ -33,8 +32,7 @@ struct DiscoveryInformation }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const DiscoveryInformation& pod); static void fillFrom(QJsonObject jo, DiscoveryInformation& pod); }; diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index 606df88b..7607c8b5 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -6,21 +6,18 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures /// Used by clients to discover homeserver information. -struct HomeserverInformation -{ +struct HomeserverInformation { /// The base URL for the homeserver for client-server connections. QString baseUrl; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod); static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); }; diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index b4304ef7..d272e527 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -6,21 +6,18 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures /// Used by clients to discover identity server information. -struct IdentityServerInformation -{ +struct IdentityServerInformation { /// The base URL for the identity server for client-server connections. QString baseUrl; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod); static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); }; diff --git a/lib/csapi/device_management.cpp b/lib/csapi/device_management.cpp index 0889089e..ba7570f7 100644 --- a/lib/csapi/device_management.cpp +++ b/lib/csapi/device_management.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetDevicesJob::Private -{ +class GetDevicesJob::Private { public: QVector devices; }; @@ -23,10 +22,9 @@ QUrl GetDevicesJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/devices"); } -static const auto GetDevicesJobName = QStringLiteral("GetDevicesJob"); - GetDevicesJob::GetDevicesJob() - : BaseJob(HttpVerb::Get, GetDevicesJobName, basePath % "/devices") + : BaseJob(HttpVerb::Get, QStringLiteral("GetDevicesJob"), + basePath % "/devices") , d(new Private) {} @@ -42,8 +40,7 @@ BaseJob::Status GetDevicesJob::parseJson(const QJsonDocument& data) return Success; } -class GetDeviceJob::Private -{ +class GetDeviceJob::Private { public: Device data; }; @@ -54,10 +51,9 @@ QUrl GetDeviceJob::makeRequestUrl(QUrl baseUrl, const QString& deviceId) basePath % "/devices/" % deviceId); } -static const auto GetDeviceJobName = QStringLiteral("GetDeviceJob"); - GetDeviceJob::GetDeviceJob(const QString& deviceId) - : BaseJob(HttpVerb::Get, GetDeviceJobName, basePath % "/devices/" % deviceId) + : BaseJob(HttpVerb::Get, QStringLiteral("GetDeviceJob"), + basePath % "/devices/" % deviceId) , d(new Private) {} @@ -68,14 +64,13 @@ const Device& GetDeviceJob::data() const { return d->data; } BaseJob::Status GetDeviceJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -static const auto UpdateDeviceJobName = QStringLiteral("UpdateDeviceJob"); - UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, const QString& displayName) - : BaseJob(HttpVerb::Put, UpdateDeviceJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("UpdateDeviceJob"), basePath % "/devices/" % deviceId) { QJsonObject _data; @@ -83,11 +78,9 @@ UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, setRequestData(_data); } -static const auto DeleteDeviceJobName = QStringLiteral("DeleteDeviceJob"); - DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, const Omittable& auth) - : BaseJob(HttpVerb::Delete, DeleteDeviceJobName, + : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), basePath % "/devices/" % deviceId) { QJsonObject _data; @@ -95,11 +88,10 @@ DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, setRequestData(_data); } -static const auto DeleteDevicesJobName = QStringLiteral("DeleteDevicesJob"); - DeleteDevicesJob::DeleteDevicesJob(const QStringList& devices, const Omittable& auth) - : BaseJob(HttpVerb::Post, DeleteDevicesJobName, basePath % "/delete_devices") + : BaseJob(HttpVerb::Post, QStringLiteral("DeleteDevicesJob"), + basePath % "/delete_devices") { QJsonObject _data; addParam<>(_data, QStringLiteral("devices"), devices); diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h index d380a44f..1c57db84 100644 --- a/lib/csapi/device_management.h +++ b/lib/csapi/device_management.h @@ -13,28 +13,25 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// List registered devices for the current user -/*! +/*! \brief List registered devices for the current user + * * Gets information about all devices for the current user. */ -class GetDevicesJob : public BaseJob -{ +class GetDevicesJob : public BaseJob { public: + /// List registered devices for the current user explicit GetDevicesJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetDevicesJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetDevicesJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetDevicesJob() override; // Result properties @@ -50,27 +47,25 @@ private: QScopedPointer d; }; -/// Get a single device -/*! +/*! \brief Get a single device + * * Gets information on a single device, by device id. */ -class GetDeviceJob : public BaseJob -{ +class GetDeviceJob : public BaseJob { public: - /*! Get a single device + /*! \brief Get a single device + * * \param deviceId * The device to retrieve. */ explicit GetDeviceJob(const QString& deviceId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetDeviceJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetDeviceJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& deviceId); - ~GetDeviceJob() override; // Result properties @@ -86,14 +81,14 @@ private: QScopedPointer d; }; -/// Update a device -/*! +/*! \brief Update a device + * * Updates the metadata on the given device. */ -class UpdateDeviceJob : public BaseJob -{ +class UpdateDeviceJob : public BaseJob { public: - /*! Update a device + /*! \brief Update a device + * * \param deviceId * The device to update. * \param displayName @@ -104,16 +99,16 @@ public: const QString& displayName = {}); }; -/// Delete a device -/*! +/*! \brief Delete a device + * * This API endpoint uses the `User-Interactive Authentication API`_. * * Deletes the given device, and invalidates any access token associated with it. */ -class DeleteDeviceJob : public BaseJob -{ +class DeleteDeviceJob : public BaseJob { public: - /*! Delete a device + /*! \brief Delete a device + * * \param deviceId * The device to delete. * \param auth @@ -124,17 +119,17 @@ public: const Omittable& auth = none); }; -/// Bulk deletion of devices -/*! +/*! \brief Bulk deletion of devices + * * This API endpoint uses the `User-Interactive Authentication API`_. * * Deletes the given devices, and invalidates any access token associated with * them. */ -class DeleteDevicesJob : public BaseJob -{ +class DeleteDevicesJob : public BaseJob { public: - /*! Bulk deletion of devices + /*! \brief Bulk deletion of devices + * * \param devices * The list of device IDs to delete. * \param auth diff --git a/lib/csapi/directory.cpp b/lib/csapi/directory.cpp index b2689d1e..0d4029bd 100644 --- a/lib/csapi/directory.cpp +++ b/lib/csapi/directory.cpp @@ -12,10 +12,8 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0/directory"); -static const auto SetRoomAliasJobName = QStringLiteral("SetRoomAliasJob"); - SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId) - : BaseJob(HttpVerb::Put, SetRoomAliasJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomAliasJob"), basePath % "/room/" % roomAlias) { QJsonObject _data; @@ -23,8 +21,7 @@ SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId setRequestData(_data); } -class GetRoomIdByAliasJob::Private -{ +class GetRoomIdByAliasJob::Private { public: QString roomId; QStringList servers; @@ -36,11 +33,8 @@ QUrl GetRoomIdByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) basePath % "/room/" % roomAlias); } -static const auto GetRoomIdByAliasJobName = - QStringLiteral("GetRoomIdByAliasJob"); - GetRoomIdByAliasJob::GetRoomIdByAliasJob(const QString& roomAlias) - : BaseJob(HttpVerb::Get, GetRoomIdByAliasJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomIdByAliasJob"), basePath % "/room/" % roomAlias, false) , d(new Private) {} @@ -66,9 +60,7 @@ QUrl DeleteRoomAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) basePath % "/room/" % roomAlias); } -static const auto DeleteRoomAliasJobName = QStringLiteral("DeleteRoomAliasJob"); - DeleteRoomAliasJob::DeleteRoomAliasJob(const QString& roomAlias) - : BaseJob(HttpVerb::Delete, DeleteRoomAliasJobName, + : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteRoomAliasJob"), basePath % "/room/" % roomAlias) {} diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h index 7863aa1a..c13ca20a 100644 --- a/lib/csapi/directory.h +++ b/lib/csapi/directory.h @@ -6,17 +6,17 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Create a new mapping from room alias to room ID. - -class SetRoomAliasJob : public BaseJob -{ +/*! \brief Create a new mapping from room alias to room ID. + * + */ +class SetRoomAliasJob : public BaseJob { public: - /*! Create a new mapping from room alias to room ID. + /*! \brief Create a new mapping from room alias to room ID. + * * \param roomAlias * The room alias to set. * \param roomId @@ -25,37 +25,36 @@ public: explicit SetRoomAliasJob(const QString& roomAlias, const QString& roomId); }; -/// Get the room ID corresponding to this room alias. -/*! +/*! \brief Get the room ID corresponding to this room alias. + * * Requests that the server resolve a room alias to a room ID. * * The server will use the federation API to resolve the alias if the * domain part of the alias does not correspond to the server's own * domain. */ -class GetRoomIdByAliasJob : public BaseJob -{ +class GetRoomIdByAliasJob : public BaseJob { public: - /*! Get the room ID corresponding to this room alias. + /*! \brief Get the room ID corresponding to this room alias. + * * \param roomAlias * The room alias. */ explicit GetRoomIdByAliasJob(const QString& roomAlias); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomIdByAliasJob is necessary but the job - * itself isn't. + * 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); - ~GetRoomIdByAliasJob() override; // Result properties /// The room ID for this room alias. const QString& roomId() const; + /// A list of servers that are aware of this room alias. const QStringList& servers() const; @@ -67,28 +66,27 @@ private: QScopedPointer d; }; -/// Remove a mapping of room alias to room ID. -/*! +/*! \brief Remove a mapping of room alias to room ID. + * * Remove a mapping of room alias to room ID. * * Servers may choose to implement additional access control checks here, for * instance that room aliases can only be deleted by their creator or a server * administrator. */ -class DeleteRoomAliasJob : public BaseJob -{ +class DeleteRoomAliasJob : public BaseJob { public: - /*! Remove a mapping of room alias to room ID. + /*! \brief Remove a mapping of room alias to room ID. + * * \param roomAlias * The room alias to remove. */ explicit DeleteRoomAliasJob(const QString& roomAlias); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * DeleteRoomAliasJob is necessary but the job - * itself isn't. + * 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); }; diff --git a/lib/csapi/event_context.cpp b/lib/csapi/event_context.cpp index d233eedb..5bb2222e 100644 --- a/lib/csapi/event_context.cpp +++ b/lib/csapi/event_context.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetEventContextJob::Private -{ +class GetEventContextJob::Private { public: QString begin; QString end; @@ -40,12 +39,10 @@ QUrl GetEventContextJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, queryToGetEventContext(limit)); } -static const auto GetEventContextJobName = QStringLiteral("GetEventContextJob"); - GetEventContextJob::GetEventContextJob(const QString& roomId, const QString& eventId, Omittable limit) - : BaseJob(HttpVerb::Get, GetEventContextJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetEventContextJob"), basePath % "/rooms/" % roomId % "/context/" % eventId, queryToGetEventContext(limit)) , d(new Private) diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index 755fc662..54441617 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -9,21 +9,20 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Get events and state around the specified event. -/*! +/*! \brief Get events and state around the specified event. + * * This API returns a number of events that happened just before and * after the specified event. This allows clients to get the context * surrounding an event. */ -class GetEventContextJob : public BaseJob -{ +class GetEventContextJob : public BaseJob { public: - /*! Get events and state around the specified event. + /*! \brief Get events and state around the specified event. + * * \param roomId * The room to get events from. * \param eventId @@ -34,32 +33,35 @@ public: explicit GetEventContextJob(const QString& roomId, const QString& eventId, Omittable limit = none); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetEventContextJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetEventContextJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId, Omittable limit = none); - ~GetEventContextJob() override; // Result properties /// A token that can be used to paginate backwards with. const QString& begin() const; + /// A token that can be used to paginate forwards with. const QString& end() const; + /// A list of room events that happened just before the /// requested event, in reverse-chronological order. RoomEvents&& eventsBefore(); + /// Details of the requested event. RoomEventPtr&& event(); + /// A list of room events that happened just after the /// requested event, in chronological order. RoomEvents&& eventsAfter(); + /// The state of the room at the last event returned. StateEvents&& state(); diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index b4160ba4..98b85f83 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -12,16 +12,13 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class DefineFilterJob::Private -{ +class DefineFilterJob::Private { public: QString filterId; }; -static const auto DefineFilterJobName = QStringLiteral("DefineFilterJob"); - DefineFilterJob::DefineFilterJob(const QString& userId, const Filter& filter) - : BaseJob(HttpVerb::Post, DefineFilterJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("DefineFilterJob"), basePath % "/user/" % userId % "/filter") , d(new Private) { @@ -43,8 +40,7 @@ BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) return Success; } -class GetFilterJob::Private -{ +class GetFilterJob::Private { public: Filter data; }; @@ -57,10 +53,8 @@ QUrl GetFilterJob::makeRequestUrl(QUrl baseUrl, const QString& userId, % filterId); } -static const auto GetFilterJobName = QStringLiteral("GetFilterJob"); - GetFilterJob::GetFilterJob(const QString& userId, const QString& filterId) - : BaseJob(HttpVerb::Get, GetFilterJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetFilterJob"), basePath % "/user/" % userId % "/filter/" % filterId) , d(new Private) {} @@ -72,5 +66,6 @@ const Filter& GetFilterJob::data() const { return d->data; } BaseJob::Status GetFilterJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h index 5001a492..7a0f8958 100644 --- a/lib/csapi/filter.h +++ b/lib/csapi/filter.h @@ -10,21 +10,20 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Upload a new filter. -/*! +/*! \brief Upload a new filter. + * * Uploads a new filter definition to the homeserver. * Returns a filter ID that may be used in future requests to * restrict which events are returned to the client. */ -class DefineFilterJob : public BaseJob -{ +class DefineFilterJob : public BaseJob { public: - /*! Upload a new filter. + /*! \brief Upload a new filter. + * * \param userId * The id of the user uploading the filter. The access token must be * authorized to make requests for this user id. \param filter Uploads a new @@ -51,12 +50,13 @@ private: QScopedPointer d; }; -/// Download a filter - -class GetFilterJob : public BaseJob -{ +/*! \brief Download a filter + * + */ +class GetFilterJob : public BaseJob { public: - /*! Download a filter + /*! \brief Download a filter + * * \param userId * The user ID to download a filter for. * \param filterId @@ -64,15 +64,13 @@ public: */ explicit GetFilterJob(const QString& userId, const QString& filterId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetFilterJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetFilterJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& filterId); - ~GetFilterJob() override; // Result properties diff --git a/lib/csapi/inviting.cpp b/lib/csapi/inviting.cpp index b60df6f8..f070c3ce 100644 --- a/lib/csapi/inviting.cpp +++ b/lib/csapi/inviting.cpp @@ -12,10 +12,8 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto InviteUserJobName = QStringLiteral("InviteUserJob"); - InviteUserJob::InviteUserJob(const QString& roomId, const QString& userId) - : BaseJob(HttpVerb::Post, InviteUserJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("InviteUserJob"), basePath % "/rooms/" % roomId % "/invite") { QJsonObject _data; diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h index faf315b1..41d78ae6 100644 --- a/lib/csapi/inviting.h +++ b/lib/csapi/inviting.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Invite a user to participate in a particular room. -/*! +/*! \brief Invite a user to participate in a particular room. + * * .. _invite-by-user-id-endpoint: * * *Note that there are two forms of this API, which are documented separately. @@ -32,10 +31,10 @@ namespace Quotient * * .. _third party invites section: `invite-by-third-party-id-endpoint`_ */ -class InviteUserJob : public BaseJob -{ +class InviteUserJob : public BaseJob { public: - /*! Invite a user to participate in a particular room. + /*! \brief Invite a user to participate in a particular room. + * * \param roomId * The room identifier (not alias) to which to invite the user. * \param userId diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index 2dd617bb..cde179e0 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const JoinRoomByIdJob::ThirdPartySigned& pod) { @@ -31,17 +29,14 @@ struct JsonObjectConverter } // namespace Quotient -class JoinRoomByIdJob::Private -{ +class JoinRoomByIdJob::Private { public: QString roomId; }; -static const auto JoinRoomByIdJobName = QStringLiteral("JoinRoomByIdJob"); - JoinRoomByIdJob::JoinRoomByIdJob( const QString& roomId, const Omittable& thirdPartySigned) - : BaseJob(HttpVerb::Post, JoinRoomByIdJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("JoinRoomByIdJob"), basePath % "/rooms/" % roomId % "/join") , d(new Private) { @@ -67,12 +62,10 @@ BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const JoinRoomJob::Signed& pod) { addParam<>(jo, QStringLiteral("sender"), pod.sender); @@ -83,8 +76,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const JoinRoomJob::ThirdPartySigned& pod) { addParam<>(jo, QStringLiteral("signed"), pod.signedData); @@ -93,8 +85,7 @@ struct JsonObjectConverter } // namespace Quotient -class JoinRoomJob::Private -{ +class JoinRoomJob::Private { public: QString roomId; }; @@ -106,12 +97,10 @@ BaseJob::Query queryToJoinRoom(const QStringList& serverName) return _q; } -static const auto JoinRoomJobName = QStringLiteral("JoinRoomJob"); - JoinRoomJob::JoinRoomJob(const QString& roomIdOrAlias, const QStringList& serverName, const Omittable& thirdPartySigned) - : BaseJob(HttpVerb::Post, JoinRoomJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("JoinRoomJob"), basePath % "/join/" % roomIdOrAlias, queryToJoinRoom(serverName)) , d(new Private) { diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index cf456da9..6d93bcb1 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -10,13 +10,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Start the requesting user participating in a particular room. -/*! +/*! \brief Start the requesting user participating in a particular room. + * * *Note that this API requires a room ID, not alias.* ``/join/{roomIdOrAlias}`` * *exists if you have a room alias.* * @@ -32,15 +31,13 @@ namespace Quotient * that it matches a pending ``m.room.third_party_invite`` event in the * room, and perform key validity checking if required by the event. */ -class JoinRoomByIdJob : public BaseJob -{ +class JoinRoomByIdJob : public BaseJob { public: // Inner data structures /// A signature of an ``m.third_party_invite`` token to prove that this user /// owns a third party identity which has been invited to the room. - struct ThirdPartySigned - { + struct ThirdPartySigned { /// The Matrix ID of the user who issued the invite. QString sender; /// The Matrix ID of the invitee. @@ -54,7 +51,8 @@ public: // Construction/destruction - /*! Start the requesting user participating in a particular room. + /*! \brief Start the requesting user participating in a particular room. + * * \param roomId * The room identifier (not alias) to join. * \param thirdPartySigned @@ -80,8 +78,8 @@ private: QScopedPointer d; }; -/// Start the requesting user participating in a particular room. -/*! +/*! \brief Start the requesting user participating in a particular room. + * * *Note that this API takes either a room ID or alias, unlike* * ``/room/{roomId}/join``. * @@ -97,23 +95,25 @@ private: * that it matches a pending ``m.room.third_party_invite`` event in the * room, and perform key validity checking if required by the event. */ -class JoinRoomJob : public BaseJob -{ +class JoinRoomJob : public BaseJob { public: // Inner data structures /// *Note that this API takes either a room ID or alias, unlike* - /// ``/room/{roomId}/join``.This API starts a user participating in a - /// particular room, if that useris allowed to participate in that room. - /// After this call, the client isallowed to see all current state events in - /// the room, and all subsequentevents associated with the room until the - /// user leaves the room.After a user has joined a room, the room will - /// appear as an entry in theresponse of the |/initialSync|_ and |/sync|_ - /// APIs.If a ``third_party_signed`` was supplied, the homeserver must - /// verifythat it matches a pending ``m.room.third_party_invite`` event in - /// theroom, and perform key validity checking if required by the event. - struct Signed - { + /// ``/room/{roomId}/join``. + /// + /// This API starts a user participating in a particular room, if that user + /// is allowed to participate in that room. After this call, the client is + /// allowed to see all current state events in the room, and all subsequent + /// events associated with the room until the user leaves the room. + /// + /// After a user has joined a room, the room will appear as an entry in the + /// response of the |/initialSync|_ and |/sync|_ APIs. + /// + /// If a ``third_party_signed`` was supplied, the homeserver must verify + /// that it matches a pending ``m.room.third_party_invite`` event in the + /// room, and perform key validity checking if required by the event. + struct Signed { /// The Matrix ID of the user who issued the invite. QString sender; /// The Matrix ID of the invitee. @@ -127,8 +127,7 @@ public: /// A signature of an ``m.third_party_invite`` token to prove that this user /// owns a third party identity which has been invited to the room. - struct ThirdPartySigned - { + struct ThirdPartySigned { /// A signature of an ``m.third_party_invite`` token to prove that this /// user owns a third party identity which has been invited to the room. Signed signedData; @@ -136,7 +135,8 @@ public: // Construction/destruction - /*! Start the requesting user participating in a particular room. + /*! \brief Start the requesting user participating in a particular room. + * * \param roomIdOrAlias * The room identifier or alias to join. * \param serverName diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index cd33ad19..b1a947b3 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -12,17 +12,15 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class UploadKeysJob::Private -{ +class UploadKeysJob::Private { public: QHash oneTimeKeyCounts; }; -static const auto UploadKeysJobName = QStringLiteral("UploadKeysJob"); - UploadKeysJob::UploadKeysJob(const Omittable& deviceKeys, const QHash& oneTimeKeys) - : BaseJob(HttpVerb::Post, UploadKeysJobName, basePath % "/keys/upload") + : BaseJob(HttpVerb::Post, QStringLiteral("UploadKeysJob"), + basePath % "/keys/upload") , d(new Private) { QJsonObject _data; @@ -50,12 +48,10 @@ BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, QueryKeysJob::UnsignedDeviceInfo& result) { @@ -64,8 +60,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, QueryKeysJob::DeviceInformation& result) { @@ -76,18 +71,16 @@ struct JsonObjectConverter } // namespace Quotient -class QueryKeysJob::Private -{ +class QueryKeysJob::Private { public: QHash failures; QHash> deviceKeys; }; -static const auto QueryKeysJobName = QStringLiteral("QueryKeysJob"); - QueryKeysJob::QueryKeysJob(const QHash& deviceKeys, Omittable timeout, const QString& token) - : BaseJob(HttpVerb::Post, QueryKeysJobName, basePath % "/keys/query") + : BaseJob(HttpVerb::Post, QStringLiteral("QueryKeysJob"), + basePath % "/keys/query") , d(new Private) { QJsonObject _data; @@ -119,19 +112,17 @@ BaseJob::Status QueryKeysJob::parseJson(const QJsonDocument& data) return Success; } -class ClaimKeysJob::Private -{ +class ClaimKeysJob::Private { public: QHash failures; QHash> oneTimeKeys; }; -static const auto ClaimKeysJobName = QStringLiteral("ClaimKeysJob"); - ClaimKeysJob::ClaimKeysJob( const QHash>& oneTimeKeys, Omittable timeout) - : BaseJob(HttpVerb::Post, ClaimKeysJobName, basePath % "/keys/claim") + : BaseJob(HttpVerb::Post, QStringLiteral("ClaimKeysJob"), + basePath % "/keys/claim") , d(new Private) { QJsonObject _data; @@ -161,8 +152,7 @@ BaseJob::Status ClaimKeysJob::parseJson(const QJsonDocument& data) return Success; } -class GetKeysChangesJob::Private -{ +class GetKeysChangesJob::Private { public: QStringList changed; QStringList left; @@ -184,11 +174,9 @@ QUrl GetKeysChangesJob::makeRequestUrl(QUrl baseUrl, const QString& from, queryToGetKeysChanges(from, to)); } -static const auto GetKeysChangesJobName = QStringLiteral("GetKeysChangesJob"); - GetKeysChangesJob::GetKeysChangesJob(const QString& from, const QString& to) - : BaseJob(HttpVerb::Get, GetKeysChangesJobName, basePath % "/keys/changes", - queryToGetKeysChanges(from, to)) + : BaseJob(HttpVerb::Get, QStringLiteral("GetKeysChangesJob"), + basePath % "/keys/changes", queryToGetKeysChanges(from, to)) , d(new Private) {} diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h index 27867b8c..2673acc5 100644 --- a/lib/csapi/keys.h +++ b/lib/csapi/keys.h @@ -14,19 +14,18 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Upload end-to-end encryption keys. -/*! +/*! \brief Upload end-to-end encryption keys. + * * Publishes end-to-end encryption keys for the device. */ -class UploadKeysJob : public BaseJob -{ +class UploadKeysJob : public BaseJob { public: - /*! Upload end-to-end encryption keys. + /*! \brief Upload end-to-end encryption keys. + * * \param deviceKeys * Identity keys for the device. May be absent if no new * identity keys are required. @@ -57,34 +56,34 @@ private: QScopedPointer d; }; -/// Download device identity keys. -/*! +/*! \brief Download device identity keys. + * * Returns the current devices and identity keys for the given users. */ -class QueryKeysJob : public BaseJob -{ +class QueryKeysJob : public BaseJob { public: // Inner data structures - /// Additional data added to the device key informationby intermediate - /// servers, and not covered by thesignatures. - struct UnsignedDeviceInfo - { + /// Additional data added to the device key information + /// by intermediate servers, and not covered by the + /// signatures. + struct UnsignedDeviceInfo { /// The display name which the user set on the device. QString deviceDisplayName; }; /// Returns the current devices and identity keys for the given users. - struct DeviceInformation : DeviceKeys - { - /// Additional data added to the device key informationby intermediate - /// servers, and not covered by thesignatures. + struct DeviceInformation : DeviceKeys { + /// Additional data added to the device key information + /// by intermediate servers, and not covered by the + /// signatures. Omittable unsignedData; }; // Construction/destruction - /*! Download device identity keys. + /*! \brief Download device identity keys. + * * \param deviceKeys * The keys to be downloaded. A map from user ID, to a list of * device IDs, or to an empty list to indicate all devices for the @@ -114,6 +113,7 @@ public: /// was unknown, no failure is recorded. Instead, the corresponding /// user or device is missing from the ``device_keys`` result. const QHash& failures() const; + /// Information on the queried devices. A map from user ID, to a /// map from device ID to device information. For each device, /// the information returned will be the same as uploaded via @@ -129,14 +129,14 @@ private: QScopedPointer d; }; -/// Claim one-time encryption keys. -/*! +/*! \brief Claim one-time encryption keys. + * * Claims one-time keys for use in pre-key messages. */ -class ClaimKeysJob : public BaseJob -{ +class ClaimKeysJob : public BaseJob { public: - /*! Claim one-time encryption keys. + /*! \brief Claim one-time encryption keys. + * * \param oneTimeKeys * The keys to be claimed. A map from user ID, to a map from * device ID to algorithm name. @@ -160,6 +160,7 @@ public: /// was unknown, no failure is recorded. Instead, the corresponding /// user or device is missing from the ``one_time_keys`` result. const QHash& failures() const; + /// One-time keys for the queried devices. A map from user ID, to a /// map from devices to a map from ``:`` to the key object. const QHash>& oneTimeKeys() const; @@ -172,8 +173,8 @@ private: QScopedPointer d; }; -/// Query users with recent device key updates. -/*! +/*! \brief Query users with recent device key updates. + * * Gets a list of users who have updated their device identity keys since a * previous sync token. * @@ -184,10 +185,10 @@ private: * * added new device identity keys or removed an existing device with * identity keys, between ``from`` and ``to``. */ -class GetKeysChangesJob : public BaseJob -{ +class GetKeysChangesJob : public BaseJob { public: - /*! Query users with recent device key updates. + /*! \brief Query users with recent device key updates. + * * \param from * The desired start point of the list. Should be the ``next_batch`` field * from a response to an earlier call to |/sync|. Users who have not @@ -202,15 +203,13 @@ public: */ explicit GetKeysChangesJob(const QString& from, const QString& to); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetKeysChangesJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetKeysChangesJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& from, const QString& to); - ~GetKeysChangesJob() override; // Result properties @@ -218,6 +217,7 @@ public: /// The Matrix User IDs of all users who updated their device /// identity keys. const QStringList& changed() const; + /// The Matrix User IDs of all users who may have left all /// the end-to-end encrypted rooms they previously shared /// with the user. diff --git a/lib/csapi/kicking.cpp b/lib/csapi/kicking.cpp index ce7fcdad..39125f42 100644 --- a/lib/csapi/kicking.cpp +++ b/lib/csapi/kicking.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto KickJobName = QStringLiteral("KickJob"); - KickJob::KickJob(const QString& roomId, const QString& userId, const QString& reason) - : BaseJob(HttpVerb::Post, KickJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("KickJob"), basePath % "/rooms/" % roomId % "/kick") { QJsonObject _data; diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h index d51edb47..0633a7f6 100644 --- a/lib/csapi/kicking.h +++ b/lib/csapi/kicking.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Kick a user from the room. -/*! +/*! \brief Kick a user from the room. + * * Kick a user from the room. * * The caller must have the required power level in order to perform this @@ -23,10 +22,10 @@ namespace Quotient * directly adjust the target member's state by making a request to * ``/rooms//state/m.room.member/``. */ -class KickJob : public BaseJob -{ +class KickJob : public BaseJob { public: - /*! Kick a user from the room. + /*! \brief Kick a user from the room. + * * \param roomId * The room identifier (not alias) from which the user should be kicked. * \param userId diff --git a/lib/csapi/leaving.cpp b/lib/csapi/leaving.cpp index abf04888..2fa1da56 100644 --- a/lib/csapi/leaving.cpp +++ b/lib/csapi/leaving.cpp @@ -18,10 +18,8 @@ QUrl LeaveRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) basePath % "/rooms/" % roomId % "/leave"); } -static const auto LeaveRoomJobName = QStringLiteral("LeaveRoomJob"); - LeaveRoomJob::LeaveRoomJob(const QString& roomId) - : BaseJob(HttpVerb::Post, LeaveRoomJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("LeaveRoomJob"), basePath % "/rooms/" % roomId % "/leave") {} @@ -31,9 +29,7 @@ QUrl ForgetRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) basePath % "/rooms/" % roomId % "/forget"); } -static const auto ForgetRoomJobName = QStringLiteral("ForgetRoomJob"); - ForgetRoomJob::ForgetRoomJob(const QString& roomId) - : BaseJob(HttpVerb::Post, ForgetRoomJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("ForgetRoomJob"), basePath % "/rooms/" % roomId % "/forget") {} diff --git a/lib/csapi/leaving.h b/lib/csapi/leaving.h index 280545b0..45b983f3 100644 --- a/lib/csapi/leaving.h +++ b/lib/csapi/leaving.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Stop the requesting user participating in a particular room. -/*! +/*! \brief Stop the requesting user participating in a particular room. + * * This API stops a user participating in a particular room. * * If the user was already in the room, they will no longer be able to see @@ -25,26 +24,25 @@ namespace Quotient * The user will still be allowed to retrieve history from the room which * they were previously allowed to see. */ -class LeaveRoomJob : public BaseJob -{ +class LeaveRoomJob : public BaseJob { public: - /*! Stop the requesting user participating in a particular room. + /*! \brief Stop the requesting user participating in a particular room. + * * \param roomId * The room identifier to leave. */ explicit LeaveRoomJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * LeaveRoomJob is necessary but the job - * itself isn't. + * 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); }; -/// Stop the requesting user remembering about a particular room. -/*! +/*! \brief Stop the requesting user remembering about a particular room. + * * This API stops a user remembering about a particular room. * * In general, history is a first class citizen in Matrix. After this API @@ -55,20 +53,19 @@ public: * If the user is currently joined to the room, they must leave the room * before calling this API. */ -class ForgetRoomJob : public BaseJob -{ +class ForgetRoomJob : public BaseJob { public: - /*! Stop the requesting user remembering about a particular room. + /*! \brief Stop the requesting user remembering about a particular room. + * * \param roomId * The room identifier to forget. */ explicit ForgetRoomJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * ForgetRoomJob is necessary but the job - * itself isn't. + * 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); }; diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index 1260499a..34eb0d42 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetJoinedRoomsJob::Private -{ +class GetJoinedRoomsJob::Private { public: QStringList joinedRooms; }; @@ -24,10 +23,9 @@ QUrl GetJoinedRoomsJob::makeRequestUrl(QUrl baseUrl) basePath % "/joined_rooms"); } -static const auto GetJoinedRoomsJobName = QStringLiteral("GetJoinedRoomsJob"); - GetJoinedRoomsJob::GetJoinedRoomsJob() - : BaseJob(HttpVerb::Get, GetJoinedRoomsJobName, basePath % "/joined_rooms") + : BaseJob(HttpVerb::Get, QStringLiteral("GetJoinedRoomsJob"), + basePath % "/joined_rooms") , d(new Private) {} diff --git a/lib/csapi/list_joined_rooms.h b/lib/csapi/list_joined_rooms.h index 5dab9dfc..a170d623 100644 --- a/lib/csapi/list_joined_rooms.h +++ b/lib/csapi/list_joined_rooms.h @@ -6,28 +6,25 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Lists the user's current rooms. -/*! +/*! \brief Lists the user's current rooms. + * * This API returns a list of the user's current rooms. */ -class GetJoinedRoomsJob : public BaseJob -{ +class GetJoinedRoomsJob : public BaseJob { public: + /// Lists the user's current rooms. explicit GetJoinedRoomsJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetJoinedRoomsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetJoinedRoomsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetJoinedRoomsJob() override; // Result properties diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 0e065440..83320ec0 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetRoomVisibilityOnDirectoryJob::Private -{ +class GetRoomVisibilityOnDirectoryJob::Private { public: QString visibility; }; @@ -25,12 +24,9 @@ QUrl GetRoomVisibilityOnDirectoryJob::makeRequestUrl(QUrl baseUrl, basePath % "/directory/list/room/" % roomId); } -static const auto GetRoomVisibilityOnDirectoryJobName = - QStringLiteral("GetRoomVisibilityOnDirectoryJob"); - GetRoomVisibilityOnDirectoryJob::GetRoomVisibilityOnDirectoryJob( const QString& roomId) - : BaseJob(HttpVerb::Get, GetRoomVisibilityOnDirectoryJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomVisibilityOnDirectoryJob"), basePath % "/directory/list/room/" % roomId, false) , d(new Private) {} @@ -51,12 +47,9 @@ GetRoomVisibilityOnDirectoryJob::parseJson(const QJsonDocument& data) return Success; } -static const auto SetRoomVisibilityOnDirectoryJobName = - QStringLiteral("SetRoomVisibilityOnDirectoryJob"); - SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob( const QString& roomId, const QString& visibility) - : BaseJob(HttpVerb::Put, SetRoomVisibilityOnDirectoryJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomVisibilityOnDirectoryJob"), basePath % "/directory/list/room/" % roomId) { QJsonObject _data; @@ -64,8 +57,7 @@ SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob( setRequestData(_data); } -class GetPublicRoomsJob::Private -{ +class GetPublicRoomsJob::Private { public: PublicRoomsResponse data; }; @@ -88,11 +80,10 @@ QUrl GetPublicRoomsJob::makeRequestUrl(QUrl baseUrl, Omittable limit, queryToGetPublicRooms(limit, since, server)); } -static const auto GetPublicRoomsJobName = QStringLiteral("GetPublicRoomsJob"); - GetPublicRoomsJob::GetPublicRoomsJob(Omittable limit, const QString& since, const QString& server) - : BaseJob(HttpVerb::Get, GetPublicRoomsJobName, basePath % "/publicRooms", + : BaseJob(HttpVerb::Get, QStringLiteral("GetPublicRoomsJob"), + basePath % "/publicRooms", queryToGetPublicRooms(limit, since, server), {}, false) , d(new Private) {} @@ -104,16 +95,15 @@ const PublicRoomsResponse& GetPublicRoomsJob::data() const { return d->data; } BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const QueryPublicRoomsJob::Filter& pod) { addParam(jo, QStringLiteral("generic_search_term"), @@ -123,8 +113,7 @@ struct JsonObjectConverter } // namespace Quotient -class QueryPublicRoomsJob::Private -{ +class QueryPublicRoomsJob::Private { public: PublicRoomsResponse data; }; @@ -136,16 +125,13 @@ BaseJob::Query queryToQueryPublicRooms(const QString& server) return _q; } -static const auto QueryPublicRoomsJobName = - QStringLiteral("QueryPublicRoomsJob"); - QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, Omittable limit, const QString& since, const Omittable& filter, Omittable includeAllNetworks, const QString& thirdPartyInstanceId) - : BaseJob(HttpVerb::Post, QueryPublicRoomsJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("QueryPublicRoomsJob"), basePath % "/publicRooms", queryToQueryPublicRooms(server)) , d(new Private) { @@ -167,5 +153,6 @@ const PublicRoomsResponse& QueryPublicRoomsJob::data() const { return d->data; } BaseJob::Status QueryPublicRoomsJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index e68030ad..0c9a2620 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -10,32 +10,29 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets the visibility of a room in the directory -/*! +/*! \brief Gets the visibility of a room in the directory + * * Gets the visibility of a given room on the server's public room directory. */ -class GetRoomVisibilityOnDirectoryJob : public BaseJob -{ +class GetRoomVisibilityOnDirectoryJob : public BaseJob { public: - /*! Gets the visibility of a room in the directory + /*! \brief Gets the visibility of a room in the directory + * * \param roomId * The room ID. */ explicit GetRoomVisibilityOnDirectoryJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomVisibilityOnDirectoryJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomVisibilityOnDirectoryJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetRoomVisibilityOnDirectoryJob() override; // Result properties @@ -51,8 +48,8 @@ private: QScopedPointer d; }; -/// Sets the visibility of a room in the room directory -/*! +/*! \brief Sets the visibility of a room in the room directory + * * Sets the visibility of a given room in the server's public room * directory. * @@ -60,10 +57,10 @@ private: * here, for instance that room visibility can only be changed by * the room creator or a server administrator. */ -class SetRoomVisibilityOnDirectoryJob : public BaseJob -{ +class SetRoomVisibilityOnDirectoryJob : public BaseJob { public: - /*! Sets the visibility of a room in the room directory + /*! \brief Sets the visibility of a room in the room directory + * * \param roomId * The room ID. * \param visibility @@ -74,17 +71,17 @@ public: const QString& visibility = {}); }; -/// Lists the public rooms on the server. -/*! +/*! \brief Lists the public rooms on the server. + * * Lists the public rooms on the server. * * This API returns paginated responses. The rooms are ordered by the number * of joined members, with the largest rooms first. */ -class GetPublicRoomsJob : public BaseJob -{ +class GetPublicRoomsJob : public BaseJob { public: - /*! Lists the public rooms on the server. + /*! \brief Lists the public rooms on the server. + * * \param limit * Limit the number of results returned. * \param since @@ -100,16 +97,14 @@ public: const QString& since = {}, const QString& server = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPublicRoomsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPublicRoomsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, Omittable limit = none, const QString& since = {}, const QString& server = {}); - ~GetPublicRoomsJob() override; // Result properties @@ -125,29 +120,28 @@ private: QScopedPointer d; }; -/// Lists the public rooms on the server with optional filter. -/*! +/*! \brief Lists the public rooms on the server with optional filter. + * * Lists the public rooms on the server, with optional filter. * * This API returns paginated responses. The rooms are ordered by the number * of joined members, with the largest rooms first. */ -class QueryPublicRoomsJob : public BaseJob -{ +class QueryPublicRoomsJob : public BaseJob { public: // Inner data structures /// Filter to apply to the results. - struct Filter - { - /// A string to search for in the room metadata, e.g. name,topic, - /// canonical alias etc. (Optional). + struct Filter { + /// A string to search for in the room metadata, e.g. name, + /// topic, canonical alias etc. (Optional). QString genericSearchTerm; }; // Construction/destruction - /*! Lists the public rooms on the server with optional filter. + /*! \brief Lists the public rooms on the server with optional filter. + * * \param server * The server to fetch the public room lists from. Defaults to the * local server. diff --git a/lib/csapi/login.cpp b/lib/csapi/login.cpp index 02730ff0..3e98f56b 100644 --- a/lib/csapi/login.cpp +++ b/lib/csapi/login.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetLoginFlowsJob::LoginFlow& result) { @@ -28,8 +26,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetLoginFlowsJob::Private -{ +class GetLoginFlowsJob::Private { public: QVector flows; }; @@ -39,10 +36,9 @@ QUrl GetLoginFlowsJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/login"); } -static const auto GetLoginFlowsJobName = QStringLiteral("GetLoginFlowsJob"); - GetLoginFlowsJob::GetLoginFlowsJob() - : BaseJob(HttpVerb::Get, GetLoginFlowsJobName, basePath % "/login", false) + : BaseJob(HttpVerb::Get, QStringLiteral("GetLoginFlowsJob"), + basePath % "/login", false) , d(new Private) {} @@ -61,8 +57,7 @@ BaseJob::Status GetLoginFlowsJob::parseJson(const QJsonDocument& data) return Success; } -class LoginJob::Private -{ +class LoginJob::Private { public: QString userId; QString accessToken; @@ -71,15 +66,14 @@ public: Omittable wellKnown; }; -static const auto LoginJobName = QStringLiteral("LoginJob"); - LoginJob::LoginJob(const QString& type, const Omittable& identifier, const QString& password, const QString& token, const QString& deviceId, const QString& initialDeviceDisplayName, const QString& user, const QString& medium, const QString& address) - : BaseJob(HttpVerb::Post, LoginJobName, basePath % "/login", false) + : BaseJob(HttpVerb::Post, QStringLiteral("LoginJob"), basePath % "/login", + false) , d(new Private) { QJsonObject _data; diff --git a/lib/csapi/login.h b/lib/csapi/login.h index dc944782..adecca6f 100644 --- a/lib/csapi/login.h +++ b/lib/csapi/login.h @@ -13,42 +13,39 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Get the supported login types to authenticate users -/*! +/*! \brief Get the supported login types to authenticate users + * * Gets the homeserver's supported login types to authenticate users. Clients * should pick one of these and supply it as the ``type`` when logging in. */ -class GetLoginFlowsJob : public BaseJob -{ +class GetLoginFlowsJob : public BaseJob { public: // Inner data structures /// Gets the homeserver's supported login types to authenticate users. - /// Clientsshould pick one of these and supply it as the ``type`` when + /// Clients should pick one of these and supply it as the ``type`` when /// logging in. - struct LoginFlow - { - /// The login type. This is supplied as the ``type`` whenlogging in. + struct LoginFlow { + /// The login type. This is supplied as the ``type`` when + /// logging in. QString type; }; // Construction/destruction + /// Get the supported login types to authenticate users explicit GetLoginFlowsJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetLoginFlowsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetLoginFlowsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetLoginFlowsJob() override; // Result properties @@ -64,8 +61,8 @@ private: QScopedPointer d; }; -/// Authenticates the user. -/*! +/*! \brief Authenticates the user. + * * Authenticates the user, and issues an access token they can * use to authorize themself in subsequent requests. * @@ -77,10 +74,10 @@ private: * invalidate any access token previously associated with that device. See * `Relationship between access tokens and devices`_. */ -class LoginJob : public BaseJob -{ +class LoginJob : public BaseJob { public: - /*! Authenticates the user. + /*! \brief Authenticates the user. + * * \param type * The login type being used. * \param identifier @@ -116,9 +113,11 @@ public: /// The fully-qualified Matrix ID that has been registered. const QString& userId() const; + /// An access token for the account. /// This access token can then be used to authorize other requests. const QString& accessToken() const; + /// The server_name of the homeserver on which the account has /// been registered. /// @@ -126,9 +125,11 @@ public: /// ``user_id`` (by splitting at the first colon) if they require /// it. Note also that ``homeserver`` is not spelt this way. const QString& homeServer() const; + /// ID of the logged-in device. Will be the same as the /// corresponding parameter in the request, if one was specified. const QString& deviceId() const; + /// Optional client configuration provided by the server. If present, /// clients SHOULD use the provided object to reconfigure themselves, /// optionally validating the URLs within. This object takes the same diff --git a/lib/csapi/logout.cpp b/lib/csapi/logout.cpp index 4b391967..36281b79 100644 --- a/lib/csapi/logout.cpp +++ b/lib/csapi/logout.cpp @@ -17,10 +17,8 @@ QUrl LogoutJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/logout"); } -static const auto LogoutJobName = QStringLiteral("LogoutJob"); - LogoutJob::LogoutJob() - : BaseJob(HttpVerb::Post, LogoutJobName, basePath % "/logout") + : BaseJob(HttpVerb::Post, QStringLiteral("LogoutJob"), basePath % "/logout") {} QUrl LogoutAllJob::makeRequestUrl(QUrl baseUrl) @@ -28,8 +26,7 @@ QUrl LogoutAllJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/logout/all"); } -static const auto LogoutAllJobName = QStringLiteral("LogoutAllJob"); - LogoutAllJob::LogoutAllJob() - : BaseJob(HttpVerb::Post, LogoutAllJobName, basePath % "/logout/all") + : BaseJob(HttpVerb::Post, QStringLiteral("LogoutAllJob"), + basePath % "/logout/all") {} diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h index 34d5a441..2a705620 100644 --- a/lib/csapi/logout.h +++ b/lib/csapi/logout.h @@ -6,32 +6,30 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Invalidates a user access token -/*! +/*! \brief Invalidates a user access token + * * Invalidates an existing access token, so that it can no longer be used for * authorization. */ -class LogoutJob : public BaseJob -{ +class LogoutJob : public BaseJob { public: + /// Invalidates a user access token explicit LogoutJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * LogoutJob is necessary but the job - * itself isn't. + * This function can be used when a URL for LogoutJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); }; -/// Invalidates all access tokens for a user -/*! +/*! \brief Invalidates all access tokens for a user + * * Invalidates all access tokens for a user, so that they can no longer be used * for authorization. This includes the access token that made this request. * @@ -41,16 +39,15 @@ public: * access tokens for the user, including the token used in the request, and * therefore the attacker is unable to take over the account in this way. */ -class LogoutAllJob : public BaseJob -{ +class LogoutAllJob : public BaseJob { public: + /// Invalidates all access tokens for a user explicit LogoutAllJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * LogoutAllJob is necessary but the job - * itself isn't. + * This function can be used when a URL for LogoutAllJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); }; diff --git a/lib/csapi/message_pagination.cpp b/lib/csapi/message_pagination.cpp index b612ee91..ba982748 100644 --- a/lib/csapi/message_pagination.cpp +++ b/lib/csapi/message_pagination.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetRoomEventsJob::Private -{ +class GetRoomEventsJob::Private { public: QString begin; QString end; @@ -38,17 +37,16 @@ QUrl GetRoomEventsJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& to, Omittable limit, const QString& filter) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/rooms/" % roomId % "/messages", - queryToGetRoomEvents(from, to, dir, limit, filter)); + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath % "/rooms/" % roomId % "/messages", + queryToGetRoomEvents(from, to, dir, limit, + filter)); } -static const auto GetRoomEventsJobName = QStringLiteral("GetRoomEventsJob"); - GetRoomEventsJob::GetRoomEventsJob(const QString& roomId, const QString& from, const QString& dir, const QString& to, Omittable limit, const QString& filter) - : BaseJob(HttpVerb::Get, GetRoomEventsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomEventsJob"), basePath % "/rooms/" % roomId % "/messages", queryToGetRoomEvents(from, to, dir, limit, filter)) , d(new Private) diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index 271e1dd9..b0d95bad 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -9,20 +9,19 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Get a list of events for this room -/*! +/*! \brief Get a list of events for this room + * * This API returns a list of message and state events for a room. It uses * pagination query parameters to paginate history in the room. */ -class GetRoomEventsJob : public BaseJob -{ +class GetRoomEventsJob : public BaseJob { public: - /*! Get a list of events for this room + /*! \brief Get a list of events for this room + * * \param roomId * The room to get events from. * \param from @@ -47,18 +46,16 @@ public: Omittable limit = none, const QString& filter = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomEventsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomEventsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& from, const QString& dir, const QString& to = {}, Omittable limit = none, const QString& filter = {}); - ~GetRoomEventsJob() override; // Result properties @@ -66,9 +63,11 @@ public: /// The token the pagination starts from. If ``dir=b`` this will be /// the token supplied in ``from``. const QString& begin() const; + /// The token the pagination ends at. If ``dir=b`` this token should /// be used again to request even earlier events. const QString& end() const; + /// A list of room events. RoomEvents&& chunk(); diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index da568a0f..d00d5425 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetNotificationsJob::Notification& result) { @@ -33,8 +31,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetNotificationsJob::Private -{ +class GetNotificationsJob::Private { public: QString nextToken; std::vector notifications; @@ -59,13 +56,10 @@ QUrl GetNotificationsJob::makeRequestUrl(QUrl baseUrl, const QString& from, queryToGetNotifications(from, limit, only)); } -static const auto GetNotificationsJobName = - QStringLiteral("GetNotificationsJob"); - GetNotificationsJob::GetNotificationsJob(const QString& from, Omittable limit, const QString& only) - : BaseJob(HttpVerb::Get, GetNotificationsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetNotificationsJob"), basePath % "/notifications", queryToGetNotifications(from, limit, only)) , d(new Private) diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index eeec5d4e..0e86e424 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -13,45 +13,43 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets a list of events that the user has been notified about -/*! +/*! \brief Gets a list of events that the user has been notified about + * * This API is used to paginate through the list of events that the * user has been, or would have been notified about. */ -class GetNotificationsJob : public BaseJob -{ +class GetNotificationsJob : public BaseJob { public: // Inner data structures - /// This API is used to paginate through the list of events that theuser has - /// been, or would have been notified about. - struct Notification - { - /// The action(s) to perform when the conditions for this rule are - /// met.See `Push Rules: API`_. + /// This API is used to paginate through the list of events that the + /// user has been, or would have been notified about. + struct Notification { + /// The action(s) to perform when the conditions for this rule are met. + /// See `Push Rules: API`_. QVector actions; /// The Event object for the event that triggered the notification. EventPtr event; /// The profile tag of the rule that matched this event. QString profileTag; - /// Indicates whether the user has sent a read receipt indicatingthat - /// they have read this message. + /// Indicates whether the user has sent a read receipt indicating + /// that they have read this message. bool read; /// The ID of the room in which the event was posted. QString roomId; - /// The unix timestamp at which the event notification was sent,in - /// milliseconds. + /// The unix timestamp at which the event notification was sent, + /// in milliseconds. int ts; }; // Construction/destruction - /*! Gets a list of events that the user has been notified about + /*! \brief Gets a list of events that the user has been notified about + * * \param from * Pagination token given to retrieve the next set of events. * \param limit @@ -65,16 +63,14 @@ public: Omittable limit = none, const QString& only = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetNotificationsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetNotificationsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable limit = none, const QString& only = {}); - ~GetNotificationsJob() override; // Result properties @@ -83,6 +79,7 @@ public: /// ``/notifications`` request in order to request more /// events. If this is absent, there are no more results. const QString& nextToken() const; + /// The list of events that triggered notifications. std::vector&& notifications(); diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index 8c00df97..9def2377 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class RequestOpenIdTokenJob::Private -{ +class RequestOpenIdTokenJob::Private { public: QString accessToken; QString tokenType; @@ -21,12 +20,9 @@ public: int expiresIn; }; -static const auto RequestOpenIdTokenJobName = - QStringLiteral("RequestOpenIdTokenJob"); - RequestOpenIdTokenJob::RequestOpenIdTokenJob(const QString& userId, const QJsonObject& body) - : BaseJob(HttpVerb::Post, RequestOpenIdTokenJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("RequestOpenIdTokenJob"), basePath % "/user/" % userId % "/openid/request_token") , d(new Private) { diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h index b2f003e5..bda4ce74 100644 --- a/lib/csapi/openid.h +++ b/lib/csapi/openid.h @@ -10,13 +10,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Get an OpenID token object to verify the requester's identity. -/*! +/*! \brief Get an OpenID token object to verify the requester's identity. + * * Gets an OpenID token object that the requester may supply to another * service to verify their identity in Matrix. The generated token is only * valid for exchanging for user information from the federation API for @@ -26,10 +25,10 @@ namespace Quotient * be used to request another OpenID access token or call ``/sync``, for * example. */ -class RequestOpenIdTokenJob : public BaseJob -{ +class RequestOpenIdTokenJob : public BaseJob { public: - /*! Get an OpenID token object to verify the requester's identity. + /*! \brief Get an OpenID token object to verify the requester's identity. + * * \param userId * The user to request and OpenID token for. Should be the user who * is authenticated for the request. @@ -47,11 +46,14 @@ public: /// the person who generated the token. This is given to the federation /// API ``GET /openid/userinfo``. const QString& accessToken() const; + /// The string ``Bearer``. const QString& tokenType() const; + /// The homeserver domain the consumer should use when attempting to /// verify the user's identity. const QString& matrixServerName() const; + /// The number of seconds before this token expires and a new one must /// be generated. int expiresIn() const; diff --git a/lib/csapi/peeking_events.cpp b/lib/csapi/peeking_events.cpp index bc29b682..4d886812 100644 --- a/lib/csapi/peeking_events.cpp +++ b/lib/csapi/peeking_events.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class PeekEventsJob::Private -{ +class PeekEventsJob::Private { public: QString begin; QString end; @@ -37,12 +36,10 @@ QUrl PeekEventsJob::makeRequestUrl(QUrl baseUrl, const QString& from, queryToPeekEvents(from, timeout, roomId)); } -static const auto PeekEventsJobName = QStringLiteral("PeekEventsJob"); - PeekEventsJob::PeekEventsJob(const QString& from, Omittable timeout, const QString& roomId) - : BaseJob(HttpVerb::Get, PeekEventsJobName, basePath % "/events", - queryToPeekEvents(from, timeout, roomId)) + : BaseJob(HttpVerb::Get, QStringLiteral("PeekEventsJob"), + basePath % "/events", queryToPeekEvents(from, timeout, roomId)) , d(new Private) {} diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index c5cc07f6..12a66a02 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -9,13 +9,12 @@ #include "events/eventloader.h" #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Listen on the event stream. -/*! +/*! \brief Listen on the event stream. + * * This will listen for new events related to a particular room and return * them to the caller. This will block until an event is received, or until * the ``timeout`` is reached. @@ -27,10 +26,10 @@ namespace Quotient * API will also be deprecated at some point, but its replacement is not * yet known. */ -class PeekEventsJob : public BaseJob -{ +class PeekEventsJob : public BaseJob { public: - /*! Listen on the event stream. + /*! \brief Listen on the event stream. + * * \param from * The token to stream from. This token is either from a previous * request to this API or from the initial sync API. @@ -43,16 +42,14 @@ public: Omittable timeout = none, const QString& roomId = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * PeekEventsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for PeekEventsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable timeout = none, const QString& roomId = {}); - ~PeekEventsJob() override; // Result properties @@ -60,9 +57,11 @@ public: /// A token which correlates to the first value in ``chunk``. This /// is usually the same token supplied to ``from=``. const QString& begin() const; + /// A token which correlates to the last value in ``chunk``. This /// token should be used in the next request to ``/events``. const QString& end() const; + /// An array of events. RoomEvents&& chunk(); diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 0f019026..932ccc6e 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SetPresenceJobName = QStringLiteral("SetPresenceJob"); - SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, const QString& statusMsg) - : BaseJob(HttpVerb::Put, SetPresenceJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetPresenceJob"), basePath % "/presence/" % userId % "/status") { QJsonObject _data; @@ -25,8 +23,7 @@ SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, setRequestData(_data); } -class GetPresenceJob::Private -{ +class GetPresenceJob::Private { public: QString presence; Omittable lastActiveAgo; @@ -40,10 +37,8 @@ QUrl GetPresenceJob::makeRequestUrl(QUrl baseUrl, const QString& userId) basePath % "/presence/" % userId % "/status"); } -static const auto GetPresenceJobName = QStringLiteral("GetPresenceJob"); - GetPresenceJob::GetPresenceJob(const QString& userId) - : BaseJob(HttpVerb::Get, GetPresenceJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetPresenceJob"), basePath % "/presence/" % userId % "/status") , d(new Private) {} diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index c5ecb987..21e57603 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -8,22 +8,21 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Update this user's presence state. -/*! +/*! \brief Update this user's presence state. + * * This API sets the given user's presence state. When setting the status, * the activity time is updated to reflect that activity; the client does * not need to specify the ``last_active_ago`` field. You cannot set the * presence state of another user. */ -class SetPresenceJob : public BaseJob -{ +class SetPresenceJob : public BaseJob { public: - /*! Update this user's presence state. + /*! \brief Update this user's presence state. + * * \param userId * The user whose presence state to update. * \param presence @@ -35,38 +34,39 @@ public: const QString& statusMsg = {}); }; -/// Get this user's presence state. -/*! +/*! \brief Get this user's presence state. + * * Get the given user's presence state. */ -class GetPresenceJob : public BaseJob -{ +class GetPresenceJob : public BaseJob { public: - /*! Get this user's presence state. + /*! \brief Get this user's presence state. + * * \param userId * The user whose presence state to get. */ explicit GetPresenceJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPresenceJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPresenceJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetPresenceJob() override; // Result properties /// This user's presence. const QString& presence() const; + /// The length of time in milliseconds since an action was performed /// by this user. Omittable lastActiveAgo() const; + /// The state message for this user if one was set. const QString& statusMsg() const; + /// Whether the user is currently active Omittable currentlyActive() const; diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index 27168f77..d86e3bdc 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SetDisplayNameJobName = QStringLiteral("SetDisplayNameJob"); - SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displayname) - : BaseJob(HttpVerb::Put, SetDisplayNameJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetDisplayNameJob"), basePath % "/profile/" % userId % "/displayname") { QJsonObject _data; @@ -24,22 +22,20 @@ SetDisplayNameJob::SetDisplayNameJob(const QString& userId, setRequestData(_data); } -class GetDisplayNameJob::Private -{ +class GetDisplayNameJob::Private { public: QString displayname; }; QUrl GetDisplayNameJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/profile/" % userId % "/displayname"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/profile/" + % userId + % "/displayname"); } -static const auto GetDisplayNameJobName = QStringLiteral("GetDisplayNameJob"); - GetDisplayNameJob::GetDisplayNameJob(const QString& userId) - : BaseJob(HttpVerb::Get, GetDisplayNameJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetDisplayNameJob"), basePath % "/profile/" % userId % "/displayname", false) , d(new Private) {} @@ -56,10 +52,8 @@ BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data) return Success; } -static const auto SetAvatarUrlJobName = QStringLiteral("SetAvatarUrlJob"); - SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl) - : BaseJob(HttpVerb::Put, SetAvatarUrlJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetAvatarUrlJob"), basePath % "/profile/" % userId % "/avatar_url") { QJsonObject _data; @@ -67,22 +61,20 @@ SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl setRequestData(_data); } -class GetAvatarUrlJob::Private -{ +class GetAvatarUrlJob::Private { public: QString avatarUrl; }; QUrl GetAvatarUrlJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/profile/" % userId % "/avatar_url"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/profile/" + % userId + % "/avatar_url"); } -static const auto GetAvatarUrlJobName = QStringLiteral("GetAvatarUrlJob"); - GetAvatarUrlJob::GetAvatarUrlJob(const QString& userId) - : BaseJob(HttpVerb::Get, GetAvatarUrlJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetAvatarUrlJob"), basePath % "/profile/" % userId % "/avatar_url", false) , d(new Private) {} @@ -99,8 +91,7 @@ BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data) return Success; } -class GetUserProfileJob::Private -{ +class GetUserProfileJob::Private { public: QString avatarUrl; QString displayname; @@ -112,10 +103,8 @@ QUrl GetUserProfileJob::makeRequestUrl(QUrl baseUrl, const QString& userId) basePath % "/profile/" % userId); } -static const auto GetUserProfileJobName = QStringLiteral("GetUserProfileJob"); - GetUserProfileJob::GetUserProfileJob(const QString& userId) - : BaseJob(HttpVerb::Get, GetUserProfileJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetUserProfileJob"), basePath % "/profile/" % userId, false) , d(new Private) {} diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 54dc53aa..8279fe20 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -6,20 +6,19 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Set the user's display name. -/*! +/*! \brief Set the user's display name. + * * This API sets the given user's display name. You must have permission to * set this user's display name, e.g. you need to have their ``access_token``. */ -class SetDisplayNameJob : public BaseJob -{ +class SetDisplayNameJob : public BaseJob { public: - /*! Set the user's display name. + /*! \brief Set the user's display name. + * * \param userId * The user whose display name to set. * \param displayname @@ -29,29 +28,27 @@ public: const QString& displayname = {}); }; -/// Get the user's display name. -/*! +/*! \brief Get the user's display name. + * * Get the user's display name. This API may be used to fetch the user's * own displayname or to query the name of other users; either locally or * on remote homeservers. */ -class GetDisplayNameJob : public BaseJob -{ +class GetDisplayNameJob : public BaseJob { public: - /*! Get the user's display name. + /*! \brief Get the user's display name. + * * \param userId * The user whose display name to get. */ explicit GetDisplayNameJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetDisplayNameJob is necessary but the job - * itself isn't. + * 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); - ~GetDisplayNameJob() override; // Result properties @@ -67,15 +64,15 @@ private: QScopedPointer d; }; -/// Set the user's avatar URL. -/*! +/*! \brief Set the user's avatar URL. + * * This API sets the given user's avatar URL. You must have permission to * set this user's avatar URL, e.g. you need to have their ``access_token``. */ -class SetAvatarUrlJob : public BaseJob -{ +class SetAvatarUrlJob : public BaseJob { public: - /*! Set the user's avatar URL. + /*! \brief Set the user's avatar URL. + * * \param userId * The user whose avatar URL to set. * \param avatarUrl @@ -85,29 +82,27 @@ public: const QString& avatarUrl = {}); }; -/// Get the user's avatar URL. -/*! +/*! \brief Get the user's avatar URL. + * * Get the user's avatar URL. This API may be used to fetch the user's * own avatar URL or to query the URL of other users; either locally or * on remote homeservers. */ -class GetAvatarUrlJob : public BaseJob -{ +class GetAvatarUrlJob : public BaseJob { public: - /*! Get the user's avatar URL. + /*! \brief Get the user's avatar URL. + * * \param userId * The user whose avatar URL to get. */ explicit GetAvatarUrlJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetAvatarUrlJob is necessary but the job - * itself isn't. + * 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); - ~GetAvatarUrlJob() override; // Result properties @@ -123,36 +118,35 @@ private: QScopedPointer d; }; -/// Get this user's profile information. -/*! +/*! \brief Get this user's profile information. + * * Get the combined profile information for this user. This API may be used * to fetch the user's own profile information or other users; either * locally or on remote homeservers. This API may return keys which are not * limited to ``displayname`` or ``avatar_url``. */ -class GetUserProfileJob : public BaseJob -{ +class GetUserProfileJob : public BaseJob { public: - /*! Get this user's profile information. + /*! \brief Get this user's profile information. + * * \param userId * The user whose profile information to get. */ explicit GetUserProfileJob(const QString& userId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetUserProfileJob is necessary but the job - * itself isn't. + * 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); - ~GetUserProfileJob() override; // Result properties /// The user's avatar URL if they have set one, otherwise not present. const QString& avatarUrl() const; + /// The user's display name if they have set one, otherwise not present. const QString& displayname() const; diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index 90877a95..ad51b901 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetPushersJob::PusherData& result) { @@ -28,8 +26,7 @@ struct JsonObjectConverter }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetPushersJob::Pusher& result) { fromJson(jo.value("pushkey"_ls), result.pushkey); @@ -45,8 +42,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetPushersJob::Private -{ +class GetPushersJob::Private { public: QVector pushers; }; @@ -56,10 +52,9 @@ QUrl GetPushersJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushers"); } -static const auto GetPushersJobName = QStringLiteral("GetPushersJob"); - GetPushersJob::GetPushersJob() - : BaseJob(HttpVerb::Get, GetPushersJobName, basePath % "/pushers") + : BaseJob(HttpVerb::Get, QStringLiteral("GetPushersJob"), + basePath % "/pushers") , d(new Private) {} @@ -79,12 +74,10 @@ BaseJob::Status GetPushersJob::parseJson(const QJsonDocument& data) } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const PostPusherJob::PusherData& pod) { addParam(jo, QStringLiteral("url"), pod.url); @@ -94,14 +87,13 @@ struct JsonObjectConverter } // namespace Quotient -static const auto PostPusherJobName = QStringLiteral("PostPusherJob"); - PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag, Omittable append) - : BaseJob(HttpVerb::Post, PostPusherJobName, basePath % "/pushers/set") + : BaseJob(HttpVerb::Post, QStringLiteral("PostPusherJob"), + basePath % "/pushers/set") { QJsonObject _data; addParam<>(_data, QStringLiteral("pushkey"), pushkey); diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index 65f8aa15..d60a2b56 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -10,69 +10,69 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets the current pushers for the authenticated user -/*! +/*! \brief Gets the current pushers for the authenticated user + * * Gets all currently active pushers for the authenticated user. */ -class GetPushersJob : public BaseJob -{ +class GetPushersJob : public BaseJob { public: // Inner data structures - /// A dictionary of information for the pusher implementationitself. - struct PusherData - { - /// Required if ``kind`` is ``http``. The URL to use to - /// sendnotifications to. + /// A dictionary of information for the pusher implementation + /// itself. + struct PusherData { + /// Required if ``kind`` is ``http``. The URL to use to send + /// notifications to. QString url; - /// The format to use when sending notifications to the PushGateway. + /// The format to use when sending notifications to the Push + /// Gateway. QString format; }; /// Gets all currently active pushers for the authenticated user. - struct Pusher - { - /// This is a unique identifier for this pusher. See ``/set`` formore - /// detail.Max length, 512 bytes. + struct Pusher { + /// This is a unique identifier for this pusher. See ``/set`` for + /// more detail. + /// Max length, 512 bytes. QString pushkey; - /// The kind of pusher. ``"http"`` is a pusher thatsends HTTP pokes. + /// The kind of pusher. ``"http"`` is a pusher that + /// sends HTTP pokes. QString kind; - /// This is a reverse-DNS style identifier for the application.Max - /// length, 64 chars. + /// This is a reverse-DNS style identifier for the application. + /// Max length, 64 chars. QString appId; - /// A string that will allow the user to identify what applicationowns - /// this pusher. + /// A string that will allow the user to identify what application + /// owns this pusher. QString appDisplayName; - /// A string that will allow the user to identify what device ownsthis - /// pusher. + /// A string that will allow the user to identify what device owns + /// this pusher. QString deviceDisplayName; - /// This string determines which set of device specific rules thispusher - /// executes. + /// This string determines which set of device specific rules this + /// pusher executes. QString profileTag; - /// The preferred language for receiving notifications (e.g. 'en'or - /// 'en-US') + /// The preferred language for receiving notifications (e.g. 'en' + /// or 'en-US') QString lang; - /// A dictionary of information for the pusher implementationitself. + /// A dictionary of information for the pusher implementation + /// itself. PusherData data; }; // Construction/destruction + /// Gets the current pushers for the authenticated user explicit GetPushersJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPushersJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPushersJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetPushersJob() override; // Result properties @@ -88,35 +88,36 @@ private: QScopedPointer d; }; -/// Modify a pusher for this user on the homeserver. -/*! +/*! \brief Modify a pusher for this user on the homeserver. + * * This endpoint allows the creation, modification and deletion of `pushers`_ * for this user ID. The behaviour of this endpoint varies depending on the * values in the JSON body. */ -class PostPusherJob : public BaseJob -{ +class PostPusherJob : public BaseJob { public: // Inner data structures - /// A dictionary of information for the pusher implementationitself. If - /// ``kind`` is ``http``, this should contain ``url``which is the URL to use - /// to send notifications to. - struct PusherData - { - /// Required if ``kind`` is ``http``. The URL to use to sendnotifications - /// to. MUST be an HTTPS URL with a path of ``/_matrix/push/v1/notify``. + /// A dictionary of information for the pusher implementation + /// itself. If ``kind`` is ``http``, this should contain ``url`` + /// which is the URL to use to send notifications to. + struct PusherData { + /// Required if ``kind`` is ``http``. The URL to use to send + /// notifications to. MUST be an HTTPS URL with a path of + /// ``/_matrix/push/v1/notify``. QString url; - /// The format to send notifications in to Push Gateways if the``kind`` - /// is ``http``. The details about what fields thehomeserver should send - /// to the push gateway are defined in the`Push Gateway Specification`_. - /// Currently the only formatavailable is 'event_id_only'. + /// The format to send notifications in to Push Gateways if the + /// ``kind`` is ``http``. The details about what fields the + /// homeserver should send to the push gateway are defined in the + /// `Push Gateway Specification`_. Currently the only format + /// available is 'event_id_only'. QString format; }; // Construction/destruction - /*! Modify a pusher for this user on the homeserver. + /*! \brief Modify a pusher for this user on the homeserver. + * * \param pushkey * This is a unique identifier for this pusher. The value you * should use for this is the routing or destination address diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index eae5445f..cd0fb02d 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetPushRulesJob::Private -{ +class GetPushRulesJob::Private { public: PushRuleset global; }; @@ -23,10 +22,9 @@ QUrl GetPushRulesJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushrules"); } -static const auto GetPushRulesJobName = QStringLiteral("GetPushRulesJob"); - GetPushRulesJob::GetPushRulesJob() - : BaseJob(HttpVerb::Get, GetPushRulesJobName, basePath % "/pushrules") + : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRulesJob"), + basePath % "/pushrules") , d(new Private) {} @@ -45,8 +43,7 @@ BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) return Success; } -class GetPushRuleJob::Private -{ +class GetPushRuleJob::Private { public: PushRule data; }; @@ -59,11 +56,9 @@ QUrl GetPushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, % "/" % ruleId); } -static const auto GetPushRuleJobName = QStringLiteral("GetPushRuleJob"); - GetPushRuleJob::GetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) - : BaseJob(HttpVerb::Get, GetPushRuleJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRuleJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) , d(new Private) {} @@ -75,6 +70,7 @@ const PushRule& GetPushRuleJob::data() const { return d->data; } BaseJob::Status GetPushRuleJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } @@ -87,11 +83,9 @@ QUrl DeletePushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, % "/" % ruleId); } -static const auto DeletePushRuleJobName = QStringLiteral("DeletePushRuleJob"); - DeletePushRuleJob::DeletePushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) - : BaseJob(HttpVerb::Delete, DeletePushRuleJobName, + : BaseJob(HttpVerb::Delete, QStringLiteral("DeletePushRuleJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) {} @@ -103,14 +97,12 @@ BaseJob::Query queryToSetPushRule(const QString& before, const QString& after) return _q; } -static const auto SetPushRuleJobName = QStringLiteral("SetPushRuleJob"); - SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions, const QString& before, const QString& after, const QVector& conditions, const QString& pattern) - : BaseJob(HttpVerb::Put, SetPushRuleJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId, queryToSetPushRule(before, after)) { @@ -121,8 +113,7 @@ SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, setRequestData(_data); } -class IsPushRuleEnabledJob::Private -{ +class IsPushRuleEnabledJob::Private { public: bool enabled; }; @@ -136,13 +127,10 @@ QUrl IsPushRuleEnabledJob::makeRequestUrl(QUrl baseUrl, const QString& scope, % "/" % ruleId % "/enabled"); } -static const auto IsPushRuleEnabledJobName = - QStringLiteral("IsPushRuleEnabledJob"); - IsPushRuleEnabledJob::IsPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId) - : BaseJob(HttpVerb::Get, IsPushRuleEnabledJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("IsPushRuleEnabledJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled") , d(new Private) @@ -163,13 +151,10 @@ BaseJob::Status IsPushRuleEnabledJob::parseJson(const QJsonDocument& data) return Success; } -static const auto SetPushRuleEnabledJobName = - QStringLiteral("SetPushRuleEnabledJob"); - SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId, bool enabled) - : BaseJob(HttpVerb::Put, SetPushRuleEnabledJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleEnabledJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled") { @@ -178,8 +163,7 @@ SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, setRequestData(_data); } -class GetPushRuleActionsJob::Private -{ +class GetPushRuleActionsJob::Private { public: QStringList actions; }; @@ -193,13 +177,10 @@ QUrl GetPushRuleActionsJob::makeRequestUrl(QUrl baseUrl, const QString& scope, % "/" % ruleId % "/actions"); } -static const auto GetPushRuleActionsJobName = - QStringLiteral("GetPushRuleActionsJob"); - GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId) - : BaseJob(HttpVerb::Get, GetPushRuleActionsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRuleActionsJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions") , d(new Private) @@ -220,14 +201,11 @@ BaseJob::Status GetPushRuleActionsJob::parseJson(const QJsonDocument& data) return Success; } -static const auto SetPushRuleActionsJobName = - QStringLiteral("SetPushRuleActionsJob"); - SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions) - : BaseJob(HttpVerb::Put, SetPushRuleActionsJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleActionsJob"), basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions") { diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index cb94fe7b..a9169151 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -14,31 +14,28 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Retrieve all push rulesets. -/*! +/*! \brief Retrieve all push rulesets. + * * Retrieve all push rulesets for this user. Clients can "drill-down" on * the rulesets by suffixing a ``scope`` to this path e.g. * ``/pushrules/global/``. This will return a subset of this data under the * specified key e.g. the ``global`` key. */ -class GetPushRulesJob : public BaseJob -{ +class GetPushRulesJob : public BaseJob { public: + /// Retrieve all push rulesets. explicit GetPushRulesJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPushRulesJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPushRulesJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetPushRulesJob() override; // Result properties @@ -54,14 +51,14 @@ private: QScopedPointer d; }; -/// Retrieve a push rule. -/*! +/*! \brief Retrieve a push rule. + * * Retrieve a single specified push rule. */ -class GetPushRuleJob : public BaseJob -{ +class GetPushRuleJob : public BaseJob { public: - /*! Retrieve a push rule. + /*! \brief Retrieve a push rule. + * * \param scope * ``global`` to specify global rules. * \param kind @@ -72,15 +69,13 @@ public: explicit GetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPushRuleJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPushRuleJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~GetPushRuleJob() override; // Result properties @@ -96,14 +91,14 @@ private: QScopedPointer d; }; -/// Delete a push rule. -/*! +/*! \brief Delete a push rule. + * * This endpoint removes the push rule defined in the path. */ -class DeletePushRuleJob : public BaseJob -{ +class DeletePushRuleJob : public BaseJob { public: - /*! Delete a push rule. + /*! \brief Delete a push rule. + * * \param scope * ``global`` to specify global rules. * \param kind @@ -114,28 +109,27 @@ public: explicit DeletePushRuleJob(const QString& scope, const QString& kind, const QString& ruleId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * DeletePushRuleJob is necessary but the job - * itself isn't. + * This function can be used when a URL for DeletePushRuleJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); }; -/// Add or change a push rule. -/*! +/*! \brief Add or change a push rule. + * * This endpoint allows the creation, modification and deletion of pushers * for this user ID. The behaviour of this endpoint varies depending on the * values in the JSON body. * * When creating push rules, they MUST be enabled by default. */ -class SetPushRuleJob : public BaseJob -{ +class SetPushRuleJob : public BaseJob { public: - /*! Add or change a push rule. + /*! \brief Add or change a push rule. + * * \param scope * ``global`` to specify global rules. * \param kind @@ -167,14 +161,14 @@ public: const QString& pattern = {}); }; -/// Get whether a push rule is enabled -/*! +/*! \brief Get whether a push rule is enabled + * * This endpoint gets whether the specified push rule is enabled. */ -class IsPushRuleEnabledJob : public BaseJob -{ +class IsPushRuleEnabledJob : public BaseJob { public: - /*! Get whether a push rule is enabled + /*! \brief Get whether a push rule is enabled + * * \param scope * Either ``global`` or ``device/`` to specify global * rules or device rules for the given ``profile_tag``. @@ -186,15 +180,13 @@ public: explicit IsPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * IsPushRuleEnabledJob is necessary but the job - * itself isn't. + * This function can be used when a URL for IsPushRuleEnabledJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~IsPushRuleEnabledJob() override; // Result properties @@ -210,14 +202,14 @@ private: QScopedPointer d; }; -/// Enable or disable a push rule. -/*! +/*! \brief Enable or disable a push rule. + * * This endpoint allows clients to enable or disable the specified push rule. */ -class SetPushRuleEnabledJob : public BaseJob -{ +class SetPushRuleEnabledJob : public BaseJob { public: - /*! Enable or disable a push rule. + /*! \brief Enable or disable a push rule. + * * \param scope * ``global`` to specify global rules. * \param kind @@ -231,14 +223,14 @@ public: const QString& ruleId, bool enabled); }; -/// The actions for a push rule -/*! +/*! \brief The actions for a push rule + * * This endpoint get the actions for the specified push rule. */ -class GetPushRuleActionsJob : public BaseJob -{ +class GetPushRuleActionsJob : public BaseJob { public: - /*! The actions for a push rule + /*! \brief The actions for a push rule + * * \param scope * Either ``global`` or ``device/`` to specify global * rules or device rules for the given ``profile_tag``. @@ -250,15 +242,13 @@ public: explicit GetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetPushRuleActionsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetPushRuleActionsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~GetPushRuleActionsJob() override; // Result properties @@ -274,15 +264,15 @@ private: QScopedPointer d; }; -/// Set the actions for a push rule. -/*! +/*! \brief Set the actions for a push rule. + * * This endpoint allows clients to change the actions of a push rule. * This can be used to change the actions of builtin rules. */ -class SetPushRuleActionsJob : public BaseJob -{ +class SetPushRuleActionsJob : public BaseJob { public: - /*! Set the actions for a push rule. + /*! \brief Set the actions for a push rule. + * * \param scope * ``global`` to specify global rules. * \param kind diff --git a/lib/csapi/read_markers.cpp b/lib/csapi/read_markers.cpp index a07d09ce..76145808 100644 --- a/lib/csapi/read_markers.cpp +++ b/lib/csapi/read_markers.cpp @@ -12,12 +12,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SetReadMarkerJobName = QStringLiteral("SetReadMarkerJob"); - SetReadMarkerJob::SetReadMarkerJob(const QString& roomId, const QString& mFullyRead, const QString& mRead) - : BaseJob(HttpVerb::Post, SetReadMarkerJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("SetReadMarkerJob"), basePath % "/rooms/" % roomId % "/read_markers") { QJsonObject _data; diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h index 35c428b2..539aa5e4 100644 --- a/lib/csapi/read_markers.h +++ b/lib/csapi/read_markers.h @@ -6,20 +6,19 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Set the position of the read marker for a room. -/*! +/*! \brief Set the position of the read marker for a room. + * * Sets the position of the read marker for a given room, and optionally * the read receipt's location. */ -class SetReadMarkerJob : public BaseJob -{ +class SetReadMarkerJob : public BaseJob { public: - /*! Set the position of the read marker for a room. + /*! \brief Set the position of the read marker for a room. + * * \param roomId * The room ID to set the read marker in for the user. * \param mFullyRead diff --git a/lib/csapi/receipts.cpp b/lib/csapi/receipts.cpp index f9a45912..87264074 100644 --- a/lib/csapi/receipts.cpp +++ b/lib/csapi/receipts.cpp @@ -12,12 +12,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto PostReceiptJobName = QStringLiteral("PostReceiptJob"); - PostReceiptJob::PostReceiptJob(const QString& roomId, const QString& receiptType, const QString& eventId, const QJsonObject& receipt) - : BaseJob(HttpVerb::Post, PostReceiptJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("PostReceiptJob"), basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" % eventId) { diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h index 376d4006..eb82fc28 100644 --- a/lib/csapi/receipts.h +++ b/lib/csapi/receipts.h @@ -8,20 +8,19 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Send a receipt for the given event ID. -/*! +/*! \brief Send a receipt for the given event ID. + * * This API updates the marker for the given receipt type to the event ID * specified. */ -class PostReceiptJob : public BaseJob -{ +class PostReceiptJob : public BaseJob { public: - /*! Send a receipt for the given event ID. + /*! \brief Send a receipt for the given event ID. + * * \param roomId * The room in which to send the event. * \param receiptType diff --git a/lib/csapi/redaction.cpp b/lib/csapi/redaction.cpp index d2dbe19b..2b6417ea 100644 --- a/lib/csapi/redaction.cpp +++ b/lib/csapi/redaction.cpp @@ -12,17 +12,14 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class RedactEventJob::Private -{ +class RedactEventJob::Private { public: QString eventId; }; -static const auto RedactEventJobName = QStringLiteral("RedactEventJob"); - RedactEventJob::RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason) - : BaseJob(HttpVerb::Put, RedactEventJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("RedactEventJob"), basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId) , d(new Private) { diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h index 42022930..0e9095d1 100644 --- a/lib/csapi/redaction.h +++ b/lib/csapi/redaction.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Strips all non-integrity-critical information out of an event. -/*! +/*! \brief Strips all non-integrity-critical information out of an event. + * * Strips all information out of an event which isn't critical to the * integrity of the server-side representation of the room. * @@ -22,10 +21,10 @@ namespace Quotient * greater than or equal to the `redact` power level of the room may * redact events there. */ -class RedactEventJob : public BaseJob -{ +class RedactEventJob : public BaseJob { public: - /*! Strips all non-integrity-critical information out of an event. + /*! \brief Strips all non-integrity-critical information out of an event. + * * \param roomId * The room from which to redact the event. * \param eventId diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 270011e1..768617cc 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class RegisterJob::Private -{ +class RegisterJob::Private { public: QString userId; QString accessToken; @@ -28,16 +27,14 @@ BaseJob::Query queryToRegister(const QString& kind) return _q; } -static const auto RegisterJobName = QStringLiteral("RegisterJob"); - RegisterJob::RegisterJob(const QString& kind, const Omittable& auth, Omittable bindEmail, const QString& username, const QString& password, const QString& deviceId, const QString& initialDeviceDisplayName, Omittable inhibitLogin) - : BaseJob(HttpVerb::Post, RegisterJobName, basePath % "/register", - queryToRegister(kind), {}, false) + : BaseJob(HttpVerb::Post, QStringLiteral("RegisterJob"), + basePath % "/register", queryToRegister(kind), {}, false) , d(new Private) { QJsonObject _data; @@ -76,19 +73,15 @@ BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) return Success; } -class RequestTokenToRegisterEmailJob::Private -{ +class RequestTokenToRegisterEmailJob::Private { public: Sid data; }; -static const auto RequestTokenToRegisterEmailJobName = - QStringLiteral("RequestTokenToRegisterEmailJob"); - RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob( const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenToRegisterEmailJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToRegisterEmailJob"), basePath % "/register/email/requestToken", false) , d(new Private) { @@ -109,23 +102,20 @@ BaseJob::Status RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class RequestTokenToRegisterMSISDNJob::Private -{ +class RequestTokenToRegisterMSISDNJob::Private { public: Sid data; }; -static const auto RequestTokenToRegisterMSISDNJobName = - QStringLiteral("RequestTokenToRegisterMSISDNJob"); - RequestTokenToRegisterMSISDNJob::RequestTokenToRegisterMSISDNJob( const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenToRegisterMSISDNJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToRegisterMSISDNJob"), basePath % "/register/msisdn/requestToken", false) , d(new Private) { @@ -147,14 +137,13 @@ BaseJob::Status RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -static const auto ChangePasswordJobName = QStringLiteral("ChangePasswordJob"); - ChangePasswordJob::ChangePasswordJob(const QString& newPassword, const Omittable& auth) - : BaseJob(HttpVerb::Post, ChangePasswordJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), basePath % "/account/password") { QJsonObject _data; @@ -163,19 +152,16 @@ ChangePasswordJob::ChangePasswordJob(const QString& newPassword, setRequestData(_data); } -class RequestTokenToResetPasswordEmailJob::Private -{ +class RequestTokenToResetPasswordEmailJob::Private { public: Sid data; }; -static const auto RequestTokenToResetPasswordEmailJobName = - QStringLiteral("RequestTokenToResetPasswordEmailJob"); - RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob( const QString& clientSecret, const QString& email, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenToResetPasswordEmailJobName, + : BaseJob(HttpVerb::Post, + QStringLiteral("RequestTokenToResetPasswordEmailJob"), basePath % "/account/password/email/requestToken", false) , d(new Private) { @@ -197,23 +183,21 @@ BaseJob::Status RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class RequestTokenToResetPasswordMSISDNJob::Private -{ +class RequestTokenToResetPasswordMSISDNJob::Private { public: Sid data; }; -static const auto RequestTokenToResetPasswordMSISDNJobName = - QStringLiteral("RequestTokenToResetPasswordMSISDNJob"); - RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob( const QString& clientSecret, const QString& country, const QString& phoneNumber, int sendAttempt, const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, RequestTokenToResetPasswordMSISDNJobName, + : BaseJob(HttpVerb::Post, + QStringLiteral("RequestTokenToResetPasswordMSISDNJob"), basePath % "/account/password/msisdn/requestToken", false) , d(new Private) { @@ -239,15 +223,13 @@ BaseJob::Status RequestTokenToResetPasswordMSISDNJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -static const auto DeactivateAccountJobName = - QStringLiteral("DeactivateAccountJob"); - DeactivateAccountJob::DeactivateAccountJob( const Omittable& auth) - : BaseJob(HttpVerb::Post, DeactivateAccountJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("DeactivateAccountJob"), basePath % "/account/deactivate") { QJsonObject _data; @@ -255,8 +237,7 @@ DeactivateAccountJob::DeactivateAccountJob( setRequestData(_data); } -class CheckUsernameAvailabilityJob::Private -{ +class CheckUsernameAvailabilityJob::Private { public: Omittable available; }; @@ -276,11 +257,8 @@ QUrl CheckUsernameAvailabilityJob::makeRequestUrl(QUrl baseUrl, queryToCheckUsernameAvailability(username)); } -static const auto CheckUsernameAvailabilityJobName = - QStringLiteral("CheckUsernameAvailabilityJob"); - CheckUsernameAvailabilityJob::CheckUsernameAvailabilityJob(const QString& username) - : BaseJob(HttpVerb::Get, CheckUsernameAvailabilityJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("CheckUsernameAvailabilityJob"), basePath % "/register/available", queryToCheckUsernameAvailability(username), {}, false) , d(new Private) diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index 89aecb80..2619dd87 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -11,13 +11,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Register for an account on this homeserver. -/*! +/*! \brief Register for an account on this homeserver. + * * This API endpoint uses the `User-Interactive Authentication API`_. * * Register for an account on this homeserver. @@ -51,10 +50,10 @@ namespace Quotient * invalidate any access token previously associated with that device. See * `Relationship between access tokens and devices`_. */ -class RegisterJob : public BaseJob -{ +class RegisterJob : public BaseJob { public: - /*! Register for an account on this homeserver. + /*! \brief Register for an account on this homeserver. + * * \param kind * The kind of account to register. Defaults to `user`. * \param auth @@ -104,10 +103,12 @@ public: /// the `Matrix specification /// `_. const QString& userId() const; + /// An access token for the account. /// This access token can then be used to authorize other requests. /// Required if the ``inhibit_login`` option is false. const QString& accessToken() const; + /// The server_name of the homeserver on which the account has /// been registered. /// @@ -115,6 +116,7 @@ public: /// ``user_id`` (by splitting at the first colon) if they require /// it. Note also that ``homeserver`` is not spelt this way. const QString& homeServer() const; + /// ID of the registered device. Will be the same as the /// corresponding parameter in the request, if one was specified. /// Required if the ``inhibit_login`` option is false. @@ -128,20 +130,22 @@ private: QScopedPointer d; }; -/// Begins the validation process for an email to be used during registration. -/*! +/*! \brief Begins the validation process for an email to be used during + * registration. + * * Proxies the Identity Service API ``validate/email/requestToken``, but * first checks that the given email address is not already associated * with an account on this homeserver. See the Identity Service API for * further information. */ -class RequestTokenToRegisterEmailJob : public BaseJob -{ +class RequestTokenToRegisterEmailJob : public BaseJob { public: - /*! Begins the validation process for an email to be used during - * registration. \param clientSecret A unique string generated by the - * client, and used to identify the validation attempt. It must be a string - * consisting of the characters + /*! \brief Begins the validation process for an email to be used during + * registration. + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param email @@ -184,21 +188,22 @@ private: QScopedPointer d; }; -/// Requests a validation token be sent to the given phone number for the -/// purpose of registering an account -/*! +/*! \brief Requests a validation token be sent to the given phone number for the + * purpose of registering an account + * * Proxies the Identity Service API ``validate/msisdn/requestToken``, but * first checks that the given phone number is not already associated * with an account on this homeserver. See the Identity Service API for * further information. */ -class RequestTokenToRegisterMSISDNJob : public BaseJob -{ +class RequestTokenToRegisterMSISDNJob : public BaseJob { public: - /*! Requests a validation token be sent to the given phone number for the - * purpose of registering an account \param clientSecret A unique string - * generated by the client, and used to identify the validation attempt. It - * must be a string consisting of the characters + /*! \brief Requests a validation token be sent to the given phone number for + * the purpose of registering an account + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param country @@ -245,8 +250,8 @@ private: QScopedPointer d; }; -/// Changes a user's password. -/*! +/*! \brief Changes a user's password. + * * Changes the password for an account on this homeserver. * * This API endpoint uses the `User-Interactive Authentication API`_. @@ -257,10 +262,10 @@ private: * The homeserver may change the flows available depending on whether a * valid access token is provided. */ -class ChangePasswordJob : public BaseJob -{ +class ChangePasswordJob : public BaseJob { public: - /*! Changes a user's password. + /*! \brief Changes a user's password. + * * \param newPassword * The new password for the account. * \param auth @@ -271,9 +276,9 @@ public: const Omittable& auth = none); }; -/// Requests a validation token be sent to the given email address for the -/// purpose of resetting a user's password -/*! +/*! \brief Requests a validation token be sent to the given email address for + * the purpose of resetting a user's password + * * Proxies the Identity Service API ``validate/email/requestToken``, but * first checks that the given email address **is** associated with an account * on this homeserver. This API should be used to request @@ -290,13 +295,14 @@ public: * .. _/register/email/requestToken: * #post-matrix-client-r0-register-email-requesttoken */ -class RequestTokenToResetPasswordEmailJob : public BaseJob -{ +class RequestTokenToResetPasswordEmailJob : public BaseJob { public: - /*! Requests a validation token be sent to the given email address for the - * purpose of resetting a user's password \param clientSecret A unique - * string generated by the client, and used to identify the validation - * attempt. It must be a string consisting of the characters + /*! \brief Requests a validation token be sent to the given email address + * for the purpose of resetting a user's password + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param email @@ -337,9 +343,9 @@ private: QScopedPointer d; }; -/// Requests a validation token be sent to the given phone number for the -/// purpose of resetting a user's password. -/*! +/*! \brief Requests a validation token be sent to the given phone number for the + * purpose of resetting a user's password. + * * Proxies the Identity Service API ``validate/msisdn/requestToken``, but * first checks that the given phone number **is** associated with an account * on this homeserver. This API should be used to request @@ -356,13 +362,14 @@ private: * .. _/register/msisdn/requestToken: * #post-matrix-client-r0-register-email-requesttoken */ -class RequestTokenToResetPasswordMSISDNJob : public BaseJob -{ +class RequestTokenToResetPasswordMSISDNJob : public BaseJob { public: - /*! Requests a validation token be sent to the given phone number for the - * purpose of resetting a user's password. \param clientSecret A unique - * string generated by the client, and used to identify the validation - * attempt. It must be a string consisting of the characters + /*! \brief Requests a validation token be sent to the given phone number for + * the purpose of resetting a user's password. + * + * \param clientSecret + * A unique string generated by the client, and used to identify the + * validation attempt. It must be a string consisting of the characters * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it * must not be empty. * \param country @@ -407,8 +414,8 @@ private: QScopedPointer d; }; -/// Deactivate a user's account. -/*! +/*! \brief Deactivate a user's account. + * * Deactivate the user's account, removing all ability for the user to * login again. * @@ -420,10 +427,10 @@ private: * The homeserver may change the flows available depending on whether a * valid access token is provided. */ -class DeactivateAccountJob : public BaseJob -{ +class DeactivateAccountJob : public BaseJob { public: - /*! Deactivate a user's account. + /*! \brief Deactivate a user's account. + * * \param auth * Additional authentication information for the user-interactive * authentication API. @@ -432,8 +439,8 @@ public: const Omittable& auth = none); }; -/// Checks to see if a username is available on the server. -/*! +/*! \brief Checks to see if a username is available on the server. + * * Checks to see if a username is available, and valid, for the server. * * The server should check to ensure that, at the time of the request, the @@ -447,23 +454,21 @@ public: * reserve the username. This can mean that the username becomes unavailable * between checking its availability and attempting to register it. */ -class CheckUsernameAvailabilityJob : public BaseJob -{ +class CheckUsernameAvailabilityJob : public BaseJob { public: - /*! Checks to see if a username is available on the server. + /*! \brief Checks to see if a username is available on the server. + * * \param username * The username to check the availability of. */ explicit CheckUsernameAvailabilityJob(const QString& username); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * CheckUsernameAvailabilityJob is necessary but the job - * itself isn't. + * This function can be used when a URL for CheckUsernameAvailabilityJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& username); - ~CheckUsernameAvailabilityJob() override; // Result properties diff --git a/lib/csapi/report_content.cpp b/lib/csapi/report_content.cpp index 352f52c9..f38b7a9a 100644 --- a/lib/csapi/report_content.cpp +++ b/lib/csapi/report_content.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto ReportContentJobName = QStringLiteral("ReportContentJob"); - ReportContentJob::ReportContentJob(const QString& roomId, const QString& eventId, int score, const QString& reason) - : BaseJob(HttpVerb::Post, ReportContentJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("ReportContentJob"), basePath % "/rooms/" % roomId % "/report/" % eventId) { QJsonObject _data; diff --git a/lib/csapi/report_content.h b/lib/csapi/report_content.h index c86a4301..0f3cc3d5 100644 --- a/lib/csapi/report_content.h +++ b/lib/csapi/report_content.h @@ -8,20 +8,19 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Reports an event as inappropriate. -/*! +/*! \brief Reports an event as inappropriate. + * * Reports an event as inappropriate to the server, which may then notify * the appropriate people. */ -class ReportContentJob : public BaseJob -{ +class ReportContentJob : public BaseJob { public: - /*! Reports an event as inappropriate. + /*! \brief Reports an event as inappropriate. + * * \param roomId * The room in which the event being reported is located. * \param eventId diff --git a/lib/csapi/room_send.cpp b/lib/csapi/room_send.cpp index 5e970d65..d278433b 100644 --- a/lib/csapi/room_send.cpp +++ b/lib/csapi/room_send.cpp @@ -12,17 +12,14 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class SendMessageJob::Private -{ +class SendMessageJob::Private { public: QString eventId; }; -static const auto SendMessageJobName = QStringLiteral("SendMessageJob"); - SendMessageJob::SendMessageJob(const QString& roomId, const QString& eventType, const QString& txnId, const QJsonObject& body) - : BaseJob(HttpVerb::Put, SendMessageJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SendMessageJob"), basePath % "/rooms/" % roomId % "/send/" % eventType % "/" % txnId) , d(new Private) { diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h index 110fc83b..96b111ff 100644 --- a/lib/csapi/room_send.h +++ b/lib/csapi/room_send.h @@ -8,13 +8,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Send a message event to the given room. -/*! +/*! \brief Send a message event to the given room. + * * This endpoint is used to send a message event to a room. Message events * allow access to historical events and pagination, making them suited * for "once-off" activity in a room. @@ -23,10 +22,10 @@ namespace Quotient * fields in this object will vary depending on the type of event. See * `Room Events`_ for the m. event specification. */ -class SendMessageJob : public BaseJob -{ +class SendMessageJob : public BaseJob { public: - /*! Send a message event to the given room. + /*! \brief Send a message event to the given room. + * * \param roomId * The room to send the event to. * \param eventType diff --git a/lib/csapi/room_state.cpp b/lib/csapi/room_state.cpp index bfcd6e17..5973b06b 100644 --- a/lib/csapi/room_state.cpp +++ b/lib/csapi/room_state.cpp @@ -12,20 +12,16 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class SetRoomStateWithKeyJob::Private -{ +class SetRoomStateWithKeyJob::Private { public: QString eventId; }; -static const auto SetRoomStateWithKeyJobName = - QStringLiteral("SetRoomStateWithKeyJob"); - SetRoomStateWithKeyJob::SetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey, const QJsonObject& body) - : BaseJob(HttpVerb::Put, SetRoomStateWithKeyJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomStateWithKeyJob"), basePath % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey) , d(new Private) @@ -45,17 +41,14 @@ BaseJob::Status SetRoomStateWithKeyJob::parseJson(const QJsonDocument& data) return Success; } -class SetRoomStateJob::Private -{ +class SetRoomStateJob::Private { public: QString eventId; }; -static const auto SetRoomStateJobName = QStringLiteral("SetRoomStateJob"); - SetRoomStateJob::SetRoomStateJob(const QString& roomId, const QString& eventType, const QJsonObject& body) - : BaseJob(HttpVerb::Put, SetRoomStateJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomStateJob"), basePath % "/rooms/" % roomId % "/state/" % eventType) , d(new Private) { diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h index 80619d63..7843cff4 100644 --- a/lib/csapi/room_state.h +++ b/lib/csapi/room_state.h @@ -8,13 +8,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Send a state event to the given room. -/*! +/*! \brief Send a state event to the given room. + * * State events can be sent using this endpoint. These events will be * overwritten if ````, ```` and ```` all * match. @@ -27,10 +26,10 @@ namespace Quotient * fields in this object will vary depending on the type of event. See * `Room Events`_ for the ``m.`` event specification. */ -class SetRoomStateWithKeyJob : public BaseJob -{ +class SetRoomStateWithKeyJob : public BaseJob { public: - /*! Send a state event to the given room. + /*! \brief Send a state event to the given room. + * * \param roomId * The room to set the state in * \param eventType @@ -70,8 +69,8 @@ private: QScopedPointer d; }; -/// Send a state event to the given room. -/*! +/*! \brief Send a state event to the given room. + * * State events can be sent using this endpoint. This endpoint is * equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` * with an empty `stateKey`. Previous state events with matching @@ -85,10 +84,10 @@ private: * fields in this object will vary depending on the type of event. See * `Room Events`_ for the ``m.`` event specification. */ -class SetRoomStateJob : public BaseJob -{ +class SetRoomStateJob : public BaseJob { public: - /*! Send a state event to the given room. + /*! \brief Send a state event to the given room. + * * \param roomId * The room to set the state in * \param eventType diff --git a/lib/csapi/room_upgrades.cpp b/lib/csapi/room_upgrades.cpp index 1d6006ef..244f5b74 100644 --- a/lib/csapi/room_upgrades.cpp +++ b/lib/csapi/room_upgrades.cpp @@ -12,16 +12,13 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class UpgradeRoomJob::Private -{ +class UpgradeRoomJob::Private { public: QString replacementRoom; }; -static const auto UpgradeRoomJobName = QStringLiteral("UpgradeRoomJob"); - UpgradeRoomJob::UpgradeRoomJob(const QString& roomId, const QString& newVersion) - : BaseJob(HttpVerb::Post, UpgradeRoomJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("UpgradeRoomJob"), basePath % "/rooms/" % roomId % "/upgrade") , d(new Private) { diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index 5f9262f1..f13a9af4 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -6,19 +6,18 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Upgrades a room to a new room version. -/*! +/*! \brief Upgrades a room to a new room version. + * * Upgrades the given room to a particular room version. */ -class UpgradeRoomJob : public BaseJob -{ +class UpgradeRoomJob : public BaseJob { public: - /*! Upgrades a room to a new room version. + /*! \brief Upgrades a room to a new room version. + * * \param roomId * The ID of the room to upgrade. * \param newVersion diff --git a/lib/csapi/rooms.cpp b/lib/csapi/rooms.cpp index 5bfbe44d..234e33df 100644 --- a/lib/csapi/rooms.cpp +++ b/lib/csapi/rooms.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetOneRoomEventJob::Private -{ +class GetOneRoomEventJob::Private { public: EventPtr data; }; @@ -26,11 +25,9 @@ QUrl GetOneRoomEventJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, % eventId); } -static const auto GetOneRoomEventJobName = QStringLiteral("GetOneRoomEventJob"); - GetOneRoomEventJob::GetOneRoomEventJob(const QString& roomId, const QString& eventId) - : BaseJob(HttpVerb::Get, GetOneRoomEventJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetOneRoomEventJob"), basePath % "/rooms/" % roomId % "/event/" % eventId) , d(new Private) {} @@ -42,6 +39,7 @@ EventPtr&& GetOneRoomEventJob::data() { return std::move(d->data); } BaseJob::Status GetOneRoomEventJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } @@ -54,13 +52,10 @@ QUrl GetRoomStateWithKeyJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, % eventType % "/" % stateKey); } -static const auto GetRoomStateWithKeyJobName = - QStringLiteral("GetRoomStateWithKeyJob"); - GetRoomStateWithKeyJob::GetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey) - : BaseJob(HttpVerb::Get, GetRoomStateWithKeyJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateWithKeyJob"), basePath % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey) {} @@ -73,17 +68,13 @@ QUrl GetRoomStateByTypeJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, % eventType); } -static const auto GetRoomStateByTypeJobName = - QStringLiteral("GetRoomStateByTypeJob"); - GetRoomStateByTypeJob::GetRoomStateByTypeJob(const QString& roomId, const QString& eventType) - : BaseJob(HttpVerb::Get, GetRoomStateByTypeJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateByTypeJob"), basePath % "/rooms/" % roomId % "/state/" % eventType) {} -class GetRoomStateJob::Private -{ +class GetRoomStateJob::Private { public: StateEvents data; }; @@ -94,10 +85,8 @@ QUrl GetRoomStateJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) basePath % "/rooms/" % roomId % "/state"); } -static const auto GetRoomStateJobName = QStringLiteral("GetRoomStateJob"); - GetRoomStateJob::GetRoomStateJob(const QString& roomId) - : BaseJob(HttpVerb::Get, GetRoomStateJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateJob"), basePath % "/rooms/" % roomId % "/state") , d(new Private) {} @@ -109,11 +98,11 @@ StateEvents&& GetRoomStateJob::data() { return std::move(d->data); } BaseJob::Status GetRoomStateJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class GetMembersByRoomJob::Private -{ +class GetMembersByRoomJob::Private { public: EventsArray chunk; }; @@ -134,19 +123,17 @@ QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& membership, const QString& notMembership) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/rooms/" % roomId % "/members", - queryToGetMembersByRoom(at, membership, notMembership)); + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath % "/rooms/" % roomId % "/members", + queryToGetMembersByRoom(at, membership, + notMembership)); } -static const auto GetMembersByRoomJobName = - QStringLiteral("GetMembersByRoomJob"); - GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, const QString& at, const QString& membership, const QString& notMembership) - : BaseJob(HttpVerb::Get, GetMembersByRoomJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetMembersByRoomJob"), basePath % "/rooms/" % roomId % "/members", queryToGetMembersByRoom(at, membership, notMembership)) , d(new Private) @@ -168,12 +155,10 @@ BaseJob::Status GetMembersByRoomJob::parseJson(const QJsonDocument& data) } // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, GetJoinedMembersByRoomJob::RoomMember& result) { @@ -184,8 +169,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetJoinedMembersByRoomJob::Private -{ +class GetJoinedMembersByRoomJob::Private { public: QHash joined; }; @@ -193,15 +177,13 @@ public: QUrl GetJoinedMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/rooms/" % roomId % "/joined_members"); + return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/rooms/" + % roomId + % "/joined_members"); } -static const auto GetJoinedMembersByRoomJobName = - QStringLiteral("GetJoinedMembersByRoomJob"); - GetJoinedMembersByRoomJob::GetJoinedMembersByRoomJob(const QString& roomId) - : BaseJob(HttpVerb::Get, GetJoinedMembersByRoomJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetJoinedMembersByRoomJob"), basePath % "/rooms/" % roomId % "/joined_members") , d(new Private) {} diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 1020fdb1..05c5b82a 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -12,20 +12,19 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Get a single event by event ID. -/*! +/*! \brief Get a single event by event ID. + * * Get a single event based on ``roomId/eventId``. You must have permission to * retrieve this event e.g. by being a member in the room for this event. */ -class GetOneRoomEventJob : public BaseJob -{ +class GetOneRoomEventJob : public BaseJob { public: - /*! Get a single event by event ID. + /*! \brief Get a single event by event ID. + * * \param roomId * The ID of the room the event is in. * \param eventId @@ -33,15 +32,13 @@ public: */ explicit GetOneRoomEventJob(const QString& roomId, const QString& eventId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetOneRoomEventJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetOneRoomEventJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId); - ~GetOneRoomEventJob() override; // Result properties @@ -57,17 +54,17 @@ private: QScopedPointer d; }; -/// Get the state identified by the type and key. -/*! +/*! \brief Get the state identified by the type and key. + * * Looks up the contents of a state event in a room. If the user is * joined to the room then the state is taken from the current * state of the room. If the user has left the room then the state is * taken from the state of the room when they left. */ -class GetRoomStateWithKeyJob : public BaseJob -{ +class GetRoomStateWithKeyJob : public BaseJob { public: - /*! Get the state identified by the type and key. + /*! \brief Get the state identified by the type and key. + * * \param roomId * The room to look up the state in. * \param eventType @@ -79,19 +76,18 @@ public: const QString& eventType, const QString& stateKey); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomStateWithKeyJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomStateWithKeyJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType, const QString& stateKey); }; -/// Get the state identified by the type, with the empty state key. -/*! +/*! \brief Get the state identified by the type, with the empty state key. + * * Looks up the contents of a state event in a room. If the user is * joined to the room then the state is taken from the current * state of the room. If the user has left the room then the state is @@ -99,10 +95,10 @@ public: * * This looks up the state event with the empty state key. */ -class GetRoomStateByTypeJob : public BaseJob -{ +class GetRoomStateByTypeJob : public BaseJob { public: - /*! Get the state identified by the type, with the empty state key. + /*! \brief Get the state identified by the type, with the empty state key. + * * \param roomId * The room to look up the state in. * \param eventType @@ -111,37 +107,34 @@ public: explicit GetRoomStateByTypeJob(const QString& roomId, const QString& eventType); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomStateByTypeJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomStateByTypeJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType); }; -/// Get all state events in the current state of a room. -/*! +/*! \brief Get all state events in the current state of a room. + * * Get the state events for the current state of a room. */ -class GetRoomStateJob : public BaseJob -{ +class GetRoomStateJob : public BaseJob { public: - /*! Get all state events in the current state of a room. + /*! \brief Get all state events in the current state of a room. + * * \param roomId * The room to look up the state for. */ explicit GetRoomStateJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomStateJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomStateJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetRoomStateJob() override; // Result properties @@ -160,14 +153,14 @@ private: QScopedPointer d; }; -/// Get the m.room.member events for the room. -/*! +/*! \brief Get the m.room.member events for the room. + * * Get the list of members for this room. */ -class GetMembersByRoomJob : public BaseJob -{ +class GetMembersByRoomJob : public BaseJob { public: - /*! Get the m.room.member events for the room. + /*! \brief Get the m.room.member events for the room. + * * \param roomId * The room to get the member events for. * \param at @@ -184,17 +177,15 @@ public: const QString& membership = {}, const QString& notMembership = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetMembersByRoomJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetMembersByRoomJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); - ~GetMembersByRoomJob() override; // Result properties @@ -210,8 +201,8 @@ private: QScopedPointer d; }; -/// Gets the list of currently joined users and their profile data. -/*! +/*! \brief Gets the list of currently joined users and their profile data. + * * This API returns a map of MXIDs to member info objects for members of the * room. The current user must be in the room for it to work, unless it is an * Application Service in which case any of the AS's users must be in the room. @@ -219,8 +210,7 @@ private: * respond than ``/members`` as it can be implemented more efficiently on the * server. */ -class GetJoinedMembersByRoomJob : public BaseJob -{ +class GetJoinedMembersByRoomJob : public BaseJob { public: // Inner data structures @@ -230,8 +220,7 @@ public: /// the room. This API is primarily for Application Services and should be /// faster to respond than ``/members`` as it can be implemented more /// efficiently on the server. - struct RoomMember - { + struct RoomMember { /// The display name of the user this object is representing. QString displayName; /// The mxc avatar url of the user this object is representing. @@ -240,20 +229,19 @@ public: // Construction/destruction - /*! Gets the list of currently joined users and their profile data. + /*! \brief Gets the list of currently joined users and their profile data. + * * \param roomId * The room to get the members of. */ explicit GetJoinedMembersByRoomJob(const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetJoinedMembersByRoomJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetJoinedMembersByRoomJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetJoinedMembersByRoomJob() override; // Result properties diff --git a/lib/csapi/sso_login_redirect.cpp b/lib/csapi/sso_login_redirect.cpp index c2cc81cf..21fe646e 100644 --- a/lib/csapi/sso_login_redirect.cpp +++ b/lib/csapi/sso_login_redirect.cpp @@ -26,10 +26,9 @@ QUrl RedirectToSSOJob::makeRequestUrl(QUrl baseUrl, const QString& redirectUrl) queryToRedirectToSSO(redirectUrl)); } -static const auto RedirectToSSOJobName = QStringLiteral("RedirectToSSOJob"); - RedirectToSSOJob::RedirectToSSOJob(const QString& redirectUrl) - : BaseJob(HttpVerb::Get, RedirectToSSOJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("RedirectToSSOJob"), basePath % "/login/sso/redirect", queryToRedirectToSSO(redirectUrl), {}, false) + {} diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h index b932a15e..8978c7f7 100644 --- a/lib/csapi/sso_login_redirect.h +++ b/lib/csapi/sso_login_redirect.h @@ -6,33 +6,31 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Redirect the user's browser to the SSO interface. -/*! +/*! \brief Redirect the user's browser to the SSO interface. + * * A web-based Matrix client should instruct the user's browser to * navigate to this endpoint in order to log in via SSO. * * The server MUST respond with an HTTP redirect to the SSO interface. */ -class RedirectToSSOJob : public BaseJob -{ +class RedirectToSSOJob : public BaseJob { public: - /*! Redirect the user's browser to the SSO interface. + /*! \brief Redirect the user's browser to the SSO interface. + * * \param redirectUrl * URI to which the user will be redirected after the homeserver has * authenticated the user with SSO. */ explicit RedirectToSSOJob(const QString& redirectUrl); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * RedirectToSSOJob is necessary but the job - * itself isn't. + * This function can be used when a URL for RedirectToSSOJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& redirectUrl); }; diff --git a/lib/csapi/tags.cpp b/lib/csapi/tags.cpp index 3df38074..0e2d122a 100644 --- a/lib/csapi/tags.cpp +++ b/lib/csapi/tags.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(QJsonObject jo, GetRoomTagsJob::Tag& result) { fromJson(jo.take("order"_ls), result.order); @@ -28,8 +26,7 @@ struct JsonObjectConverter } // namespace Quotient -class GetRoomTagsJob::Private -{ +class GetRoomTagsJob::Private { public: QHash tags; }; @@ -42,10 +39,8 @@ QUrl GetRoomTagsJob::makeRequestUrl(QUrl baseUrl, const QString& userId, % roomId % "/tags"); } -static const auto GetRoomTagsJobName = QStringLiteral("GetRoomTagsJob"); - GetRoomTagsJob::GetRoomTagsJob(const QString& userId, const QString& roomId) - : BaseJob(HttpVerb::Get, GetRoomTagsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomTagsJob"), basePath % "/user/" % userId % "/rooms/" % roomId % "/tags") , d(new Private) {} @@ -65,11 +60,9 @@ BaseJob::Status GetRoomTagsJob::parseJson(const QJsonDocument& data) return Success; } -static const auto SetRoomTagJobName = QStringLiteral("SetRoomTagJob"); - SetRoomTagJob::SetRoomTagJob(const QString& userId, const QString& roomId, const QString& tag, Omittable order) - : BaseJob(HttpVerb::Put, SetRoomTagJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomTagJob"), basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) { QJsonObject _data; @@ -85,10 +78,8 @@ QUrl DeleteRoomTagJob::makeRequestUrl(QUrl baseUrl, const QString& userId, % roomId % "/tags/" % tag); } -static const auto DeleteRoomTagJobName = QStringLiteral("DeleteRoomTagJob"); - DeleteRoomTagJob::DeleteRoomTagJob(const QString& userId, const QString& roomId, const QString& tag) - : BaseJob(HttpVerb::Delete, DeleteRoomTagJobName, + : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteRoomTagJob"), basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) {} diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h index 8ddebd6b..1ffb0f81 100644 --- a/lib/csapi/tags.h +++ b/lib/csapi/tags.h @@ -11,34 +11,31 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// List the tags for a room. -/*! +/*! \brief List the tags for a room. + * * List the tags set by a user on a room. */ -class GetRoomTagsJob : public BaseJob -{ +class GetRoomTagsJob : public BaseJob { public: // Inner data structures /// List the tags set by a user on a room. - struct Tag - { - /// A number in a range ``[0,1]`` describing a relativeposition of the - /// room under the given tag. + struct Tag { + /// A number in a range ``[0,1]`` describing a relative + /// position of the room under the given tag. Omittable order; - /// List the tags set by a user on a room. QVariantHash additionalProperties; }; // Construction/destruction - /*! List the tags for a room. + /*! \brief List the tags for a room. + * * \param userId * The id of the user to get tags for. The access token must be * authorized to make requests for this user ID. @@ -47,15 +44,13 @@ public: */ explicit GetRoomTagsJob(const QString& userId, const QString& roomId); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetRoomTagsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetRoomTagsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId); - ~GetRoomTagsJob() override; // Result properties @@ -71,14 +66,14 @@ private: QScopedPointer d; }; -/// Add a tag to a room. -/*! +/*! \brief Add a tag to a room. + * * Add a tag to the room. */ -class SetRoomTagJob : public BaseJob -{ +class SetRoomTagJob : public BaseJob { public: - /*! Add a tag to a room. + /*! \brief Add a tag to a room. + * * \param userId * The id of the user to add a tag for. The access token must be * authorized to make requests for this user ID. @@ -94,14 +89,14 @@ public: const QString& tag, Omittable order = none); }; -/// Remove a tag from the room. -/*! +/*! \brief Remove a tag from the room. + * * Remove a tag from the room. */ -class DeleteRoomTagJob : public BaseJob -{ +class DeleteRoomTagJob : public BaseJob { public: - /*! Remove a tag from the room. + /*! \brief Remove a tag from the room. + * * \param userId * The id of the user to remove a tag for. The access token must be * authorized to make requests for this user ID. @@ -113,11 +108,10 @@ public: explicit DeleteRoomTagJob(const QString& userId, const QString& roomId, const QString& tag); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * DeleteRoomTagJob is necessary but the job - * itself isn't. + * This function can be used when a URL for DeleteRoomTagJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& tag); diff --git a/lib/csapi/third_party_lookup.cpp b/lib/csapi/third_party_lookup.cpp index 678f6b3c..9d92e407 100644 --- a/lib/csapi/third_party_lookup.cpp +++ b/lib/csapi/third_party_lookup.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetProtocolsJob::Private -{ +class GetProtocolsJob::Private { public: QHash data; }; @@ -24,10 +23,8 @@ QUrl GetProtocolsJob::makeRequestUrl(QUrl baseUrl) basePath % "/thirdparty/protocols"); } -static const auto GetProtocolsJobName = QStringLiteral("GetProtocolsJob"); - GetProtocolsJob::GetProtocolsJob() - : BaseJob(HttpVerb::Get, GetProtocolsJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetProtocolsJob"), basePath % "/thirdparty/protocols") , d(new Private) {} @@ -42,11 +39,11 @@ const QHash& GetProtocolsJob::data() const BaseJob::Status GetProtocolsJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class GetProtocolMetadataJob::Private -{ +class GetProtocolMetadataJob::Private { public: ThirdPartyProtocol data; }; @@ -54,15 +51,12 @@ public: QUrl GetProtocolMetadataJob::makeRequestUrl(QUrl baseUrl, const QString& protocol) { - return BaseJob::makeRequestUrl( - std::move(baseUrl), basePath % "/thirdparty/protocol/" % protocol); + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath % "/thirdparty/protocol/" % protocol); } -static const auto GetProtocolMetadataJobName = - QStringLiteral("GetProtocolMetadataJob"); - GetProtocolMetadataJob::GetProtocolMetadataJob(const QString& protocol) - : BaseJob(HttpVerb::Get, GetProtocolMetadataJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("GetProtocolMetadataJob"), basePath % "/thirdparty/protocol/" % protocol) , d(new Private) {} @@ -77,11 +71,11 @@ const ThirdPartyProtocol& GetProtocolMetadataJob::data() const BaseJob::Status GetProtocolMetadataJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class QueryLocationByProtocolJob::Private -{ +class QueryLocationByProtocolJob::Private { public: QVector data; }; @@ -102,12 +96,9 @@ QUrl QueryLocationByProtocolJob::makeRequestUrl(QUrl baseUrl, queryToQueryLocationByProtocol(searchFields)); } -static const auto QueryLocationByProtocolJobName = - QStringLiteral("QueryLocationByProtocolJob"); - QueryLocationByProtocolJob::QueryLocationByProtocolJob( const QString& protocol, const QString& searchFields) - : BaseJob(HttpVerb::Get, QueryLocationByProtocolJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("QueryLocationByProtocolJob"), basePath % "/thirdparty/location/" % protocol, queryToQueryLocationByProtocol(searchFields)) , d(new Private) @@ -123,11 +114,11 @@ const QVector& QueryLocationByProtocolJob::data() const BaseJob::Status QueryLocationByProtocolJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class QueryUserByProtocolJob::Private -{ +class QueryUserByProtocolJob::Private { public: QVector data; }; @@ -148,12 +139,9 @@ QUrl QueryUserByProtocolJob::makeRequestUrl(QUrl baseUrl, queryToQueryUserByProtocol(fields)); } -static const auto QueryUserByProtocolJobName = - QStringLiteral("QueryUserByProtocolJob"); - QueryUserByProtocolJob::QueryUserByProtocolJob(const QString& protocol, const QString& fields) - : BaseJob(HttpVerb::Get, QueryUserByProtocolJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("QueryUserByProtocolJob"), basePath % "/thirdparty/user/" % protocol, queryToQueryUserByProtocol(fields)) , d(new Private) @@ -169,11 +157,11 @@ const QVector& QueryUserByProtocolJob::data() const BaseJob::Status QueryUserByProtocolJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class QueryLocationByAliasJob::Private -{ +class QueryLocationByAliasJob::Private { public: QVector data; }; @@ -192,11 +180,8 @@ QUrl QueryLocationByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& alias) queryToQueryLocationByAlias(alias)); } -static const auto QueryLocationByAliasJobName = - QStringLiteral("QueryLocationByAliasJob"); - QueryLocationByAliasJob::QueryLocationByAliasJob(const QString& alias) - : BaseJob(HttpVerb::Get, QueryLocationByAliasJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("QueryLocationByAliasJob"), basePath % "/thirdparty/location", queryToQueryLocationByAlias(alias)) , d(new Private) @@ -212,11 +197,11 @@ const QVector& QueryLocationByAliasJob::data() const BaseJob::Status QueryLocationByAliasJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } -class QueryUserByIDJob::Private -{ +class QueryUserByIDJob::Private { public: QVector data; }; @@ -235,10 +220,8 @@ QUrl QueryUserByIDJob::makeRequestUrl(QUrl baseUrl, const QString& userid) queryToQueryUserByID(userid)); } -static const auto QueryUserByIDJobName = QStringLiteral("QueryUserByIDJob"); - QueryUserByIDJob::QueryUserByIDJob(const QString& userid) - : BaseJob(HttpVerb::Get, QueryUserByIDJobName, + : BaseJob(HttpVerb::Get, QStringLiteral("QueryUserByIDJob"), basePath % "/thirdparty/user", queryToQueryUserByID(userid)) , d(new Private) {} @@ -253,5 +236,6 @@ const QVector& QueryUserByIDJob::data() const BaseJob::Status QueryUserByIDJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/third_party_lookup.h b/lib/csapi/third_party_lookup.h index 63607549..c8ca8cbb 100644 --- a/lib/csapi/third_party_lookup.h +++ b/lib/csapi/third_party_lookup.h @@ -15,30 +15,27 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Retrieve metadata about all protocols that a homeserver supports. -/*! +/*! \brief Retrieve metadata about all protocols that a homeserver supports. + * * Fetches the overall metadata about protocols supported by the * homeserver. Includes both the available protocols and all fields * required for queries against each protocol. */ -class GetProtocolsJob : public BaseJob -{ +class GetProtocolsJob : public BaseJob { public: + /// Retrieve metadata about all protocols that a homeserver supports. explicit GetProtocolsJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetProtocolsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetProtocolsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetProtocolsJob() override; // Result properties @@ -54,27 +51,28 @@ private: QScopedPointer d; }; -/// Retrieve metadata about a specific protocol that the homeserver supports. -/*! +/*! \brief Retrieve metadata about a specific protocol that the homeserver + * supports. + * * Fetches the metadata from the homeserver about a particular third party * protocol. */ -class GetProtocolMetadataJob : public BaseJob -{ +class GetProtocolMetadataJob : public BaseJob { public: - /*! Retrieve metadata about a specific protocol that the homeserver - * supports. \param protocol The name of the protocol. + /*! \brief Retrieve metadata about a specific protocol that the homeserver + * supports. + * + * \param protocol + * The name of the protocol. */ explicit GetProtocolMetadataJob(const QString& protocol); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetProtocolMetadataJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetProtocolMetadataJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol); - ~GetProtocolMetadataJob() override; // Result properties @@ -90,8 +88,8 @@ private: QScopedPointer d; }; -/// Retrieve Matrix-side portals rooms leading to a third party location. -/*! +/*! \brief Retrieve Matrix-side portals rooms leading to a third party location. + * * Requesting this endpoint with a valid protocol name results in a list * of successful mapping results in a JSON array. Each result contains * objects to represent the Matrix room or rooms that represent a portal @@ -101,10 +99,11 @@ private: * identifier. It should attempt to canonicalise the identifier as much * as reasonably possible given the network type. */ -class QueryLocationByProtocolJob : public BaseJob -{ +class QueryLocationByProtocolJob : public BaseJob { public: - /*! Retrieve Matrix-side portals rooms leading to a third party location. + /*! \brief Retrieve Matrix-side portals rooms leading to a third party + * location. + * * \param protocol * The protocol used to communicate to the third party network. * \param searchFields @@ -114,15 +113,13 @@ public: explicit QueryLocationByProtocolJob(const QString& protocol, const QString& searchFields = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * QueryLocationByProtocolJob is necessary but the job - * itself isn't. + * This function can be used when a URL for QueryLocationByProtocolJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& searchFields = {}); - ~QueryLocationByProtocolJob() override; // Result properties @@ -138,15 +135,15 @@ private: QScopedPointer d; }; -/// Retrieve the Matrix User ID of a corresponding third party user. -/*! +/*! \brief Retrieve the Matrix User ID of a corresponding third party user. + * * Retrieve a Matrix User ID linked to a user on the third party service, given * a set of user parameters. */ -class QueryUserByProtocolJob : public BaseJob -{ +class QueryUserByProtocolJob : public BaseJob { public: - /*! Retrieve the Matrix User ID of a corresponding third party user. + /*! \brief Retrieve the Matrix User ID of a corresponding third party user. + * * \param protocol * The name of the protocol. * \param fields @@ -156,15 +153,13 @@ public: explicit QueryUserByProtocolJob(const QString& protocol, const QString& fields = {}); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * QueryUserByProtocolJob is necessary but the job - * itself isn't. + * This function can be used when a URL for QueryUserByProtocolJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& fields = {}); - ~QueryUserByProtocolJob() override; // Result properties @@ -180,28 +175,26 @@ private: QScopedPointer d; }; -/// Reverse-lookup third party locations given a Matrix room alias. -/*! +/*! \brief Reverse-lookup third party locations given a Matrix room alias. + * * Retrieve an array of third party network locations from a Matrix room * alias. */ -class QueryLocationByAliasJob : public BaseJob -{ +class QueryLocationByAliasJob : public BaseJob { public: - /*! Reverse-lookup third party locations given a Matrix room alias. + /*! \brief Reverse-lookup third party locations given a Matrix room alias. + * * \param alias * The Matrix room alias to look up. */ explicit QueryLocationByAliasJob(const QString& alias); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * QueryLocationByAliasJob is necessary but the job - * itself isn't. + * This function can be used when a URL for QueryLocationByAliasJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& alias); - ~QueryLocationByAliasJob() override; // Result properties @@ -217,27 +210,25 @@ private: QScopedPointer d; }; -/// Reverse-lookup third party users given a Matrix User ID. -/*! +/*! \brief Reverse-lookup third party users given a Matrix User ID. + * * Retrieve an array of third party users from a Matrix User ID. */ -class QueryUserByIDJob : public BaseJob -{ +class QueryUserByIDJob : public BaseJob { public: - /*! Reverse-lookup third party users given a Matrix User ID. + /*! \brief Reverse-lookup third party users given a Matrix User ID. + * * \param userid * The Matrix User ID to look up. */ explicit QueryUserByIDJob(const QString& userid); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * QueryUserByIDJob is necessary but the job - * itself isn't. + * This function can be used when a URL for QueryUserByIDJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userid); - ~QueryUserByIDJob() override; // Result properties diff --git a/lib/csapi/third_party_membership.cpp b/lib/csapi/third_party_membership.cpp index 7e401163..15da6b3c 100644 --- a/lib/csapi/third_party_membership.cpp +++ b/lib/csapi/third_party_membership.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto InviteBy3PIDJobName = QStringLiteral("InviteBy3PIDJob"); - InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, const QString& medium, const QString& address) - : BaseJob(HttpVerb::Post, InviteBy3PIDJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("InviteBy3PIDJob"), basePath % "/rooms/" % roomId % "/invite") { QJsonObject _data; diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h index bd4c6896..c85e8ab4 100644 --- a/lib/csapi/third_party_membership.h +++ b/lib/csapi/third_party_membership.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Invite a user to participate in a particular room. -/*! +/*! \brief Invite a user to participate in a particular room. + * * .. _invite-by-third-party-id-endpoint: * * *Note that there are two forms of this API, which are documented separately. @@ -58,10 +57,10 @@ namespace Quotient * * .. _joining rooms section: `invite-by-user-id-endpoint`_ */ -class InviteBy3PIDJob : public BaseJob -{ +class InviteBy3PIDJob : public BaseJob { public: - /*! Invite a user to participate in a particular room. + /*! \brief Invite a user to participate in a particular room. + * * \param roomId * The room identifier (not alias) to which to invite the user. * \param idServer diff --git a/lib/csapi/to_device.cpp b/lib/csapi/to_device.cpp index 3f6e8097..e277503c 100644 --- a/lib/csapi/to_device.cpp +++ b/lib/csapi/to_device.cpp @@ -12,12 +12,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SendToDeviceJobName = QStringLiteral("SendToDeviceJob"); - SendToDeviceJob::SendToDeviceJob( const QString& eventType, const QString& txnId, const QHash>& messages) - : BaseJob(HttpVerb::Put, SendToDeviceJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SendToDeviceJob"), basePath % "/sendToDevice/" % eventType % "/" % txnId) { QJsonObject _data; diff --git a/lib/csapi/to_device.h b/lib/csapi/to_device.h index 14445439..48f0ec83 100644 --- a/lib/csapi/to_device.h +++ b/lib/csapi/to_device.h @@ -9,20 +9,19 @@ #include #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Send an event to a given set of devices. -/*! +/*! \brief Send an event to a given set of devices. + * * This endpoint is used to send send-to-device events to a set of * client devices. */ -class SendToDeviceJob : public BaseJob -{ +class SendToDeviceJob : public BaseJob { public: - /*! Send an event to a given set of devices. + /*! \brief Send an event to a given set of devices. + * * \param eventType * The type of event to send. * \param txnId diff --git a/lib/csapi/typing.cpp b/lib/csapi/typing.cpp index 064ebe39..261622c4 100644 --- a/lib/csapi/typing.cpp +++ b/lib/csapi/typing.cpp @@ -12,11 +12,9 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -static const auto SetTypingJobName = QStringLiteral("SetTypingJob"); - SetTypingJob::SetTypingJob(const QString& userId, const QString& roomId, bool typing, Omittable timeout) - : BaseJob(HttpVerb::Put, SetTypingJobName, + : BaseJob(HttpVerb::Put, QStringLiteral("SetTypingJob"), basePath % "/rooms/" % roomId % "/typing/" % userId) { QJsonObject _data; diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h index 350c209d..dbf17a3e 100644 --- a/lib/csapi/typing.h +++ b/lib/csapi/typing.h @@ -8,22 +8,21 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Informs the server that the user has started or stopped typing. -/*! +/*! \brief Informs the server that the user has started or stopped typing. + * * This tells the server that the user is typing for the next N * milliseconds where N is the value specified in the ``timeout`` key. * Alternatively, if ``typing`` is ``false``, it tells the server that the * user has stopped typing. */ -class SetTypingJob : public BaseJob -{ +class SetTypingJob : public BaseJob { public: - /*! Informs the server that the user has started or stopped typing. + /*! \brief Informs the server that the user has started or stopped typing. + * * \param userId * The user who has started to type. * \param roomId diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index 77c297dd..5d2a6133 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -13,12 +13,10 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); // Converters -namespace Quotient -{ +namespace Quotient { template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void fillFrom(const QJsonObject& jo, SearchUserDirectoryJob::User& result) { @@ -30,19 +28,15 @@ struct JsonObjectConverter } // namespace Quotient -class SearchUserDirectoryJob::Private -{ +class SearchUserDirectoryJob::Private { public: QVector results; bool limited; }; -static const auto SearchUserDirectoryJobName = - QStringLiteral("SearchUserDirectoryJob"); - SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, Omittable limit) - : BaseJob(HttpVerb::Post, SearchUserDirectoryJobName, + : BaseJob(HttpVerb::Post, QStringLiteral("SearchUserDirectoryJob"), basePath % "/user_directory/search") , d(new Private) { diff --git a/lib/csapi/users.h b/lib/csapi/users.h index d9a16a9d..de4eb529 100644 --- a/lib/csapi/users.h +++ b/lib/csapi/users.h @@ -10,13 +10,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Searches the user directory. -/*! +/*! \brief Searches the user directory. + * * Performs a search for users on the homeserver. The homeserver may * determine which subset of users are searched, however the homeserver * MUST at a minimum consider the users the requesting user shares a @@ -28,22 +27,21 @@ namespace Quotient * names preferably using a collation determined based upon the * ``Accept-Language`` header provided in the request, if present. */ -class SearchUserDirectoryJob : public BaseJob -{ +class SearchUserDirectoryJob : public BaseJob { public: // Inner data structures - /// Performs a search for users on the homeserver. The homeserver - /// maydetermine which subset of users are searched, however the - /// homeserverMUST at a minimum consider the users the requesting user - /// shares aroom with and those who reside in public rooms (known to the - /// homeserver).The search MUST consider local users to the homeserver, and - /// SHOULDquery remote users as part of the search.The search is performed - /// case-insensitively on user IDs and displaynames preferably using a - /// collation determined based upon the ``Accept-Language`` header provided - /// in the request, if present. - struct User - { + /// Performs a search for users on the homeserver. The homeserver may + /// determine which subset of users are searched, however the homeserver + /// MUST at a minimum consider the users the requesting user shares a + /// room with and those who reside in public rooms (known to the + /// homeserver). The search MUST consider local users to the homeserver, and + /// SHOULD query remote users as part of the search. + /// + /// The search is performed case-insensitively on user IDs and display + /// names preferably using a collation determined based upon the + /// ``Accept-Language`` header provided in the request, if present. + struct User { /// The user's matrix user ID. QString userId; /// The display name of the user, if one exists. @@ -54,7 +52,8 @@ public: // Construction/destruction - /*! Searches the user directory. + /*! \brief Searches the user directory. + * * \param searchTerm * The term to search for * \param limit @@ -69,6 +68,7 @@ public: /// Ordered by rank and then whether or not profile info is available. const QVector& results() const; + /// Indicates if the result list has been truncated by the limit. bool limited() const; diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 9607a1b6..8cce03b9 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client"); -class GetVersionsJob::Private -{ +class GetVersionsJob::Private { public: QStringList versions; QHash unstableFeatures; @@ -24,10 +23,9 @@ QUrl GetVersionsJob::makeRequestUrl(QUrl baseUrl) return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/versions"); } -static const auto GetVersionsJobName = QStringLiteral("GetVersionsJob"); - GetVersionsJob::GetVersionsJob() - : BaseJob(HttpVerb::Get, GetVersionsJobName, basePath % "/versions", false) + : BaseJob(HttpVerb::Get, QStringLiteral("GetVersionsJob"), + basePath % "/versions", false) , d(new Private) {} diff --git a/lib/csapi/versions.h b/lib/csapi/versions.h index aa8cbac6..4c4f8109 100644 --- a/lib/csapi/versions.h +++ b/lib/csapi/versions.h @@ -10,13 +10,12 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets the versions of the specification supported by the server. -/*! +/*! \brief Gets the versions of the specification supported by the server. + * * Gets the versions of the specification supported by the server. * * Values will take the form ``rX.Y.Z``. @@ -38,25 +37,24 @@ namespace Quotient * upgrade appropriately. Additionally, clients should avoid using unstable * features in their stable releases. */ -class GetVersionsJob : public BaseJob -{ +class GetVersionsJob : public BaseJob { public: + /// Gets the versions of the specification supported by the server. explicit GetVersionsJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetVersionsJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetVersionsJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetVersionsJob() override; // Result properties /// The supported versions. const QStringList& versions() const; + /// Experimental features the server supports. Features not listed here, /// or the lack of this property all together, indicate that a feature is /// not supported. diff --git a/lib/csapi/voip.cpp b/lib/csapi/voip.cpp index c95afe16..c98824b3 100644 --- a/lib/csapi/voip.cpp +++ b/lib/csapi/voip.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetTurnServerJob::Private -{ +class GetTurnServerJob::Private { public: QJsonObject data; }; @@ -24,10 +23,9 @@ QUrl GetTurnServerJob::makeRequestUrl(QUrl baseUrl) basePath % "/voip/turnServer"); } -static const auto GetTurnServerJobName = QStringLiteral("GetTurnServerJob"); - GetTurnServerJob::GetTurnServerJob() - : BaseJob(HttpVerb::Get, GetTurnServerJobName, basePath % "/voip/turnServer") + : BaseJob(HttpVerb::Get, QStringLiteral("GetTurnServerJob"), + basePath % "/voip/turnServer") , d(new Private) {} @@ -38,5 +36,6 @@ const QJsonObject& GetTurnServerJob::data() const { return d->data; } BaseJob::Status GetTurnServerJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/voip.h b/lib/csapi/voip.h index 38abfa27..d3eb3c4b 100644 --- a/lib/csapi/voip.h +++ b/lib/csapi/voip.h @@ -8,29 +8,26 @@ #include -namespace Quotient -{ +namespace Quotient { // Operations -/// Obtain TURN server credentials. -/*! +/*! \brief Obtain TURN server credentials. + * * This API provides credentials for the client to use when initiating * calls. */ -class GetTurnServerJob : public BaseJob -{ +class GetTurnServerJob : public BaseJob { public: + /// Obtain TURN server credentials. explicit GetTurnServerJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetTurnServerJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetTurnServerJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTurnServerJob() override; // Result properties diff --git a/lib/csapi/wellknown.cpp b/lib/csapi/wellknown.cpp index 9a52a2a5..464068e7 100644 --- a/lib/csapi/wellknown.cpp +++ b/lib/csapi/wellknown.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/.well-known"); -class GetWellknownJob::Private -{ +class GetWellknownJob::Private { public: DiscoveryInformation data; }; @@ -24,11 +23,9 @@ QUrl GetWellknownJob::makeRequestUrl(QUrl baseUrl) basePath % "/matrix/client"); } -static const auto GetWellknownJobName = QStringLiteral("GetWellknownJob"); - GetWellknownJob::GetWellknownJob() - : BaseJob(HttpVerb::Get, GetWellknownJobName, basePath % "/matrix/client", - false) + : BaseJob(HttpVerb::Get, QStringLiteral("GetWellknownJob"), + basePath % "/matrix/client", false) , d(new Private) {} @@ -39,5 +36,6 @@ const DiscoveryInformation& GetWellknownJob::data() const { return d->data; } BaseJob::Status GetWellknownJob::parseJson(const QJsonDocument& data) { fromJson(data, d->data); + return Success; } diff --git a/lib/csapi/wellknown.h b/lib/csapi/wellknown.h index bce67d00..94d3631c 100644 --- a/lib/csapi/wellknown.h +++ b/lib/csapi/wellknown.h @@ -10,13 +10,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets Matrix server discovery information about the domain. -/*! +/*! \brief Gets Matrix server discovery information about the domain. + * * Gets discovery information about the domain. The file may include * additional keys, which MUST follow the Java package naming convention, * e.g. ``com.example.myapp.property``. This ensures property names are @@ -26,19 +25,17 @@ namespace Quotient * Note that this endpoint is not necessarily handled by the homeserver, * but by another webserver, to be used for discovering the homeserver URL. */ -class GetWellknownJob : public BaseJob -{ +class GetWellknownJob : public BaseJob { public: + /// Gets Matrix server discovery information about the domain. explicit GetWellknownJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetWellknownJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetWellknownJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetWellknownJob() override; // Result properties diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index fb7f54dc..8d4681e4 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -12,8 +12,7 @@ using namespace Quotient; static const auto basePath = QStringLiteral("/_matrix/client/r0"); -class GetTokenOwnerJob::Private -{ +class GetTokenOwnerJob::Private { public: QString userId; }; @@ -24,10 +23,9 @@ QUrl GetTokenOwnerJob::makeRequestUrl(QUrl baseUrl) basePath % "/account/whoami"); } -static const auto GetTokenOwnerJobName = QStringLiteral("GetTokenOwnerJob"); - GetTokenOwnerJob::GetTokenOwnerJob() - : BaseJob(HttpVerb::Get, GetTokenOwnerJobName, basePath % "/account/whoami") + : BaseJob(HttpVerb::Get, QStringLiteral("GetTokenOwnerJob"), + basePath % "/account/whoami") , d(new Private) {} diff --git a/lib/csapi/whoami.h b/lib/csapi/whoami.h index bbbb3899..dda8d8c8 100644 --- a/lib/csapi/whoami.h +++ b/lib/csapi/whoami.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -namespace Quotient -{ +namespace Quotient { // Operations -/// Gets information about the owner of an access token. -/*! +/*! \brief Gets information about the owner of an access token. + * * Gets information about the owner of a given access token. * * Note that, as with the rest of the Client-Server API, @@ -22,19 +21,17 @@ namespace Quotient * is registered by the appservice, and return it in the response * body. */ -class GetTokenOwnerJob : public BaseJob -{ +class GetTokenOwnerJob : public BaseJob { public: + /// Gets information about the owner of an access token. explicit GetTokenOwnerJob(); - /*! Construct a URL without creating a full-fledged job object + /*! \brief Construct a URL without creating a full-fledged job object * - * This function can be used when a URL for - * GetTokenOwnerJob is necessary but the job - * itself isn't. + * This function can be used when a URL for GetTokenOwnerJob + * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTokenOwnerJob() override; // Result properties diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h index 99487073..32c6eaaf 100644 --- a/lib/identity/definitions/request_email_validation.h +++ b/lib/identity/definitions/request_email_validation.h @@ -6,35 +6,36 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct RequestEmailValidation -{ - /// A unique string generated by the client, and used to identify - /// thevalidation attempt. It must be a string consisting of the - /// characters``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters - /// and itmust not be empty. +struct RequestEmailValidation { + /// A unique string generated by the client, and used to identify the + /// validation attempt. It must be a string consisting of the characters + /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it + /// must not be empty. QString clientSecret; + /// The email address to validate. QString email; - /// The server will only send an email if the ``send_attempt``is a number - /// greater than the most recent one which it has seen,scoped to that - /// ``email`` + ``client_secret`` pair. This is toavoid repeatedly sending - /// the same email in the case of requestretries between the POSTing user - /// and the identity server.The client should increment this value if they - /// desire a newemail (e.g. a reminder) to be sent. + + /// The server will only send an email if the ``send_attempt`` + /// is a number greater than the most recent one which it has seen, + /// scoped to that ``email`` + ``client_secret`` pair. This is to + /// avoid repeatedly sending the same email in the case of request + /// retries between the POSTing user and the identity server. + /// The client should increment this value if they desire a new + /// email (e.g. a reminder) to be sent. int sendAttempt; - /// Optional. When the validation is completed, the identityserver will - /// redirect the user to this URL. + + /// Optional. When the validation is completed, the identity + /// server will redirect the user to this URL. QString nextLink; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RequestEmailValidation& pod); static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod); }; diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h index ecccf567..90aed7b8 100644 --- a/lib/identity/definitions/request_msisdn_validation.h +++ b/lib/identity/definitions/request_msisdn_validation.h @@ -6,39 +6,40 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct RequestMsisdnValidation -{ - /// A unique string generated by the client, and used to identify - /// thevalidation attempt. It must be a string consisting of the - /// characters``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters - /// and itmust not be empty. +struct RequestMsisdnValidation { + /// A unique string generated by the client, and used to identify the + /// validation attempt. It must be a string consisting of the characters + /// ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it + /// must not be empty. QString clientSecret; - /// The two-letter uppercase ISO country code that the number - /// in``phone_number`` should be parsed as if it were dialled from. + + /// The two-letter uppercase ISO country code that the number in + /// ``phone_number`` should be parsed as if it were dialled from. QString country; + /// The phone number to validate. QString phoneNumber; - /// The server will only send an SMS if the ``send_attempt`` is anumber - /// greater than the most recent one which it has seen,scoped to that - /// ``country`` + ``phone_number`` + ``client_secret``triple. This is to - /// avoid repeatedly sending the same SMS inthe case of request retries - /// between the POSTing user and theidentity server. The client should - /// increment this value ifthey desire a new SMS (e.g. a reminder) to be - /// sent. + + /// The server will only send an SMS if the ``send_attempt`` is a + /// number greater than the most recent one which it has seen, + /// scoped to that ``country`` + ``phone_number`` + ``client_secret`` + /// triple. This is to avoid repeatedly sending the same SMS in + /// the case of request retries between the POSTing user and the + /// identity server. The client should increment this value if + /// they desire a new SMS (e.g. a reminder) to be sent. int sendAttempt; - /// Optional. When the validation is completed, the identityserver will - /// redirect the user to this URL. + + /// Optional. When the validation is completed, the identity + /// server will redirect the user to this URL. QString nextLink; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const RequestMsisdnValidation& pod); static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod); }; diff --git a/lib/identity/definitions/sid.h b/lib/identity/definitions/sid.h index 0f7ce58a..981826f6 100644 --- a/lib/identity/definitions/sid.h +++ b/lib/identity/definitions/sid.h @@ -6,23 +6,20 @@ #include "converters.h" -namespace Quotient -{ +namespace Quotient { // Data structures -struct Sid -{ - /// The session ID. Session IDs are opaque strings generated by the - /// identityserver. They must consist entirely of the - /// characters``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 - /// characters and theymust not be empty. +struct Sid { + /// The session ID. Session IDs are opaque strings generated by the identity + /// server. They must consist entirely of the characters + /// ``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 characters and + /// they must not be empty. QString sid; }; template <> -struct JsonObjectConverter -{ +struct JsonObjectConverter { static void dumpTo(QJsonObject& jo, const Sid& pod); static void fillFrom(const QJsonObject& jo, Sid& pod); }; -- cgit v1.2.3 From 3c2f98375c4b82e7f1ee8a0f5d477e55c8685075 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 12 May 2020 21:23:56 +0200 Subject: basejob.cpp: nitpicks from clang-format --- lib/jobs/basejob.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 8e1a63aa..470250bf 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -149,15 +149,9 @@ BaseJob::~BaseJob() qCDebug(d->logCat) << this << "destroyed"; } -QUrl BaseJob::requestUrl() const -{ - return d->reply ? d->reply->url() : QUrl(); -} +QUrl BaseJob::requestUrl() const { return d->reply ? d->reply->url() : QUrl(); } -bool BaseJob::isBackground() const -{ - return d->inBackground; -} +bool BaseJob::isBackground() const { return d->inBackground; } const QString& BaseJob::apiEndpoint() const { return d->apiEndpoint; } @@ -215,7 +209,7 @@ QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path, // QUrl::adjusted(QUrl::StripTrailingSlashes) doesn't help with root '/' while (pathBase.endsWith('/')) pathBase.chop(1); - if (!path.startsWith('/')) // Normally API files do start with '/' + if (!path.startsWith('/')) // Normally API files do start with '/' pathBase.push_back('/'); // so this shouldn't be needed these days baseUrl.setPath(pathBase + path, QUrl::TolerantMode); @@ -259,11 +253,11 @@ void BaseJob::Private::sendRequest() } } -void BaseJob::doPrepare() {} +void BaseJob::doPrepare() { } -void BaseJob::onSentRequest(QNetworkReply*) {} +void BaseJob::onSentRequest(QNetworkReply*) { } -void BaseJob::beforeAbandon(QNetworkReply*) {} +void BaseJob::beforeAbandon(QNetworkReply*) { } void BaseJob::initiate(ConnectionData* connData, bool inBackground) { @@ -273,7 +267,8 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) doPrepare(); if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) - && d->requestData.source() && !d->requestData.source()->isReadable()) { + && d->requestData.source() + && !d->requestData.source()->isReadable()) { setStatus(FileError, "Request data not ready"); } Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this -- cgit v1.2.3 From dacc4a9727cb1c400b70ae2b9d470ce42c24ead3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 27 May 2020 07:48:03 +0200 Subject: room.cpp: clean away unused variables --- lib/room.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index c9363a00..fa6971c9 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2420,7 +2420,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [] (const RoomNameEvent&) { return NameChange; } - , [this,oldStateEvent] (const RoomAliasesEvent& ae) { + , [] (const RoomAliasesEvent&) { // clang-format on // This event has been removed by MSC-2432 return NoChange; -- cgit v1.2.3 From cf4d3d7ed82e62b57a11fbc7f491535a761dc75c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 27 May 2020 19:03:52 +0200 Subject: Move around and format code No functional changes here. --- lib/connection.cpp | 4 +-- lib/jobs/basejob.cpp | 94 +++++++++++++++++++++++++++++----------------------- lib/jobs/basejob.h | 17 ++++++---- lib/syncdata.h | 2 +- 4 files changed, 66 insertions(+), 51 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 9f4f7082..efc7163e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -20,7 +20,7 @@ #include "connectiondata.h" #ifdef Quotient_E2EE_ENABLED -#include "encryptionmanager.h" +# include "encryptionmanager.h" #endif // Quotient_E2EE_ENABLED #include "room.h" #include "settings.h" @@ -45,7 +45,7 @@ #include "jobs/syncjob.h" #ifdef Quotient_E2EE_ENABLED -#include "account.h" // QtOlm +# include "account.h" // QtOlm #endif // Quotient_E2EE_ENABLED #include diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 470250bf..100c05f2 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -35,6 +35,47 @@ using namespace Quotient; using std::chrono::seconds, std::chrono::milliseconds; using namespace std::chrono_literals; +BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode) +{ + if (httpCode / 10 == 41) // 41x errors + return httpCode == 410 ? IncorrectRequestError : NotFoundError; + switch (httpCode) { + case 401: + return Unauthorised; + // clang-format off + case 403: case 407: // clang-format on + return ContentAccessError; + case 404: + return NotFoundError; + // clang-format off + case 400: case 405: case 406: case 426: case 428: case 505: // clang-format on + case 494: // Unofficial nginx "Request header too large" + case 497: // Unofficial nginx "HTTP request sent to HTTPS port" + return IncorrectRequestError; + case 429: + return TooManyRequestsError; + case 501: + case 510: + return RequestNotImplementedError; + case 511: + return NetworkAuthRequiredError; + default: + return NetworkError; + } +} + +QDebug BaseJob::Status::dumpToLog(QDebug dbg) const +{ + QDebugStateSaver _s(dbg); + dbg.noquote().nospace(); + if (auto* const k = QMetaEnum::fromType().valueToKey(code)) { + const QByteArray b = k; + dbg << b.mid(b.lastIndexOf(':')); + } else + dbg << code; + return dbg << ": " << message; +} + struct NetworkReplyDeleter : public QScopedPointerDeleteLater { static inline void cleanup(QNetworkReply* reply) { @@ -316,7 +357,15 @@ void BaseJob::sendRequest() << "Request could not start:" << d->dumpRequest(); } -void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } +BaseJob::Status BaseJob::Private::parseJsonDocument() +{ + QJsonParseError error { 0, QJsonParseError::MissingObject }; + jsonResponse = QJsonDocument::fromJson(d->rawResponse, &error); + return { error.error == QJsonParseError::NoError + ? BaseJob::NoError + : BaseJob::IncorrectResponse, + error.errorString() }; +} void BaseJob::gotReply() { @@ -363,47 +412,6 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } -BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode) -{ - if (httpCode / 10 == 41) // 41x errors - return httpCode == 410 ? IncorrectRequestError : NotFoundError; - switch (httpCode) { - case 401: - return Unauthorised; - // clang-format off - case 403: case 407: // clang-format on - return ContentAccessError; - case 404: - return NotFoundError; - // clang-format off - case 400: case 405: case 406: case 426: case 428: case 505: // clang-format on - case 494: // Unofficial nginx "Request header too large" - case 497: // Unofficial nginx "HTTP request sent to HTTPS port" - return IncorrectRequestError; - case 429: - return TooManyRequestsError; - case 501: case 510: - return RequestNotImplementedError; - case 511: - return NetworkAuthRequiredError; - default: - return NetworkError; - } -} - -QDebug BaseJob::Status::dumpToLog(QDebug dbg) const -{ - QDebugStateSaver _s(dbg); - dbg.noquote().nospace(); - if (auto* const k = QMetaEnum::fromType().valueToKey(code)) { - const QByteArray b = k; - dbg << b.mid(b.lastIndexOf(':')); - } else - dbg << code; - return dbg << ": " << message; - -} - BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const { // QNetworkReply error codes seem to be flawed when it comes to HTTP; @@ -440,6 +448,8 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const return Status::fromHttpCode(httpCode, message); } +void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } + BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) { d->rawResponse = reply->readAll(); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 2049f59c..954b0777 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -170,6 +170,8 @@ public: * recommended to present a sample of raw data as "details" next to * error messages. Note that the default \p bytesAtMost value is * also tailored to UI cases. + * + * \sa rawData */ QString rawDataSample(int bytesAtMost = 65535) const; @@ -322,6 +324,7 @@ protected: * on retries. */ virtual void doPrepare(); + /*! Postprocessing after the network request has been sent * * This method is called every time the job receives a running @@ -331,13 +334,15 @@ protected: virtual void onSentRequest(QNetworkReply*); 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. + /*! \brief Check the pending or received reply for upfront issues + * + * This is invoked when headers are first received and also once + * the complete reply is obtained; the base implementation checks the HTTP + * headers to detect general issues such as network errors or access denial. + * It cannot read the response body (use parseReply/parseError to check + * for problems in the body). Returning anything except NoError/Success + * prevents further processing of the reply. * - * @param reply the reply received from the server * @return the result of checking the reply * * @see gotReply diff --git a/lib/syncdata.h b/lib/syncdata.h index 6e7183ee..41b8186e 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -75,7 +75,7 @@ public: static const QString UnreadCountKey; }; -// QVector cannot work with non-copiable objects, std::vector can. +// QVector cannot work with non-copyable objects, std::vector can. using SyncDataList = std::vector; class SyncData { -- cgit v1.2.3 From a9edfbd19a624aa4e882df2f5e577ba1831914a3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 28 May 2020 08:42:04 +0200 Subject: BaseJob::rawData: overload for (even) quicker access No functional changes either. --- lib/jobs/basejob.cpp | 2 ++ lib/jobs/basejob.h | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 100c05f2..e649ea87 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -612,6 +612,8 @@ QByteArray BaseJob::rawData(int bytesAtMost) const : d->rawResponse; } +const QByteArray& BaseJob::rawData() const { return d->rawResponse; } + QString BaseJob::rawDataSample(int bytesAtMost) const { auto data = rawData(bytesAtMost); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 954b0777..010aca78 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -157,11 +157,16 @@ public: /** Short human-friendly message on the job status */ QString statusCaption() const; - /** Get raw response body as received from the server - * \param bytesAtMost return this number of leftmost bytes, or -1 - * to return the entire response + /*! Get first bytes of the raw response body as received from the server + * + * \param bytesAtMost the number of leftmost bytes to return + * + * \sa rawDataSample */ - QByteArray rawData(int bytesAtMost = -1) const; + QByteArray rawData(int bytesAtMost) const; + + /*! Access the whole response body as received from the server */ + const QByteArray& rawData() const; /** Get UI-friendly sample of raw data * -- cgit v1.2.3 From 51ef6011d6dfc55ea0c8b0cdd1d9e7315087aad9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 28 May 2020 13:24:16 +0200 Subject: BaseJob: use non-deprecated API for Qt 5.15 Qt 5.15 renamed HTTP2AllowedAttribute to Http2AllowedAttribute, deprecating the old spelling. --- lib/jobs/basejob.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index e649ea87..26782edc 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -273,7 +273,13 @@ void BaseJob::Private::sendRequest() // Pipelining doesn't fly quite well with SSL, occasionally crashing at // what seems like an attempt to write to a closed channel. // req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); - req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); + req.setAttribute( +#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) + QNetworkRequest::Http2AllowedAttribute +#else + QNetworkRequest::HTTP2AllowedAttribute +#endif + , true); Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) req.setRawHeader(it.key(), it.value()); -- cgit v1.2.3 From 7ac93db4553aa624326a8e28b74151c784b937a7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 1 Jun 2020 14:47:15 +0200 Subject: Remove the piece of code introduced too early on parseJsonDocument() will come later. --- lib/jobs/basejob.cpp | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 26782edc..2519713e 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -363,16 +363,6 @@ void BaseJob::sendRequest() << "Request could not start:" << d->dumpRequest(); } -BaseJob::Status BaseJob::Private::parseJsonDocument() -{ - QJsonParseError error { 0, QJsonParseError::MissingObject }; - jsonResponse = QJsonDocument::fromJson(d->rawResponse, &error); - return { error.error == QJsonParseError::NoError - ? BaseJob::NoError - : BaseJob::IncorrectResponse, - error.errorString() }; -} - void BaseJob::gotReply() { checkReply(); -- cgit v1.2.3 From 51d27f3bcaacefda78dc033021b6a85152ab972e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 1 Jun 2020 14:53:59 +0200 Subject: GTAD parts: new home and format updates for GTAD 0.7 All GTAD-related files (gtad.yaml and templates) from now live in their dedicated gtad/ directory - this helps against removing them accidentally along with the rest of the generated files. The format to list generated files in gtad.yaml has changed a bit before GTAD 0.7 beta2; gtad.yaml in this commit conforms to the new structure. --- lib/csapi/gtad.yaml | 165 ------------------------------------ lib/csapi/preamble.mustache | 3 - lib/csapi/{{base}}.cpp.mustache | 182 ---------------------------------------- lib/csapi/{{base}}.h.mustache | 119 -------------------------- 4 files changed, 469 deletions(-) delete mode 100644 lib/csapi/gtad.yaml delete mode 100644 lib/csapi/preamble.mustache delete mode 100644 lib/csapi/{{base}}.cpp.mustache delete mode 100644 lib/csapi/{{base}}.h.mustache (limited to 'lib') diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml deleted file mode 100644 index 086bd4f7..00000000 --- a/lib/csapi/gtad.yaml +++ /dev/null @@ -1,165 +0,0 @@ -analyzer: - subst: - "%CLIENT_RELEASE_LABEL%": r0 - "%CLIENT_MAJOR_VERSION%": r0 - identifiers: - signed: signedData - unsigned: unsignedData - PushRule/default: isDefault - default: defaultVersion # getCapabilities/RoomVersionsCapability - origin_server_ts: originServerTimestamp # Instead of originServerTs - start: begin # Because start() is a method in BaseJob - m.upload.size: uploadSize - m.homeserver: homeserver - m.identity_server: identityServer - m.change_password: changePassword - m.room_versions: roomVersions - AuthenticationData/additionalProperties: authInfo - - # Structure inside `types`: - # - swaggerType: - # OR - # - swaggerType: - # - swaggerFormat: - # - /swaggerFormatRegEx/: - # - //: # default, if the format doesn't mach anything above - # WHERE - # targetTypeSpec = targetType OR - # { type: targetType, imports: , } - # swaggerType can be +set/+on pair; attributes from the map under +set - # are added to each type from the sequence under +on. - types: - - +set: &UseOmittable - useOmittable: - imports: [ '"converters.h"' ] - omittedValue: 'none' # See `none` in converters.h - +on: - - integer: - - int64: qint64 - - int32: qint32 - - //: int - - number: - - float: float - - //: double - - boolean: bool - - string: - - byte: &ByteStream - type: QIODevice* - imports: - - binary: *ByteStream - - +set: { avoidCopy: } - +on: - - date: - type: QDate - initializer: QDate::fromString("{{defaultValue}}") - imports: - - dateTime: - type: QDateTime - initializer: QDateTime::fromString("{{defaultValue}}") - imports: - - //: &QString - type: QString - initializer: QStringLiteral("{{defaultValue}}") - isString: - - file: *ByteStream - - +set: { avoidCopy: } - +on: - - object: &QJsonObject { type: QJsonObject, imports: } - - $ref: - - +set: { moveOnly: } - +on: - - /state_event.yaml$/: - { type: StateEventPtr, imports: '"events/eventloader.h"' } - - /room_event.yaml$/: - { type: RoomEventPtr, imports: '"events/eventloader.h"' } - - /event.yaml$/: - { type: EventPtr, imports: '"events/eventloader.h"' } - - /m\.room\.member$/: pass # This $ref is only used in an array, see below - - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types - - schema: # Properties of inline structure definitions - - TurnServerCredentials: *QJsonObject # Because it's used as is - - //: *UseOmittable - - array: - - string: QStringList - - +set: { moveOnly: } - +on: - - /^Notification|Result$/: - type: "std::vector<{{1}}>" - imports: '"events/eventloader.h"' - - /m\.room\.member$/: - type: "EventsArray" - imports: '"events/roommemberevent.h"' - - /state_event.yaml$/: StateEvents - - /room_event.yaml$/: RoomEvents - - /event.yaml$/: Events - - //: { type: "QVector<{{1}}>", imports: } - - map: # `additionalProperties` in OpenAPI - - RoomState: - type: "UnorderedMap" - moveOnly: - imports: '"util.h"' - - /.+/: - type: "QHash" - imports: - - //: - type: QVariantHash - imports: - - variant: # A sequence `type` (multitype) in OpenAPI - - /^string,null|null,string$/: *QString - - //: { type: QVariant, imports: } - - #operations: - -mustache: - constants: - # Syntax elements used by GTAD -# _quote: '"' # Common quote for left and right -# _leftQuote: '"' -# _rightQuote: '"' -# _joinChar: ',' # The character used by {{_join}} - not working yet - _comment: '//' - copyrightName: Kitsune Ral - copyrightEmail: - - partials: - _typeRenderer: "{{#scope}}{{scopeCamelCase}}Job::{{/scope}}{{>name}}" - omittedValue: '{}' # default value to initialize omitted parameters with - initializer: '{{defaultValue}}' - cjoin: '{{#hasMore}}, {{/hasMore}}' - - openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" - closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" - - maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" - qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" - - maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" - qualifiedMaybeCrefType: - "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" - - maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" - takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" - - initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" - joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' - joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' - passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' - - # Doc-comment blocks. Comment indent is managed by clang-format - # (without clang-format there'd have to be a separate partial definition - # for each indent...) - - # For structures that are not supposed to have a summary (e.g., JSON schema) - docCommentShort: |- - {{#description}} - /// {{_}}{{/description}} - docCommentSummary: |- - {{#summary}} \brief {{summary}} - *{{/summary}} - - templates: - - "{{base}}.h.mustache" - - "{{base}}.cpp.mustache" - - #outFilesList: apifiles.txt - diff --git a/lib/csapi/preamble.mustache b/lib/csapi/preamble.mustache deleted file mode 100644 index 3ba87d61..00000000 --- a/lib/csapi/preamble.mustache +++ /dev/null @@ -1,3 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache deleted file mode 100644 index b3bd4de9..00000000 --- a/lib/csapi/{{base}}.cpp.mustache +++ /dev/null @@ -1,182 +0,0 @@ -{{>preamble}} -#include "{{filenameBase}}.h" -{{^models}} -#include "converters.h"{{/models}} -{{#operations}} - {{#producesNonJson?}} -#include - {{/producesNonJson?}} -#include -{{/operations}} - -using namespace Quotient; - -{{#models.model}} - {{#in?}} -void JsonObjectConverter<{{qualifiedName}}>::dumpTo( - QJsonObject& jo, const {{qualifiedName}}& pod) -{ {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} -} - {{/in?}} - - {{#out?}} -void JsonObjectConverter<{{qualifiedName}}>::fillFrom( - {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) -{ {{#parents}} - fillFromJson<{{qualifiedName}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} -} - {{/out?}} - -{{/models.model}} -{{#operations}} - -static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); - {{#operation}}{{#models}} - -// Converters -namespace Quotient { - {{#model}} - -template <> struct JsonObjectConverter<{{qualifiedName}}> { - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) - { {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} - } - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) - { {{#parents}} - fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), - result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} - } - {{/out?}} -}; - {{/model}} - -} // namespace Quotient - {{/models}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -class {{camelCaseOperationId}}Job::Private -{ - public:{{#allProperties}} - {{>maybeOmittableType}} {{paramName}};{{/allProperties}} -}; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} - {{#queryParams?}} - -BaseJob::Query queryTo{{camelCaseOperationId}}( - {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) -{ - BaseJob::Query _q;{{#queryParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, - QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} - return _q; -} - {{/queryParams?}} - {{^bodyParams}} - -QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) -{ - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, - queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); -} {{/bodyParams}} - -{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( - {{#allParams}}{{>joinedParamDef}}{{/allParams}}) - : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, - QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} - basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} - {{#queryParams? - }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) - {{/queryParams? - }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) - {{#responses}}{{#normalResponse?}}{{#allProperties? - }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} -{ {{#headerParams}} - setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); - {{/headerParams}}{{#bodyParams?}} - {{#inlineBody}} - setRequestData(Data({{!avoid extra linebreaks}}{{ - #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ - ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? - }})); - {{/inlineBody}}{{^inlineBody}} - QJsonObject _data; - {{#bodyParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, - QStringLiteral("{{baseName}}"), {{paramName}}); - {{/bodyParams}} - setRequestData(_data); - {{/inlineBody}} - {{/bodyParams?}}{{#producesNonJson?}} - setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); - {{/producesNonJson?}} -} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; - {{#allProperties}} - -{{>qualifiedMaybeCrefType}} - {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} -{ - return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; -} - {{/allProperties}} - - {{#producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) -{ - {{! We don't check for required headers yet }} - {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); - {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} - return Success; -} - {{/producesNonJson?}}{{^producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) -{ - {{#inlineResponse}} - fromJson(data, d->{{paramName}}); - {{/inlineResponse}}{{^inlineResponse}} - auto json = data.object(); - {{#properties}}{{#required?}} - if (!json.contains("{{baseName}}"_ls)) - return { IncorrectResponse, - "The key '{{baseName}}' not found in the response" }; - {{/required?}} - fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); - {{/properties}} - {{/inlineResponse}} - - return Success; -} - {{/producesNonJson?}} - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -{{/operation}}{{/operations}} diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache deleted file mode 100644 index 404aafe8..00000000 --- a/lib/csapi/{{base}}.h.mustache +++ /dev/null @@ -1,119 +0,0 @@ -{{>preamble}} -#pragma once - -{{#operations}} -#include "jobs/basejob.h"{{/operations}} -{{#models}} -#include "converters.h"{{/models}} -{{#imports}} -#include {{_}}{{/imports}} - -namespace Quotient { -{{#models}} - -// Data structures - - {{#model}} -{{>docCommentShort}} -struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} -{ {{#vars}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}}{{#propertyMap}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} -}; - -template <> struct JsonObjectConverter<{{name}}> -{ - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{name}}& pod); - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); - {{/out?}} -}; - {{/model}} -{{/models}} -{{#operations}} - -// Operations - {{#operation}} - -/*!{{>docCommentSummary}}{{#description}} - * {{_}}{{/description}} - */ -class {{camelCaseOperationId}}Job : public BaseJob { -public: {{#models}} - // Inner data structures - {{#model}} - - {{>docCommentShort}} - struct {{name}}{{#parents?}} : - {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} - { - {{#vars}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}} - {{#propertyMap}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} - }; - {{/model}} - - // Construction/destruction - - {{/models}} - {{^allParams?}} - {{#summary}} - /// {{summary}} - {{/summary}} - {{/allParams?}}{{#allParams?}} - /*!{{>docCommentSummary}} - {{#allParams}} - * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}} - {{/allParams}} - */ - {{/allParams?}} - explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); - {{^bodyParams}} - - /*! \brief Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for {{camelCaseOperationId}}Job - * is necessary but the job itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); - {{/bodyParams}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - ~{{camelCaseOperationId}}Job() override; - - // Result properties - {{#allProperties}} - - {{>docCommentShort}} - {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; - {{/allProperties}} - -protected: {{#producesNonJson?}} - Status parseReply(QNetworkReply* reply) override; - {{/producesNonJson?}}{{^producesNonJson?}} - Status parseJson(const QJsonDocument& data) override; - {{/producesNonJson?}} - -private: - class Private; - QScopedPointer d; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -}; - {{/operation}} -{{/operations}} - -} // namespace Quotient -- cgit v1.2.3 From 79ede9bcb6d4fc5e269f506b2a10bfe4c302040a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 07:40:01 +0200 Subject: Connection::run(): Q_INVOKABLE and chaining --- lib/connection.cpp | 7 +++++-- lib/connection.h | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index efc7163e..f497601e 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1622,11 +1622,14 @@ void Connection::setLazyLoading(bool newValue) } } -void Connection::run(BaseJob* job, RunningPolicy runningPolicy) +BaseJob* Connection::run(BaseJob* job, RunningPolicy runningPolicy) { - job->setParent(this); // Protects from #397, #398 + // Reparent to protect from #397, #398 and to prevent BaseJob* from being + // garbage-collected if made by or returned to QML/JavaScript. + job->setParent(this); connect(job, &BaseJob::failure, this, &Connection::requestFailed); job->initiate(d->data.get(), runningPolicy & BackgroundRequest); + return job; } void Connection::getTurnServers() diff --git a/lib/connection.h b/lib/connection.h index 7998afa8..12b0756d 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -422,7 +422,8 @@ public: void setLazyLoading(bool newValue); /*! Start a pre-created job object on this connection */ - void run(BaseJob* job, RunningPolicy runningPolicy = ForegroundRequest); + Q_INVOKABLE BaseJob* run(BaseJob* job, + RunningPolicy runningPolicy = ForegroundRequest); /*! Start a job of a specified type with specified arguments and policy * -- cgit v1.2.3 From db8b9569f83ba3643c9005359ba556f2c3e357f4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 07:42:33 +0200 Subject: Use CBOR for binary JSON caching on Qt 5.15+ Qt 5.15 deprecates binary JSON format in favour of CBOR now used as a backend for its JSON classes. --- lib/connection.cpp | 25 +++++++++++++++++++++---- lib/syncdata.cpp | 12 ++++++++---- lib/syncdata.h | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index f497601e..26de0c5f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -48,6 +48,10 @@ # include "account.h" // QtOlm #endif // Quotient_E2EE_ENABLED +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) +# include +#endif + #include #include #include @@ -1492,9 +1496,16 @@ void Connection::saveRoomState(Room* r) const QFile outRoomFile { stateCacheDir().filePath( SyncData::fileNameForRoom(r->id())) }; if (outRoomFile.open(QFile::WriteOnly)) { +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto data = + d->cacheToBinary + ? QCborValue::fromJsonValue(r->toJson()).toCbor() + : QJsonDocument(r->toJson()).toJson(QJsonDocument::Compact); +#else QJsonDocument json { r->toJson() }; - auto data = d->cacheToBinary ? json.toBinaryData() - : json.toJson(QJsonDocument::Compact); + const auto data = d->cacheToBinary ? json.toBinaryData() + : json.toJson(QJsonDocument::Compact); +#endif outRoomFile.write(data.data(), data.size()); qCDebug(MAIN) << "Room state cache saved to" << outRoomFile.fileName(); } else { @@ -1558,9 +1569,15 @@ void Connection::saveState() const { QStringLiteral("events"), accountDataEvents } }); } +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + const auto data = + d->cacheToBinary ? QCborValue::fromJsonValue(rootObj).toCbor() + : QJsonDocument(rootObj).toJson(QJsonDocument::Compact); +#else QJsonDocument json { rootObj }; - auto data = d->cacheToBinary ? json.toBinaryData() - : json.toJson(QJsonDocument::Compact); + const auto data = d->cacheToBinary ? json.toBinaryData() + : json.toJson(QJsonDocument::Compact); +#endif qCDebug(PROFILER) << "Cache for" << userId() << "generated in" << et; outFile.write(data.data(), data.size()); diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index 6e68e2cd..a9ec5ee7 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -157,10 +157,14 @@ QJsonObject SyncData::loadJson(const QString& fileName) } auto data = roomFile.readAll(); - const auto json = (data.startsWith('{') - ? QJsonDocument::fromJson(data) - : QJsonDocument::fromBinaryData(data)) - .object(); + const auto json = data.startsWith('{') + ? QJsonDocument::fromJson(data).object() +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + : QCborValue::fromCbor(data).toJsonValue().toObject() +#else + : QJsonDocument::fromBinaryData(data).object() +#endif + ; if (json.isEmpty()) { qCWarning(MAIN) << "State cache in" << fileName << "is broken or empty, discarding"; diff --git a/lib/syncdata.h b/lib/syncdata.h index 41b8186e..752dd485 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -102,7 +102,7 @@ public: QStringList unresolvedRooms() const { return unresolvedRoomIds; } - static std::pair cacheVersion() { return { 10, 0 }; } + static std::pair cacheVersion() { return { 11, 0 }; } static QString fileNameForRoom(QString roomId); private: -- cgit v1.2.3 From ba3da1a570cefc43c5d78d3af716432a478cdd9d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 07:31:46 +0200 Subject: Avoid Qt 5.15 deprecation warning operator+() is no more wanted with iterators on associative containers. --- lib/room.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index fa6971c9..23e07cae 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1425,8 +1425,8 @@ QString Room::roomMembername(const User* u) const if (namesakesIt == d->membersMap.cend()) return u->fullName(this); - auto nextUserIt = namesakesIt + 1; - if (nextUserIt == d->membersMap.cend() || nextUserIt.key() != username) + auto nextUserIt = namesakesIt; + if (++nextUserIt == d->membersMap.cend() || nextUserIt.key() != username) return username; // No disambiguation necessary // Check if we can get away just attaching the bridge postfix -- cgit v1.2.3 From 04956392c4b1102ac9ae627c324493f035887472 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 18:09:52 +0200 Subject: BaseJob: expose statusCode as Q_PROPERTY --- lib/jobs/basejob.h | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 010aca78..6920ebc1 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -38,6 +38,7 @@ class BaseJob : public QObject { Q_OBJECT Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries) + Q_PROPERTY(int statusCode READ error NOTIFY statusChanged) public: /*! The status code of a job * -- cgit v1.2.3 From eccab6df7003bb9c20c48dfe0af57502383ba42e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 18:11:02 +0200 Subject: MediaThumbnailJob: be specific about the transform What's needed for a thumbnail is normally "scale", not "crop" (as these are defined in The Spec). --- lib/jobs/mediathumbnailjob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index 0a346392..df3763b2 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -31,7 +31,7 @@ QUrl MediaThumbnailJob::makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri, MediaThumbnailJob::MediaThumbnailJob(const QString& serverName, const QString& mediaId, QSize requestedSize) : GetContentThumbnailJob(serverName, mediaId, requestedSize.width(), - requestedSize.height()) + requestedSize.height(), "scale") {} MediaThumbnailJob::MediaThumbnailJob(const QUrl& mxcUri, QSize requestedSize) -- cgit v1.2.3 From 21c04d5b035cec0b6378e60acc93523f52c1c973 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 5 Jun 2020 18:09:12 +0200 Subject: BaseJob: jsonData() and prepareResult/Error() * JSON response is stored internally in BaseJob, rather than passed around virtual response handlers. This allow to lazily deserialise parts of the JSON response when the client calls for them instead of deserialising upon arrival and storing POD pieces. This is incompatible with the current generated code, so temporarily FTBFS. * BaseJob::loadFromJson() and BaseJob::takeFromJson() have been added to facilitate picking parts of the result as described above in derived job classes. * BaseJob::jsonData(), BaseJob::jsonItems() and (protected) BaseJob::reply() for direct access to the response in its various forms. * To further eliminate boilerplate code in generated job classes, a group of *ExpectedKeys() methods has been added - this allows to reflect the API definition of required response keys in a more "declarative" way, delegating validation to BaseJob. * parseReply() and parseJson() pair turns to singular prepareResult(). Thanks to all the changes above, in most cases it will not need overriding, unlike before. * BaseJob::Private::parseJson() is introduced, to wrap QJsonDocument::parseJson() into something less verbose. This serves a completely different purpose to the former BaseJob::parseJson(). * BaseJob::doCheckReply() takes the place, and the name, of checkReply(). --- lib/jobs/basejob.cpp | 164 ++++++++++++++++++++++++++++++----------- lib/jobs/basejob.h | 134 +++++++++++++++++++++++---------- lib/jobs/downloadfilejob.cpp | 4 +- lib/jobs/downloadfilejob.h | 4 +- lib/jobs/mediathumbnailjob.cpp | 6 +- lib/jobs/mediathumbnailjob.h | 2 +- lib/jobs/syncjob.cpp | 4 +- lib/jobs/syncjob.h | 2 +- 8 files changed, 226 insertions(+), 94 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 2519713e..3978dbcb 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -19,12 +19,11 @@ #include "basejob.h" #include "connectiondata.h" -#include "util.h" -#include #include #include #include +#include #include #include #include @@ -37,6 +36,7 @@ using namespace std::chrono_literals; BaseJob::StatusCode BaseJob::Status::fromHttpCode(int httpCode) { + // Based on https://en.wikipedia.org/wiki/List_of_HTTP_status_codes if (httpCode / 10 == 41) // 41x errors return httpCode == 410 ? IncorrectRequestError : NotFoundError; switch (httpCode) { @@ -113,6 +113,13 @@ public: } void sendRequest(); + /*! \brief Parse the response byte array into JSON + * + * This calls QJsonDocument::fromJson() on rawResponse, converts + * the QJsonParseError result to BaseJob::Status and stores the resulting + * JSON in jsonResponse. + */ + Status parseJson(); ConnectionData* connection = nullptr; @@ -131,9 +138,17 @@ public: // type QMimeType is of little help with MIME type globs (`text/*` etc.) QByteArrayList expectedContentTypes { "application/json" }; + QByteArrayList expectedKeys; + QScopedPointer reply; Status status = Unprepared; QByteArray rawResponse; + /// Contains a null document in case of non-JSON body (for a successful + /// or unsuccessful response); a document with QJsonObject or QJsonArray + /// in case of a successful response with JSON payload, as per the API + /// definition (including an empty JSON object - QJsonObject{}); + /// and QJsonObject in case of an API error. + QJsonDocument jsonResponse; QUrl errorUrl; //< May contain a URL to help with some errors LoggingCategory logCat = JOBS; @@ -243,6 +258,19 @@ void BaseJob::setExpectedContentTypes(const QByteArrayList& contentTypes) d->expectedContentTypes = contentTypes; } +const QByteArrayList BaseJob::expectedKeys() const { return d->expectedKeys; } + +void BaseJob::addExpectedKey(const QByteArray& key) { d->expectedKeys << key; } + +void BaseJob::setExpectedKeys(const QByteArrayList& keys) +{ + d->expectedKeys = keys; +} + +const QNetworkReply* BaseJob::reply() const { return d->reply.data(); } + +QNetworkReply* BaseJob::reply() { return d->reply.data(); } + QUrl BaseJob::makeRequestUrl(QUrl baseUrl, const QString& path, const QUrlQuery& query) { @@ -304,7 +332,7 @@ void BaseJob::doPrepare() { } void BaseJob::onSentRequest(QNetworkReply*) { } -void BaseJob::beforeAbandon(QNetworkReply*) { } +void BaseJob::beforeAbandon() { } void BaseJob::initiate(ConnectionData* connData, bool inBackground) { @@ -346,40 +374,74 @@ void BaseJob::sendRequest() emit aboutToSendRequest(); d->sendRequest(); Q_ASSERT(d->reply); - connect(d->reply.data(), &QNetworkReply::finished, this, &BaseJob::gotReply); + connect(reply(), &QNetworkReply::finished, this, [this] { + gotReply(); + finishJob(); + }); if (d->reply->isRunning()) { - connect(d->reply.data(), &QNetworkReply::metaDataChanged, this, - &BaseJob::checkReply); - connect(d->reply.data(), &QNetworkReply::uploadProgress, this, + connect(reply(), &QNetworkReply::metaDataChanged, this, + [this] { checkReply(reply()); }); + connect(reply(), &QNetworkReply::uploadProgress, this, &BaseJob::uploadProgress); - connect(d->reply.data(), &QNetworkReply::downloadProgress, this, + connect(reply(), &QNetworkReply::downloadProgress, this, &BaseJob::downloadProgress); d->timer.start(getCurrentTimeout()); qCInfo(d->logCat).noquote() << "Sent" << d->dumpRequest(); - onSentRequest(d->reply.data()); + onSentRequest(reply()); emit sentRequest(); } else qCCritical(d->logCat).noquote() << "Request could not start:" << d->dumpRequest(); } +BaseJob::Status BaseJob::Private::parseJson() +{ + QJsonParseError error { 0, QJsonParseError::MissingObject }; + jsonResponse = QJsonDocument::fromJson(rawResponse, &error); + return { error.error == QJsonParseError::NoError + ? BaseJob::NoError + : BaseJob::IncorrectResponse, + error.errorString() }; +} + void BaseJob::gotReply() { - checkReply(); + setStatus(checkReply(reply())); + + if (status().good() + && d->expectedContentTypes == QByteArrayList { "application/json" }) { + d->rawResponse = reply()->readAll(); + setStatus(d->parseJson()); + if (status().good() && !expectedKeys().empty()) { + const auto& responseObject = jsonData(); + QByteArrayList missingKeys; + for (const auto& k: expectedKeys()) + if (!responseObject.contains(k)) + missingKeys.push_back(k); + if (!missingKeys.empty()) + setStatus(IncorrectResponse, tr("Required JSON keys missing: ") + + missingKeys.join()); + } + if (!status().good()) // Bad JSON in a "good" reply: bail out + return; + } // else { + // If the endpoint expects anything else than just (API-related) JSON + // reply()->readAll() is not performed and the whole reply processing + // is left to derived job classes: they may read it piecemeal or customise + // per content type in prepareResult(), or even have read it already + // (see, e.g., DownloadFileJob). + // } + if (status().good()) - setStatus(parseReply(d->reply.data())); + setStatus(prepareResult()); else { - d->rawResponse = d->reply->readAll(); - const auto jsonBody = d->reply->rawHeader("Content-Type") - == "application/json"; + d->rawResponse = reply()->readAll(); qCDebug(d->logCat).noquote() - << "Error body (truncated if long):" << d->rawResponse.left(500); - if (jsonBody) - setStatus( - parseError(d->reply.data(), - QJsonDocument::fromJson(d->rawResponse).object())); + << "Error body (truncated if long):" << rawDataSample(500); + // Parse the error payload and update the status if needed + if (const auto newStatus = prepareError(); !newStatus.good()) + setStatus(newStatus); } - finishJob(); } bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) @@ -408,12 +470,10 @@ bool checkContentType(const QByteArray& type, const QByteArrayList& patterns) return false; } -BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const +BaseJob::Status BaseJob::checkReply(const QNetworkReply* reply) const { - // QNetworkReply error codes seem to be flawed when it comes to HTTP; - // see, e.g., https://github.com/quotient-im/libQuotient/issues/200 - // so check genuine HTTP codes. The below processing is based on - // https://en.wikipedia.org/wiki/List_of_HTTP_status_codes + // QNetworkReply error codes are insufficient for our purposes (e.g. they + // don't allow to discern HTTP code 429) so check the original code instead const auto httpCodeHeader = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (!httpCodeHeader.isValid()) { @@ -444,24 +504,23 @@ BaseJob::Status BaseJob::doCheckReply(QNetworkReply* reply) const return Status::fromHttpCode(httpCode, message); } -void BaseJob::checkReply() { setStatus(doCheckReply(d->reply.data())); } +BaseJob::Status BaseJob::prepareResult() { return Success; } -BaseJob::Status BaseJob::parseReply(QNetworkReply* reply) +BaseJob::Status BaseJob::prepareError() { - d->rawResponse = reply->readAll(); - QJsonParseError error { 0, QJsonParseError::MissingObject }; - const auto& json = QJsonDocument::fromJson(d->rawResponse, &error); - if (error.error == QJsonParseError::NoError) - return parseJson(json); + // Since it's an error, the expected content type is of no help; + // check the actually advertised content type instead + if (reply()->rawHeader("Content-Type") != "application/json") + return NoError; // Retain the status if the error payload is not JSON - return { IncorrectResponseError, error.errorString() }; -} + if (const auto status = d->parseJson(); !status.good()) + return status; -BaseJob::Status BaseJob::parseJson(const QJsonDocument&) { return Success; } + if (d->jsonResponse.isArray()) + return { IncorrectResponse, + tr("Malformed error JSON: an array instead of an object") }; -BaseJob::Status BaseJob::parseError(QNetworkReply* /*reply*/, - const QJsonObject& errorJson) -{ + const auto& errorJson = jsonData(); const auto errCode = errorJson.value("errcode"_ls).toString(); if (error() == TooManyRequestsError || errCode == "M_LIMIT_EXCEEDED") { QString msg = tr("Too many requests"); @@ -499,6 +558,16 @@ BaseJob::Status BaseJob::parseError(QNetworkReply* /*reply*/, return d->status; } +QJsonValue BaseJob::takeValueFromJson(const QString& key) +{ + if (!d->jsonResponse.isObject()) + return QJsonValue::Undefined; + auto o = d->jsonResponse.object(); + auto v = o.take(key); + d->jsonResponse.setObject(o); + return v; +} + void BaseJob::stop() { // This method is (also) used to semi-finalise the job before retrying; so @@ -616,10 +685,19 @@ QString BaseJob::rawDataSample(int bytesAtMost) const Q_ASSERT(data.size() <= d->rawResponse.size()); return data.size() == d->rawResponse.size() ? data - : data - + tr("...(truncated, %Ln bytes in total)", - "Comes after trimmed raw network response", - d->rawResponse.size()); + : data + tr("...(truncated, %Ln bytes in total)", + "Comes after trimmed raw network response", + d->rawResponse.size()); +} + +QJsonObject BaseJob::jsonData() const +{ + return d->jsonResponse.object(); +} + +QJsonArray BaseJob::jsonItems() const +{ + return d->jsonResponse.array(); } QString BaseJob::statusCaption() const @@ -704,7 +782,7 @@ void BaseJob::setStatus(int code, QString message) void BaseJob::abandon() { - beforeAbandon(d->reply ? d->reply.data() : nullptr); + beforeAbandon(); d->timer.stop(); d->retryTimer.stop(); // In case abandon() was called between retries setStatus(Abandoned); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index 6920ebc1..be2926be 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -18,13 +18,11 @@ #pragma once -#include "../logging.h" #include "requestdata.h" +#include "../logging.h" +#include "../converters.h" -#include #include -#include -#include class QNetworkReply; class QSslError; @@ -181,6 +179,49 @@ public: */ QString rawDataSample(int bytesAtMost = 65535) const; + /** Get the response body as a JSON object + * + * If the job's returned content type is not `application/json` + * or if the top-level JSON entity is not an object, an empty object + * is returned. + */ + QJsonObject jsonData() const; + + /** Get the response body as a JSON array + * + * If the job's returned content type is not `application/json` + * or if the top-level JSON entity is not an array, an empty array + * is returned. + */ + QJsonArray jsonItems() const; + + /** Load the property from the JSON response assuming a given C++ type + * + * If there's no top-level JSON object in the response or if there's + * no node with the key \p keyName, \p defaultValue is returned. + */ + template // Waiting for QStringViews... + T loadFromJson(const StrT& keyName, T&& defaultValue = {}) const + { + const auto& jv = jsonData().value(keyName); + return jv.isUndefined() ? std::forward(defaultValue) + : fromJson(jv); + } + + /** Load the property from the JSON response and delete it from JSON + * + * If there's no top-level JSON object in the response or if there's + * no node with the key \p keyName, \p defaultValue is returned. + */ + template + T takeFromJson(const QString& key, T&& defaultValue = {}) + { + if (const auto& jv = takeValueFromJson(key); !jv.isUndefined()) + return fromJson(jv); + + return std::forward(defaultValue); + } + /** Error (more generally, status) code * Equivalent to status().code * \sa status @@ -314,6 +355,12 @@ protected: const QByteArrayList& expectedContentTypes() const; void addExpectedContentType(const QByteArray& contentType); void setExpectedContentTypes(const QByteArrayList& contentTypes); + const QByteArrayList expectedKeys() const; + void addExpectedKey(const QByteArray &key); + void setExpectedKeys(const QByteArrayList &keys); + + const QNetworkReply* reply() const; + QNetworkReply* reply(); /** Construct a URL out of baseUrl, path and query * @@ -338,50 +385,42 @@ protected: * successfully sending a network request (including retries). */ virtual void onSentRequest(QNetworkReply*); - virtual void beforeAbandon(QNetworkReply*); + virtual void beforeAbandon(); - /*! \brief Check the pending or received reply for upfront issues + /*! \brief An extension point for additional reply processing. * - * This is invoked when headers are first received and also once - * the complete reply is obtained; the base implementation checks the HTTP - * headers to detect general issues such as network errors or access denial. - * It cannot read the response body (use parseReply/parseError to check - * for problems in the body). Returning anything except NoError/Success - * prevents further processing of the reply. - * - * @return the result of checking the reply + * The base implementation does nothing and returns Success. * - * @see gotReply + * \sa gotReply */ - virtual Status doCheckReply(QNetworkReply* reply) const; + virtual Status prepareResult(); - /** - * 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 + /*! \brief Process details of the error * - * @see gotReply, parseJson + * The function processes the reply in case when status from checkReply() + * was not good (usually because of an unsuccessful HTTP code). + * The base implementation assumes Matrix JSON error object in the body; + * overrides are strongly recommended to call it for all stock Matrix + * responses as early as possible but in addition can process custom errors, + * with JSON or non-JSON payload. */ - virtual Status parseReply(QNetworkReply* reply); + virtual Status prepareError(); - /** - * Processes the JSON document received from the Matrix server. - * By default returns successful status without analysing the JSON. + /*! \brief Get direct access to the JSON response object in the job * - * @param json valid JSON document received from the server + * This allows to implement deserialisation with "move" semantics for parts + * of the response. Assuming that the response body is a valid JSON object, + * the function calls QJsonObject::take(key) on it and returns the result. * - * @see parseReply - */ - virtual Status parseJson(const QJsonDocument&); - - /** - * Processes the reply in case of unsuccessful HTTP code. - * The body is already loaded from the reply object to errorJson. - * @param reply the HTTP reply from the server - * @param errorJson the JSON payload describing the error + * \return QJsonValue::Null, if the response content type is not + * advertised as `application/json`; + * QJsonValue::Undefined, if the response is a JSON object but + * doesn't have \p key; + * the value for \p key otherwise. + * + * \sa takeFromJson */ - virtual Status parseError(QNetworkReply*, const QJsonObject& errorJson); + QJsonValue takeValueFromJson(const QString& key); void setStatus(Status s); void setStatus(int code, QString message); @@ -397,9 +436,28 @@ protected: protected slots: void timeout(); + /*! \brief Check the pending or received reply for upfront issues + * + * This is invoked when headers are first received and also once + * the complete reply is obtained; the base implementation checks the HTTP + * headers to detect general issues such as network errors or access denial + * and it's strongly recommended to call it from overrides, + * as early as possible. + * This slot is const and cannot read the response body. If you need to read + * the body on the fly, override onSentRequest() and connect in it + * to reply->readyRead(); and if you only need to validate the body after + * it fully arrived, use prepareResult() for that). Returning anything + * except NoError/Success switches further processing from prepareResult() + * to prepareError(). + * + * @return the result of checking the reply + * + * @see gotReply + */ + virtual Status checkReply(const QNetworkReply *reply) const; + private slots: void sendRequest(); - void checkReply(); void gotReply(); friend class ConnectionData; // to provide access to sendRequest() diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 3e037680..7b4cf690 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -86,14 +86,14 @@ void DownloadFileJob::onSentRequest(QNetworkReply* reply) }); } -void DownloadFileJob::beforeAbandon(QNetworkReply*) +void DownloadFileJob::beforeAbandon() { if (d->targetFile) d->targetFile->remove(); d->tempFile->remove(); } -BaseJob::Status DownloadFileJob::parseReply(QNetworkReply*) +BaseJob::Status DownloadFileJob::prepareResult() { if (d->targetFile) { d->targetFile->close(); diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index 06dc145c..e00fd9e4 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -19,7 +19,7 @@ private: void doPrepare() override; void onSentRequest(QNetworkReply* reply) override; - void beforeAbandon(QNetworkReply*) override; - Status parseReply(QNetworkReply*) override; + void beforeAbandon() override; + Status prepareResult() override; }; } // namespace Quotient diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index df3763b2..33f4236c 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -48,12 +48,8 @@ QImage MediaThumbnailJob::scaledThumbnail(QSize toSize) const Qt::SmoothTransformation); } -BaseJob::Status MediaThumbnailJob::parseReply(QNetworkReply* reply) +BaseJob::Status MediaThumbnailJob::prepareResult() { - auto result = GetContentThumbnailJob::parseReply(reply); - if (!result.good()) - return result; - if (_thumbnail.loadFromData(data()->readAll())) return Success; diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index 75e2e55a..e6d39085 100644 --- a/lib/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h @@ -37,7 +37,7 @@ public: QImage scaledThumbnail(QSize toSize) const; protected: - Status parseReply(QNetworkReply* reply) override; + Status prepareResult() override; private: QImage _thumbnail; diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index cd7709e1..6b8cfe4b 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -49,9 +49,9 @@ SyncJob::SyncJob(const QString& since, const Filter& filter, int timeout, timeout, presence) {} -BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) +BaseJob::Status SyncJob::prepareResult() { - d.parseJson(data.object()); + d.parseJson(jsonData()); if (d.unresolvedRooms().isEmpty()) return BaseJob::Success; diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index df419ba8..bf139a7b 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -33,7 +33,7 @@ public: SyncData&& takeData() { return std::move(d); } protected: - Status parseJson(const QJsonDocument& data) override; + Status prepareResult() override; private: SyncData d; -- cgit v1.2.3 From 2795c514f283fb274c8e70e81b04be3d02703fe7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 29 May 2020 14:31:57 +0200 Subject: util.cpp: drop OptimizeOnFirstUsage option on newer Qt Qt 5.12+ always optimise QRegularExpression on first usage. --- lib/util.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 4cbebfe2..61661de8 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -28,7 +28,9 @@ static const auto RegExpOptions = QRegularExpression::CaseInsensitiveOption - | QRegularExpression::OptimizeOnFirstUsageOption +#if QT_VERSION < QT_VERSION_CHECK(5, 12, 0) + | QRegularExpression::OptimizeOnFirstUsageOption // Default since 5.12 +#endif | QRegularExpression::UseUnicodePropertiesOption; // Converts all that looks like a URL into HTML links -- cgit v1.2.3 From f6faea5971d94627da67dc1442504da07374958d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jun 2020 13:54:23 +0200 Subject: AccountSettins: use perfect forwarding --- lib/settings.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/settings.h b/lib/settings.h index 5a097285..c45764a6 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -25,6 +25,7 @@ class QVariant; namespace Quotient { + class Settings : public QSettings { Q_OBJECT public: @@ -151,8 +152,9 @@ class AccountSettings : public SettingsGroup { WRITE setEncryptionAccountPickle) public: template - explicit AccountSettings(const QString& accountId, ArgTs... qsettingsArgs) - : SettingsGroup("Accounts/" + accountId, qsettingsArgs...) + explicit AccountSettings(const QString& accountId, ArgTs&&... qsettingsArgs) + : SettingsGroup("Accounts/" + accountId, + std::forward(qsettingsArgs)...) {} QString userId() const; -- cgit v1.2.3 From e1f5d0aa2c33e6da6c3a609c8bc7e0b5867e748d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jun 2020 16:19:55 +0200 Subject: SsoSession: let Private be a class everywhere Fixes a compilation warning that Private is a struct in one place and a class in another. --- lib/ssosession.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/ssosession.cpp b/lib/ssosession.cpp index 0f8f96e1..be701204 100644 --- a/lib/ssosession.cpp +++ b/lib/ssosession.cpp @@ -10,7 +10,8 @@ using namespace Quotient; -struct SsoSession::Private { +class SsoSession::Private { +public: Private(SsoSession* q, const QString& initialDeviceName = {}, const QString& deviceId = {}, Connection* connection = nullptr) : initialDeviceName(initialDeviceName) @@ -38,7 +39,7 @@ struct SsoSession::Private { processCallback(); }); QObject::connect(socket, &QTcpSocket::disconnected, socket, - [this] { socket->deleteLater(); }); + &QTcpSocket::deleteLater); }); } void processCallback(); -- cgit v1.2.3 From 32729d9a7519cd2c4cddb0174b8329c6fd4a4a83 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 7 Jun 2020 19:46:40 +0200 Subject: Update generated files according to gtad/* changes --- lib/application-service/definitions/location.cpp | 23 -- lib/application-service/definitions/location.h | 18 +- lib/application-service/definitions/protocol.cpp | 59 --- lib/application-service/definitions/protocol.h | 53 ++- lib/application-service/definitions/user.cpp | 23 -- lib/application-service/definitions/user.h | 18 +- lib/csapi/account-data.cpp | 27 +- lib/csapi/account-data.h | 16 +- lib/csapi/admin.cpp | 66 +--- lib/csapi/admin.h | 48 ++- lib/csapi/administrative_contact.cpp | 174 +++------ lib/csapi/administrative_contact.h | 332 ++++++++++------ lib/csapi/appservice_room_directory.cpp | 9 +- lib/csapi/appservice_room_directory.h | 5 +- lib/csapi/banning.cpp | 13 +- lib/csapi/banning.h | 7 +- lib/csapi/capabilities.cpp | 68 +--- lib/csapi/capabilities.h | 46 ++- lib/csapi/content-repo.cpp | 198 ++-------- lib/csapi/content-repo.h | 166 ++++---- lib/csapi/create_room.cpp | 55 +-- lib/csapi/create_room.h | 118 ++++-- lib/csapi/definitions/auth_data.cpp | 23 -- lib/csapi/definitions/auth_data.h | 20 +- lib/csapi/definitions/client_device.cpp | 23 -- lib/csapi/definitions/client_device.h | 20 +- lib/csapi/definitions/device_keys.cpp | 27 -- lib/csapi/definitions/device_keys.h | 23 +- lib/csapi/definitions/event_filter.cpp | 27 -- lib/csapi/definitions/event_filter.h | 20 +- lib/csapi/definitions/openid_token.h | 48 +++ lib/csapi/definitions/public_rooms_response.cpp | 56 --- lib/csapi/definitions/public_rooms_response.h | 51 ++- lib/csapi/definitions/push_condition.cpp | 25 -- lib/csapi/definitions/push_condition.h | 25 +- lib/csapi/definitions/push_rule.cpp | 28 -- lib/csapi/definitions/push_rule.h | 26 +- lib/csapi/definitions/push_ruleset.cpp | 27 -- lib/csapi/definitions/push_ruleset.h | 22 +- lib/csapi/definitions/request_email_validation.h | 48 +++ lib/csapi/definitions/request_msisdn_validation.h | 48 +++ lib/csapi/definitions/request_token_response.h | 45 +++ lib/csapi/definitions/room_event_filter.cpp | 25 -- lib/csapi/definitions/room_event_filter.h | 38 +- lib/csapi/definitions/sync_filter.cpp | 68 ---- lib/csapi/definitions/sync_filter.h | 97 ++--- lib/csapi/definitions/third_party_signed.h | 44 +++ lib/csapi/definitions/user_identifier.cpp | 21 - lib/csapi/definitions/user_identifier.h | 17 +- lib/csapi/definitions/wellknown/full.cpp | 24 -- lib/csapi/definitions/wellknown/full.h | 21 +- lib/csapi/definitions/wellknown/homeserver.cpp | 19 - lib/csapi/definitions/wellknown/homeserver.h | 13 +- .../definitions/wellknown/identity_server.cpp | 19 - lib/csapi/definitions/wellknown/identity_server.h | 13 +- lib/csapi/device_management.cpp | 62 +-- lib/csapi/device_management.h | 36 +- lib/csapi/directory.cpp | 59 ++- lib/csapi/directory.h | 74 +++- lib/csapi/event_context.cpp | 67 +--- lib/csapi/event_context.h | 52 ++- lib/csapi/filter.cpp | 55 +-- lib/csapi/filter.h | 35 +- lib/csapi/inviting.cpp | 9 +- lib/csapi/inviting.h | 4 +- lib/csapi/joining.cpp | 100 +---- lib/csapi/joining.h | 121 ++---- lib/csapi/keys.cpp | 153 +------- lib/csapi/keys.h | 112 +++--- lib/csapi/kicking.cpp | 8 +- lib/csapi/kicking.h | 5 +- lib/csapi/leaving.cpp | 16 +- lib/csapi/leaving.h | 4 +- lib/csapi/list_joined_rooms.cpp | 33 +- lib/csapi/list_joined_rooms.h | 15 +- lib/csapi/list_public_rooms.cpp | 104 +---- lib/csapi/list_public_rooms.h | 124 ++++-- lib/csapi/login.cpp | 94 +---- lib/csapi/login.h | 83 ++-- lib/csapi/logout.cpp | 17 +- lib/csapi/logout.h | 9 +- lib/csapi/message_pagination.cpp | 47 +-- lib/csapi/message_pagination.h | 41 +- lib/csapi/notifications.cpp | 61 +-- lib/csapi/notifications.h | 38 +- lib/csapi/openid.cpp | 55 +-- lib/csapi/openid.h | 38 +- lib/csapi/peeking_events.cpp | 41 +- lib/csapi/peeking_events.h | 21 +- lib/csapi/presence.cpp | 54 +-- lib/csapi/presence.h | 30 +- lib/csapi/profile.cpp | 97 +---- lib/csapi/profile.h | 47 +-- lib/csapi/pusher.cpp | 79 +--- lib/csapi/pusher.h | 58 ++- lib/csapi/pushrules.cpp | 143 ++----- lib/csapi/pushrules.h | 90 +++-- lib/csapi/read_markers.cpp | 9 +- lib/csapi/read_markers.h | 5 +- lib/csapi/receipts.cpp | 8 +- lib/csapi/receipts.h | 8 +- lib/csapi/redaction.cpp | 27 +- lib/csapi/redaction.h | 23 +- lib/csapi/registration.cpp | 233 ++--------- lib/csapi/registration.h | 428 ++++++++++----------- lib/csapi/report_content.cpp | 9 +- lib/csapi/report_content.h | 8 +- lib/csapi/room_send.cpp | 26 +- lib/csapi/room_send.h | 19 +- lib/csapi/room_state.cpp | 53 +-- lib/csapi/room_state.h | 96 ++--- lib/csapi/room_upgrades.cpp | 34 +- lib/csapi/room_upgrades.h | 18 +- lib/csapi/rooms.cpp | 158 ++------ lib/csapi/rooms.h | 129 +++---- lib/csapi/sso_login_redirect.cpp | 12 +- lib/csapi/sso_login_redirect.h | 3 +- lib/csapi/tags.cpp | 65 +--- lib/csapi/tags.h | 42 +- lib/csapi/third_party_lookup.cpp | 166 ++------ lib/csapi/third_party_lookup.h | 92 ++--- lib/csapi/third_party_membership.cpp | 11 +- lib/csapi/third_party_membership.h | 21 +- lib/csapi/to_device.cpp | 9 +- lib/csapi/to_device.h | 8 +- lib/csapi/typing.cpp | 9 +- lib/csapi/typing.h | 8 +- lib/csapi/users.cpp | 57 +-- lib/csapi/users.h | 42 +- lib/csapi/versions.cpp | 38 +- lib/csapi/versions.h | 24 +- lib/csapi/voip.cpp | 26 +- lib/csapi/voip.h | 14 +- lib/csapi/wellknown.cpp | 26 +- lib/csapi/wellknown.h | 17 +- lib/csapi/whoami.cpp | 30 +- lib/csapi/whoami.h | 12 +- .../definitions/request_email_validation.cpp | 25 -- .../definitions/request_email_validation.h | 26 +- .../definitions/request_msisdn_validation.cpp | 27 -- .../definitions/request_msisdn_validation.h | 29 +- lib/identity/definitions/sid.cpp | 17 - lib/identity/definitions/sid.h | 27 -- 143 files changed, 2564 insertions(+), 4594 deletions(-) delete mode 100644 lib/application-service/definitions/location.cpp delete mode 100644 lib/application-service/definitions/protocol.cpp delete mode 100644 lib/application-service/definitions/user.cpp delete mode 100644 lib/csapi/definitions/auth_data.cpp delete mode 100644 lib/csapi/definitions/client_device.cpp delete mode 100644 lib/csapi/definitions/device_keys.cpp delete mode 100644 lib/csapi/definitions/event_filter.cpp create mode 100644 lib/csapi/definitions/openid_token.h delete mode 100644 lib/csapi/definitions/public_rooms_response.cpp delete mode 100644 lib/csapi/definitions/push_condition.cpp delete mode 100644 lib/csapi/definitions/push_rule.cpp delete mode 100644 lib/csapi/definitions/push_ruleset.cpp create mode 100644 lib/csapi/definitions/request_email_validation.h create mode 100644 lib/csapi/definitions/request_msisdn_validation.h create mode 100644 lib/csapi/definitions/request_token_response.h delete mode 100644 lib/csapi/definitions/room_event_filter.cpp delete mode 100644 lib/csapi/definitions/sync_filter.cpp create mode 100644 lib/csapi/definitions/third_party_signed.h delete mode 100644 lib/csapi/definitions/user_identifier.cpp delete mode 100644 lib/csapi/definitions/wellknown/full.cpp delete mode 100644 lib/csapi/definitions/wellknown/homeserver.cpp delete mode 100644 lib/csapi/definitions/wellknown/identity_server.cpp delete mode 100644 lib/identity/definitions/request_email_validation.cpp delete mode 100644 lib/identity/definitions/request_msisdn_validation.cpp delete mode 100644 lib/identity/definitions/sid.cpp delete mode 100644 lib/identity/definitions/sid.h (limited to 'lib') diff --git a/lib/application-service/definitions/location.cpp b/lib/application-service/definitions/location.cpp deleted file mode 100644 index 0a054029..00000000 --- a/lib/application-service/definitions/location.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "location.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const ThirdPartyLocation& pod) -{ - addParam<>(jo, QStringLiteral("alias"), pod.alias); - addParam<>(jo, QStringLiteral("protocol"), pod.protocol); - addParam<>(jo, QStringLiteral("fields"), pod.fields); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - ThirdPartyLocation& result) -{ - fromJson(jo.value("alias"_ls), result.alias); - fromJson(jo.value("protocol"_ls), result.protocol); - fromJson(jo.value("fields"_ls), result.fields); -} diff --git a/lib/application-service/definitions/location.h b/lib/application-service/definitions/location.h index e4c2d096..6801c99f 100644 --- a/lib/application-service/definitions/location.h +++ b/lib/application-service/definitions/location.h @@ -6,12 +6,8 @@ #include "converters.h" -#include - namespace Quotient { -// Data structures - struct ThirdPartyLocation { /// An alias for a matrix room. QString alias; @@ -25,8 +21,18 @@ struct ThirdPartyLocation { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod); - static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod); + static void dumpTo(QJsonObject& jo, const ThirdPartyLocation& pod) + { + addParam<>(jo, QStringLiteral("alias"), pod.alias); + addParam<>(jo, QStringLiteral("protocol"), pod.protocol); + addParam<>(jo, QStringLiteral("fields"), pod.fields); + } + static void fillFrom(const QJsonObject& jo, ThirdPartyLocation& pod) + { + fromJson(jo.value("alias"_ls), pod.alias); + fromJson(jo.value("protocol"_ls), pod.protocol); + fromJson(jo.value("fields"_ls), pod.fields); + } }; } // namespace Quotient diff --git a/lib/application-service/definitions/protocol.cpp b/lib/application-service/definitions/protocol.cpp deleted file mode 100644 index 8c66aa4d..00000000 --- a/lib/application-service/definitions/protocol.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "protocol.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const FieldType& pod) -{ - addParam<>(jo, QStringLiteral("regexp"), pod.regexp); - addParam<>(jo, QStringLiteral("placeholder"), pod.placeholder); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - FieldType& result) -{ - fromJson(jo.value("regexp"_ls), result.regexp); - fromJson(jo.value("placeholder"_ls), result.placeholder); -} - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const ProtocolInstance& pod) -{ - addParam<>(jo, QStringLiteral("desc"), pod.desc); - addParam(jo, QStringLiteral("icon"), pod.icon); - addParam<>(jo, QStringLiteral("fields"), pod.fields); - addParam<>(jo, QStringLiteral("network_id"), pod.networkId); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - ProtocolInstance& result) -{ - fromJson(jo.value("desc"_ls), result.desc); - fromJson(jo.value("icon"_ls), result.icon); - fromJson(jo.value("fields"_ls), result.fields); - fromJson(jo.value("network_id"_ls), result.networkId); -} - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const ThirdPartyProtocol& pod) -{ - addParam<>(jo, QStringLiteral("user_fields"), pod.userFields); - addParam<>(jo, QStringLiteral("location_fields"), pod.locationFields); - addParam<>(jo, QStringLiteral("icon"), pod.icon); - addParam<>(jo, QStringLiteral("field_types"), pod.fieldTypes); - addParam<>(jo, QStringLiteral("instances"), pod.instances); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - ThirdPartyProtocol& result) -{ - fromJson(jo.value("user_fields"_ls), result.userFields); - fromJson(jo.value("location_fields"_ls), result.locationFields); - fromJson(jo.value("icon"_ls), result.icon); - fromJson(jo.value("field_types"_ls), result.fieldTypes); - fromJson(jo.value("instances"_ls), result.instances); -} diff --git a/lib/application-service/definitions/protocol.h b/lib/application-service/definitions/protocol.h index ac1e50e2..6aee9c57 100644 --- a/lib/application-service/definitions/protocol.h +++ b/lib/application-service/definitions/protocol.h @@ -6,14 +6,7 @@ #include "converters.h" -#include -#include -#include - namespace Quotient { - -// Data structures - /// Definition of valid values for a field. struct FieldType { /// A regular expression for validation of a field's value. This may be @@ -27,8 +20,16 @@ struct FieldType { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const FieldType& pod); - static void fillFrom(const QJsonObject& jo, FieldType& pod); + static void dumpTo(QJsonObject& jo, const FieldType& pod) + { + addParam<>(jo, QStringLiteral("regexp"), pod.regexp); + addParam<>(jo, QStringLiteral("placeholder"), pod.placeholder); + } + static void fillFrom(const QJsonObject& jo, FieldType& pod) + { + fromJson(jo.value("regexp"_ls), pod.regexp); + fromJson(jo.value("placeholder"_ls), pod.placeholder); + } }; struct ProtocolInstance { @@ -48,8 +49,20 @@ struct ProtocolInstance { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod); - static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod); + static void dumpTo(QJsonObject& jo, const ProtocolInstance& pod) + { + addParam<>(jo, QStringLiteral("desc"), pod.desc); + addParam(jo, QStringLiteral("icon"), pod.icon); + addParam<>(jo, QStringLiteral("fields"), pod.fields); + addParam<>(jo, QStringLiteral("network_id"), pod.networkId); + } + static void fillFrom(const QJsonObject& jo, ProtocolInstance& pod) + { + fromJson(jo.value("desc"_ls), pod.desc); + fromJson(jo.value("icon"_ls), pod.icon); + fromJson(jo.value("fields"_ls), pod.fields); + fromJson(jo.value("network_id"_ls), pod.networkId); + } }; struct ThirdPartyProtocol { @@ -84,8 +97,22 @@ struct ThirdPartyProtocol { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod); - static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod); + static void dumpTo(QJsonObject& jo, const ThirdPartyProtocol& pod) + { + addParam<>(jo, QStringLiteral("user_fields"), pod.userFields); + addParam<>(jo, QStringLiteral("location_fields"), pod.locationFields); + addParam<>(jo, QStringLiteral("icon"), pod.icon); + addParam<>(jo, QStringLiteral("field_types"), pod.fieldTypes); + addParam<>(jo, QStringLiteral("instances"), pod.instances); + } + static void fillFrom(const QJsonObject& jo, ThirdPartyProtocol& pod) + { + fromJson(jo.value("user_fields"_ls), pod.userFields); + fromJson(jo.value("location_fields"_ls), pod.locationFields); + fromJson(jo.value("icon"_ls), pod.icon); + fromJson(jo.value("field_types"_ls), pod.fieldTypes); + fromJson(jo.value("instances"_ls), pod.instances); + } }; } // namespace Quotient diff --git a/lib/application-service/definitions/user.cpp b/lib/application-service/definitions/user.cpp deleted file mode 100644 index 17d15a20..00000000 --- a/lib/application-service/definitions/user.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "user.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const ThirdPartyUser& pod) -{ - addParam<>(jo, QStringLiteral("userid"), pod.userid); - addParam<>(jo, QStringLiteral("protocol"), pod.protocol); - addParam<>(jo, QStringLiteral("fields"), pod.fields); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - ThirdPartyUser& result) -{ - fromJson(jo.value("userid"_ls), result.userid); - fromJson(jo.value("protocol"_ls), result.protocol); - fromJson(jo.value("fields"_ls), result.fields); -} diff --git a/lib/application-service/definitions/user.h b/lib/application-service/definitions/user.h index 0d1984b6..3342ef80 100644 --- a/lib/application-service/definitions/user.h +++ b/lib/application-service/definitions/user.h @@ -6,12 +6,8 @@ #include "converters.h" -#include - namespace Quotient { -// Data structures - struct ThirdPartyUser { /// A Matrix User ID represting a third party user. QString userid; @@ -25,8 +21,18 @@ struct ThirdPartyUser { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const ThirdPartyUser& pod); - static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod); + static void dumpTo(QJsonObject& jo, const ThirdPartyUser& pod) + { + addParam<>(jo, QStringLiteral("userid"), pod.userid); + addParam<>(jo, QStringLiteral("protocol"), pod.protocol); + addParam<>(jo, QStringLiteral("fields"), pod.fields); + } + static void fillFrom(const QJsonObject& jo, ThirdPartyUser& pod) + { + fromJson(jo.value("userid"_ls), pod.userid); + fromJson(jo.value("protocol"_ls), pod.protocol); + fromJson(jo.value("fields"_ls), pod.fields); + } }; } // namespace Quotient diff --git a/lib/csapi/account-data.cpp b/lib/csapi/account-data.cpp index b7825718..6a40e908 100644 --- a/lib/csapi/account-data.cpp +++ b/lib/csapi/account-data.cpp @@ -4,18 +4,15 @@ #include "account-data.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SetAccountDataJob::SetAccountDataJob(const QString& userId, const QString& type, const QJsonObject& content) : BaseJob(HttpVerb::Put, QStringLiteral("SetAccountDataJob"), - basePath % "/user/" % userId % "/account_data/" % type) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/account_data/" % type) { setRequestData(Data(toJson(content))); } @@ -24,13 +21,14 @@ QUrl GetAccountDataJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& type) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId - % "/account_data/" % type); + QStringLiteral("/_matrix/client/r0") % "/user/" + % userId % "/account_data/" % type); } GetAccountDataJob::GetAccountDataJob(const QString& userId, const QString& type) : BaseJob(HttpVerb::Get, QStringLiteral("GetAccountDataJob"), - basePath % "/user/" % userId % "/account_data/" % type) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/account_data/" % type) {} SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, @@ -38,8 +36,8 @@ SetAccountDataPerRoomJob::SetAccountDataPerRoomJob(const QString& userId, const QString& type, const QJsonObject& content) : BaseJob(HttpVerb::Put, QStringLiteral("SetAccountDataPerRoomJob"), - basePath % "/user/" % userId % "/rooms/" % roomId - % "/account_data/" % type) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/rooms/" % roomId % "/account_data/" % type) { setRequestData(Data(toJson(content))); } @@ -50,14 +48,15 @@ QUrl GetAccountDataPerRoomJob::makeRequestUrl(QUrl baseUrl, const QString& type) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId % "/rooms/" - % roomId % "/account_data/" % type); + QStringLiteral("/_matrix/client/r0") + % "/user/" % userId % "/rooms/" % roomId + % "/account_data/" % type); } GetAccountDataPerRoomJob::GetAccountDataPerRoomJob(const QString& userId, const QString& roomId, const QString& type) : BaseJob(HttpVerb::Get, QStringLiteral("GetAccountDataPerRoomJob"), - basePath % "/user/" % userId % "/rooms/" % roomId - % "/account_data/" % type) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/rooms/" % roomId % "/account_data/" % type) {} diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index a8dba5c0..1c7b555b 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -6,12 +6,8 @@ #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Set some account_data for the user. * * Set some account_data for the client. This config is only visible to the user @@ -21,13 +17,16 @@ namespace Quotient { class SetAccountDataJob : public BaseJob { public: /*! \brief Set some account_data for the user. + * * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. + * * \param type * The event type of the account_data to set. Custom types should be * namespaced to avoid clashes. + * * \param content * The content of the account_data */ @@ -43,10 +42,12 @@ public: class GetAccountDataJob : public BaseJob { public: /*! \brief Get some account_data for the user. + * * * \param userId * The ID of the user to get account_data for. The access token must be * authorized to make requests for this user ID. + * * \param type * The event type of the account_data to get. Custom types should be * namespaced to avoid clashes. @@ -71,15 +72,19 @@ public: class SetAccountDataPerRoomJob : public BaseJob { public: /*! \brief Set some account_data for the user. + * * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. + * * \param roomId * The ID of the room to set account_data on. + * * \param type * The event type of the account_data to set. Custom types should be * namespaced to avoid clashes. + * * \param content * The content of the account_data */ @@ -96,12 +101,15 @@ public: class GetAccountDataPerRoomJob : public BaseJob { public: /*! \brief Get some account_data for the user. + * * * \param userId * The ID of the user to set account_data for. The access token must be * authorized to make requests for this user ID. + * * \param roomId * The ID of the room to get account_data for. + * * \param type * The event type of the account_data to get. Custom types should be * namespaced to avoid clashes. diff --git a/lib/csapi/admin.cpp b/lib/csapi/admin.cpp index 14173e94..9619c441 100644 --- a/lib/csapi/admin.cpp +++ b/lib/csapi/admin.cpp @@ -4,78 +4,18 @@ #include "admin.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetWhoIsJob::ConnectionInfo& result) - { - fromJson(jo.value("ip"_ls), result.ip); - fromJson(jo.value("last_seen"_ls), result.lastSeen); - fromJson(jo.value("user_agent"_ls), result.userAgent); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, GetWhoIsJob::SessionInfo& result) - { - fromJson(jo.value("connections"_ls), result.connections); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, GetWhoIsJob::DeviceInfo& result) - { - fromJson(jo.value("sessions"_ls), result.sessions); - } -}; - -} // namespace Quotient - -class GetWhoIsJob::Private { -public: - QString userId; - QHash devices; -}; - QUrl GetWhoIsJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/admin/whois/" % userId); + QStringLiteral("/_matrix/client/r0") + % "/admin/whois/" % userId); } GetWhoIsJob::GetWhoIsJob(const QString& userId) : BaseJob(HttpVerb::Get, QStringLiteral("GetWhoIsJob"), - basePath % "/admin/whois/" % userId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/admin/whois/" % userId) {} - -GetWhoIsJob::~GetWhoIsJob() = default; - -const QString& GetWhoIsJob::userId() const { return d->userId; } - -const QHash& GetWhoIsJob::devices() const -{ - return d->devices; -} - -BaseJob::Status GetWhoIsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("user_id"_ls), d->userId); - fromJson(json.value("devices"_ls), d->devices); - - return Success; -} diff --git a/lib/csapi/admin.h b/lib/csapi/admin.h index 75ae1eb0..6ad7d08d 100644 --- a/lib/csapi/admin.h +++ b/lib/csapi/admin.h @@ -4,17 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief Gets information about a particular user. * * Gets information about a particular user. @@ -65,6 +58,7 @@ public: // Construction/destruction /*! \brief Gets information about a particular user. + * * * \param userId * The user to look up. @@ -77,22 +71,44 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetWhoIsJob() override; // Result properties /// The Matrix user ID of the user. - const QString& userId() const; + QString userId() const { return loadFromJson("user_id"_ls); } - /// Each key is an identitfier for one of the user's devices. - const QHash& devices() const; + /// Each key is an identifier for one of the user's devices. + QHash devices() const + { + return loadFromJson>("devices"_ls); + } +}; + +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetWhoIsJob::ConnectionInfo& result) + { + fromJson(jo.value("ip"_ls), result.ip); + fromJson(jo.value("last_seen"_ls), result.lastSeen); + fromJson(jo.value("user_agent"_ls), result.userAgent); + } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, GetWhoIsJob::SessionInfo& result) + { + fromJson(jo.value("connections"_ls), result.connections); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, GetWhoIsJob::DeviceInfo& result) + { + fromJson(jo.value("sessions"_ls), result.sessions); + } }; } // namespace Quotient diff --git a/lib/csapi/administrative_contact.cpp b/lib/csapi/administrative_contact.cpp index 36d93e73..fa4f475a 100644 --- a/lib/csapi/administrative_contact.cpp +++ b/lib/csapi/administrative_contact.cpp @@ -4,164 +4,100 @@ #include "administrative_contact.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetAccount3PIDsJob::ThirdPartyIdentifier& result) - { - fromJson(jo.value("medium"_ls), result.medium); - fromJson(jo.value("address"_ls), result.address); - fromJson(jo.value("validated_at"_ls), result.validatedAt); - fromJson(jo.value("added_at"_ls), result.addedAt); - } -}; - -} // namespace Quotient - -class GetAccount3PIDsJob::Private { -public: - QVector threepids; -}; - QUrl GetAccount3PIDsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/account/3pid"); + QStringLiteral("/_matrix/client/r0") + % "/account/3pid"); } GetAccount3PIDsJob::GetAccount3PIDsJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetAccount3PIDsJob"), - basePath % "/account/3pid") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/account/3pid") {} -GetAccount3PIDsJob::~GetAccount3PIDsJob() = default; - -const QVector& -GetAccount3PIDsJob::threepids() const +Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds) + : BaseJob(HttpVerb::Post, QStringLiteral("Post3PIDsJob"), + QStringLiteral("/_matrix/client/r0") % "/account/3pid") { - return d->threepids; + QJsonObject _data; + addParam<>(_data, QStringLiteral("three_pid_creds"), threePidCreds); + setRequestData(std::move(_data)); } -BaseJob::Status GetAccount3PIDsJob::parseJson(const QJsonDocument& data) +Add3PIDJob::Add3PIDJob(const QString& clientSecret, const QString& sid, + const Omittable& auth) + : BaseJob(HttpVerb::Post, QStringLiteral("Add3PIDJob"), + QStringLiteral("/_matrix/client/r0") % "/account/3pid/add") { - auto json = data.object(); - fromJson(json.value("threepids"_ls), d->threepids); - - return Success; + QJsonObject _data; + addParam(_data, QStringLiteral("auth"), auth); + addParam<>(_data, QStringLiteral("client_secret"), clientSecret); + addParam<>(_data, QStringLiteral("sid"), sid); + setRequestData(std::move(_data)); } -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, - const Post3PIDsJob::ThreePidCredentials& pod) - { - addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); - addParam<>(jo, QStringLiteral("id_server"), pod.idServer); - addParam<>(jo, QStringLiteral("sid"), pod.sid); - } -}; - -} // namespace Quotient - -Post3PIDsJob::Post3PIDsJob(const ThreePidCredentials& threePidCreds, - Omittable bind) - : BaseJob(HttpVerb::Post, QStringLiteral("Post3PIDsJob"), - basePath % "/account/3pid") +Bind3PIDJob::Bind3PIDJob(const QString& clientSecret, const QString& idServer, + const QString& idAccessToken, const QString& sid) + : BaseJob(HttpVerb::Post, QStringLiteral("Bind3PIDJob"), + QStringLiteral("/_matrix/client/r0") % "/account/3pid/bind") { QJsonObject _data; - addParam<>(_data, QStringLiteral("three_pid_creds"), threePidCreds); - addParam(_data, QStringLiteral("bind"), bind); - setRequestData(_data); + addParam<>(_data, QStringLiteral("client_secret"), clientSecret); + addParam<>(_data, QStringLiteral("id_server"), idServer); + addParam<>(_data, QStringLiteral("id_access_token"), idAccessToken); + addParam<>(_data, QStringLiteral("sid"), sid); + setRequestData(std::move(_data)); } Delete3pidFromAccountJob::Delete3pidFromAccountJob(const QString& medium, - const QString& address) + const QString& address, + const QString& idServer) : BaseJob(HttpVerb::Post, QStringLiteral("Delete3pidFromAccountJob"), - basePath % "/account/3pid/delete") + QStringLiteral("/_matrix/client/r0") % "/account/3pid/delete") { QJsonObject _data; + addParam(_data, QStringLiteral("id_server"), idServer); addParam<>(_data, QStringLiteral("medium"), medium); addParam<>(_data, QStringLiteral("address"), address); - setRequestData(_data); + setRequestData(std::move(_data)); + addExpectedKey("id_server_unbind_result"); } -class RequestTokenTo3PIDEmailJob::Private { -public: - Sid data; -}; - -RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob( - const QString& clientSecret, const QString& email, int sendAttempt, - const QString& idServer, const QString& nextLink) - : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenTo3PIDEmailJob"), - basePath % "/account/3pid/email/requestToken", false) - , d(new Private) +Unbind3pidFromAccountJob::Unbind3pidFromAccountJob(const QString& medium, + const QString& address, + const QString& idServer) + : BaseJob(HttpVerb::Post, QStringLiteral("Unbind3pidFromAccountJob"), + QStringLiteral("/_matrix/client/r0") % "/account/3pid/unbind") { QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("email"), email); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); + addParam(_data, QStringLiteral("id_server"), idServer); + addParam<>(_data, QStringLiteral("medium"), medium); + addParam<>(_data, QStringLiteral("address"), address); + setRequestData(std::move(_data)); + addExpectedKey("id_server_unbind_result"); } -RequestTokenTo3PIDEmailJob::~RequestTokenTo3PIDEmailJob() = default; - -const Sid& RequestTokenTo3PIDEmailJob::data() const { return d->data; } - -BaseJob::Status RequestTokenTo3PIDEmailJob::parseJson(const QJsonDocument& data) +RequestTokenTo3PIDEmailJob::RequestTokenTo3PIDEmailJob( + const EmailValidationData& body) + : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenTo3PIDEmailJob"), + QStringLiteral("/_matrix/client/r0") + % "/account/3pid/email/requestToken", + false) { - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } -class RequestTokenTo3PIDMSISDNJob::Private { -public: - Sid data; -}; - RequestTokenTo3PIDMSISDNJob::RequestTokenTo3PIDMSISDNJob( - const QString& clientSecret, const QString& country, - const QString& phoneNumber, int sendAttempt, const QString& idServer, - const QString& nextLink) + const MsisdnValidationData& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenTo3PIDMSISDNJob"), - basePath % "/account/3pid/msisdn/requestToken", false) - , d(new Private) -{ - QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("country"), country); - addParam<>(_data, QStringLiteral("phone_number"), phoneNumber); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); -} - -RequestTokenTo3PIDMSISDNJob::~RequestTokenTo3PIDMSISDNJob() = default; - -const Sid& RequestTokenTo3PIDMSISDNJob::data() const { return d->data; } - -BaseJob::Status RequestTokenTo3PIDMSISDNJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") + % "/account/3pid/msisdn/requestToken", + false) { - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index af98fe9c..53c89272 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -4,18 +4,15 @@ #pragma once -#include "converters.h" - -#include "csapi/../identity/definitions/sid.h" +#include "csapi/./definitions/request_email_validation.h" +#include "csapi/./definitions/request_msisdn_validation.h" +#include "csapi/definitions/auth_data.h" +#include "csapi/definitions/request_token_response.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Gets a list of a user's third party identifiers. * * Gets a list of the third party identifiers that the homeserver has @@ -63,7 +60,6 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetAccount3PIDsJob() override; // Result properties @@ -75,19 +71,36 @@ public: /// /// Identifiers in this list may be used by the homeserver as, for example, /// identifiers that it will accept to reset the user's account password. - const QVector& threepids() const; - -protected: - Status parseJson(const QJsonDocument& data) override; + QVector threepids() const + { + return loadFromJson>("threepids"_ls); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetAccount3PIDsJob::ThirdPartyIdentifier& result) + { + fromJson(jo.value("medium"_ls), result.medium); + fromJson(jo.value("address"_ls), result.address); + fromJson(jo.value("validated_at"_ls), result.validatedAt); + fromJson(jo.value("added_at"_ls), result.addedAt); + } }; /*! \brief Adds contact information to the user's account. * * Adds contact information to the user's account. + * + * This endpoint is deprecated in favour of the more specific ``/3pid/add`` + * and ``/3pid/bind`` endpoints. + * + * .. Note:: + * Previously this endpoint supported a ``bind`` parameter. This parameter + * has been removed, making this endpoint behave as though it was ``false``. + * This results in this endpoint being an equivalent to ``/3pid/bind`` rather + * than dual-purpose. */ class Post3PIDsJob : public BaseJob { public: @@ -99,6 +112,10 @@ public: QString clientSecret; /// The identity server to use. QString idServer; + /// An access token previously registered with the identity server. + /// Servers can treat this as optional to distinguish between + /// r0.5-compatible clients and this specification version. + QString idAccessToken; /// The session identifier given by the identity server. QString sid; }; @@ -106,153 +123,248 @@ public: // Construction/destruction /*! \brief Adds contact information to the user's account. + * * * \param threePidCreds * The third party credentials to associate with the account. - * \param bind - * Whether the homeserver should also bind this third party - * identifier to the account's Matrix ID with the passed identity - * server. Default: ``false``. */ - explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds, - Omittable bind = none); + explicit Post3PIDsJob(const ThreePidCredentials& threePidCreds); +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, + const Post3PIDsJob::ThreePidCredentials& pod) + { + addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); + addParam<>(jo, QStringLiteral("id_server"), pod.idServer); + addParam<>(jo, QStringLiteral("id_access_token"), pod.idAccessToken); + addParam<>(jo, QStringLiteral("sid"), pod.sid); + } +}; + +/*! \brief Adds contact information to the user's account. + * + * This API endpoint uses the `User-Interactive Authentication API`_. + * + * Adds contact information to the user's account. Homeservers should use 3PIDs + * added through this endpoint for password resets instead of relying on the + * identity server. + * + * Homeservers should prevent the caller from adding a 3PID to their account if + * it has already been added to another user's account on the homeserver. + */ +class Add3PIDJob : public BaseJob { +public: + /*! \brief Adds contact information to the user's account. + * + * + * \param clientSecret + * The client secret used in the session with the homeserver. + * + * \param sid + * The session identifier given by the homeserver. + * + * \param auth + * Additional authentication information for the + * user-interactive authentication API. + */ + explicit Add3PIDJob(const QString& clientSecret, const QString& sid, + const Omittable& auth = none); +}; + +/*! \brief Binds a 3PID to the user's account through an Identity Service. + * + * Binds a 3PID to the user's account through the specified identity server. + * + * Homeservers should not prevent this request from succeeding if another user + * has bound the 3PID. Homeservers should simply proxy any errors received by + * the identity server to the caller. + * + * Homeservers should track successful binds so they can be unbound later. + */ +class Bind3PIDJob : public BaseJob { +public: + /*! \brief Binds a 3PID to the user's account through an Identity Service. + * + * + * \param clientSecret + * The client secret used in the session with the identity server. + * + * \param idServer + * The identity server to use. + * + * \param idAccessToken + * An access token previously registered with the identity server. + * + * \param sid + * The session identifier given by the identity server. + */ + explicit Bind3PIDJob(const QString& clientSecret, const QString& idServer, + const QString& idAccessToken, const QString& sid); }; /*! \brief Deletes a third party identifier from the user's account * * Removes a third party identifier from the user's account. This might not * cause an unbind of the identifier from the identity server. + * + * Unlike other endpoints, this endpoint does not take an ``id_access_token`` + * parameter because the homeserver is expected to sign the request to the + * identity server instead. */ class Delete3pidFromAccountJob : public BaseJob { public: /*! \brief Deletes a third party identifier from the user's account + * * * \param medium * The medium of the third party identifier being removed. + * * \param address * The third party address being removed. + * + * \param idServer + * The identity server to unbind from. If not provided, the homeserver + * MUST use the ``id_server`` the identifier was added through. If the + * homeserver does not know the original ``id_server``, it MUST return + * a ``id_server_unbind_result`` of ``no-support``. */ explicit Delete3pidFromAccountJob(const QString& medium, - const QString& address); + const QString& address, + const QString& idServer = {}); + + // Result properties + + /// An indicator as to whether or not the homeserver was able to unbind + /// the 3PID from the identity server. ``success`` indicates that the + /// indentity server has unbound the identifier whereas ``no-support`` + /// indicates that the identity server refuses to support the request + /// or the homeserver was not able to determine an identity server to + /// unbind from. + QString idServerUnbindResult() const + { + return loadFromJson("id_server_unbind_result"_ls); + } +}; + +/*! \brief Removes a user's third party identifier from an identity server. + * + * Removes a user's third party identifier from the provided identity server + * without removing it from the homeserver. + * + * Unlike other endpoints, this endpoint does not take an ``id_access_token`` + * parameter because the homeserver is expected to sign the request to the + * identity server instead. + */ +class Unbind3pidFromAccountJob : public BaseJob { +public: + /*! \brief Removes a user's third party identifier from an identity server. + * + * + * \param medium + * The medium of the third party identifier being removed. + * + * \param address + * The third party address being removed. + * + * \param idServer + * The identity server to unbind from. If not provided, the homeserver + * MUST use the ``id_server`` the identifier was added through. If the + * homeserver does not know the original ``id_server``, it MUST return + * a ``id_server_unbind_result`` of ``no-support``. + */ + explicit Unbind3pidFromAccountJob(const QString& medium, + const QString& address, + const QString& idServer = {}); + + // Result properties + + /// An indicator as to whether or not the identity server was able to unbind + /// the 3PID. ``success`` indicates that the identity server has unbound the + /// identifier whereas ``no-support`` indicates that the identity server + /// refuses to support the request or the homeserver was not able to + /// determine an identity server to unbind from. + QString idServerUnbindResult() const + { + return loadFromJson("id_server_unbind_result"_ls); + } }; /*! \brief Begins the validation process for an email address for association * with the user's account. * - * Proxies the Identity Service API ``validate/email/requestToken``, but - * first checks that the given email address is **not** already associated - * with an account on this homeserver. This API should be used to request - * validation tokens when adding an email address to an account. This API's - * parameters and response are identical to that of the - * |/register/email/requestToken|_ endpoint. + * The homeserver must check that the given email address is **not** + * already associated with an account on this homeserver. This API should + * be used to request validation tokens when adding an email address to an + * account. This API's parameters and response are identical to that of + * the |/register/email/requestToken|_ endpoint. The homeserver should validate + * the email itself, either by sending a validation email itself or by using + * a service it has control over. */ class RequestTokenTo3PIDEmailJob : public BaseJob { public: /*! \brief Begins the validation process for an email address for * association with the user's account. * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given email address is **not** + * already associated with an account on this homeserver. This API should + * be used to request validation tokens when adding an email address to an + * account. This API's parameters and response are identical to that of + * the |/register/email/requestToken|_ endpoint. The homeserver should + * validate the email itself, either by sending a validation email itself or + * by using a service it has control over. */ - explicit RequestTokenTo3PIDEmailJob(const QString& clientSecret, - const QString& email, int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenTo3PIDEmailJob() override; + explicit RequestTokenTo3PIDEmailJob(const EmailValidationData& body); // Result properties - /// An email was sent to the given address. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// An email was sent to the given address. Note that this may be an + /// email containing the validation token or it may be informing the + /// user of an error. + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Begins the validation process for a phone number for association with * the user's account. * - * Proxies the Identity Service API ``validate/msisdn/requestToken``, but - * first checks that the given phone number is **not** already associated - * with an account on this homeserver. This API should be used to request - * validation tokens when adding a phone number to an account. This API's - * parameters and response are identical to that of the - * |/register/msisdn/requestToken|_ endpoint. + * The homeserver must check that the given phone number is **not** + * already associated with an account on this homeserver. This API should + * be used to request validation tokens when adding a phone number to an + * account. This API's parameters and response are identical to that of + * the |/register/msisdn/requestToken|_ endpoint. The homeserver should validate + * the phone number itself, either by sending a validation message itself or by + * using a service it has control over. */ class RequestTokenTo3PIDMSISDNJob : public BaseJob { public: /*! \brief Begins the validation process for a phone number for association * with the user's account. * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given phone number is **not** + * already associated with an account on this homeserver. This API should + * be used to request validation tokens when adding a phone number to an + * account. This API's parameters and response are identical to that of + * the |/register/msisdn/requestToken|_ endpoint. The homeserver should + * validate the phone number itself, either by sending a validation message + * itself or by using a service it has control over. */ - explicit RequestTokenTo3PIDMSISDNJob(const QString& clientSecret, - const QString& country, - const QString& phoneNumber, - int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenTo3PIDMSISDNJob() override; + explicit RequestTokenTo3PIDMSISDNJob(const MsisdnValidationData& body); // Result properties /// An SMS message was sent to the given phone number. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; } // namespace Quotient diff --git a/lib/csapi/appservice_room_directory.cpp b/lib/csapi/appservice_room_directory.cpp index bfcab2a7..e8ec55bf 100644 --- a/lib/csapi/appservice_room_directory.cpp +++ b/lib/csapi/appservice_room_directory.cpp @@ -4,21 +4,18 @@ #include "appservice_room_directory.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - UpdateAppserviceRoomDirectoryVsibilityJob::UpdateAppserviceRoomDirectoryVsibilityJob( const QString& networkId, const QString& roomId, const QString& visibility) : BaseJob(HttpVerb::Put, QStringLiteral("UpdateAppserviceRoomDirectoryVsibilityJob"), - basePath % "/directory/list/appservice/" % networkId % "/" % roomId) + QStringLiteral("/_matrix/client/r0") + % "/directory/list/appservice/" % networkId % "/" % roomId) { QJsonObject _data; addParam<>(_data, QStringLiteral("visibility"), visibility); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h index f0ad78b0..d103177c 100644 --- a/lib/csapi/appservice_room_directory.h +++ b/lib/csapi/appservice_room_directory.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Updates a room's visibility in the application service's room * directory. * @@ -28,12 +26,15 @@ public: /*! \brief Updates a room's visibility in the application service's room * directory. * + * * \param networkId * The protocol (network) ID to update the room list for. This would * have been provided by the application service as being listed as * a supported protocol. + * * \param roomId * The room ID to add to the directory. + * * \param visibility * Whether the room should be visible (public) in the directory * or not (private). diff --git a/lib/csapi/banning.cpp b/lib/csapi/banning.cpp index 6ac9b3ba..8a8ec664 100644 --- a/lib/csapi/banning.cpp +++ b/lib/csapi/banning.cpp @@ -4,30 +4,27 @@ #include "banning.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - BanJob::BanJob(const QString& roomId, const QString& userId, const QString& reason) : BaseJob(HttpVerb::Post, QStringLiteral("BanJob"), - basePath % "/rooms/" % roomId % "/ban") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId % "/ban") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); addParam(_data, QStringLiteral("reason"), reason); - setRequestData(_data); + setRequestData(std::move(_data)); } UnbanJob::UnbanJob(const QString& roomId, const QString& userId) : BaseJob(HttpVerb::Post, QStringLiteral("UnbanJob"), - basePath % "/rooms/" % roomId % "/unban") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/unban") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h index 6bf782e7..4faaaa81 100644 --- a/lib/csapi/banning.h +++ b/lib/csapi/banning.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Ban a user in the room. * * Ban a user in the room. If the user is currently in the room, also kick them. @@ -23,11 +21,14 @@ namespace Quotient { class BanJob : public BaseJob { public: /*! \brief Ban a user in the room. + * * * \param roomId * The room identifier (not alias) from which the user should be banned. + * * \param userId * The fully qualified user ID of the user being banned. + * * \param reason * The reason the user has been banned. This will be supplied as the * ``reason`` on the target's updated `m.room.member`_ event. @@ -48,9 +49,11 @@ public: class UnbanJob : public BaseJob { public: /*! \brief Unban a user from the room. + * * * \param roomId * The room identifier (not alias) from which the user should be unbanned. + * * \param userId * The fully qualified user ID of the user being unbanned. */ diff --git a/lib/csapi/capabilities.cpp b/lib/csapi/capabilities.cpp index 8c7ed141..33a53cad 100644 --- a/lib/csapi/capabilities.cpp +++ b/lib/csapi/capabilities.cpp @@ -4,80 +4,20 @@ #include "capabilities.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetCapabilitiesJob::ChangePasswordCapability& result) - { - fromJson(jo.value("enabled"_ls), result.enabled); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetCapabilitiesJob::RoomVersionsCapability& result) - { - fromJson(jo.value("default"_ls), result.defaultVersion); - fromJson(jo.value("available"_ls), result.available); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(QJsonObject jo, - GetCapabilitiesJob::Capabilities& result) - { - fromJson(jo.take("m.change_password"_ls), result.changePassword); - fromJson(jo.take("m.room_versions"_ls), result.roomVersions); - fromJson(jo, result.additionalProperties); - } -}; - -} // namespace Quotient - -class GetCapabilitiesJob::Private { -public: - Capabilities capabilities; -}; - QUrl GetCapabilitiesJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/capabilities"); + QStringLiteral("/_matrix/client/r0") + % "/capabilities"); } GetCapabilitiesJob::GetCapabilitiesJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetCapabilitiesJob"), - basePath % "/capabilities") - , d(new Private) -{} - -GetCapabilitiesJob::~GetCapabilitiesJob() = default; - -const GetCapabilitiesJob::Capabilities& GetCapabilitiesJob::capabilities() const + QStringLiteral("/_matrix/client/r0") % "/capabilities") { - return d->capabilities; -} - -BaseJob::Status GetCapabilitiesJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("capabilities"_ls)) - return { IncorrectResponse, - "The key 'capabilities' not found in the response" }; - fromJson(json.value("capabilities"_ls), d->capabilities); - - return Success; + addExpectedKey("capabilities"); } diff --git a/lib/csapi/capabilities.h b/lib/csapi/capabilities.h index 9f46ab2e..da50c8c1 100644 --- a/lib/csapi/capabilities.h +++ b/lib/csapi/capabilities.h @@ -4,17 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief Gets information about the server's capabilities. * * Gets information about the server's supported feature set @@ -61,20 +54,45 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetCapabilitiesJob() override; // Result properties /// The custom capabilities the server supports, using the /// Java package naming convention. - const Capabilities& capabilities() const; + Capabilities capabilities() const + { + return loadFromJson("capabilities"_ls); + } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetCapabilitiesJob::ChangePasswordCapability& result) + { + fromJson(jo.value("enabled"_ls), result.enabled); + } +}; + +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetCapabilitiesJob::RoomVersionsCapability& result) + { + fromJson(jo.value("default"_ls), result.defaultVersion); + fromJson(jo.value("available"_ls), result.available); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(QJsonObject jo, + GetCapabilitiesJob::Capabilities& result) + { + fromJson(jo.take("m.change_password"_ls), result.changePassword); + fromJson(jo.take("m.room_versions"_ls), result.roomVersions); + fromJson(jo, result.additionalProperties); + } }; } // namespace Quotient diff --git a/lib/csapi/content-repo.cpp b/lib/csapi/content-repo.cpp index d811426d..7ae89739 100644 --- a/lib/csapi/content-repo.cpp +++ b/lib/csapi/content-repo.cpp @@ -4,21 +4,11 @@ #include "content-repo.h" -#include "converters.h" - #include -#include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/media/r0"); - -class UploadContentJob::Private { -public: - QString contentUri; -}; - -BaseJob::Query queryToUploadContent(const QString& filename) +auto queryToUploadContent(const QString& filename) { BaseJob::Query _q; addParam(_q, QStringLiteral("filename"), filename); @@ -28,36 +18,15 @@ BaseJob::Query queryToUploadContent(const QString& filename) UploadContentJob::UploadContentJob(QIODevice* content, const QString& filename, const QString& contentType) : BaseJob(HttpVerb::Post, QStringLiteral("UploadContentJob"), - basePath % "/upload", queryToUploadContent(filename)) - , d(new Private) + QStringLiteral("/_matrix/media/r0") % "/upload", + queryToUploadContent(filename)) { setRequestHeader("Content-Type", contentType.toLatin1()); setRequestData(Data(content)); + addExpectedKey("content_uri"); } -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"_ls)) - return { IncorrectResponse, - "The key 'content_uri' not found in the response" }; - fromJson(json.value("content_uri"_ls), d->contentUri); - - return Success; -} - -class GetContentJob::Private { -public: - QString contentType; - QString contentDisposition; - QIODevice* data; -}; - -BaseJob::Query queryToGetContent(bool allowRemote) +auto queryToGetContent(bool allowRemote) { BaseJob::Query _q; addParam(_q, QStringLiteral("allow_remote"), allowRemote); @@ -68,7 +37,8 @@ QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, bool allowRemote) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/download/" % serverName % "/" + QStringLiteral("/_matrix/media/r0") + % "/download/" % serverName % "/" % mediaId, queryToGetContent(allowRemote)); } @@ -76,40 +46,14 @@ QUrl GetContentJob::makeRequestUrl(QUrl baseUrl, const QString& serverName, GetContentJob::GetContentJob(const QString& serverName, const QString& mediaId, bool allowRemote) : BaseJob(HttpVerb::Get, QStringLiteral("GetContentJob"), - basePath % "/download/" % serverName % "/" % mediaId, + QStringLiteral("/_matrix/media/r0") % "/download/" % serverName + % "/" % mediaId, queryToGetContent(allowRemote), {}, 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::data() const { return d->data; } - -BaseJob::Status GetContentJob::parseReply(QNetworkReply* reply) -{ - d->contentType = reply->rawHeader("Content-Type"); - d->contentDisposition = reply->rawHeader("Content-Disposition"); - d->data = reply; - return Success; -} - -class GetContentOverrideNameJob::Private { -public: - QString contentType; - QString contentDisposition; - QIODevice* data; -}; - -BaseJob::Query queryToGetContentOverrideName(bool allowRemote) +auto queryToGetContentOverrideName(bool allowRemote) { BaseJob::Query _q; addParam(_q, QStringLiteral("allow_remote"), allowRemote); @@ -123,7 +67,8 @@ QUrl GetContentOverrideNameJob::makeRequestUrl(QUrl baseUrl, bool allowRemote) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/download/" % serverName % "/" + QStringLiteral("/_matrix/media/r0") + % "/download/" % serverName % "/" % mediaId % "/" % fileName, queryToGetContentOverrideName(allowRemote)); } @@ -133,45 +78,15 @@ GetContentOverrideNameJob::GetContentOverrideNameJob(const QString& serverName, const QString& fileName, bool allowRemote) : BaseJob(HttpVerb::Get, QStringLiteral("GetContentOverrideNameJob"), - basePath % "/download/" % serverName % "/" % mediaId % "/" - % fileName, + QStringLiteral("/_matrix/media/r0") % "/download/" % serverName + % "/" % mediaId % "/" % fileName, queryToGetContentOverrideName(allowRemote), {}, 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::data() const { return d->data; } - -BaseJob::Status GetContentOverrideNameJob::parseReply(QNetworkReply* reply) -{ - d->contentType = reply->rawHeader("Content-Type"); - d->contentDisposition = reply->rawHeader("Content-Disposition"); - d->data = reply; - return Success; -} - -class GetContentThumbnailJob::Private { -public: - QString contentType; - QIODevice* data; -}; - -BaseJob::Query queryToGetContentThumbnail(int width, int height, - const QString& method, - bool allowRemote) +auto queryToGetContentThumbnail(int width, int height, const QString& method, + bool allowRemote) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("width"), width); @@ -189,7 +104,8 @@ QUrl GetContentThumbnailJob::makeRequestUrl(QUrl baseUrl, { return BaseJob::makeRequestUrl( std::move(baseUrl), - basePath % "/thumbnail/" % serverName % "/" % mediaId, + QStringLiteral("/_matrix/media/r0") % "/thumbnail/" % serverName % "/" + % mediaId, queryToGetContentThumbnail(width, height, method, allowRemote)); } @@ -198,37 +114,15 @@ GetContentThumbnailJob::GetContentThumbnailJob(const QString& serverName, int height, const QString& method, bool allowRemote) : BaseJob(HttpVerb::Get, QStringLiteral("GetContentThumbnailJob"), - basePath % "/thumbnail/" % serverName % "/" % mediaId, + QStringLiteral("/_matrix/media/r0") % "/thumbnail/" % serverName + % "/" % mediaId, queryToGetContentThumbnail(width, height, method, allowRemote), {}, false) - , d(new Private) { setExpectedContentTypes({ "image/jpeg", "image/png" }); } -GetContentThumbnailJob::~GetContentThumbnailJob() = default; - -const QString& GetContentThumbnailJob::contentType() const -{ - return d->contentType; -} - -QIODevice* GetContentThumbnailJob::data() const { return d->data; } - -BaseJob::Status GetContentThumbnailJob::parseReply(QNetworkReply* reply) -{ - d->contentType = reply->rawHeader("Content-Type"); - d->data = reply; - return Success; -} - -class GetUrlPreviewJob::Private { -public: - Omittable matrixImageSize; - QString ogImage; -}; - -BaseJob::Query queryToGetUrlPreview(const QString& url, Omittable ts) +auto queryToGetUrlPreview(const QString& url, Omittable ts) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("url"), url); @@ -239,58 +133,26 @@ BaseJob::Query queryToGetUrlPreview(const QString& url, Omittable ts) QUrl GetUrlPreviewJob::makeRequestUrl(QUrl baseUrl, const QString& url, Omittable ts) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/preview_url", + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/media/r0") + % "/preview_url", queryToGetUrlPreview(url, ts)); } GetUrlPreviewJob::GetUrlPreviewJob(const QString& url, Omittable ts) : BaseJob(HttpVerb::Get, QStringLiteral("GetUrlPreviewJob"), - basePath % "/preview_url", queryToGetUrlPreview(url, ts)) - , d(new Private) + QStringLiteral("/_matrix/media/r0") % "/preview_url", + queryToGetUrlPreview(url, ts)) {} -GetUrlPreviewJob::~GetUrlPreviewJob() = default; - -Omittable 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(); - fromJson(json.value("matrix:image:size"_ls), d->matrixImageSize); - fromJson(json.value("og:image"_ls), d->ogImage); - - return Success; -} - -class GetConfigJob::Private { -public: - Omittable uploadSize; -}; - QUrl GetConfigJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/config"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/media/r0") + % "/config"); } GetConfigJob::GetConfigJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetConfigJob"), - basePath % "/config") - , d(new Private) + QStringLiteral("/_matrix/media/r0") % "/config") {} - -GetConfigJob::~GetConfigJob() = default; - -Omittable GetConfigJob::uploadSize() const { return d->uploadSize; } - -BaseJob::Status GetConfigJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("m.upload.size"_ls), d->uploadSize); - - return Success; -} diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h index 6263ef0b..0eb273bc 100644 --- a/lib/csapi/content-repo.h +++ b/lib/csapi/content-repo.h @@ -4,45 +4,40 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" #include +#include namespace Quotient { -// Operations - /*! \brief Upload some content to the content repository. * */ class UploadContentJob : public BaseJob { public: /*! \brief Upload some content to the content repository. + * * * \param content + * The content to be uploaded. + * * \param filename * The name of the file being uploaded + * * \param contentType * The content type of the file being uploaded */ explicit UploadContentJob(QIODevice* content, const QString& filename = {}, const QString& contentType = {}); - ~UploadContentJob() override; - // Result properties - /// The MXC URI to the uploaded content. - const QString& contentUri() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// The `MXC URI`_ to the uploaded content. + QString contentUri() const + { + return loadFromJson("content_uri"_ls); + } }; /*! \brief Download content from the content repository. @@ -51,11 +46,14 @@ private: class GetContentJob : public BaseJob { public: /*! \brief Download content from the content repository. + * * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) + * * \param mediaId * The media ID from the ``mxc://`` URI (the path component) + * * \param allowRemote * Indicates to the server that it should not attempt to fetch the media * if it is deemed remote. This is to prevent routing loops where the server @@ -71,44 +69,47 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, bool allowRemote = true); - ~GetContentJob() override; // Result properties /// The content type of the file that was previously uploaded. - const QString& contentType() const; + QString contentType() const { return reply()->rawHeader("Content-Type"); } /// The name of the file that was previously uploaded, if set. - const QString& contentDisposition() const; + QString contentDisposition() const + { + return reply()->rawHeader("Content-Disposition"); + } /// The content that was previously uploaded. - QIODevice* data() const; - -protected: - Status parseReply(QNetworkReply* reply) override; - -private: - class Private; - QScopedPointer d; + QIODevice* data() { return reply(); } }; -/*! \brief Download content from the content repository as a given filename. +/*! \brief Download content from the content repository. This is the same as +the download endpoint above, except permitting a desired file name. * */ class GetContentOverrideNameJob : public BaseJob { public: - /*! \brief Download content from the content repository as a given filename. + /*! \brief Download content from the content repository. This is the same as +the download endpoint above, except permitting a desired file name. + * * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) + * * \param mediaId * The media ID from the ``mxc://`` URI (the path component) + * * \param fileName - * The filename to give in the Content-Disposition + * A filename to give in the ``Content-Disposition`` header. + * * \param allowRemote * Indicates to the server that it should not attempt to fetch the media - * if it is deemed remote. This is to prevent routing loops where the server - * contacts itself. Defaults to true if not provided. +if it is deemed + * remote. This is to prevent routing loops where the server contacts +itself. Defaults to + * true if not provided. */ explicit GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, @@ -123,50 +124,57 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& serverName, const QString& mediaId, const QString& fileName, bool allowRemote = true); - ~GetContentOverrideNameJob() override; // Result properties /// The content type of the file that was previously uploaded. - const QString& contentType() const; + QString contentType() const { return reply()->rawHeader("Content-Type"); } - /// The name of file given in the request - const QString& contentDisposition() const; + /// The ``fileName`` requested or the name of the file that was previously + /// uploaded, if set. + QString contentDisposition() const + { + return reply()->rawHeader("Content-Disposition"); + } /// The content that was previously uploaded. - QIODevice* data() const; - -protected: - Status parseReply(QNetworkReply* reply) override; - -private: - class Private; - QScopedPointer d; + QIODevice* data() { return reply(); } }; -/*! \brief Download a thumbnail of the content from the content repository. +/*! \brief Download a thumbnail of content from the content repository. See the +`thumbnailing <#thumbnails>`_ section for more information. * */ class GetContentThumbnailJob : public BaseJob { public: - /*! \brief Download a thumbnail of the content from the content repository. + /*! \brief Download a thumbnail of content from the content repository. See +the `thumbnailing <#thumbnails>`_ section for more information. + * * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) + * * \param mediaId * The media ID from the ``mxc://`` URI (the path component) + * * \param width - * The *desired* width of the thumbnail. The actual thumbnail may not - * match the size specified. + * The *desired* width of the thumbnail. The actual thumbnail may be + * larger than the size specified. + * * \param height - * The *desired* height of the thumbnail. The actual thumbnail may not - * match the size specified. + * The *desired* height of the thumbnail. The actual thumbnail may be + * larger than the size specified. + * * \param method - * The desired resizing method. + * The desired resizing method. See the `thumbnailing <#thumbnails>`_ + * section for more information. + * * \param allowRemote * Indicates to the server that it should not attempt to fetch the media - * if it is deemed remote. This is to prevent routing loops where the server - * contacts itself. Defaults to true if not provided. +if it is deemed + * remote. This is to prevent routing loops where the server contacts +itself. Defaults to + * true if not provided. */ explicit GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, @@ -182,33 +190,35 @@ public: const QString& mediaId, int width, int height, const QString& method = {}, bool allowRemote = true); - ~GetContentThumbnailJob() override; // Result properties /// The content type of the thumbnail. - const QString& contentType() const; + QString contentType() const { return reply()->rawHeader("Content-Type"); } /// A thumbnail of the requested content. - QIODevice* data() const; - -protected: - Status parseReply(QNetworkReply* reply) override; - -private: - class Private; - QScopedPointer d; + QIODevice* data() { return reply(); } }; /*! \brief Get information about a URL for a client * + * Get information about a URL for the client. Typically this is called when a + * client sees a URL in a message and wants to render a preview for the user. + * + * .. Note:: + * Clients should consider avoiding this endpoint for URLs posted in encrypted + * rooms. Encrypted rooms often contain more sensitive information the users + * do not want to share with the homeserver, and this can mean that the URLs + * being shared should also not be shared with the homeserver. */ class GetUrlPreviewJob : public BaseJob { public: /*! \brief Get information about a URL for a client + * * * \param url - * The URL to get a preview of + * The URL to get a preview of. + * * \param ts * The preferred point in time to return a preview for. The server may * return a newer version if it does not have the requested version @@ -223,22 +233,17 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& url, Omittable ts = none); - ~GetUrlPreviewJob() override; // Result properties /// The byte-size of the image. Omitted if there is no image attached. - Omittable matrixImageSize() const; + Omittable matrixImageSize() const + { + return loadFromJson>("matrix:image:size"_ls); + } - /// An MXC URI to the image. Omitted if there is no image. - const QString& ogImage() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// An `MXC URI`_ to the image. Omitted if there is no image. + QString ogImage() const { return loadFromJson("og:image"_ls); } }; /*! \brief Get the configuration for the content repository. @@ -265,21 +270,16 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetConfigJob() override; // Result properties /// The maximum size an upload can be in bytes. /// Clients SHOULD use this as a guide when uploading content. /// If not listed or null, the size limit should be treated as unknown. - Omittable uploadSize() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Omittable uploadSize() const + { + return loadFromJson>("m.upload.size"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/create_room.cpp b/lib/csapi/create_room.cpp index ec2a1855..a94f9951 100644 --- a/lib/csapi/create_room.cpp +++ b/lib/csapi/create_room.cpp @@ -4,44 +4,10 @@ #include "create_room.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const CreateRoomJob::Invite3pid& pod) - { - addParam<>(jo, QStringLiteral("id_server"), pod.idServer); - addParam<>(jo, QStringLiteral("medium"), pod.medium); - addParam<>(jo, QStringLiteral("address"), pod.address); - } -}; - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const CreateRoomJob::StateEvent& pod) - { - addParam<>(jo, QStringLiteral("type"), pod.type); - addParam(jo, QStringLiteral("state_key"), pod.stateKey); - addParam<>(jo, QStringLiteral("content"), pod.content); - } -}; - -} // namespace Quotient - -class CreateRoomJob::Private { -public: - QString roomId; -}; - CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& roomAliasName, const QString& name, const QString& topic, const QStringList& invite, @@ -52,8 +18,7 @@ CreateRoomJob::CreateRoomJob(const QString& visibility, const QString& preset, Omittable isDirect, const QJsonObject& powerLevelContentOverride) : BaseJob(HttpVerb::Post, QStringLiteral("CreateRoomJob"), - basePath % "/createRoom") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/createRoom") { QJsonObject _data; addParam(_data, QStringLiteral("visibility"), visibility); @@ -71,20 +36,6 @@ CreateRoomJob::CreateRoomJob(const QString& visibility, addParam(_data, QStringLiteral("is_direct"), isDirect); addParam(_data, QStringLiteral("power_level_content_override"), powerLevelContentOverride); - setRequestData(_data); -} - -CreateRoomJob::~CreateRoomJob() = default; - -const QString& CreateRoomJob::roomId() const { return d->roomId; } - -BaseJob::Status CreateRoomJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("room_id"_ls)) - return { IncorrectResponse, - "The key 'room_id' not found in the response" }; - fromJson(json.value("room_id"_ls), d->roomId); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("room_id"); } diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index f2bd9333..82db9e59 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -4,17 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief Create a new room * * Create a new room with various configuration options. @@ -23,21 +16,27 @@ namespace Quotient { * the new room, including checking power levels for each event. It MUST * apply the events implied by the request in the following order: * - * 0. A default ``m.room.power_levels`` event, giving the room creator + * 1. The ``m.room.create`` event itself. Must be the first event in the + * room. + * + * 2. An ``m.room.member`` event for the creator to join the room. This is + * needed so the remaining events can be sent. + * + * 3. A default ``m.room.power_levels`` event, giving the room creator * (and not other members) permission to send state events. Overridden * by the ``power_level_content_override`` parameter. * - * 1. Events set by the ``preset``. Currently these are the + * 4. Events set by the ``preset``. Currently these are the * ``m.room.join_rules``, * ``m.room.history_visibility``, and ``m.room.guest_access`` state events. * - * 2. Events listed in ``initial_state``, in the order that they are + * 5. Events listed in ``initial_state``, in the order that they are * listed. * - * 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + * 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and * ``m.room.topic`` state events). * - * 4. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` + * 7. Invite events implied by ``invite`` and ``invite_3pid`` (``m.room.member`` * with * ``membership: invite`` and ``m.room.third_party_invite``). * @@ -69,23 +68,29 @@ public: /// the new room, including checking power levels for each event. It MUST /// apply the events implied by the request in the following order: /// - /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// 1. The ``m.room.create`` event itself. Must be the first event in the + /// room. + /// + /// 2. An ``m.room.member`` event for the creator to join the room. This is + /// needed so the remaining events can be sent. + /// + /// 3. A default ``m.room.power_levels`` event, giving the room creator /// (and not other members) permission to send state events. Overridden /// by the ``power_level_content_override`` parameter. /// - /// 1. Events set by the ``preset``. Currently these are the + /// 4. Events set by the ``preset``. Currently these are the /// ``m.room.join_rules``, /// ``m.room.history_visibility``, and ``m.room.guest_access`` state /// events. /// - /// 2. Events listed in ``initial_state``, in the order that they are + /// 5. Events listed in ``initial_state``, in the order that they are /// listed. /// - /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and /// ``m.room.topic`` /// state events). /// - /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// 7. Invite events implied by ``invite`` and ``invite_3pid`` /// (``m.room.member`` with /// ``membership: invite`` and ``m.room.third_party_invite``). /// @@ -111,6 +116,10 @@ public: /// The hostname+port of the identity server which should be used for /// third party identifier lookups. QString idServer; + /// An access token previously registered with the identity server. + /// Servers can treat this as optional to distinguish between + /// r0.5-compatible clients and this specification version. + QString idAccessToken; /// The kind of address being passed in the address field, for example /// ``email``. QString medium; @@ -124,23 +133,29 @@ public: /// the new room, including checking power levels for each event. It MUST /// apply the events implied by the request in the following order: /// - /// 0. A default ``m.room.power_levels`` event, giving the room creator + /// 1. The ``m.room.create`` event itself. Must be the first event in the + /// room. + /// + /// 2. An ``m.room.member`` event for the creator to join the room. This is + /// needed so the remaining events can be sent. + /// + /// 3. A default ``m.room.power_levels`` event, giving the room creator /// (and not other members) permission to send state events. Overridden /// by the ``power_level_content_override`` parameter. /// - /// 1. Events set by the ``preset``. Currently these are the + /// 4. Events set by the ``preset``. Currently these are the /// ``m.room.join_rules``, /// ``m.room.history_visibility``, and ``m.room.guest_access`` state /// events. /// - /// 2. Events listed in ``initial_state``, in the order that they are + /// 5. Events listed in ``initial_state``, in the order that they are /// listed. /// - /// 3. Events implied by ``name`` and ``topic`` (``m.room.name`` and + /// 6. Events implied by ``name`` and ``topic`` (``m.room.name`` and /// ``m.room.topic`` /// state events). /// - /// 4. Invite events implied by ``invite`` and ``invite_3pid`` + /// 7. Invite events implied by ``invite`` and ``invite_3pid`` /// (``m.room.member`` with /// ``membership: invite`` and ``m.room.third_party_invite``). /// @@ -174,6 +189,7 @@ public: // Construction/destruction /*! \brief Create a new room + * * * \param visibility * A ``public`` visibility indicates that the room will be shown @@ -182,6 +198,7 @@ public: * ``private`` visibility if this key is not included. NB: This * should not be confused with ``join_rules`` which also uses the * word ``public``. + * * \param roomAliasName * The desired room alias **local part**. If this is included, a * room alias will be created and mapped to the newly created @@ -192,35 +209,46 @@ public: * * The complete room alias will become the canonical alias for * the room. + * * \param name * If this is included, an ``m.room.name`` event will be sent * into the room to indicate the name of the room. See Room * Events for more information on ``m.room.name``. + * * \param topic * If this is included, an ``m.room.topic`` event will be sent * into the room to indicate the topic for the room. See Room * Events for more information on ``m.room.topic``. + * * \param invite * A list of user IDs to invite to the room. This will tell the * server to invite everyone in the list to the newly created room. + * * \param invite3pid * A list of objects representing third party IDs to invite into * the room. + * * \param roomVersion * The room version to set for the room. If not provided, the homeserver * is to use its configured default. If provided, the homeserver will return * a 400 error with the errcode ``M_UNSUPPORTED_ROOM_VERSION`` if it does - * not support the room version. \param creationContent Extra keys, such as - * ``m.federate``, to be added to the content of the `m.room.create`_ event. - * The server will clobber the following keys: ``creator``, - * ``room_version``. Future versions of the specification may allow the - * server to clobber other keys. \param initialState A list of state events - * to set in the new room. This allows the user to override the default - * state events set in the new room. The expected format of the state events - * are an object with type, state_key and content keys set. + * not support the room version. + * + * \param creationContent + * Extra keys, such as ``m.federate``, to be added to the content + * of the `m.room.create`_ event. The server will clobber the following + * keys: ``creator``, ``room_version``. Future versions of the + * specification may allow the server to clobber other keys. + * + * \param initialState + * A list of state events to set in the new room. This allows + * the user to override the default state events set in the new + * room. The expected format of the state events are an object + * with type, state_key and content keys set. * * Takes precedence over events set by ``preset``, but gets * overriden by ``name`` and ``topic`` keys. + * * \param preset * Convenience parameter for setting various default state events * based on a preset. @@ -229,10 +257,12 @@ public: * which preset to use. A visbility of ``public`` equates to a preset of * ``public_chat`` and ``private`` visibility equates to a preset of * ``private_chat``. + * * \param isDirect * This flag makes the server set the ``is_direct`` flag on the * ``m.room.member`` events sent to the users in ``invite`` and * ``invite_3pid``. See `Direct Messaging`_ for more information. + * * \param powerLevelContentOverride * The power level content to override in the default power level * event. This object is applied on top of the generated @@ -251,19 +281,31 @@ public: Omittable isDirect = none, const QJsonObject& powerLevelContentOverride = {}); - ~CreateRoomJob() override; - // Result properties /// The created room's ID. - const QString& roomId() const; + QString roomId() const { return loadFromJson("room_id"_ls); } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const CreateRoomJob::Invite3pid& pod) + { + addParam<>(jo, QStringLiteral("id_server"), pod.idServer); + addParam<>(jo, QStringLiteral("id_access_token"), pod.idAccessToken); + addParam<>(jo, QStringLiteral("medium"), pod.medium); + addParam<>(jo, QStringLiteral("address"), pod.address); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const CreateRoomJob::StateEvent& pod) + { + addParam<>(jo, QStringLiteral("type"), pod.type); + addParam(jo, QStringLiteral("state_key"), pod.stateKey); + addParam<>(jo, QStringLiteral("content"), pod.content); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/auth_data.cpp b/lib/csapi/definitions/auth_data.cpp deleted file mode 100644 index edeb7111..00000000 --- a/lib/csapi/definitions/auth_data.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "auth_data.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const AuthenticationData& pod) -{ - fillJson(jo, pod.authInfo); - addParam<>(jo, QStringLiteral("type"), pod.type); - addParam(jo, QStringLiteral("session"), pod.session); -} - -void JsonObjectConverter::fillFrom(QJsonObject jo, - AuthenticationData& result) -{ - fromJson(jo.take("type"_ls), result.type); - fromJson(jo.take("session"_ls), result.session); - fromJson(jo, result.authInfo); -} diff --git a/lib/csapi/definitions/auth_data.h b/lib/csapi/definitions/auth_data.h index e564f7f3..e92596d0 100644 --- a/lib/csapi/definitions/auth_data.h +++ b/lib/csapi/definitions/auth_data.h @@ -6,13 +6,7 @@ #include "converters.h" -#include -#include - namespace Quotient { - -// Data structures - /// Used by clients to submit authentication information to the /// interactive-authentication API struct AuthenticationData { @@ -28,8 +22,18 @@ struct AuthenticationData { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const AuthenticationData& pod); - static void fillFrom(QJsonObject jo, AuthenticationData& pod); + static void dumpTo(QJsonObject& jo, const AuthenticationData& pod) + { + fillJson(jo, pod.authInfo); + addParam<>(jo, QStringLiteral("type"), pod.type); + addParam(jo, QStringLiteral("session"), pod.session); + } + static void fillFrom(QJsonObject jo, AuthenticationData& pod) + { + fromJson(jo.take("type"_ls), pod.type); + fromJson(jo.take("session"_ls), pod.session); + fromJson(jo, pod.authInfo); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/client_device.cpp b/lib/csapi/definitions/client_device.cpp deleted file mode 100644 index 09544138..00000000 --- a/lib/csapi/definitions/client_device.cpp +++ /dev/null @@ -1,23 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "client_device.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const Device& pod) -{ - addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); - addParam(jo, QStringLiteral("display_name"), pod.displayName); - addParam(jo, QStringLiteral("last_seen_ip"), pod.lastSeenIp); - addParam(jo, QStringLiteral("last_seen_ts"), pod.lastSeenTs); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, Device& result) -{ - fromJson(jo.value("device_id"_ls), result.deviceId); - fromJson(jo.value("display_name"_ls), result.displayName); - fromJson(jo.value("last_seen_ip"_ls), result.lastSeenIp); - fromJson(jo.value("last_seen_ts"_ls), result.lastSeenTs); -} diff --git a/lib/csapi/definitions/client_device.h b/lib/csapi/definitions/client_device.h index 2cf75950..a5ab1bfc 100644 --- a/lib/csapi/definitions/client_device.h +++ b/lib/csapi/definitions/client_device.h @@ -7,9 +7,6 @@ #include "converters.h" namespace Quotient { - -// Data structures - /// A client device struct Device { /// Identifier of this device. @@ -31,8 +28,21 @@ struct Device { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const Device& pod); - static void fillFrom(const QJsonObject& jo, Device& pod); + static void dumpTo(QJsonObject& jo, const Device& pod) + { + addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); + addParam(jo, QStringLiteral("display_name"), + pod.displayName); + addParam(jo, QStringLiteral("last_seen_ip"), pod.lastSeenIp); + addParam(jo, QStringLiteral("last_seen_ts"), pod.lastSeenTs); + } + static void fillFrom(const QJsonObject& jo, Device& pod) + { + fromJson(jo.value("device_id"_ls), pod.deviceId); + fromJson(jo.value("display_name"_ls), pod.displayName); + fromJson(jo.value("last_seen_ip"_ls), pod.lastSeenIp); + fromJson(jo.value("last_seen_ts"_ls), pod.lastSeenTs); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/device_keys.cpp b/lib/csapi/definitions/device_keys.cpp deleted file mode 100644 index 0583840d..00000000 --- a/lib/csapi/definitions/device_keys.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "device_keys.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const DeviceKeys& pod) -{ - addParam<>(jo, QStringLiteral("user_id"), pod.userId); - addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); - addParam<>(jo, QStringLiteral("algorithms"), pod.algorithms); - addParam<>(jo, QStringLiteral("keys"), pod.keys); - addParam<>(jo, QStringLiteral("signatures"), pod.signatures); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - DeviceKeys& result) -{ - fromJson(jo.value("user_id"_ls), result.userId); - fromJson(jo.value("device_id"_ls), result.deviceId); - fromJson(jo.value("algorithms"_ls), result.algorithms); - fromJson(jo.value("keys"_ls), result.keys); - fromJson(jo.value("signatures"_ls), result.signatures); -} diff --git a/lib/csapi/definitions/device_keys.h b/lib/csapi/definitions/device_keys.h index a067b4f3..3065f218 100644 --- a/lib/csapi/definitions/device_keys.h +++ b/lib/csapi/definitions/device_keys.h @@ -6,12 +6,7 @@ #include "converters.h" -#include - namespace Quotient { - -// Data structures - /// Device identity keys struct DeviceKeys { /// The ID of the user the device belongs to. Must match the user ID used @@ -40,8 +35,22 @@ struct DeviceKeys { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const DeviceKeys& pod); - static void fillFrom(const QJsonObject& jo, DeviceKeys& pod); + static void dumpTo(QJsonObject& jo, const DeviceKeys& pod) + { + addParam<>(jo, QStringLiteral("user_id"), pod.userId); + addParam<>(jo, QStringLiteral("device_id"), pod.deviceId); + addParam<>(jo, QStringLiteral("algorithms"), pod.algorithms); + addParam<>(jo, QStringLiteral("keys"), pod.keys); + addParam<>(jo, QStringLiteral("signatures"), pod.signatures); + } + static void fillFrom(const QJsonObject& jo, DeviceKeys& pod) + { + fromJson(jo.value("user_id"_ls), pod.userId); + fromJson(jo.value("device_id"_ls), pod.deviceId); + fromJson(jo.value("algorithms"_ls), pod.algorithms); + fromJson(jo.value("keys"_ls), pod.keys); + fromJson(jo.value("signatures"_ls), pod.signatures); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/event_filter.cpp b/lib/csapi/definitions/event_filter.cpp deleted file mode 100644 index b21e08b5..00000000 --- a/lib/csapi/definitions/event_filter.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "event_filter.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const EventFilter& pod) -{ - addParam(jo, QStringLiteral("limit"), pod.limit); - addParam(jo, QStringLiteral("not_senders"), pod.notSenders); - addParam(jo, QStringLiteral("not_types"), pod.notTypes); - addParam(jo, QStringLiteral("senders"), pod.senders); - addParam(jo, QStringLiteral("types"), pod.types); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - EventFilter& result) -{ - fromJson(jo.value("limit"_ls), result.limit); - fromJson(jo.value("not_senders"_ls), result.notSenders); - fromJson(jo.value("not_types"_ls), result.notTypes); - fromJson(jo.value("senders"_ls), result.senders); - fromJson(jo.value("types"_ls), result.types); -} diff --git a/lib/csapi/definitions/event_filter.h b/lib/csapi/definitions/event_filter.h index 3958b125..67497412 100644 --- a/lib/csapi/definitions/event_filter.h +++ b/lib/csapi/definitions/event_filter.h @@ -8,8 +8,6 @@ namespace Quotient { -// Data structures - struct EventFilter { /// The maximum number of events to return. Omittable limit; @@ -37,8 +35,22 @@ struct EventFilter { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const EventFilter& pod); - static void fillFrom(const QJsonObject& jo, EventFilter& pod); + static void dumpTo(QJsonObject& jo, const EventFilter& pod) + { + addParam(jo, QStringLiteral("limit"), pod.limit); + addParam(jo, QStringLiteral("not_senders"), pod.notSenders); + addParam(jo, QStringLiteral("not_types"), pod.notTypes); + addParam(jo, QStringLiteral("senders"), pod.senders); + addParam(jo, QStringLiteral("types"), pod.types); + } + static void fillFrom(const QJsonObject& jo, EventFilter& pod) + { + fromJson(jo.value("limit"_ls), pod.limit); + fromJson(jo.value("not_senders"_ls), pod.notSenders); + fromJson(jo.value("not_types"_ls), pod.notTypes); + fromJson(jo.value("senders"_ls), pod.senders); + fromJson(jo.value("types"_ls), pod.types); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/openid_token.h b/lib/csapi/definitions/openid_token.h new file mode 100644 index 00000000..5e68c376 --- /dev/null +++ b/lib/csapi/definitions/openid_token.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ + +#pragma once + +#include "converters.h" + +namespace Quotient { + +struct OpenidToken { + /// An access token the consumer may use to verify the identity of + /// the person who generated the token. This is given to the federation + /// API ``GET /openid/userinfo`` to verify the user's identity. + QString accessToken; + + /// The string ``Bearer``. + QString tokenType; + + /// The homeserver domain the consumer should use when attempting to + /// verify the user's identity. + QString matrixServerName; + + /// The number of seconds before this token expires and a new one must + /// be generated. + int expiresIn; +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const OpenidToken& pod) + { + addParam<>(jo, QStringLiteral("access_token"), pod.accessToken); + addParam<>(jo, QStringLiteral("token_type"), pod.tokenType); + addParam<>(jo, QStringLiteral("matrix_server_name"), + pod.matrixServerName); + addParam<>(jo, QStringLiteral("expires_in"), pod.expiresIn); + } + static void fillFrom(const QJsonObject& jo, OpenidToken& pod) + { + fromJson(jo.value("access_token"_ls), pod.accessToken); + fromJson(jo.value("token_type"_ls), pod.tokenType); + fromJson(jo.value("matrix_server_name"_ls), pod.matrixServerName); + fromJson(jo.value("expires_in"_ls), pod.expiresIn); + } +}; + +} // namespace Quotient diff --git a/lib/csapi/definitions/public_rooms_response.cpp b/lib/csapi/definitions/public_rooms_response.cpp deleted file mode 100644 index b6009718..00000000 --- a/lib/csapi/definitions/public_rooms_response.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "public_rooms_response.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const PublicRoomsChunk& pod) -{ - addParam(jo, QStringLiteral("aliases"), pod.aliases); - addParam(jo, QStringLiteral("canonical_alias"), - pod.canonicalAlias); - addParam(jo, QStringLiteral("name"), pod.name); - addParam<>(jo, QStringLiteral("num_joined_members"), pod.numJoinedMembers); - addParam<>(jo, QStringLiteral("room_id"), pod.roomId); - addParam(jo, QStringLiteral("topic"), pod.topic); - addParam<>(jo, QStringLiteral("world_readable"), pod.worldReadable); - addParam<>(jo, QStringLiteral("guest_can_join"), pod.guestCanJoin); - addParam(jo, QStringLiteral("avatar_url"), pod.avatarUrl); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - PublicRoomsChunk& result) -{ - fromJson(jo.value("aliases"_ls), result.aliases); - fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); - fromJson(jo.value("name"_ls), result.name); - fromJson(jo.value("num_joined_members"_ls), result.numJoinedMembers); - fromJson(jo.value("room_id"_ls), result.roomId); - fromJson(jo.value("topic"_ls), result.topic); - fromJson(jo.value("world_readable"_ls), result.worldReadable); - fromJson(jo.value("guest_can_join"_ls), result.guestCanJoin); - fromJson(jo.value("avatar_url"_ls), result.avatarUrl); -} - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const PublicRoomsResponse& pod) -{ - addParam<>(jo, QStringLiteral("chunk"), pod.chunk); - addParam(jo, QStringLiteral("next_batch"), pod.nextBatch); - addParam(jo, QStringLiteral("prev_batch"), pod.prevBatch); - addParam(jo, QStringLiteral("total_room_count_estimate"), - pod.totalRoomCountEstimate); -} - -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, PublicRoomsResponse& result) -{ - fromJson(jo.value("chunk"_ls), result.chunk); - fromJson(jo.value("next_batch"_ls), result.nextBatch); - fromJson(jo.value("prev_batch"_ls), result.prevBatch); - fromJson(jo.value("total_room_count_estimate"_ls), - result.totalRoomCountEstimate); -} diff --git a/lib/csapi/definitions/public_rooms_response.h b/lib/csapi/definitions/public_rooms_response.h index 36aa52b9..8f30e607 100644 --- a/lib/csapi/definitions/public_rooms_response.h +++ b/lib/csapi/definitions/public_rooms_response.h @@ -6,12 +6,8 @@ #include "converters.h" -#include - namespace Quotient { -// Data structures - struct PublicRoomsChunk { /// Aliases of the room. May be empty. QStringList aliases; @@ -45,9 +41,34 @@ struct PublicRoomsChunk { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod); - static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod); + static void dumpTo(QJsonObject& jo, const PublicRoomsChunk& pod) + { + addParam(jo, QStringLiteral("aliases"), pod.aliases); + addParam(jo, QStringLiteral("canonical_alias"), + pod.canonicalAlias); + addParam(jo, QStringLiteral("name"), pod.name); + addParam<>(jo, QStringLiteral("num_joined_members"), + pod.numJoinedMembers); + addParam<>(jo, QStringLiteral("room_id"), pod.roomId); + addParam(jo, QStringLiteral("topic"), pod.topic); + addParam<>(jo, QStringLiteral("world_readable"), pod.worldReadable); + addParam<>(jo, QStringLiteral("guest_can_join"), pod.guestCanJoin); + addParam(jo, QStringLiteral("avatar_url"), pod.avatarUrl); + } + static void fillFrom(const QJsonObject& jo, PublicRoomsChunk& pod) + { + fromJson(jo.value("aliases"_ls), pod.aliases); + fromJson(jo.value("canonical_alias"_ls), pod.canonicalAlias); + fromJson(jo.value("name"_ls), pod.name); + fromJson(jo.value("num_joined_members"_ls), pod.numJoinedMembers); + fromJson(jo.value("room_id"_ls), pod.roomId); + fromJson(jo.value("topic"_ls), pod.topic); + fromJson(jo.value("world_readable"_ls), pod.worldReadable); + fromJson(jo.value("guest_can_join"_ls), pod.guestCanJoin); + fromJson(jo.value("avatar_url"_ls), pod.avatarUrl); + } }; + /// A list of the rooms on the server. struct PublicRoomsResponse { /// A paginated chunk of public rooms. @@ -70,8 +91,22 @@ struct PublicRoomsResponse { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod); - static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod); + static void dumpTo(QJsonObject& jo, const PublicRoomsResponse& pod) + { + addParam<>(jo, QStringLiteral("chunk"), pod.chunk); + addParam(jo, QStringLiteral("next_batch"), pod.nextBatch); + addParam(jo, QStringLiteral("prev_batch"), pod.prevBatch); + addParam(jo, QStringLiteral("total_room_count_estimate"), + pod.totalRoomCountEstimate); + } + static void fillFrom(const QJsonObject& jo, PublicRoomsResponse& pod) + { + fromJson(jo.value("chunk"_ls), pod.chunk); + fromJson(jo.value("next_batch"_ls), pod.nextBatch); + fromJson(jo.value("prev_batch"_ls), pod.prevBatch); + fromJson(jo.value("total_room_count_estimate"_ls), + pod.totalRoomCountEstimate); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/push_condition.cpp b/lib/csapi/definitions/push_condition.cpp deleted file mode 100644 index 343b4f1a..00000000 --- a/lib/csapi/definitions/push_condition.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "push_condition.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const PushCondition& pod) -{ - addParam<>(jo, QStringLiteral("kind"), pod.kind); - addParam(jo, QStringLiteral("key"), pod.key); - addParam(jo, QStringLiteral("pattern"), pod.pattern); - addParam(jo, QStringLiteral("is"), pod.is); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - PushCondition& result) -{ - fromJson(jo.value("kind"_ls), result.kind); - fromJson(jo.value("key"_ls), result.key); - fromJson(jo.value("pattern"_ls), result.pattern); - fromJson(jo.value("is"_ls), result.is); -} diff --git a/lib/csapi/definitions/push_condition.h b/lib/csapi/definitions/push_condition.h index 189153b3..a6decf1b 100644 --- a/lib/csapi/definitions/push_condition.h +++ b/lib/csapi/definitions/push_condition.h @@ -8,13 +8,18 @@ namespace Quotient { -// Data structures - struct PushCondition { + /// The kind of condition to apply. See `conditions <#conditions>`_ for + /// more information on the allowed kinds and how they work. QString kind; /// Required for ``event_match`` conditions. The dot-separated field of the /// event to match. + /// + /// Required for ``sender_notification_permission`` conditions. The field in + /// the power level event the user needs a minimum power level for. Fields + /// must be specified under the ``notifications`` property in the power + /// level event's ``content``. QString key; /// Required for ``event_match`` conditions. The glob-style pattern to @@ -32,8 +37,20 @@ struct PushCondition { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PushCondition& pod); - static void fillFrom(const QJsonObject& jo, PushCondition& pod); + static void dumpTo(QJsonObject& jo, const PushCondition& pod) + { + addParam<>(jo, QStringLiteral("kind"), pod.kind); + addParam(jo, QStringLiteral("key"), pod.key); + addParam(jo, QStringLiteral("pattern"), pod.pattern); + addParam(jo, QStringLiteral("is"), pod.is); + } + static void fillFrom(const QJsonObject& jo, PushCondition& pod) + { + fromJson(jo.value("kind"_ls), pod.kind); + fromJson(jo.value("key"_ls), pod.key); + fromJson(jo.value("pattern"_ls), pod.pattern); + fromJson(jo.value("is"_ls), pod.is); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/push_rule.cpp b/lib/csapi/definitions/push_rule.cpp deleted file mode 100644 index eae7e446..00000000 --- a/lib/csapi/definitions/push_rule.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "push_rule.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const PushRule& pod) -{ - addParam<>(jo, QStringLiteral("actions"), pod.actions); - addParam<>(jo, QStringLiteral("default"), pod.isDefault); - addParam<>(jo, QStringLiteral("enabled"), pod.enabled); - addParam<>(jo, QStringLiteral("rule_id"), pod.ruleId); - addParam(jo, QStringLiteral("conditions"), pod.conditions); - addParam(jo, QStringLiteral("pattern"), pod.pattern); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - PushRule& result) -{ - fromJson(jo.value("actions"_ls), result.actions); - fromJson(jo.value("default"_ls), result.isDefault); - fromJson(jo.value("enabled"_ls), result.enabled); - fromJson(jo.value("rule_id"_ls), result.ruleId); - fromJson(jo.value("conditions"_ls), result.conditions); - fromJson(jo.value("pattern"_ls), result.pattern); -} diff --git a/lib/csapi/definitions/push_rule.h b/lib/csapi/definitions/push_rule.h index c09d063f..43749bae 100644 --- a/lib/csapi/definitions/push_rule.h +++ b/lib/csapi/definitions/push_rule.h @@ -8,14 +8,8 @@ #include "csapi/definitions/push_condition.h" -#include -#include -#include - namespace Quotient { -// Data structures - struct PushRule { /// The actions to perform when this rule is matched. QVector actions; @@ -41,8 +35,24 @@ struct PushRule { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PushRule& pod); - static void fillFrom(const QJsonObject& jo, PushRule& pod); + static void dumpTo(QJsonObject& jo, const PushRule& pod) + { + addParam<>(jo, QStringLiteral("actions"), pod.actions); + addParam<>(jo, QStringLiteral("default"), pod.isDefault); + addParam<>(jo, QStringLiteral("enabled"), pod.enabled); + addParam<>(jo, QStringLiteral("rule_id"), pod.ruleId); + addParam(jo, QStringLiteral("conditions"), pod.conditions); + addParam(jo, QStringLiteral("pattern"), pod.pattern); + } + static void fillFrom(const QJsonObject& jo, PushRule& pod) + { + fromJson(jo.value("actions"_ls), pod.actions); + fromJson(jo.value("default"_ls), pod.isDefault); + fromJson(jo.value("enabled"_ls), pod.enabled); + fromJson(jo.value("rule_id"_ls), pod.ruleId); + fromJson(jo.value("conditions"_ls), pod.conditions); + fromJson(jo.value("pattern"_ls), pod.pattern); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/push_ruleset.cpp b/lib/csapi/definitions/push_ruleset.cpp deleted file mode 100644 index a2db35d9..00000000 --- a/lib/csapi/definitions/push_ruleset.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "push_ruleset.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const PushRuleset& pod) -{ - addParam(jo, QStringLiteral("content"), pod.content); - addParam(jo, QStringLiteral("override"), pod.override); - addParam(jo, QStringLiteral("room"), pod.room); - addParam(jo, QStringLiteral("sender"), pod.sender); - addParam(jo, QStringLiteral("underride"), pod.underride); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - PushRuleset& result) -{ - fromJson(jo.value("content"_ls), result.content); - fromJson(jo.value("override"_ls), result.override); - fromJson(jo.value("room"_ls), result.room); - fromJson(jo.value("sender"_ls), result.sender); - fromJson(jo.value("underride"_ls), result.underride); -} diff --git a/lib/csapi/definitions/push_ruleset.h b/lib/csapi/definitions/push_ruleset.h index 98a21408..ba780a33 100644 --- a/lib/csapi/definitions/push_ruleset.h +++ b/lib/csapi/definitions/push_ruleset.h @@ -8,12 +8,8 @@ #include "csapi/definitions/push_rule.h" -#include - namespace Quotient { -// Data structures - struct PushRuleset { QVector content; @@ -28,8 +24,22 @@ struct PushRuleset { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PushRuleset& pod); - static void fillFrom(const QJsonObject& jo, PushRuleset& pod); + static void dumpTo(QJsonObject& jo, const PushRuleset& pod) + { + addParam(jo, QStringLiteral("content"), pod.content); + addParam(jo, QStringLiteral("override"), pod.override); + addParam(jo, QStringLiteral("room"), pod.room); + addParam(jo, QStringLiteral("sender"), pod.sender); + addParam(jo, QStringLiteral("underride"), pod.underride); + } + static void fillFrom(const QJsonObject& jo, PushRuleset& pod) + { + fromJson(jo.value("content"_ls), pod.content); + fromJson(jo.value("override"_ls), pod.override); + fromJson(jo.value("room"_ls), pod.room); + fromJson(jo.value("sender"_ls), pod.sender); + fromJson(jo.value("underride"_ls), pod.underride); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/request_email_validation.h b/lib/csapi/definitions/request_email_validation.h new file mode 100644 index 00000000..e6ab261d --- /dev/null +++ b/lib/csapi/definitions/request_email_validation.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ + +#pragma once + +#include "converters.h" + +#include "csapi/./definitions/../../identity/definitions/request_email_validation.h" + +namespace Quotient { + +struct EmailValidationData : RequestEmailValidation { + /// The hostname of the identity server to communicate with. May optionally + /// include a port. This parameter is ignored when the homeserver handles + /// 3PID verification. + /// + /// This parameter is deprecated with a plan to be removed in a future + /// specification version for ``/account/password`` and ``/register`` + /// requests. + QString idServer; + + /// An access token previously registered with the identity server. Servers + /// can treat this as optional to distinguish between r0.5-compatible + /// clients and this specification version. + /// + /// Required if an ``id_server`` is supplied. + QString idAccessToken; +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const EmailValidationData& pod) + { + fillJson(jo, pod); + addParam(jo, QStringLiteral("id_server"), pod.idServer); + addParam(jo, QStringLiteral("id_access_token"), + pod.idAccessToken); + } + static void fillFrom(const QJsonObject& jo, EmailValidationData& pod) + { + fillFromJson(jo, pod); + fromJson(jo.value("id_server"_ls), pod.idServer); + fromJson(jo.value("id_access_token"_ls), pod.idAccessToken); + } +}; + +} // namespace Quotient diff --git a/lib/csapi/definitions/request_msisdn_validation.h b/lib/csapi/definitions/request_msisdn_validation.h new file mode 100644 index 00000000..6f161f00 --- /dev/null +++ b/lib/csapi/definitions/request_msisdn_validation.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ + +#pragma once + +#include "converters.h" + +#include "csapi/./definitions/../../identity/definitions/request_msisdn_validation.h" + +namespace Quotient { + +struct MsisdnValidationData : RequestMsisdnValidation { + /// The hostname of the identity server to communicate with. May optionally + /// include a port. This parameter is ignored when the homeserver handles + /// 3PID verification. + /// + /// This parameter is deprecated with a plan to be removed in a future + /// specification version for ``/account/password`` and ``/register`` + /// requests. + QString idServer; + + /// An access token previously registered with the identity server. Servers + /// can treat this as optional to distinguish between r0.5-compatible + /// clients and this specification version. + /// + /// Required if an ``id_server`` is supplied. + QString idAccessToken; +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const MsisdnValidationData& pod) + { + fillJson(jo, pod); + addParam(jo, QStringLiteral("id_server"), pod.idServer); + addParam(jo, QStringLiteral("id_access_token"), + pod.idAccessToken); + } + static void fillFrom(const QJsonObject& jo, MsisdnValidationData& pod) + { + fillFromJson(jo, pod); + fromJson(jo.value("id_server"_ls), pod.idServer); + fromJson(jo.value("id_access_token"_ls), pod.idAccessToken); + } +}; + +} // namespace Quotient diff --git a/lib/csapi/definitions/request_token_response.h b/lib/csapi/definitions/request_token_response.h new file mode 100644 index 00000000..00222839 --- /dev/null +++ b/lib/csapi/definitions/request_token_response.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ + +#pragma once + +#include "converters.h" + +namespace Quotient { + +struct RequestTokenResponse { + /// The session ID. Session IDs are opaque strings that must consist + /// entirely of the characters ``[0-9a-zA-Z.=_-]``. Their length must not + /// exceed 255 characters and they must not be empty. + QString sid; + + /// An optional field containing a URL where the client must submit the + /// validation token to, with identical parameters to the Identity Service + /// API's ``POST /validate/email/submitToken`` endpoint (without the + /// requirement for an access token). The homeserver must send this token to + /// the user (if applicable), who should then be prompted to provide it to + /// the client. + /// + /// If this field is not present, the client can assume that verification + /// will happen without the client's involvement provided the homeserver + /// advertises this specification version in the ``/versions`` response + /// (ie: r0.5.0). + QString submitUrl; +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const RequestTokenResponse& pod) + { + addParam<>(jo, QStringLiteral("sid"), pod.sid); + addParam(jo, QStringLiteral("submit_url"), pod.submitUrl); + } + static void fillFrom(const QJsonObject& jo, RequestTokenResponse& pod) + { + fromJson(jo.value("sid"_ls), pod.sid); + fromJson(jo.value("submit_url"_ls), pod.submitUrl); + } +}; + +} // namespace Quotient diff --git a/lib/csapi/definitions/room_event_filter.cpp b/lib/csapi/definitions/room_event_filter.cpp deleted file mode 100644 index 5613d8d2..00000000 --- a/lib/csapi/definitions/room_event_filter.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "room_event_filter.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const RoomEventFilter& pod) -{ - fillJson(jo, pod); - addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); - addParam(jo, QStringLiteral("rooms"), pod.rooms); - addParam(jo, QStringLiteral("contains_url"), pod.containsUrl); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - RoomEventFilter& result) -{ - fillFromJson(jo, result); - fromJson(jo.value("not_rooms"_ls), result.notRooms); - fromJson(jo.value("rooms"_ls), result.rooms); - fromJson(jo.value("contains_url"_ls), result.containsUrl); -} diff --git a/lib/csapi/definitions/room_event_filter.h b/lib/csapi/definitions/room_event_filter.h index 756f9ada..11e87fde 100644 --- a/lib/csapi/definitions/room_event_filter.h +++ b/lib/csapi/definitions/room_event_filter.h @@ -10,9 +10,19 @@ namespace Quotient { -// Data structures - struct RoomEventFilter : EventFilter { + /// If ``true``, enables lazy-loading of membership events. See + /// `Lazy-loading room members <#lazy-loading-room-members>`_ + /// for more information. Defaults to ``false``. + Omittable lazyLoadMembers; + + /// If ``true``, sends all membership events for all events, even if they + /// have already been sent to the client. Does not apply unless + /// ``lazy_load_members`` is ``true``. See `Lazy-loading room members + /// <#lazy-loading-room-members>`_ for more information. Defaults to + /// ``false``. + Omittable includeRedundantMembers; + /// A list of room IDs to exclude. If this list is absent then no rooms are /// excluded. A matching room will be excluded even if it is listed in the /// ``'rooms'`` filter. @@ -30,8 +40,28 @@ struct RoomEventFilter : EventFilter { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod); - static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod); + static void dumpTo(QJsonObject& jo, const RoomEventFilter& pod) + { + fillJson(jo, pod); + addParam(jo, QStringLiteral("lazy_load_members"), + pod.lazyLoadMembers); + addParam(jo, QStringLiteral("include_redundant_members"), + pod.includeRedundantMembers); + addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); + addParam(jo, QStringLiteral("rooms"), pod.rooms); + addParam(jo, QStringLiteral("contains_url"), + pod.containsUrl); + } + static void fillFrom(const QJsonObject& jo, RoomEventFilter& pod) + { + fillFromJson(jo, pod); + fromJson(jo.value("lazy_load_members"_ls), pod.lazyLoadMembers); + fromJson(jo.value("include_redundant_members"_ls), + pod.includeRedundantMembers); + fromJson(jo.value("not_rooms"_ls), pod.notRooms); + fromJson(jo.value("rooms"_ls), pod.rooms); + fromJson(jo.value("contains_url"_ls), pod.containsUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/sync_filter.cpp b/lib/csapi/definitions/sync_filter.cpp deleted file mode 100644 index 15c4bdc1..00000000 --- a/lib/csapi/definitions/sync_filter.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "sync_filter.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const StateFilter& pod) -{ - fillJson(jo, pod); - addParam(jo, QStringLiteral("lazy_load_members"), - pod.lazyLoadMembers); - addParam(jo, QStringLiteral("include_redundant_members"), - pod.includeRedundantMembers); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - StateFilter& result) -{ - fillFromJson(jo, result); - fromJson(jo.value("lazy_load_members"_ls), result.lazyLoadMembers); - fromJson(jo.value("include_redundant_members"_ls), - result.includeRedundantMembers); -} - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const RoomFilter& pod) -{ - addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); - addParam(jo, QStringLiteral("rooms"), pod.rooms); - addParam(jo, QStringLiteral("ephemeral"), pod.ephemeral); - addParam(jo, QStringLiteral("include_leave"), pod.includeLeave); - addParam(jo, QStringLiteral("state"), pod.state); - addParam(jo, QStringLiteral("timeline"), pod.timeline); - addParam(jo, QStringLiteral("account_data"), pod.accountData); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, - RoomFilter& result) -{ - fromJson(jo.value("not_rooms"_ls), result.notRooms); - fromJson(jo.value("rooms"_ls), result.rooms); - fromJson(jo.value("ephemeral"_ls), result.ephemeral); - fromJson(jo.value("include_leave"_ls), result.includeLeave); - fromJson(jo.value("state"_ls), result.state); - fromJson(jo.value("timeline"_ls), result.timeline); - fromJson(jo.value("account_data"_ls), result.accountData); -} - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const Filter& pod) -{ - addParam(jo, QStringLiteral("event_fields"), pod.eventFields); - addParam(jo, QStringLiteral("event_format"), pod.eventFormat); - addParam(jo, QStringLiteral("presence"), pod.presence); - addParam(jo, QStringLiteral("account_data"), pod.accountData); - addParam(jo, QStringLiteral("room"), pod.room); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, Filter& result) -{ - fromJson(jo.value("event_fields"_ls), result.eventFields); - fromJson(jo.value("event_format"_ls), result.eventFormat); - fromJson(jo.value("presence"_ls), result.presence); - fromJson(jo.value("account_data"_ls), result.accountData); - fromJson(jo.value("room"_ls), result.room); -} diff --git a/lib/csapi/definitions/sync_filter.h b/lib/csapi/definitions/sync_filter.h index ad8d055d..9c8f08d5 100644 --- a/lib/csapi/definitions/sync_filter.h +++ b/lib/csapi/definitions/sync_filter.h @@ -10,40 +10,6 @@ #include "csapi/definitions/room_event_filter.h" namespace Quotient { - -// Data structures - -/// The state events to include for rooms. -struct StateFilter : RoomEventFilter { - /// If ``true``, the only ``m.room.member`` events returned in - /// the ``state`` section of the ``/sync`` response are those - /// which are definitely necessary for a client to display - /// the ``sender`` of the timeline events in that response. - /// If ``false``, ``m.room.member`` events are not filtered. - /// By default, servers should suppress duplicate redundant - /// lazy-loaded ``m.room.member`` events from being sent to a given - /// client across multiple calls to ``/sync``, given that most clients - /// cache membership events (see ``include_redundant_members`` - /// to change this behaviour). - Omittable lazyLoadMembers; - - /// If ``true``, the ``state`` section of the ``/sync`` response will - /// always contain the ``m.room.member`` events required to display - /// the ``sender`` of the timeline events in that response, assuming - /// ``lazy_load_members`` is enabled. This means that redundant - /// duplicate member events may be returned across multiple calls to - /// ``/sync``. This is useful for naive clients who never track - /// membership data. If ``false``, duplicate ``m.room.member`` events - /// may be suppressed by the server across multiple calls to ``/sync``. - /// If ``lazy_load_members`` is ``false`` this field is ignored. - Omittable includeRedundantMembers; -}; - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const StateFilter& pod); - static void fillFrom(const QJsonObject& jo, StateFilter& pod); -}; /// Filters to be applied to room data. struct RoomFilter { /// A list of room IDs to exclude. If this list is absent then no rooms are @@ -59,30 +25,50 @@ struct RoomFilter { /// The events that aren't recorded in the room history, e.g. typing and /// receipts, to include for rooms. - Omittable ephemeral; + RoomEventFilter ephemeral; /// Include rooms that the user has left in the sync, default false Omittable includeLeave; /// The state events to include for rooms. - Omittable state; + RoomEventFilter state; /// The message and state update events to include for rooms. - Omittable timeline; + RoomEventFilter timeline; /// The per user account data to include for rooms. - Omittable accountData; + RoomEventFilter accountData; }; template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const RoomFilter& pod); - static void fillFrom(const QJsonObject& jo, RoomFilter& pod); + static void dumpTo(QJsonObject& jo, const RoomFilter& pod) + { + addParam(jo, QStringLiteral("not_rooms"), pod.notRooms); + addParam(jo, QStringLiteral("rooms"), pod.rooms); + addParam(jo, QStringLiteral("ephemeral"), pod.ephemeral); + addParam(jo, QStringLiteral("include_leave"), + pod.includeLeave); + addParam(jo, QStringLiteral("state"), pod.state); + addParam(jo, QStringLiteral("timeline"), pod.timeline); + addParam(jo, QStringLiteral("account_data"), + pod.accountData); + } + static void fillFrom(const QJsonObject& jo, RoomFilter& pod) + { + fromJson(jo.value("not_rooms"_ls), pod.notRooms); + fromJson(jo.value("rooms"_ls), pod.rooms); + fromJson(jo.value("ephemeral"_ls), pod.ephemeral); + fromJson(jo.value("include_leave"_ls), pod.includeLeave); + fromJson(jo.value("state"_ls), pod.state); + fromJson(jo.value("timeline"_ls), pod.timeline); + fromJson(jo.value("account_data"_ls), pod.accountData); + } }; struct Filter { /// List of event fields to include. If this list is absent then all fields - /// are included. The entries may include '.' charaters to indicate + /// are included. The entries may include '.' characters to indicate /// sub-fields. So ['content.body'] will include the 'body' field of the /// 'content' object. A literal '.' character in a field name may be escaped /// using a '\\'. A server may include more fields than were requested. @@ -90,23 +76,40 @@ struct Filter { /// The format to use for events. 'client' will return the events in a /// format suitable for clients. 'federation' will return the raw event as - /// receieved over federation. The default is 'client'. + /// received over federation. The default is 'client'. QString eventFormat; /// The presence updates to include. - Omittable presence; + EventFilter presence; /// The user account data that isn't associated with rooms to include. - Omittable accountData; + EventFilter accountData; /// Filters to be applied to room data. - Omittable room; + RoomFilter room; }; template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const Filter& pod); - static void fillFrom(const QJsonObject& jo, Filter& pod); + static void dumpTo(QJsonObject& jo, const Filter& pod) + { + addParam(jo, QStringLiteral("event_fields"), + pod.eventFields); + addParam(jo, QStringLiteral("event_format"), + pod.eventFormat); + addParam(jo, QStringLiteral("presence"), pod.presence); + addParam(jo, QStringLiteral("account_data"), + pod.accountData); + addParam(jo, QStringLiteral("room"), pod.room); + } + static void fillFrom(const QJsonObject& jo, Filter& pod) + { + fromJson(jo.value("event_fields"_ls), pod.eventFields); + fromJson(jo.value("event_format"_ls), pod.eventFormat); + fromJson(jo.value("presence"_ls), pod.presence); + fromJson(jo.value("account_data"_ls), pod.accountData); + fromJson(jo.value("room"_ls), pod.room); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/third_party_signed.h b/lib/csapi/definitions/third_party_signed.h new file mode 100644 index 00000000..526545d0 --- /dev/null +++ b/lib/csapi/definitions/third_party_signed.h @@ -0,0 +1,44 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ + +#pragma once + +#include "converters.h" + +namespace Quotient { +/// A signature of an ``m.third_party_invite`` token to prove that this user +/// owns a third party identity which has been invited to the room. +struct ThirdPartySigned { + /// The Matrix ID of the user who issued the invite. + QString sender; + + /// The Matrix ID of the invitee. + QString mxid; + + /// The state key of the m.third_party_invite event. + QString token; + + /// A signatures object containing a signature of the entire signed object. + QHash> signatures; +}; + +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const ThirdPartySigned& pod) + { + addParam<>(jo, QStringLiteral("sender"), pod.sender); + addParam<>(jo, QStringLiteral("mxid"), pod.mxid); + addParam<>(jo, QStringLiteral("token"), pod.token); + addParam<>(jo, QStringLiteral("signatures"), pod.signatures); + } + static void fillFrom(const QJsonObject& jo, ThirdPartySigned& pod) + { + fromJson(jo.value("sender"_ls), pod.sender); + fromJson(jo.value("mxid"_ls), pod.mxid); + fromJson(jo.value("token"_ls), pod.token); + fromJson(jo.value("signatures"_ls), pod.signatures); + } +}; + +} // namespace Quotient diff --git a/lib/csapi/definitions/user_identifier.cpp b/lib/csapi/definitions/user_identifier.cpp deleted file mode 100644 index 9e9b4fe9..00000000 --- a/lib/csapi/definitions/user_identifier.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "user_identifier.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, - const UserIdentifier& pod) -{ - fillJson(jo, pod.additionalProperties); - addParam<>(jo, QStringLiteral("type"), pod.type); -} - -void JsonObjectConverter::fillFrom(QJsonObject jo, - UserIdentifier& result) -{ - fromJson(jo.take("type"_ls), result.type); - fromJson(jo, result.additionalProperties); -} diff --git a/lib/csapi/definitions/user_identifier.h b/lib/csapi/definitions/user_identifier.h index 72a81677..dadf6f97 100644 --- a/lib/csapi/definitions/user_identifier.h +++ b/lib/csapi/definitions/user_identifier.h @@ -6,12 +6,7 @@ #include "converters.h" -#include - namespace Quotient { - -// Data structures - /// Identification information for a user struct UserIdentifier { /// The type of identification. See `Identifier types`_ for supported @@ -24,8 +19,16 @@ struct UserIdentifier { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const UserIdentifier& pod); - static void fillFrom(QJsonObject jo, UserIdentifier& pod); + static void dumpTo(QJsonObject& jo, const UserIdentifier& pod) + { + fillJson(jo, pod.additionalProperties); + addParam<>(jo, QStringLiteral("type"), pod.type); + } + static void fillFrom(QJsonObject jo, UserIdentifier& pod) + { + fromJson(jo.take("type"_ls), pod.type); + fromJson(jo, pod.additionalProperties); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/full.cpp b/lib/csapi/definitions/wellknown/full.cpp deleted file mode 100644 index 595db0e5..00000000 --- a/lib/csapi/definitions/wellknown/full.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "full.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const DiscoveryInformation& pod) -{ - fillJson(jo, pod.additionalProperties); - addParam<>(jo, QStringLiteral("m.homeserver"), pod.homeserver); - addParam(jo, QStringLiteral("m.identity_server"), - pod.identityServer); -} - -void JsonObjectConverter::fillFrom( - QJsonObject jo, DiscoveryInformation& result) -{ - fromJson(jo.take("m.homeserver"_ls), result.homeserver); - fromJson(jo.take("m.identity_server"_ls), result.identityServer); - fromJson(jo, result.additionalProperties); -} diff --git a/lib/csapi/definitions/wellknown/full.h b/lib/csapi/definitions/wellknown/full.h index 88d7da79..a0ef2076 100644 --- a/lib/csapi/definitions/wellknown/full.h +++ b/lib/csapi/definitions/wellknown/full.h @@ -9,13 +9,7 @@ #include "csapi/definitions/wellknown/homeserver.h" #include "csapi/definitions/wellknown/identity_server.h" -#include -#include - namespace Quotient { - -// Data structures - /// Used by clients to determine the homeserver, identity server, and other /// optional components they should be interacting with. struct DiscoveryInformation { @@ -33,8 +27,19 @@ struct DiscoveryInformation { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const DiscoveryInformation& pod); - static void fillFrom(QJsonObject jo, DiscoveryInformation& pod); + static void dumpTo(QJsonObject& jo, const DiscoveryInformation& pod) + { + fillJson(jo, pod.additionalProperties); + addParam<>(jo, QStringLiteral("m.homeserver"), pod.homeserver); + addParam(jo, QStringLiteral("m.identity_server"), + pod.identityServer); + } + static void fillFrom(QJsonObject jo, DiscoveryInformation& pod) + { + fromJson(jo.take("m.homeserver"_ls), pod.homeserver); + fromJson(jo.take("m.identity_server"_ls), pod.identityServer); + fromJson(jo, pod.additionalProperties); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/homeserver.cpp b/lib/csapi/definitions/wellknown/homeserver.cpp deleted file mode 100644 index 7b87aa95..00000000 --- a/lib/csapi/definitions/wellknown/homeserver.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "homeserver.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const HomeserverInformation& pod) -{ - addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); -} - -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, HomeserverInformation& result) -{ - fromJson(jo.value("base_url"_ls), result.baseUrl); -} diff --git a/lib/csapi/definitions/wellknown/homeserver.h b/lib/csapi/definitions/wellknown/homeserver.h index 7607c8b5..5cfaca24 100644 --- a/lib/csapi/definitions/wellknown/homeserver.h +++ b/lib/csapi/definitions/wellknown/homeserver.h @@ -7,9 +7,6 @@ #include "converters.h" namespace Quotient { - -// Data structures - /// Used by clients to discover homeserver information. struct HomeserverInformation { /// The base URL for the homeserver for client-server connections. @@ -18,8 +15,14 @@ struct HomeserverInformation { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod); - static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod); + static void dumpTo(QJsonObject& jo, const HomeserverInformation& pod) + { + addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); + } + static void fillFrom(const QJsonObject& jo, HomeserverInformation& pod) + { + fromJson(jo.value("base_url"_ls), pod.baseUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/definitions/wellknown/identity_server.cpp b/lib/csapi/definitions/wellknown/identity_server.cpp deleted file mode 100644 index 3b2a5720..00000000 --- a/lib/csapi/definitions/wellknown/identity_server.cpp +++ /dev/null @@ -1,19 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "identity_server.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const IdentityServerInformation& pod) -{ - addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); -} - -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, IdentityServerInformation& result) -{ - fromJson(jo.value("base_url"_ls), result.baseUrl); -} diff --git a/lib/csapi/definitions/wellknown/identity_server.h b/lib/csapi/definitions/wellknown/identity_server.h index d272e527..3bd07bd1 100644 --- a/lib/csapi/definitions/wellknown/identity_server.h +++ b/lib/csapi/definitions/wellknown/identity_server.h @@ -7,9 +7,6 @@ #include "converters.h" namespace Quotient { - -// Data structures - /// Used by clients to discover identity server information. struct IdentityServerInformation { /// The base URL for the identity server for client-server connections. @@ -18,8 +15,14 @@ struct IdentityServerInformation { template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod); - static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod); + static void dumpTo(QJsonObject& jo, const IdentityServerInformation& pod) + { + addParam<>(jo, QStringLiteral("base_url"), pod.baseUrl); + } + static void fillFrom(const QJsonObject& jo, IdentityServerInformation& pod) + { + fromJson(jo.value("base_url"_ls), pod.baseUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/device_management.cpp b/lib/csapi/device_management.cpp index ba7570f7..eac9a545 100644 --- a/lib/csapi/device_management.cpp +++ b/lib/csapi/device_management.cpp @@ -4,97 +4,61 @@ #include "device_management.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetDevicesJob::Private { -public: - QVector devices; -}; - QUrl GetDevicesJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/devices"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/devices"); } GetDevicesJob::GetDevicesJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetDevicesJob"), - basePath % "/devices") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/devices") {} -GetDevicesJob::~GetDevicesJob() = default; - -const QVector& GetDevicesJob::devices() const { return d->devices; } - -BaseJob::Status GetDevicesJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("devices"_ls), d->devices); - - return Success; -} - -class GetDeviceJob::Private { -public: - Device data; -}; - QUrl GetDeviceJob::makeRequestUrl(QUrl baseUrl, const QString& deviceId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/devices/" % deviceId); + QStringLiteral("/_matrix/client/r0") + % "/devices/" % deviceId); } GetDeviceJob::GetDeviceJob(const QString& deviceId) : BaseJob(HttpVerb::Get, QStringLiteral("GetDeviceJob"), - basePath % "/devices/" % deviceId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/devices/" % deviceId) {} -GetDeviceJob::~GetDeviceJob() = default; - -const Device& GetDeviceJob::data() const { return d->data; } - -BaseJob::Status GetDeviceJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - UpdateDeviceJob::UpdateDeviceJob(const QString& deviceId, const QString& displayName) : BaseJob(HttpVerb::Put, QStringLiteral("UpdateDeviceJob"), - basePath % "/devices/" % deviceId) + QStringLiteral("/_matrix/client/r0") % "/devices/" % deviceId) { QJsonObject _data; addParam(_data, QStringLiteral("display_name"), displayName); - setRequestData(_data); + setRequestData(std::move(_data)); } DeleteDeviceJob::DeleteDeviceJob(const QString& deviceId, const Omittable& auth) : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteDeviceJob"), - basePath % "/devices/" % deviceId) + QStringLiteral("/_matrix/client/r0") % "/devices/" % deviceId) { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); - setRequestData(_data); + setRequestData(std::move(_data)); } DeleteDevicesJob::DeleteDevicesJob(const QStringList& devices, const Omittable& auth) : BaseJob(HttpVerb::Post, QStringLiteral("DeleteDevicesJob"), - basePath % "/delete_devices") + QStringLiteral("/_matrix/client/r0") % "/delete_devices") { QJsonObject _data; addParam<>(_data, QStringLiteral("devices"), devices); addParam(_data, QStringLiteral("auth"), auth); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h index 1c57db84..0abd5b84 100644 --- a/lib/csapi/device_management.h +++ b/lib/csapi/device_management.h @@ -4,19 +4,13 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/auth_data.h" #include "csapi/definitions/client_device.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief List registered devices for the current user * * Gets information about all devices for the current user. @@ -32,19 +26,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetDevicesJob() override; // Result properties /// A list of all registered devices for this user. - const QVector& devices() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QVector devices() const + { + return loadFromJson>("devices"_ls); + } }; /*! \brief Get a single device @@ -54,6 +43,7 @@ private: class GetDeviceJob : public BaseJob { public: /*! \brief Get a single device + * * * \param deviceId * The device to retrieve. @@ -66,19 +56,11 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& deviceId); - ~GetDeviceJob() override; // Result properties /// Device information - const Device& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Device data() const { return fromJson(jsonData()); } }; /*! \brief Update a device @@ -88,9 +70,11 @@ private: class UpdateDeviceJob : public BaseJob { public: /*! \brief Update a device + * * * \param deviceId * The device to update. + * * \param displayName * The new display name for this device. If not given, the * display name is unchanged. @@ -108,9 +92,11 @@ public: class DeleteDeviceJob : public BaseJob { public: /*! \brief Delete a device + * * * \param deviceId * The device to delete. + * * \param auth * Additional authentication information for the * user-interactive authentication API. @@ -129,9 +115,11 @@ public: class DeleteDevicesJob : public BaseJob { public: /*! \brief Bulk deletion of devices + * * * \param devices * The list of device IDs to delete. + * * \param auth * Additional authentication information for the * user-interactive authentication API. diff --git a/lib/csapi/directory.cpp b/lib/csapi/directory.cpp index 0d4029bd..25ea82e2 100644 --- a/lib/csapi/directory.cpp +++ b/lib/csapi/directory.cpp @@ -4,63 +4,58 @@ #include "directory.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0/directory"); - SetRoomAliasJob::SetRoomAliasJob(const QString& roomAlias, const QString& roomId) : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomAliasJob"), - basePath % "/room/" % roomAlias) + QStringLiteral("/_matrix/client/r0") % "/directory/room/" + % roomAlias) { QJsonObject _data; addParam<>(_data, QStringLiteral("room_id"), roomId); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetRoomIdByAliasJob::Private { -public: - QString roomId; - QStringList servers; -}; - QUrl GetRoomIdByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/room/" % roomAlias); + QStringLiteral("/_matrix/client/r0") + % "/directory/room/" % roomAlias); } GetRoomIdByAliasJob::GetRoomIdByAliasJob(const QString& roomAlias) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomIdByAliasJob"), - basePath % "/room/" % roomAlias, false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/directory/room/" + % roomAlias, + false) {} -GetRoomIdByAliasJob::~GetRoomIdByAliasJob() = default; - -const QString& GetRoomIdByAliasJob::roomId() const { return d->roomId; } - -const QStringList& GetRoomIdByAliasJob::servers() const { return d->servers; } - -BaseJob::Status GetRoomIdByAliasJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("room_id"_ls), d->roomId); - fromJson(json.value("servers"_ls), d->servers); - - return Success; -} - QUrl DeleteRoomAliasJob::makeRequestUrl(QUrl baseUrl, const QString& roomAlias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/room/" % roomAlias); + QStringLiteral("/_matrix/client/r0") + % "/directory/room/" % roomAlias); } DeleteRoomAliasJob::DeleteRoomAliasJob(const QString& roomAlias) : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteRoomAliasJob"), - basePath % "/room/" % roomAlias) + QStringLiteral("/_matrix/client/r0") % "/directory/room/" + % roomAlias) {} + +QUrl GetLocalAliasesJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/aliases"); +} + +GetLocalAliasesJob::GetLocalAliasesJob(const QString& roomId) + : BaseJob(HttpVerb::Get, QStringLiteral("GetLocalAliasesJob"), + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/aliases") +{ + addExpectedKey("aliases"); +} diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h index c13ca20a..7ae44d1d 100644 --- a/lib/csapi/directory.h +++ b/lib/csapi/directory.h @@ -8,17 +8,17 @@ namespace Quotient { -// Operations - /*! \brief Create a new mapping from room alias to room ID. * */ class SetRoomAliasJob : public BaseJob { public: /*! \brief Create a new mapping from room alias to room ID. + * * * \param roomAlias * The room alias to set. + * * \param roomId * The room ID to set. */ @@ -36,6 +36,7 @@ public: class GetRoomIdByAliasJob : public BaseJob { public: /*! \brief Get the room ID corresponding to this room alias. + * * * \param roomAlias * The room alias. @@ -48,22 +49,17 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomAlias); - ~GetRoomIdByAliasJob() override; // Result properties /// The room ID for this room alias. - const QString& roomId() const; + QString roomId() const { return loadFromJson("room_id"_ls); } /// A list of servers that are aware of this room alias. - const QStringList& servers() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QStringList servers() const + { + return loadFromJson("servers"_ls); + } }; /*! \brief Remove a mapping of room alias to room ID. @@ -73,10 +69,19 @@ private: * Servers may choose to implement additional access control checks here, for * instance that room aliases can only be deleted by their creator or a server * administrator. + * + * .. Note:: + * Servers may choose to update the ``alt_aliases`` for the + * ``m.room.canonical_alias`` state event in the room when an alias is removed. + * Servers which choose to update the canonical alias event are recommended to, + * in addition to their other relevant permission checks, delete the alias and + * return a successful response even if the user does not have permission to + * update the ``m.room.canonical_alias`` event. */ class DeleteRoomAliasJob : public BaseJob { public: /*! \brief Remove a mapping of room alias to room ID. + * * * \param roomAlias * The room alias to remove. @@ -91,4 +96,49 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomAlias); }; +/*! \brief Get a list of local aliases on a given room. + * + * Get a list of aliases maintained by the local server for the + * given room. + * + * This endpoint can be called by users who are in the room (external + * users receive an ``M_FORBIDDEN`` error response). If the room's + * ``m.room.history_visibility`` maps to ``world_readable``, any + * user can call this endpoint. + * + * Servers may choose to implement additional access control checks here, + * such as allowing server administrators to view aliases regardless of + * membership. + * + * .. Note:: + * Clients are recommended not to display this list of aliases prominently + * as they are not curated, unlike those listed in the + * ``m.room.canonical_alias`` state event. + */ +class GetLocalAliasesJob : public BaseJob { +public: + /*! \brief Get a list of local aliases on a given room. + * + * + * \param roomId + * The room ID to find local aliases of. + */ + explicit GetLocalAliasesJob(const QString& roomId); + + /*! \brief Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for GetLocalAliasesJob + * is necessary but the job itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); + + // Result properties + + /// The server's local aliases on the room. Can be empty. + QStringList aliases() const + { + return loadFromJson("aliases"_ls); + } +}; + } // namespace Quotient diff --git a/lib/csapi/event_context.cpp b/lib/csapi/event_context.cpp index 5bb2222e..d2a5f522 100644 --- a/lib/csapi/event_context.cpp +++ b/lib/csapi/event_context.cpp @@ -4,79 +4,36 @@ #include "event_context.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetEventContextJob::Private { -public: - QString begin; - QString end; - RoomEvents eventsBefore; - RoomEventPtr event; - RoomEvents eventsAfter; - StateEvents state; -}; - -BaseJob::Query queryToGetEventContext(Omittable limit) +auto queryToGetEventContext(Omittable limit, const QString& filter) { BaseJob::Query _q; addParam(_q, QStringLiteral("limit"), limit); + addParam(_q, QStringLiteral("filter"), filter); return _q; } QUrl GetEventContextJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId, - Omittable limit) + Omittable limit, + const QString& filter) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/context/" + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/context/" % eventId, - queryToGetEventContext(limit)); + queryToGetEventContext(limit, filter)); } GetEventContextJob::GetEventContextJob(const QString& roomId, const QString& eventId, - Omittable limit) + Omittable limit, + const QString& filter) : BaseJob(HttpVerb::Get, QStringLiteral("GetEventContextJob"), - basePath % "/rooms/" % roomId % "/context/" % eventId, - queryToGetEventContext(limit)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/context/" % eventId, + queryToGetEventContext(limit, filter)) {} - -GetEventContextJob::~GetEventContextJob() = default; - -const QString& GetEventContextJob::begin() const { return d->begin; } - -const QString& GetEventContextJob::end() const { return d->end; } - -RoomEvents&& GetEventContextJob::eventsBefore() -{ - return std::move(d->eventsBefore); -} - -RoomEventPtr&& GetEventContextJob::event() { return std::move(d->event); } - -RoomEvents&& GetEventContextJob::eventsAfter() -{ - return std::move(d->eventsAfter); -} - -StateEvents&& GetEventContextJob::state() { return std::move(d->state); } - -BaseJob::Status GetEventContextJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("start"_ls), d->begin); - fromJson(json.value("end"_ls), d->end); - fromJson(json.value("events_before"_ls), d->eventsBefore); - fromJson(json.value("event"_ls), d->event); - fromJson(json.value("events_after"_ls), d->eventsAfter); - fromJson(json.value("state"_ls), d->state); - - return Success; -} diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index 54441617..2f9c66d8 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -4,34 +4,47 @@ #pragma once -#include "converters.h" - #include "events/eventloader.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Get events and state around the specified event. * * This API returns a number of events that happened just before and * after the specified event. This allows clients to get the context * surrounding an event. + * + * *Note*: This endpoint supports lazy-loading of room member events. See + * `Lazy-loading room members <#lazy-loading-room-members>`_ for more + * information. */ class GetEventContextJob : public BaseJob { public: /*! \brief Get events and state around the specified event. + * * * \param roomId * The room to get events from. + * * \param eventId * The event to get context around. + * * \param limit * The maximum number of events to return. Default: 10. + * + * \param filter + * A JSON ``RoomEventFilter`` to filter the returned events with. The + * filter is only applied to ``events_before``, ``events_after``, and + * ``state``. It is not applied to the ``event`` itself. The filter may + * be applied before or/and after the ``limit`` parameter - whichever the + * homeserver prefers. + * + * See `Filtering <#filtering>`_ for more information. */ explicit GetEventContextJob(const QString& roomId, const QString& eventId, - Omittable limit = none); + Omittable limit = none, + const QString& filter = {}); /*! \brief Construct a URL without creating a full-fledged job object * @@ -40,37 +53,36 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId, - Omittable limit = none); - ~GetEventContextJob() override; + Omittable limit = none, + const QString& filter = {}); // Result properties /// A token that can be used to paginate backwards with. - const QString& begin() const; + QString begin() const { return loadFromJson("start"_ls); } /// A token that can be used to paginate forwards with. - const QString& end() const; + QString end() const { return loadFromJson("end"_ls); } /// A list of room events that happened just before the /// requested event, in reverse-chronological order. - RoomEvents&& eventsBefore(); + RoomEvents eventsBefore() + { + return takeFromJson("events_before"_ls); + } /// Details of the requested event. - RoomEventPtr&& event(); + RoomEventPtr event() { return takeFromJson("event"_ls); } /// A list of room events that happened just after the /// requested event, in chronological order. - RoomEvents&& eventsAfter(); + RoomEvents eventsAfter() + { + return takeFromJson("events_after"_ls); + } /// The state of the room at the last event returned. - StateEvents&& state(); - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + StateEvents state() { return takeFromJson("state"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/filter.cpp b/lib/csapi/filter.cpp index 98b85f83..bb3a893f 100644 --- a/lib/csapi/filter.cpp +++ b/lib/csapi/filter.cpp @@ -4,68 +4,29 @@ #include "filter.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class DefineFilterJob::Private { -public: - QString filterId; -}; - DefineFilterJob::DefineFilterJob(const QString& userId, const Filter& filter) : BaseJob(HttpVerb::Post, QStringLiteral("DefineFilterJob"), - basePath % "/user/" % userId % "/filter") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/filter") { setRequestData(Data(toJson(filter))); + addExpectedKey("filter_id"); } -DefineFilterJob::~DefineFilterJob() = default; - -const QString& DefineFilterJob::filterId() const { return d->filterId; } - -BaseJob::Status DefineFilterJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("filter_id"_ls)) - return { IncorrectResponse, - "The key 'filter_id' not found in the response" }; - fromJson(json.value("filter_id"_ls), d->filterId); - - return Success; -} - -class GetFilterJob::Private { -public: - Filter data; -}; - QUrl GetFilterJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& filterId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/user/" - % userId % "/filter/" - % filterId); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") % "/user/" + % userId % "/filter/" % filterId); } GetFilterJob::GetFilterJob(const QString& userId, const QString& filterId) : BaseJob(HttpVerb::Get, QStringLiteral("GetFilterJob"), - basePath % "/user/" % userId % "/filter/" % filterId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/filter/" % filterId) {} - -GetFilterJob::~GetFilterJob() = default; - -const Filter& GetFilterJob::data() const { return d->data; } - -BaseJob::Status GetFilterJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h index 7a0f8958..9aa7dc79 100644 --- a/lib/csapi/filter.h +++ b/lib/csapi/filter.h @@ -4,16 +4,12 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/sync_filter.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Upload a new filter. * * Uploads a new filter definition to the homeserver. @@ -23,31 +19,24 @@ namespace Quotient { class DefineFilterJob : public BaseJob { public: /*! \brief Upload a new filter. + * * * \param userId * The id of the user uploading the filter. The access token must be - * authorized to make requests for this user id. \param filter Uploads a new - * filter definition to the homeserver. Returns a filter ID that may be used - * in future requests to restrict which events are returned to the client. + * authorized to make requests for this user id. + * + * \param filter + * The filter to upload. */ explicit DefineFilterJob(const QString& userId, const Filter& filter); - ~DefineFilterJob() override; - // Result properties /// The ID of the filter that was created. Cannot start /// with a ``{`` as this character is used to determine /// if the filter provided is inline JSON or a previously /// declared filter by homeservers on some APIs. - const QString& filterId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString filterId() const { return loadFromJson("filter_id"_ls); } }; /*! \brief Download a filter @@ -56,9 +45,11 @@ private: class GetFilterJob : public BaseJob { public: /*! \brief Download a filter + * * * \param userId * The user ID to download a filter for. + * * \param filterId * The filter ID to download. */ @@ -71,19 +62,11 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& filterId); - ~GetFilterJob() override; // Result properties /// "The filter defintion" - const Filter& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Filter data() const { return fromJson(jsonData()); } }; } // namespace Quotient diff --git a/lib/csapi/inviting.cpp b/lib/csapi/inviting.cpp index f070c3ce..01620f9e 100644 --- a/lib/csapi/inviting.cpp +++ b/lib/csapi/inviting.cpp @@ -4,19 +4,16 @@ #include "inviting.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - InviteUserJob::InviteUserJob(const QString& roomId, const QString& userId) : BaseJob(HttpVerb::Post, QStringLiteral("InviteUserJob"), - basePath % "/rooms/" % roomId % "/invite") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/invite") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h index 41d78ae6..a7ec6093 100644 --- a/lib/csapi/inviting.h +++ b/lib/csapi/inviting.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Invite a user to participate in a particular room. * * .. _invite-by-user-id-endpoint: @@ -34,9 +32,11 @@ namespace Quotient { class InviteUserJob : public BaseJob { public: /*! \brief Invite a user to participate in a particular room. + * * * \param roomId * The room identifier (not alias) to which to invite the user. + * * \param userId * The fully qualified user ID of the invitee. */ diff --git a/lib/csapi/joining.cpp b/lib/csapi/joining.cpp index cde179e0..4761e949 100644 --- a/lib/csapi/joining.cpp +++ b/lib/csapi/joining.cpp @@ -4,93 +4,23 @@ #include "joining.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, - const JoinRoomByIdJob::ThirdPartySigned& pod) - { - addParam<>(jo, QStringLiteral("sender"), pod.sender); - addParam<>(jo, QStringLiteral("mxid"), pod.mxid); - addParam<>(jo, QStringLiteral("token"), pod.token); - addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - } -}; - -} // namespace Quotient - -class JoinRoomByIdJob::Private { -public: - QString roomId; -}; - JoinRoomByIdJob::JoinRoomByIdJob( const QString& roomId, const Omittable& thirdPartySigned) : BaseJob(HttpVerb::Post, QStringLiteral("JoinRoomByIdJob"), - basePath % "/rooms/" % roomId % "/join") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId % "/join") { QJsonObject _data; addParam(_data, QStringLiteral("third_party_signed"), thirdPartySigned); - setRequestData(_data); + setRequestData(std::move(_data)); + addExpectedKey("room_id"); } -JoinRoomByIdJob::~JoinRoomByIdJob() = default; - -const QString& JoinRoomByIdJob::roomId() const { return d->roomId; } - -BaseJob::Status JoinRoomByIdJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("room_id"_ls)) - return { IncorrectResponse, - "The key 'room_id' not found in the response" }; - fromJson(json.value("room_id"_ls), d->roomId); - - return Success; -} - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const JoinRoomJob::Signed& pod) - { - addParam<>(jo, QStringLiteral("sender"), pod.sender); - addParam<>(jo, QStringLiteral("mxid"), pod.mxid); - addParam<>(jo, QStringLiteral("token"), pod.token); - addParam<>(jo, QStringLiteral("signatures"), pod.signatures); - } -}; - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const JoinRoomJob::ThirdPartySigned& pod) - { - addParam<>(jo, QStringLiteral("signed"), pod.signedData); - } -}; - -} // namespace Quotient - -class JoinRoomJob::Private { -public: - QString roomId; -}; - -BaseJob::Query queryToJoinRoom(const QStringList& serverName) +auto queryToJoinRoom(const QStringList& serverName) { BaseJob::Query _q; addParam(_q, QStringLiteral("server_name"), serverName); @@ -101,26 +31,12 @@ JoinRoomJob::JoinRoomJob(const QString& roomIdOrAlias, const QStringList& serverName, const Omittable& thirdPartySigned) : BaseJob(HttpVerb::Post, QStringLiteral("JoinRoomJob"), - basePath % "/join/" % roomIdOrAlias, queryToJoinRoom(serverName)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/join/" % roomIdOrAlias, + queryToJoinRoom(serverName)) { QJsonObject _data; addParam(_data, QStringLiteral("third_party_signed"), thirdPartySigned); - setRequestData(_data); -} - -JoinRoomJob::~JoinRoomJob() = default; - -const QString& JoinRoomJob::roomId() const { return d->roomId; } - -BaseJob::Status JoinRoomJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("room_id"_ls)) - return { IncorrectResponse, - "The key 'room_id' not found in the response" }; - fromJson(json.value("room_id"_ls), d->roomId); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("room_id"); } diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index 6d93bcb1..6822fbdf 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -4,16 +4,12 @@ #pragma once -#include "converters.h" +#include "csapi/definitions/third_party_signed.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Start the requesting user participating in a particular room. * * *Note that this API requires a room ID, not alias.* ``/join/{roomIdOrAlias}`` @@ -33,49 +29,36 @@ namespace Quotient { */ class JoinRoomByIdJob : public BaseJob { public: - // Inner data structures - - /// A signature of an ``m.third_party_invite`` token to prove that this user - /// owns a third party identity which has been invited to the room. - struct ThirdPartySigned { - /// The Matrix ID of the user who issued the invite. - QString sender; - /// The Matrix ID of the invitee. - QString mxid; - /// The state key of the m.third_party_invite event. - QString token; - /// A signatures object containing a signature of the entire signed - /// object. - QJsonObject signatures; - }; - - // Construction/destruction - /*! \brief Start the requesting user participating in a particular room. + * * * \param roomId * The room identifier (not alias) to join. + * * \param thirdPartySigned - * A signature of an ``m.third_party_invite`` token to prove that this - * user owns a third party identity which has been invited to the room. + * *Note that this API requires a room ID, not alias.* + * ``/join/{roomIdOrAlias}`` *exists if you have a room alias.* + * + * This API starts a user participating in a particular room, if that user + * is allowed to participate in that room. After this call, the client is + * allowed to see all current state events in the room, and all subsequent + * events associated with the room until the user leaves the room. + * + * After a user has joined a room, the room will appear as an entry in the + * response of the |/initialSync|_ and |/sync|_ APIs. + * + * If a ``third_party_signed`` was supplied, the homeserver must verify + * that it matches a pending ``m.room.third_party_invite`` event in the + * room, and perform key validity checking if required by the event. */ explicit JoinRoomByIdJob( const QString& roomId, const Omittable& thirdPartySigned = none); - ~JoinRoomByIdJob() override; - // Result properties /// The joined room ID. - const QString& roomId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString roomId() const { return loadFromJson("room_id"_ls); } }; /*! \brief Start the requesting user participating in a particular room. @@ -97,72 +80,40 @@ private: */ class JoinRoomJob : public BaseJob { public: - // Inner data structures - - /// *Note that this API takes either a room ID or alias, unlike* - /// ``/room/{roomId}/join``. - /// - /// This API starts a user participating in a particular room, if that user - /// is allowed to participate in that room. After this call, the client is - /// allowed to see all current state events in the room, and all subsequent - /// events associated with the room until the user leaves the room. - /// - /// After a user has joined a room, the room will appear as an entry in the - /// response of the |/initialSync|_ and |/sync|_ APIs. - /// - /// If a ``third_party_signed`` was supplied, the homeserver must verify - /// that it matches a pending ``m.room.third_party_invite`` event in the - /// room, and perform key validity checking if required by the event. - struct Signed { - /// The Matrix ID of the user who issued the invite. - QString sender; - /// The Matrix ID of the invitee. - QString mxid; - /// The state key of the m.third_party_invite event. - QString token; - /// A signatures object containing a signature of the entire signed - /// object. - QJsonObject signatures; - }; - - /// A signature of an ``m.third_party_invite`` token to prove that this user - /// owns a third party identity which has been invited to the room. - struct ThirdPartySigned { - /// A signature of an ``m.third_party_invite`` token to prove that this - /// user owns a third party identity which has been invited to the room. - Signed signedData; - }; - - // Construction/destruction - /*! \brief Start the requesting user participating in a particular room. + * * * \param roomIdOrAlias * The room identifier or alias to join. + * * \param serverName * The servers to attempt to join the room through. One of the servers * must be participating in the room. + * * \param thirdPartySigned - * A signature of an ``m.third_party_invite`` token to prove that this - * user owns a third party identity which has been invited to the room. + * *Note that this API takes either a room ID or alias, unlike* + * ``/room/{roomId}/join``. + * + * This API starts a user participating in a particular room, if that user + * is allowed to participate in that room. After this call, the client is + * allowed to see all current state events in the room, and all subsequent + * events associated with the room until the user leaves the room. + * + * After a user has joined a room, the room will appear as an entry in the + * response of the |/initialSync|_ and |/sync|_ APIs. + * + * If a ``third_party_signed`` was supplied, the homeserver must verify + * that it matches a pending ``m.room.third_party_invite`` event in the + * room, and perform key validity checking if required by the event. */ explicit JoinRoomJob( const QString& roomIdOrAlias, const QStringList& serverName = {}, const Omittable& thirdPartySigned = none); - ~JoinRoomJob() override; - // Result properties /// The joined room ID. - const QString& roomId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString roomId() const { return loadFromJson("room_id"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/keys.cpp b/lib/csapi/keys.cpp index b1a947b3..34ab47c9 100644 --- a/lib/csapi/keys.cpp +++ b/lib/csapi/keys.cpp @@ -4,161 +4,48 @@ #include "keys.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class UploadKeysJob::Private { -public: - QHash oneTimeKeyCounts; -}; - UploadKeysJob::UploadKeysJob(const Omittable& deviceKeys, const QHash& oneTimeKeys) : BaseJob(HttpVerb::Post, QStringLiteral("UploadKeysJob"), - basePath % "/keys/upload") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/keys/upload") { QJsonObject _data; addParam(_data, QStringLiteral("device_keys"), deviceKeys); addParam(_data, QStringLiteral("one_time_keys"), oneTimeKeys); - setRequestData(_data); -} - -UploadKeysJob::~UploadKeysJob() = default; - -const QHash& UploadKeysJob::oneTimeKeyCounts() const -{ - return d->oneTimeKeyCounts; -} - -BaseJob::Status UploadKeysJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("one_time_key_counts"_ls)) - return { IncorrectResponse, - "The key 'one_time_key_counts' not found in the response" }; - fromJson(json.value("one_time_key_counts"_ls), d->oneTimeKeyCounts); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("one_time_key_counts"); } -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - QueryKeysJob::UnsignedDeviceInfo& result) - { - fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - QueryKeysJob::DeviceInformation& result) - { - fillFromJson(jo, result); - fromJson(jo.value("unsigned"_ls), result.unsignedData); - } -}; - -} // namespace Quotient - -class QueryKeysJob::Private { -public: - QHash failures; - QHash> deviceKeys; -}; - QueryKeysJob::QueryKeysJob(const QHash& deviceKeys, Omittable timeout, const QString& token) : BaseJob(HttpVerb::Post, QStringLiteral("QueryKeysJob"), - basePath % "/keys/query") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/keys/query") { QJsonObject _data; addParam(_data, QStringLiteral("timeout"), timeout); addParam<>(_data, QStringLiteral("device_keys"), deviceKeys); addParam(_data, QStringLiteral("token"), token); - setRequestData(_data); -} - -QueryKeysJob::~QueryKeysJob() = default; - -const QHash& QueryKeysJob::failures() const -{ - return d->failures; -} - -const QHash>& -QueryKeysJob::deviceKeys() const -{ - return d->deviceKeys; + setRequestData(std::move(_data)); } -BaseJob::Status QueryKeysJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("failures"_ls), d->failures); - fromJson(json.value("device_keys"_ls), d->deviceKeys); - - return Success; -} - -class ClaimKeysJob::Private { -public: - QHash failures; - QHash> oneTimeKeys; -}; - ClaimKeysJob::ClaimKeysJob( const QHash>& oneTimeKeys, Omittable timeout) : BaseJob(HttpVerb::Post, QStringLiteral("ClaimKeysJob"), - basePath % "/keys/claim") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/keys/claim") { QJsonObject _data; addParam(_data, QStringLiteral("timeout"), timeout); addParam<>(_data, QStringLiteral("one_time_keys"), oneTimeKeys); - setRequestData(_data); -} - -ClaimKeysJob::~ClaimKeysJob() = default; - -const QHash& ClaimKeysJob::failures() const -{ - return d->failures; -} - -const QHash>& ClaimKeysJob::oneTimeKeys() const -{ - return d->oneTimeKeys; + setRequestData(std::move(_data)); + addExpectedKey("one_time_keys"); } -BaseJob::Status ClaimKeysJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("failures"_ls), d->failures); - fromJson(json.value("one_time_keys"_ls), d->oneTimeKeys); - - return Success; -} - -class GetKeysChangesJob::Private { -public: - QStringList changed; - QStringList left; -}; - -BaseJob::Query queryToGetKeysChanges(const QString& from, const QString& to) +auto queryToGetKeysChanges(const QString& from, const QString& to) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("from"), from); @@ -170,27 +57,13 @@ QUrl GetKeysChangesJob::makeRequestUrl(QUrl baseUrl, const QString& from, const QString& to) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/keys/changes", + QStringLiteral("/_matrix/client/r0") + % "/keys/changes", queryToGetKeysChanges(from, to)); } GetKeysChangesJob::GetKeysChangesJob(const QString& from, const QString& to) : BaseJob(HttpVerb::Get, QStringLiteral("GetKeysChangesJob"), - basePath % "/keys/changes", queryToGetKeysChanges(from, to)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/keys/changes", + queryToGetKeysChanges(from, to)) {} - -GetKeysChangesJob::~GetKeysChangesJob() = default; - -const QStringList& GetKeysChangesJob::changed() const { return d->changed; } - -const QStringList& GetKeysChangesJob::left() const { return d->left; } - -BaseJob::Status GetKeysChangesJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("changed"_ls), d->changed); - fromJson(json.value("left"_ls), d->left); - - return Success; -} diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h index 2673acc5..b7a8dc71 100644 --- a/lib/csapi/keys.h +++ b/lib/csapi/keys.h @@ -4,20 +4,12 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/device_keys.h" #include "jobs/basejob.h" -#include -#include -#include - namespace Quotient { -// Operations - /*! \brief Upload end-to-end encryption keys. * * Publishes end-to-end encryption keys for the device. @@ -25,35 +17,31 @@ namespace Quotient { class UploadKeysJob : public BaseJob { public: /*! \brief Upload end-to-end encryption keys. + * * * \param deviceKeys * Identity keys for the device. May be absent if no new * identity keys are required. + * * \param oneTimeKeys * One-time public keys for "pre-key" messages. The names of * the properties should be in the format * ``:``. The format of the key is determined - * by the key algorithm. + * by the `key algorithm <#key-algorithms>`_. * * May be absent if no new one-time keys are required. */ explicit UploadKeysJob(const Omittable& deviceKeys = none, const QHash& oneTimeKeys = {}); - ~UploadKeysJob() override; - // Result properties /// For each key algorithm, the number of unclaimed one-time keys /// of that type currently held on the server for this device. - const QHash& oneTimeKeyCounts() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QHash oneTimeKeyCounts() const + { + return loadFromJson>("one_time_key_counts"_ls); + } }; /*! \brief Download device identity keys. @@ -73,7 +61,9 @@ public: }; /// Returns the current devices and identity keys for the given users. - struct DeviceInformation : DeviceKeys { + struct DeviceKeys : + + Quotient::DeviceKeys { /// Additional data added to the device key information /// by intermediate servers, and not covered by the /// signatures. @@ -83,14 +73,17 @@ public: // Construction/destruction /*! \brief Download device identity keys. + * * * \param deviceKeys * The keys to be downloaded. A map from user ID, to a list of * device IDs, or to an empty list to indicate all devices for the * corresponding user. + * * \param timeout * The time (in milliseconds) to wait when downloading keys from * remote servers. 10 seconds is the recommended default. + * * \param token * If the client is fetching keys as a result of a device update received * in a sync request, this should be the 'since' token of that sync @@ -101,8 +94,6 @@ public: Omittable timeout = none, const QString& token = {}); - ~QueryKeysJob() override; - // Result properties /// If any remote homeservers could not be reached, they are @@ -112,21 +103,39 @@ public: /// If the homeserver could be reached, but the user or device /// was unknown, no failure is recorded. Instead, the corresponding /// user or device is missing from the ``device_keys`` result. - const QHash& failures() const; + QHash failures() const + { + return loadFromJson>("failures"_ls); + } /// Information on the queried devices. A map from user ID, to a /// map from device ID to device information. For each device, /// the information returned will be the same as uploaded via /// ``/keys/upload``, with the addition of an ``unsigned`` /// property. - const QHash>& deviceKeys() const; + QHash> deviceKeys() const + { + return loadFromJson>>( + "device_keys"_ls); + } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + QueryKeysJob::UnsignedDeviceInfo& result) + { + fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, QueryKeysJob::DeviceKeys& result) + { + fillFromJson(jo, result); + fromJson(jo.value("unsigned"_ls), result.unsignedData); + } }; /*! \brief Claim one-time encryption keys. @@ -136,10 +145,12 @@ private: class ClaimKeysJob : public BaseJob { public: /*! \brief Claim one-time encryption keys. + * * * \param oneTimeKeys * The keys to be claimed. A map from user ID, to a map from * device ID to algorithm name. + * * \param timeout * The time (in milliseconds) to wait when downloading keys from * remote servers. 10 seconds is the recommended default. @@ -148,8 +159,6 @@ public: const QHash>& oneTimeKeys, Omittable timeout = none); - ~ClaimKeysJob() override; - // Result properties /// If any remote homeservers could not be reached, they are @@ -159,18 +168,22 @@ public: /// If the homeserver could be reached, but the user or device /// was unknown, no failure is recorded. Instead, the corresponding /// user or device is missing from the ``one_time_keys`` result. - const QHash& failures() const; + QHash failures() const + { + return loadFromJson>("failures"_ls); + } /// One-time keys for the queried devices. A map from user ID, to a - /// map from devices to a map from ``:`` to the key object. - const QHash>& oneTimeKeys() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// map from devices to a map from ``:`` to the key + /// object. + /// + /// See the `key algorithms <#key-algorithms>`_ section for information + /// on the Key Object format. + QHash> oneTimeKeys() const + { + return loadFromJson>>( + "one_time_keys"_ls); + } }; /*! \brief Query users with recent device key updates. @@ -188,6 +201,7 @@ private: class GetKeysChangesJob : public BaseJob { public: /*! \brief Query users with recent device key updates. + * * * \param from * The desired start point of the list. Should be the ``next_batch`` field @@ -195,6 +209,7 @@ public: * uploaded new device identity keys since this point, nor deleted * existing devices with identity keys since then, will be excluded * from the results. + * * \param to * The desired end point of the list. Should be the ``next_batch`` * field from a recent call to |/sync| - typically the most recent @@ -210,25 +225,20 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& from, const QString& to); - ~GetKeysChangesJob() override; // Result properties /// The Matrix User IDs of all users who updated their device /// identity keys. - const QStringList& changed() const; + QStringList changed() const + { + return loadFromJson("changed"_ls); + } /// The Matrix User IDs of all users who may have left all /// the end-to-end encrypted rooms they previously shared /// with the user. - const QStringList& left() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QStringList left() const { return loadFromJson("left"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/kicking.cpp b/lib/csapi/kicking.cpp index 39125f42..7de5ce01 100644 --- a/lib/csapi/kicking.cpp +++ b/lib/csapi/kicking.cpp @@ -4,21 +4,17 @@ #include "kicking.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - KickJob::KickJob(const QString& roomId, const QString& userId, const QString& reason) : BaseJob(HttpVerb::Post, QStringLiteral("KickJob"), - basePath % "/rooms/" % roomId % "/kick") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId % "/kick") { QJsonObject _data; addParam<>(_data, QStringLiteral("user_id"), userId); addParam(_data, QStringLiteral("reason"), reason); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h index 0633a7f6..594f5845 100644 --- a/lib/csapi/kicking.h +++ b/lib/csapi/kicking.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Kick a user from the room. * * Kick a user from the room. @@ -25,11 +23,14 @@ namespace Quotient { class KickJob : public BaseJob { public: /*! \brief Kick a user from the room. + * * * \param roomId * The room identifier (not alias) from which the user should be kicked. + * * \param userId * The fully qualified user ID of the user being kicked. + * * \param reason * The reason the user has been kicked. This will be supplied as the * ``reason`` on the target's updated `m.room.member`_ event. diff --git a/lib/csapi/leaving.cpp b/lib/csapi/leaving.cpp index 2fa1da56..8bd170bf 100644 --- a/lib/csapi/leaving.cpp +++ b/lib/csapi/leaving.cpp @@ -4,32 +4,32 @@ #include "leaving.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - QUrl LeaveRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/leave"); + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/leave"); } LeaveRoomJob::LeaveRoomJob(const QString& roomId) : BaseJob(HttpVerb::Post, QStringLiteral("LeaveRoomJob"), - basePath % "/rooms/" % roomId % "/leave") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/leave") {} QUrl ForgetRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/forget"); + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/forget"); } ForgetRoomJob::ForgetRoomJob(const QString& roomId) : BaseJob(HttpVerb::Post, QStringLiteral("ForgetRoomJob"), - basePath % "/rooms/" % roomId % "/forget") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/forget") {} diff --git a/lib/csapi/leaving.h b/lib/csapi/leaving.h index 45b983f3..0a4c401c 100644 --- a/lib/csapi/leaving.h +++ b/lib/csapi/leaving.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Stop the requesting user participating in a particular room. * * This API stops a user participating in a particular room. @@ -27,6 +25,7 @@ namespace Quotient { class LeaveRoomJob : public BaseJob { public: /*! \brief Stop the requesting user participating in a particular room. + * * * \param roomId * The room identifier to leave. @@ -56,6 +55,7 @@ public: class ForgetRoomJob : public BaseJob { public: /*! \brief Stop the requesting user remembering about a particular room. + * * * \param roomId * The room identifier to forget. diff --git a/lib/csapi/list_joined_rooms.cpp b/lib/csapi/list_joined_rooms.cpp index 34eb0d42..8d7e267f 100644 --- a/lib/csapi/list_joined_rooms.cpp +++ b/lib/csapi/list_joined_rooms.cpp @@ -4,45 +4,20 @@ #include "list_joined_rooms.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetJoinedRoomsJob::Private { -public: - QStringList joinedRooms; -}; - QUrl GetJoinedRoomsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/joined_rooms"); + QStringLiteral("/_matrix/client/r0") + % "/joined_rooms"); } GetJoinedRoomsJob::GetJoinedRoomsJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetJoinedRoomsJob"), - basePath % "/joined_rooms") - , d(new Private) -{} - -GetJoinedRoomsJob::~GetJoinedRoomsJob() = default; - -const QStringList& GetJoinedRoomsJob::joinedRooms() const + QStringLiteral("/_matrix/client/r0") % "/joined_rooms") { - return d->joinedRooms; -} - -BaseJob::Status GetJoinedRoomsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("joined_rooms"_ls)) - return { IncorrectResponse, - "The key 'joined_rooms' not found in the response" }; - fromJson(json.value("joined_rooms"_ls), d->joinedRooms); - - return Success; + addExpectedKey("joined_rooms"); } diff --git a/lib/csapi/list_joined_rooms.h b/lib/csapi/list_joined_rooms.h index a170d623..1034aa7b 100644 --- a/lib/csapi/list_joined_rooms.h +++ b/lib/csapi/list_joined_rooms.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Lists the user's current rooms. * * This API returns a list of the user's current rooms. @@ -25,19 +23,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetJoinedRoomsJob() override; // Result properties /// The ID of each room in which the user has ``joined`` membership. - const QStringList& joinedRooms() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QStringList joinedRooms() const + { + return loadFromJson("joined_rooms"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index 83320ec0..cdc5c716 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -4,66 +4,39 @@ #include "list_public_rooms.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetRoomVisibilityOnDirectoryJob::Private { -public: - QString visibility; -}; - QUrl GetRoomVisibilityOnDirectoryJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/directory/list/room/" % roomId); + QStringLiteral("/_matrix/client/r0") + % "/directory/list/room/" % roomId); } GetRoomVisibilityOnDirectoryJob::GetRoomVisibilityOnDirectoryJob( const QString& roomId) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomVisibilityOnDirectoryJob"), - basePath % "/directory/list/room/" % roomId, false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/directory/list/room/" + % roomId, + false) {} -GetRoomVisibilityOnDirectoryJob::~GetRoomVisibilityOnDirectoryJob() = default; - -const QString& GetRoomVisibilityOnDirectoryJob::visibility() const -{ - return d->visibility; -} - -BaseJob::Status -GetRoomVisibilityOnDirectoryJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("visibility"_ls), d->visibility); - - return Success; -} - SetRoomVisibilityOnDirectoryJob::SetRoomVisibilityOnDirectoryJob( const QString& roomId, const QString& visibility) : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomVisibilityOnDirectoryJob"), - basePath % "/directory/list/room/" % roomId) + QStringLiteral("/_matrix/client/r0") % "/directory/list/room/" + % roomId) { QJsonObject _data; addParam(_data, QStringLiteral("visibility"), visibility); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetPublicRoomsJob::Private { -public: - PublicRoomsResponse data; -}; - -BaseJob::Query queryToGetPublicRooms(Omittable limit, const QString& since, - const QString& server) +auto queryToGetPublicRooms(Omittable limit, const QString& since, + const QString& server) { BaseJob::Query _q; addParam(_q, QStringLiteral("limit"), limit); @@ -76,49 +49,20 @@ QUrl GetPublicRoomsJob::makeRequestUrl(QUrl baseUrl, Omittable limit, const QString& since, const QString& server) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/publicRooms", + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/publicRooms", queryToGetPublicRooms(limit, since, server)); } GetPublicRoomsJob::GetPublicRoomsJob(Omittable limit, const QString& since, const QString& server) : BaseJob(HttpVerb::Get, QStringLiteral("GetPublicRoomsJob"), - basePath % "/publicRooms", + QStringLiteral("/_matrix/client/r0") % "/publicRooms", queryToGetPublicRooms(limit, since, server), {}, false) - , d(new Private) {} -GetPublicRoomsJob::~GetPublicRoomsJob() = default; - -const PublicRoomsResponse& GetPublicRoomsJob::data() const { return d->data; } - -BaseJob::Status GetPublicRoomsJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const QueryPublicRoomsJob::Filter& pod) - { - addParam(jo, QStringLiteral("generic_search_term"), - pod.genericSearchTerm); - } -}; - -} // namespace Quotient - -class QueryPublicRoomsJob::Private { -public: - PublicRoomsResponse data; -}; - -BaseJob::Query queryToQueryPublicRooms(const QString& server) +auto queryToQueryPublicRooms(const QString& server) { BaseJob::Query _q; addParam(_q, QStringLiteral("server"), server); @@ -132,8 +76,8 @@ QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, Omittable includeAllNetworks, const QString& thirdPartyInstanceId) : BaseJob(HttpVerb::Post, QStringLiteral("QueryPublicRoomsJob"), - basePath % "/publicRooms", queryToQueryPublicRooms(server)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/publicRooms", + queryToQueryPublicRooms(server)) { QJsonObject _data; addParam(_data, QStringLiteral("limit"), limit); @@ -143,16 +87,6 @@ QueryPublicRoomsJob::QueryPublicRoomsJob(const QString& server, includeAllNetworks); addParam(_data, QStringLiteral("third_party_instance_id"), thirdPartyInstanceId); - setRequestData(_data); -} - -QueryPublicRoomsJob::~QueryPublicRoomsJob() = default; - -const PublicRoomsResponse& QueryPublicRoomsJob::data() const { return d->data; } - -BaseJob::Status QueryPublicRoomsJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("chunk"); } diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index 0c9a2620..5ef7c23e 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -4,16 +4,12 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/public_rooms_response.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Gets the visibility of a room in the directory * * Gets the visibility of a given room on the server's public room directory. @@ -21,6 +17,7 @@ namespace Quotient { class GetRoomVisibilityOnDirectoryJob : public BaseJob { public: /*! \brief Gets the visibility of a room in the directory + * * * \param roomId * The room ID. @@ -33,19 +30,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetRoomVisibilityOnDirectoryJob() override; // Result properties /// The visibility of the room in the directory. - const QString& visibility() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString visibility() const + { + return loadFromJson("visibility"_ls); + } }; /*! \brief Sets the visibility of a room in the room directory @@ -60,9 +52,11 @@ private: class SetRoomVisibilityOnDirectoryJob : public BaseJob { public: /*! \brief Sets the visibility of a room in the room directory + * * * \param roomId * The room ID. + * * \param visibility * The new visibility setting for the room. * Defaults to 'public'. @@ -81,14 +75,17 @@ public: class GetPublicRoomsJob : public BaseJob { public: /*! \brief Lists the public rooms on the server. + * * * \param limit * Limit the number of results returned. + * * \param since * A pagination token from a previous request, allowing clients to * get the next (or previous) batch of rooms. * The direction of pagination is specified solely by which token * is supplied, rather than via an explicit flag. + * * \param server * The server to fetch the public room lists from. Defaults to the * local server. @@ -105,19 +102,14 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, Omittable limit = none, const QString& since = {}, const QString& server = {}); - ~GetPublicRoomsJob() override; // Result properties /// A list of the rooms on the server. - const PublicRoomsResponse& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + PublicRoomsResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Lists the public rooms on the server with optional filter. @@ -138,25 +130,58 @@ public: QString genericSearchTerm; }; + /// Lists the public rooms on the server, with optional filter. + /// + /// This API returns paginated responses. The rooms are ordered by the + /// number of joined members, with the largest rooms first. + struct PublicRoomsChunk { + /// Aliases of the room. May be empty. + QStringList aliases; + /// The canonical alias of the room, if any. + QString canonicalAlias; + /// The name of the room, if any. + QString name; + /// The number of members joined to the room. + int numJoinedMembers; + /// The ID of the room. + QString roomId; + /// The topic of the room, if any. + QString topic; + /// Whether the room may be viewed by guest users without joining. + bool worldReadable; + /// Whether guest users may join the room and participate in it. + /// If they can, they will be subject to ordinary power level + /// rules like any other user. + bool guestCanJoin; + /// The URL for the room's avatar, if one is set. + QString avatarUrl; + }; + // Construction/destruction /*! \brief Lists the public rooms on the server with optional filter. + * * * \param server * The server to fetch the public room lists from. Defaults to the * local server. + * * \param limit * Limit the number of results returned. + * * \param since * A pagination token from a previous request, allowing clients * to get the next (or previous) batch of rooms. The direction * of pagination is specified solely by which token is supplied, * rather than via an explicit flag. + * * \param filter * Filter to apply to the results. + * * \param includeAllNetworks * Whether or not to include all known networks/protocols from * application services on the homeserver. Defaults to false. + * * \param thirdPartyInstanceId * The specific third party network/protocol to request from the * homeserver. Can only be used if ``include_all_networks`` is false. @@ -168,19 +193,56 @@ public: Omittable includeAllNetworks = none, const QString& thirdPartyInstanceId = {}); - ~QueryPublicRoomsJob() override; - // Result properties - /// A list of the rooms on the server. - const PublicRoomsResponse& data() const; + /// A paginated chunk of public rooms. + QVector chunk() const + { + return loadFromJson>("chunk"_ls); + } + + /// A pagination token for the response. The absence of this token + /// means there are no more results to fetch and the client should + /// stop paginating. + QString nextBatch() const { return loadFromJson("next_batch"_ls); } + + /// A pagination token that allows fetching previous results. The + /// absence of this token means there are no results before this + /// batch, i.e. this is the first batch. + QString prevBatch() const { return loadFromJson("prev_batch"_ls); } + + /// An estimate on the total number of public rooms, if the + /// server has an estimate. + Omittable totalRoomCountEstimate() const + { + return loadFromJson>("total_room_count_estimate"_ls); + } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const QueryPublicRoomsJob::Filter& pod) + { + addParam(jo, QStringLiteral("generic_search_term"), + pod.genericSearchTerm); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + QueryPublicRoomsJob::PublicRoomsChunk& result) + { + fromJson(jo.value("aliases"_ls), result.aliases); + fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); + fromJson(jo.value("name"_ls), result.name); + fromJson(jo.value("num_joined_members"_ls), result.numJoinedMembers); + fromJson(jo.value("room_id"_ls), result.roomId); + fromJson(jo.value("topic"_ls), result.topic); + fromJson(jo.value("world_readable"_ls), result.worldReadable); + fromJson(jo.value("guest_can_join"_ls), result.guestCanJoin); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/login.cpp b/lib/csapi/login.cpp index 3e98f56b..a5bac9ea 100644 --- a/lib/csapi/login.cpp +++ b/lib/csapi/login.cpp @@ -4,77 +4,29 @@ #include "login.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetLoginFlowsJob::LoginFlow& result) - { - fromJson(jo.value("type"_ls), result.type); - } -}; - -} // namespace Quotient - -class GetLoginFlowsJob::Private { -public: - QVector flows; -}; - QUrl GetLoginFlowsJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/login"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/login"); } GetLoginFlowsJob::GetLoginFlowsJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetLoginFlowsJob"), - basePath % "/login", false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/login", false) {} -GetLoginFlowsJob::~GetLoginFlowsJob() = default; - -const QVector& GetLoginFlowsJob::flows() const -{ - return d->flows; -} - -BaseJob::Status GetLoginFlowsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("flows"_ls), d->flows); - - return Success; -} - -class LoginJob::Private { -public: - QString userId; - QString accessToken; - QString homeServer; - QString deviceId; - Omittable wellKnown; -}; - LoginJob::LoginJob(const QString& type, const Omittable& identifier, const QString& password, const QString& token, const QString& deviceId, - const QString& initialDeviceDisplayName, const QString& user, - const QString& medium, const QString& address) - : BaseJob(HttpVerb::Post, QStringLiteral("LoginJob"), basePath % "/login", - false) - , d(new Private) + const QString& initialDeviceDisplayName) + : BaseJob(HttpVerb::Post, QStringLiteral("LoginJob"), + QStringLiteral("/_matrix/client/r0") % "/login", false) { QJsonObject _data; addParam<>(_data, QStringLiteral("type"), type); @@ -84,35 +36,5 @@ LoginJob::LoginJob(const QString& type, addParam(_data, QStringLiteral("device_id"), deviceId); addParam(_data, QStringLiteral("initial_device_display_name"), initialDeviceDisplayName); - addParam(_data, QStringLiteral("user"), user); - addParam(_data, QStringLiteral("medium"), medium); - addParam(_data, QStringLiteral("address"), address); - 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; } - -const Omittable& LoginJob::wellKnown() const -{ - return d->wellKnown; -} - -BaseJob::Status LoginJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("user_id"_ls), d->userId); - fromJson(json.value("access_token"_ls), d->accessToken); - fromJson(json.value("home_server"_ls), d->homeServer); - fromJson(json.value("device_id"_ls), d->deviceId); - fromJson(json.value("well_known"_ls), d->wellKnown); - - return Success; + setRequestData(std::move(_data)); } diff --git a/lib/csapi/login.h b/lib/csapi/login.h index adecca6f..0483b77c 100644 --- a/lib/csapi/login.h +++ b/lib/csapi/login.h @@ -4,19 +4,13 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/user_identifier.h" #include "csapi/definitions/wellknown/full.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Get the supported login types to authenticate users * * Gets the homeserver's supported login types to authenticate users. Clients @@ -46,19 +40,23 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetLoginFlowsJob() override; // Result properties /// The homeserver's supported login types - const QVector& flows() const; - -protected: - Status parseJson(const QJsonDocument& data) override; + QVector flows() const + { + return loadFromJson>("flows"_ls); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetLoginFlowsJob::LoginFlow& result) + { + fromJson(jo.value("type"_ls), result.type); + } }; /*! \brief Authenticates the user. @@ -77,46 +75,48 @@ private: class LoginJob : public BaseJob { public: /*! \brief Authenticates the user. + * * * \param type * The login type being used. + * * \param identifier * Identification information for the user. + * * \param password * Required when ``type`` is ``m.login.password``. The user's * password. + * * \param token * Required when ``type`` is ``m.login.token``. Part of `Token-based`_ - * login. \param deviceId ID of the client device. If this does not - * correspond to a known client device, a new device will be created. The - * server will auto-generate a device_id if this is not specified. \param - * initialDeviceDisplayName A display name to assign to the newly-created - * device. Ignored if ``device_id`` corresponds to a known device. \param - * user The fully qualified user ID or just local part of the user ID, to - * log in. Deprecated in favour of ``identifier``. \param medium When - * logging in using a third party identifier, the medium of the identifier. - * Must be 'email'. Deprecated in favour of ``identifier``. \param address - * Third party identifier for the user. Deprecated in favour of - * ``identifier``. + * login. + * + * \param deviceId + * ID of the client device. If this does not correspond to a + * known client device, a new device will be created. The server + * will auto-generate a device_id if this is not specified. + * + * \param initialDeviceDisplayName + * A display name to assign to the newly-created device. Ignored + * if ``device_id`` corresponds to a known device. */ explicit LoginJob(const QString& type, const Omittable& identifier = none, const QString& password = {}, const QString& token = {}, const QString& deviceId = {}, - const QString& initialDeviceDisplayName = {}, - const QString& user = {}, const QString& medium = {}, - const QString& address = {}); - - ~LoginJob() override; + const QString& initialDeviceDisplayName = {}); // Result properties /// The fully-qualified Matrix ID that has been registered. - const QString& userId() const; + QString userId() const { return loadFromJson("user_id"_ls); } /// An access token for the account. /// This access token can then be used to authorize other requests. - const QString& accessToken() const; + QString accessToken() const + { + return loadFromJson("access_token"_ls); + } /// The server_name of the homeserver on which the account has /// been registered. @@ -124,24 +124,23 @@ public: /// **Deprecated**. Clients should extract the server_name from /// ``user_id`` (by splitting at the first colon) if they require /// it. Note also that ``homeserver`` is not spelt this way. - const QString& homeServer() const; + QString homeServer() const + { + return loadFromJson("home_server"_ls); + } /// ID of the logged-in device. Will be the same as the /// corresponding parameter in the request, if one was specified. - const QString& deviceId() const; + QString deviceId() const { return loadFromJson("device_id"_ls); } /// Optional client configuration provided by the server. If present, /// clients SHOULD use the provided object to reconfigure themselves, /// optionally validating the URLs within. This object takes the same /// form as the one returned from .well-known autodiscovery. - const Omittable& wellKnown() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Omittable wellKnown() const + { + return loadFromJson>("well_known"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/logout.cpp b/lib/csapi/logout.cpp index 36281b79..9583b8ec 100644 --- a/lib/csapi/logout.cpp +++ b/lib/csapi/logout.cpp @@ -4,29 +4,30 @@ #include "logout.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - QUrl LogoutJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/logout"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/logout"); } LogoutJob::LogoutJob() - : BaseJob(HttpVerb::Post, QStringLiteral("LogoutJob"), basePath % "/logout") + : BaseJob(HttpVerb::Post, QStringLiteral("LogoutJob"), + QStringLiteral("/_matrix/client/r0") % "/logout") {} QUrl LogoutAllJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/logout/all"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/logout/all"); } LogoutAllJob::LogoutAllJob() : BaseJob(HttpVerb::Post, QStringLiteral("LogoutAllJob"), - basePath % "/logout/all") + QStringLiteral("/_matrix/client/r0") % "/logout/all") {} diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h index 2a705620..e9a7ef89 100644 --- a/lib/csapi/logout.h +++ b/lib/csapi/logout.h @@ -8,12 +8,11 @@ namespace Quotient { -// Operations - /*! \brief Invalidates a user access token * * Invalidates an existing access token, so that it can no longer be used for - * authorization. + * authorization. The device associated with the access token is also deleted. + * `Device keys <#device-keys>`_ for the device are deleted alongside the device. */ class LogoutJob : public BaseJob { public: @@ -31,7 +30,9 @@ public: /*! \brief Invalidates all access tokens for a user * * Invalidates all access tokens for a user, so that they can no longer be used - * for authorization. This includes the access token that made this request. + * for authorization. This includes the access token that made this request. All + * devices for the user are also deleted. `Device keys <#device-keys>`_ for the + * device are deleted alongside the device. * * This endpoint does not require UI authorization because UI authorization is * designed to protect against attacks where the someone gets hold of a single diff --git a/lib/csapi/message_pagination.cpp b/lib/csapi/message_pagination.cpp index ba982748..855c051f 100644 --- a/lib/csapi/message_pagination.cpp +++ b/lib/csapi/message_pagination.cpp @@ -4,24 +4,13 @@ #include "message_pagination.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetRoomEventsJob::Private { -public: - QString begin; - QString end; - RoomEvents chunk; -}; - -BaseJob::Query queryToGetRoomEvents(const QString& from, const QString& to, - const QString& dir, Omittable limit, - const QString& filter) +auto queryToGetRoomEvents(const QString& from, const QString& to, + const QString& dir, Omittable limit, + const QString& filter) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("from"), from); @@ -37,35 +26,17 @@ QUrl GetRoomEventsJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& to, Omittable limit, const QString& filter) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/messages", - queryToGetRoomEvents(from, to, dir, limit, - filter)); + return BaseJob::makeRequestUrl( + std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId % "/messages", + queryToGetRoomEvents(from, to, dir, limit, filter)); } GetRoomEventsJob::GetRoomEventsJob(const QString& roomId, const QString& from, const QString& dir, const QString& to, Omittable limit, const QString& filter) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomEventsJob"), - basePath % "/rooms/" % roomId % "/messages", + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/messages", queryToGetRoomEvents(from, to, dir, limit, filter)) - , d(new Private) {} - -GetRoomEventsJob::~GetRoomEventsJob() = default; - -const QString& GetRoomEventsJob::begin() const { return d->begin; } - -const QString& GetRoomEventsJob::end() const { return d->end; } - -RoomEvents&& GetRoomEventsJob::chunk() { return std::move(d->chunk); } - -BaseJob::Status GetRoomEventsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("start"_ls), d->begin); - fromJson(json.value("end"_ls), d->end); - fromJson(json.value("chunk"_ls), d->chunk); - - return Success; -} diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index b0d95bad..e9f9e6d4 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -4,40 +4,46 @@ #pragma once -#include "converters.h" - #include "events/eventloader.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Get a list of events for this room * * This API returns a list of message and state events for a room. It uses * pagination query parameters to paginate history in the room. + * + * *Note*: This endpoint supports lazy-loading of room member events. See + * `Lazy-loading room members <#lazy-loading-room-members>`_ for more + * information. */ class GetRoomEventsJob : public BaseJob { public: /*! \brief Get a list of events for this room + * * * \param roomId * The room to get events from. + * * \param from * The token to start returning events from. This token can be obtained * from a ``prev_batch`` token returned for each room by the sync API, * or from a ``start`` or ``end`` token returned by a previous request * to this endpoint. + * * \param dir * The direction to return events from. + * * \param to * The token to stop returning events at. This token can be obtained from * a ``prev_batch`` token returned for each room by the sync endpoint, * or from a ``start`` or ``end`` token returned by a previous request to * this endpoint. + * * \param limit * The maximum number of events to return. Default: 10. + * * \param filter * A JSON RoomEventFilter to filter returned events with. */ @@ -56,27 +62,32 @@ public: const QString& to = {}, Omittable limit = none, const QString& filter = {}); - ~GetRoomEventsJob() override; // Result properties /// The token the pagination starts from. If ``dir=b`` this will be /// the token supplied in ``from``. - const QString& begin() const; + QString begin() const { return loadFromJson("start"_ls); } /// The token the pagination ends at. If ``dir=b`` this token should /// be used again to request even earlier events. - const QString& end() const; - - /// A list of room events. - RoomEvents&& chunk(); + QString end() const { return loadFromJson("end"_ls); } -protected: - Status parseJson(const QJsonDocument& data) override; + /// A list of room events. The order depends on the ``dir`` parameter. + /// For ``dir=b`` events will be in reverse-chronological order, + /// for ``dir=f`` in chronological order, so that events start + /// at the ``from`` point. + RoomEvents chunk() { return takeFromJson("chunk"_ls); } -private: - class Private; - QScopedPointer d; + /// A list of state events relevant to showing the ``chunk``. For example, if + /// ``lazy_load_members`` is enabled in the filter then this may contain + /// the membership events for the senders of events in the ``chunk``. + /// + /// Unless ``include_redundant_members`` is ``true``, the server + /// may remove membership events which would have already been + /// sent to the client in prior calls to this endpoint, assuming + /// the membership of those members has not changed. + StateEvents state() { return takeFromJson("state"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/notifications.cpp b/lib/csapi/notifications.cpp index d00d5425..a479d500 100644 --- a/lib/csapi/notifications.cpp +++ b/lib/csapi/notifications.cpp @@ -4,41 +4,12 @@ #include "notifications.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetNotificationsJob::Notification& result) - { - fromJson(jo.value("actions"_ls), result.actions); - fromJson(jo.value("event"_ls), result.event); - fromJson(jo.value("profile_tag"_ls), result.profileTag); - fromJson(jo.value("read"_ls), result.read); - fromJson(jo.value("room_id"_ls), result.roomId); - fromJson(jo.value("ts"_ls), result.ts); - } -}; - -} // namespace Quotient - -class GetNotificationsJob::Private { -public: - QString nextToken; - std::vector notifications; -}; - -BaseJob::Query queryToGetNotifications(const QString& from, - Omittable limit, const QString& only) +auto queryToGetNotifications(const QString& from, Omittable limit, + const QString& only) { BaseJob::Query _q; addParam(_q, QStringLiteral("from"), from); @@ -52,7 +23,8 @@ QUrl GetNotificationsJob::makeRequestUrl(QUrl baseUrl, const QString& from, const QString& only) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/notifications", + QStringLiteral("/_matrix/client/r0") + % "/notifications", queryToGetNotifications(from, limit, only)); } @@ -60,29 +32,8 @@ GetNotificationsJob::GetNotificationsJob(const QString& from, Omittable limit, const QString& only) : BaseJob(HttpVerb::Get, QStringLiteral("GetNotificationsJob"), - basePath % "/notifications", + QStringLiteral("/_matrix/client/r0") % "/notifications", queryToGetNotifications(from, limit, only)) - , d(new Private) -{} - -GetNotificationsJob::~GetNotificationsJob() = default; - -const QString& GetNotificationsJob::nextToken() const { return d->nextToken; } - -std::vector&& -GetNotificationsJob::notifications() -{ - return std::move(d->notifications); -} - -BaseJob::Status GetNotificationsJob::parseJson(const QJsonDocument& data) { - auto json = data.object(); - fromJson(json.value("next_token"_ls), d->nextToken); - if (!json.contains("notifications"_ls)) - return { IncorrectResponse, - "The key 'notifications' not found in the response" }; - fromJson(json.value("notifications"_ls), d->notifications); - - return Success; + addExpectedKey("notifications"); } diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index 0e86e424..095e9325 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -4,19 +4,11 @@ #pragma once -#include "converters.h" - #include "events/eventloader.h" #include "jobs/basejob.h" -#include -#include -#include - namespace Quotient { -// Operations - /*! \brief Gets a list of events that the user has been notified about * * This API is used to paginate through the list of events that the @@ -49,11 +41,14 @@ public: // Construction/destruction /*! \brief Gets a list of events that the user has been notified about + * * * \param from * Pagination token given to retrieve the next set of events. + * * \param limit * Limit on the number of events to return in this request. + * * \param only * Allows basic filtering of events returned. Supply ``highlight`` * to return only events where the notification had the highlight @@ -71,24 +66,33 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable limit = none, const QString& only = {}); - ~GetNotificationsJob() override; // Result properties /// The token to supply in the ``from`` param of the next /// ``/notifications`` request in order to request more /// events. If this is absent, there are no more results. - const QString& nextToken() const; + QString nextToken() const { return loadFromJson("next_token"_ls); } /// The list of events that triggered notifications. - std::vector&& notifications(); - -protected: - Status parseJson(const QJsonDocument& data) override; + std::vector notifications() + { + return takeFromJson>("notifications"_ls); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetNotificationsJob::Notification& result) + { + fromJson(jo.value("actions"_ls), result.actions); + fromJson(jo.value("event"_ls), result.event); + fromJson(jo.value("profile_tag"_ls), result.profileTag); + fromJson(jo.value("read"_ls), result.read); + fromJson(jo.value("room_id"_ls), result.roomId); + fromJson(jo.value("ts"_ls), result.ts); + } }; } // namespace Quotient diff --git a/lib/csapi/openid.cpp b/lib/csapi/openid.cpp index 9def2377..3941e9c0 100644 --- a/lib/csapi/openid.cpp +++ b/lib/csapi/openid.cpp @@ -4,66 +4,15 @@ #include "openid.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class RequestOpenIdTokenJob::Private { -public: - QString accessToken; - QString tokenType; - QString matrixServerName; - int expiresIn; -}; - RequestOpenIdTokenJob::RequestOpenIdTokenJob(const QString& userId, const QJsonObject& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestOpenIdTokenJob"), - basePath % "/user/" % userId % "/openid/request_token") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/openid/request_token") { setRequestData(Data(toJson(body))); } - -RequestOpenIdTokenJob::~RequestOpenIdTokenJob() = default; - -const QString& RequestOpenIdTokenJob::accessToken() const -{ - return d->accessToken; -} - -const QString& RequestOpenIdTokenJob::tokenType() const { return d->tokenType; } - -const QString& RequestOpenIdTokenJob::matrixServerName() const -{ - return d->matrixServerName; -} - -int RequestOpenIdTokenJob::expiresIn() const { return d->expiresIn; } - -BaseJob::Status RequestOpenIdTokenJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("access_token"_ls)) - return { IncorrectResponse, - "The key 'access_token' not found in the response" }; - fromJson(json.value("access_token"_ls), d->accessToken); - if (!json.contains("token_type"_ls)) - return { IncorrectResponse, - "The key 'token_type' not found in the response" }; - fromJson(json.value("token_type"_ls), d->tokenType); - if (!json.contains("matrix_server_name"_ls)) - return { IncorrectResponse, - "The key 'matrix_server_name' not found in the response" }; - fromJson(json.value("matrix_server_name"_ls), d->matrixServerName); - if (!json.contains("expires_in"_ls)) - return { IncorrectResponse, - "The key 'expires_in' not found in the response" }; - fromJson(json.value("expires_in"_ls), d->expiresIn); - - return Success; -} diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h index bda4ce74..e69f9ad0 100644 --- a/lib/csapi/openid.h +++ b/lib/csapi/openid.h @@ -4,16 +4,12 @@ #pragma once -#include "converters.h" +#include "csapi/definitions/openid_token.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Get an OpenID token object to verify the requester's identity. * * Gets an OpenID token object that the requester may supply to another @@ -28,42 +24,26 @@ namespace Quotient { class RequestOpenIdTokenJob : public BaseJob { public: /*! \brief Get an OpenID token object to verify the requester's identity. + * * * \param userId * The user to request and OpenID token for. Should be the user who * is authenticated for the request. + * * \param body * An empty object. Reserved for future expansion. */ explicit RequestOpenIdTokenJob(const QString& userId, const QJsonObject& body = {}); - ~RequestOpenIdTokenJob() override; - // Result properties - /// An access token the consumer may use to verify the identity of - /// the person who generated the token. This is given to the federation - /// API ``GET /openid/userinfo``. - const QString& accessToken() const; - - /// The string ``Bearer``. - const QString& tokenType() const; - - /// The homeserver domain the consumer should use when attempting to - /// verify the user's identity. - const QString& matrixServerName() const; - - /// The number of seconds before this token expires and a new one must - /// be generated. - int expiresIn() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// OpenID token information. This response is nearly compatible with the + /// response documented in the `OpenID 1.0 Specification + /// `_ + /// with the only difference being the lack of an ``id_token``. Instead, + /// the Matrix homeserver's name is provided. + OpenidToken data() const { return fromJson(jsonData()); } }; } // namespace Quotient diff --git a/lib/csapi/peeking_events.cpp b/lib/csapi/peeking_events.cpp index 4d886812..70a5b6f3 100644 --- a/lib/csapi/peeking_events.cpp +++ b/lib/csapi/peeking_events.cpp @@ -4,23 +4,12 @@ #include "peeking_events.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class PeekEventsJob::Private { -public: - QString begin; - QString end; - RoomEvents chunk; -}; - -BaseJob::Query queryToPeekEvents(const QString& from, Omittable timeout, - const QString& roomId) +auto queryToPeekEvents(const QString& from, Omittable timeout, + const QString& roomId) { BaseJob::Query _q; addParam(_q, QStringLiteral("from"), from); @@ -32,31 +21,15 @@ BaseJob::Query queryToPeekEvents(const QString& from, Omittable timeout, QUrl PeekEventsJob::makeRequestUrl(QUrl baseUrl, const QString& from, Omittable timeout, const QString& roomId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/events", + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/events", queryToPeekEvents(from, timeout, roomId)); } PeekEventsJob::PeekEventsJob(const QString& from, Omittable timeout, const QString& roomId) : BaseJob(HttpVerb::Get, QStringLiteral("PeekEventsJob"), - basePath % "/events", queryToPeekEvents(from, timeout, roomId)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/events", + queryToPeekEvents(from, timeout, roomId)) {} - -PeekEventsJob::~PeekEventsJob() = default; - -const QString& PeekEventsJob::begin() const { return d->begin; } - -const QString& PeekEventsJob::end() const { return d->end; } - -RoomEvents&& PeekEventsJob::chunk() { return std::move(d->chunk); } - -BaseJob::Status PeekEventsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("start"_ls), d->begin); - fromJson(json.value("end"_ls), d->end); - fromJson(json.value("chunk"_ls), d->chunk); - - return Success; -} diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index 12a66a02..bfb9e24a 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -4,15 +4,11 @@ #pragma once -#include "converters.h" - #include "events/eventloader.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Listen on the event stream. * * This will listen for new events related to a particular room and return @@ -29,12 +25,15 @@ namespace Quotient { class PeekEventsJob : public BaseJob { public: /*! \brief Listen on the event stream. + * * * \param from * The token to stream from. This token is either from a previous * request to this API or from the initial sync API. + * * \param timeout * The maximum time in milliseconds to wait for an event. + * * \param roomId * The room ID for which events should be returned. */ @@ -50,27 +49,19 @@ public: static QUrl makeRequestUrl(QUrl baseUrl, const QString& from = {}, Omittable timeout = none, const QString& roomId = {}); - ~PeekEventsJob() override; // Result properties /// A token which correlates to the first value in ``chunk``. This /// is usually the same token supplied to ``from=``. - const QString& begin() const; + QString begin() const { return loadFromJson("start"_ls); } /// A token which correlates to the last value in ``chunk``. This /// token should be used in the next request to ``/events``. - const QString& end() const; + QString end() const { return loadFromJson("end"_ls); } /// An array of events. - RoomEvents&& chunk(); - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + RoomEvents chunk() { return takeFromJson("chunk"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/presence.cpp b/lib/csapi/presence.cpp index 932ccc6e..58d0d157 100644 --- a/lib/csapi/presence.cpp +++ b/lib/csapi/presence.cpp @@ -4,71 +4,33 @@ #include "presence.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SetPresenceJob::SetPresenceJob(const QString& userId, const QString& presence, const QString& statusMsg) : BaseJob(HttpVerb::Put, QStringLiteral("SetPresenceJob"), - basePath % "/presence/" % userId % "/status") + QStringLiteral("/_matrix/client/r0") % "/presence/" % userId + % "/status") { QJsonObject _data; addParam<>(_data, QStringLiteral("presence"), presence); addParam(_data, QStringLiteral("status_msg"), statusMsg); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetPresenceJob::Private { -public: - QString presence; - Omittable lastActiveAgo; - QString statusMsg; - Omittable currentlyActive; -}; - QUrl GetPresenceJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/presence/" % userId % "/status"); + QStringLiteral("/_matrix/client/r0") + % "/presence/" % userId % "/status"); } GetPresenceJob::GetPresenceJob(const QString& userId) : BaseJob(HttpVerb::Get, QStringLiteral("GetPresenceJob"), - basePath % "/presence/" % userId % "/status") - , d(new Private) -{} - -GetPresenceJob::~GetPresenceJob() = default; - -const QString& GetPresenceJob::presence() const { return d->presence; } - -Omittable GetPresenceJob::lastActiveAgo() const -{ - return d->lastActiveAgo; -} - -const QString& GetPresenceJob::statusMsg() const { return d->statusMsg; } - -Omittable GetPresenceJob::currentlyActive() const + QStringLiteral("/_matrix/client/r0") % "/presence/" % userId + % "/status") { - return d->currentlyActive; -} - -BaseJob::Status GetPresenceJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("presence"_ls)) - return { IncorrectResponse, - "The key 'presence' not found in the response" }; - fromJson(json.value("presence"_ls), d->presence); - fromJson(json.value("last_active_ago"_ls), d->lastActiveAgo); - fromJson(json.value("status_msg"_ls), d->statusMsg); - fromJson(json.value("currently_active"_ls), d->currentlyActive); - - return Success; + addExpectedKey("presence"); } diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index 21e57603..b34028e7 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -4,14 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Update this user's presence state. * * This API sets the given user's presence state. When setting the status, @@ -22,11 +18,14 @@ namespace Quotient { class SetPresenceJob : public BaseJob { public: /*! \brief Update this user's presence state. + * * * \param userId * The user whose presence state to update. + * * \param presence * The new presence state. + * * \param statusMsg * The status message to attach to this state. */ @@ -41,6 +40,7 @@ public: class GetPresenceJob : public BaseJob { public: /*! \brief Get this user's presence state. + * * * \param userId * The user whose presence state to get. @@ -53,29 +53,27 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetPresenceJob() override; // Result properties /// This user's presence. - const QString& presence() const; + QString presence() const { return loadFromJson("presence"_ls); } /// The length of time in milliseconds since an action was performed /// by this user. - Omittable lastActiveAgo() const; + Omittable lastActiveAgo() const + { + return loadFromJson>("last_active_ago"_ls); + } /// The state message for this user if one was set. - const QString& statusMsg() const; + QString statusMsg() const { return loadFromJson("status_msg"_ls); } /// Whether the user is currently active - Omittable currentlyActive() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Omittable currentlyActive() const + { + return loadFromJson>("currently_active"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index d86e3bdc..cb8f72be 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -4,122 +4,67 @@ #include "profile.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SetDisplayNameJob::SetDisplayNameJob(const QString& userId, const QString& displayname) : BaseJob(HttpVerb::Put, QStringLiteral("SetDisplayNameJob"), - basePath % "/profile/" % userId % "/displayname") + QStringLiteral("/_matrix/client/r0") % "/profile/" % userId + % "/displayname") { QJsonObject _data; addParam(_data, QStringLiteral("displayname"), displayname); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetDisplayNameJob::Private { -public: - QString displayname; -}; - QUrl GetDisplayNameJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/profile/" - % userId - % "/displayname"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/profile/" % userId % "/displayname"); } GetDisplayNameJob::GetDisplayNameJob(const QString& userId) : BaseJob(HttpVerb::Get, QStringLiteral("GetDisplayNameJob"), - basePath % "/profile/" % userId % "/displayname", false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/profile/" % userId + % "/displayname", + false) {} -GetDisplayNameJob::~GetDisplayNameJob() = default; - -const QString& GetDisplayNameJob::displayname() const { return d->displayname; } - -BaseJob::Status GetDisplayNameJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("displayname"_ls), d->displayname); - - return Success; -} - SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl) : BaseJob(HttpVerb::Put, QStringLiteral("SetAvatarUrlJob"), - basePath % "/profile/" % userId % "/avatar_url") + QStringLiteral("/_matrix/client/r0") % "/profile/" % userId + % "/avatar_url") { QJsonObject _data; addParam(_data, QStringLiteral("avatar_url"), avatarUrl); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetAvatarUrlJob::Private { -public: - QString avatarUrl; -}; - QUrl GetAvatarUrlJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/profile/" - % userId - % "/avatar_url"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/profile/" % userId % "/avatar_url"); } GetAvatarUrlJob::GetAvatarUrlJob(const QString& userId) : BaseJob(HttpVerb::Get, QStringLiteral("GetAvatarUrlJob"), - basePath % "/profile/" % userId % "/avatar_url", false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/profile/" % userId + % "/avatar_url", + false) {} -GetAvatarUrlJob::~GetAvatarUrlJob() = default; - -const QString& GetAvatarUrlJob::avatarUrl() const { return d->avatarUrl; } - -BaseJob::Status GetAvatarUrlJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("avatar_url"_ls), d->avatarUrl); - - return Success; -} - -class GetUserProfileJob::Private { -public: - QString avatarUrl; - QString displayname; -}; - QUrl GetUserProfileJob::makeRequestUrl(QUrl baseUrl, const QString& userId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/profile/" % userId); + QStringLiteral("/_matrix/client/r0") + % "/profile/" % userId); } GetUserProfileJob::GetUserProfileJob(const QString& userId) : BaseJob(HttpVerb::Get, QStringLiteral("GetUserProfileJob"), - basePath % "/profile/" % userId, false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/profile/" % userId, false) {} - -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(); - fromJson(json.value("avatar_url"_ls), d->avatarUrl); - fromJson(json.value("displayname"_ls), d->displayname); - - return Success; -} diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 8279fe20..5b0d06d9 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Set the user's display name. * * This API sets the given user's display name. You must have permission to @@ -18,9 +16,11 @@ namespace Quotient { class SetDisplayNameJob : public BaseJob { public: /*! \brief Set the user's display name. + * * * \param userId * The user whose display name to set. + * * \param displayname * The new display name for this user. */ @@ -37,6 +37,7 @@ public: class GetDisplayNameJob : public BaseJob { public: /*! \brief Get the user's display name. + * * * \param userId * The user whose display name to get. @@ -49,19 +50,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetDisplayNameJob() override; // Result properties /// The user's display name if they have set one, otherwise not present. - const QString& displayname() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString displayname() const + { + return loadFromJson("displayname"_ls); + } }; /*! \brief Set the user's avatar URL. @@ -72,9 +68,11 @@ private: class SetAvatarUrlJob : public BaseJob { public: /*! \brief Set the user's avatar URL. + * * * \param userId * The user whose avatar URL to set. + * * \param avatarUrl * The new avatar URL for this user. */ @@ -91,6 +89,7 @@ public: class GetAvatarUrlJob : public BaseJob { public: /*! \brief Get the user's avatar URL. + * * * \param userId * The user whose avatar URL to get. @@ -103,19 +102,11 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetAvatarUrlJob() override; // Result properties /// The user's avatar URL if they have set one, otherwise not present. - const QString& avatarUrl() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString avatarUrl() const { return loadFromJson("avatar_url"_ls); } }; /*! \brief Get this user's profile information. @@ -128,6 +119,7 @@ private: class GetUserProfileJob : public BaseJob { public: /*! \brief Get this user's profile information. + * * * \param userId * The user whose profile information to get. @@ -140,22 +132,17 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId); - ~GetUserProfileJob() override; // Result properties /// The user's avatar URL if they have set one, otherwise not present. - const QString& avatarUrl() const; + QString avatarUrl() const { return loadFromJson("avatar_url"_ls); } /// The user's display name if they have set one, otherwise not present. - const QString& displayname() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString displayname() const + { + return loadFromJson("displayname"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/pusher.cpp b/lib/csapi/pusher.cpp index ad51b901..028022c5 100644 --- a/lib/csapi/pusher.cpp +++ b/lib/csapi/pusher.cpp @@ -4,96 +4,29 @@ #include "pusher.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetPushersJob::PusherData& result) - { - fromJson(jo.value("url"_ls), result.url); - fromJson(jo.value("format"_ls), result.format); - } -}; - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, GetPushersJob::Pusher& result) - { - fromJson(jo.value("pushkey"_ls), result.pushkey); - fromJson(jo.value("kind"_ls), result.kind); - fromJson(jo.value("app_id"_ls), result.appId); - fromJson(jo.value("app_display_name"_ls), result.appDisplayName); - fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); - fromJson(jo.value("profile_tag"_ls), result.profileTag); - fromJson(jo.value("lang"_ls), result.lang); - fromJson(jo.value("data"_ls), result.data); - } -}; - -} // namespace Quotient - -class GetPushersJob::Private { -public: - QVector pushers; -}; - QUrl GetPushersJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushers"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/pushers"); } GetPushersJob::GetPushersJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetPushersJob"), - basePath % "/pushers") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/pushers") {} -GetPushersJob::~GetPushersJob() = default; - -const QVector& GetPushersJob::pushers() const -{ - return d->pushers; -} - -BaseJob::Status GetPushersJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("pushers"_ls), d->pushers); - - return Success; -} - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const PostPusherJob::PusherData& pod) - { - addParam(jo, QStringLiteral("url"), pod.url); - addParam(jo, QStringLiteral("format"), pod.format); - } -}; - -} // namespace Quotient - PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, const QString& appId, const QString& appDisplayName, const QString& deviceDisplayName, const QString& lang, const PusherData& data, const QString& profileTag, Omittable append) : BaseJob(HttpVerb::Post, QStringLiteral("PostPusherJob"), - basePath % "/pushers/set") + QStringLiteral("/_matrix/client/r0") % "/pushers/set") { QJsonObject _data; addParam<>(_data, QStringLiteral("pushkey"), pushkey); @@ -105,5 +38,5 @@ PostPusherJob::PostPusherJob(const QString& pushkey, const QString& kind, addParam<>(_data, QStringLiteral("lang"), lang); addParam<>(_data, QStringLiteral("data"), data); addParam(_data, QStringLiteral("append"), append); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index d60a2b56..40cd5796 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -4,16 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Gets the current pushers for the authenticated user * * Gets all currently active pushers for the authenticated user. @@ -73,19 +67,39 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetPushersJob() override; // Result properties /// An array containing the current pushers for the user - const QVector& pushers() const; + QVector pushers() const + { + return loadFromJson>("pushers"_ls); + } +}; -protected: - Status parseJson(const QJsonDocument& data) override; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetPushersJob::PusherData& result) + { + fromJson(jo.value("url"_ls), result.url); + fromJson(jo.value("format"_ls), result.format); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, GetPushersJob::Pusher& result) + { + fromJson(jo.value("pushkey"_ls), result.pushkey); + fromJson(jo.value("kind"_ls), result.kind); + fromJson(jo.value("app_id"_ls), result.appId); + fromJson(jo.value("app_display_name"_ls), result.appDisplayName); + fromJson(jo.value("device_display_name"_ls), result.deviceDisplayName); + fromJson(jo.value("profile_tag"_ls), result.profileTag); + fromJson(jo.value("lang"_ls), result.lang); + fromJson(jo.value("data"_ls), result.data); + } }; /*! \brief Modify a pusher for this user on the homeserver. @@ -117,6 +131,7 @@ public: // Construction/destruction /*! \brief Modify a pusher for this user on the homeserver. + * * * \param pushkey * This is a unique identifier for this pusher. The value you @@ -128,10 +143,12 @@ public: * * If the ``kind`` is ``"email"``, this is the email address to * send notifications to. + * * \param kind * The kind of pusher to configure. ``"http"`` makes a pusher that * sends HTTP pokes. ``"email"`` makes a pusher that emails the * user with unread notifications. ``null`` deletes the pusher. + * * \param appId * This is a reverse-DNS style identifier for the application. * It is recommended that this end with the platform, such that @@ -139,22 +156,28 @@ public: * Max length, 64 chars. * * If the ``kind`` is ``"email"``, this is ``"m.email"``. + * * \param appDisplayName * A string that will allow the user to identify what application * owns this pusher. + * * \param deviceDisplayName * A string that will allow the user to identify what device owns * this pusher. + * * \param lang * The preferred language for receiving notifications (e.g. 'en' * or 'en-US'). + * * \param data * A dictionary of information for the pusher implementation * itself. If ``kind`` is ``http``, this should contain ``url`` * which is the URL to use to send notifications to. + * * \param profileTag * This string determines which set of device specific rules this * pusher executes. + * * \param append * If true, the homeserver should add another pusher with the * given pushkey and App ID in addition to any others with @@ -170,4 +193,13 @@ public: Omittable append = none); }; +template <> +struct JsonObjectConverter { + static void dumpTo(QJsonObject& jo, const PostPusherJob::PusherData& pod) + { + addParam(jo, QStringLiteral("url"), pod.url); + addParam(jo, QStringLiteral("format"), pod.format); + } +}; + } // namespace Quotient diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index cd0fb02d..f2a16f1b 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -4,92 +4,58 @@ #include "pushrules.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetPushRulesJob::Private { -public: - PushRuleset global; -}; - QUrl GetPushRulesJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushrules"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/pushrules"); } GetPushRulesJob::GetPushRulesJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRulesJob"), - basePath % "/pushrules") - , d(new Private) -{} - -GetPushRulesJob::~GetPushRulesJob() = default; - -const PushRuleset& GetPushRulesJob::global() const { return d->global; } - -BaseJob::Status GetPushRulesJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") % "/pushrules") { - auto json = data.object(); - if (!json.contains("global"_ls)) - return { IncorrectResponse, - "The key 'global' not found in the response" }; - fromJson(json.value("global"_ls), d->global); - - return Success; + addExpectedKey("global"); } -class GetPushRuleJob::Private { -public: - PushRule data; -}; - QUrl GetPushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushrules/" - % scope % "/" % kind - % "/" % ruleId); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/pushrules/" % scope % "/" % kind + % "/" % ruleId); } GetPushRuleJob::GetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRuleJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId) {} -GetPushRuleJob::~GetPushRuleJob() = default; - -const PushRule& GetPushRuleJob::data() const { return d->data; } - -BaseJob::Status GetPushRuleJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - QUrl DeletePushRuleJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/pushrules/" - % scope % "/" % kind - % "/" % ruleId); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/pushrules/" % scope % "/" % kind + % "/" % ruleId); } DeletePushRuleJob::DeletePushRuleJob(const QString& scope, const QString& kind, const QString& ruleId) : BaseJob(HttpVerb::Delete, QStringLiteral("DeletePushRuleJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId) + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId) {} -BaseJob::Query queryToSetPushRule(const QString& before, const QString& after) +auto queryToSetPushRule(const QString& before, const QString& after) { BaseJob::Query _q; addParam(_q, QStringLiteral("before"), before); @@ -103,27 +69,24 @@ SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, const QVector& conditions, const QString& pattern) : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId, + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId, queryToSetPushRule(before, after)) { QJsonObject _data; addParam<>(_data, QStringLiteral("actions"), actions); addParam(_data, QStringLiteral("conditions"), conditions); addParam(_data, QStringLiteral("pattern"), pattern); - setRequestData(_data); + setRequestData(std::move(_data)); } -class IsPushRuleEnabledJob::Private { -public: - bool enabled; -}; - QUrl IsPushRuleEnabledJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind + QStringLiteral("/_matrix/client/r0") + % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/enabled"); } @@ -131,49 +94,31 @@ IsPushRuleEnabledJob::IsPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId) : BaseJob(HttpVerb::Get, QStringLiteral("IsPushRuleEnabledJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId - % "/enabled") - , d(new Private) -{} - -IsPushRuleEnabledJob::~IsPushRuleEnabledJob() = default; - -bool IsPushRuleEnabledJob::enabled() const { return d->enabled; } - -BaseJob::Status IsPushRuleEnabledJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId % "/enabled") { - auto json = data.object(); - if (!json.contains("enabled"_ls)) - return { IncorrectResponse, - "The key 'enabled' not found in the response" }; - fromJson(json.value("enabled"_ls), d->enabled); - - return Success; + addExpectedKey("enabled"); } SetPushRuleEnabledJob::SetPushRuleEnabledJob(const QString& scope, const QString& kind, const QString& ruleId, bool enabled) : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleEnabledJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId - % "/enabled") + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId % "/enabled") { QJsonObject _data; addParam<>(_data, QStringLiteral("enabled"), enabled); - setRequestData(_data); + setRequestData(std::move(_data)); } -class GetPushRuleActionsJob::Private { -public: - QStringList actions; -}; - QUrl GetPushRuleActionsJob::makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/pushrules/" % scope % "/" % kind + QStringLiteral("/_matrix/client/r0") + % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions"); } @@ -181,24 +126,10 @@ GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId) : BaseJob(HttpVerb::Get, QStringLiteral("GetPushRuleActionsJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId - % "/actions") - , d(new Private) -{} - -GetPushRuleActionsJob::~GetPushRuleActionsJob() = default; - -const QStringList& GetPushRuleActionsJob::actions() const { return d->actions; } - -BaseJob::Status GetPushRuleActionsJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId % "/actions") { - auto json = data.object(); - if (!json.contains("actions"_ls)) - return { IncorrectResponse, - "The key 'actions' not found in the response" }; - fromJson(json.value("actions"_ls), d->actions); - - return Success; + addExpectedKey("actions"); } SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, @@ -206,10 +137,10 @@ SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, const QString& ruleId, const QStringList& actions) : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleActionsJob"), - basePath % "/pushrules/" % scope % "/" % kind % "/" % ruleId - % "/actions") + QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" + % kind % "/" % ruleId % "/actions") { QJsonObject _data; addParam<>(_data, QStringLiteral("actions"), actions); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index a9169151..0971dc6b 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -4,20 +4,14 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/push_condition.h" #include "csapi/definitions/push_rule.h" #include "csapi/definitions/push_ruleset.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Retrieve all push rulesets. * * Retrieve all push rulesets for this user. Clients can "drill-down" on @@ -36,19 +30,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetPushRulesJob() override; // Result properties /// The global ruleset. - const PushRuleset& global() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + PushRuleset global() const + { + return loadFromJson("global"_ls); + } }; /*! \brief Retrieve a push rule. @@ -58,11 +47,14 @@ private: class GetPushRuleJob : public BaseJob { public: /*! \brief Retrieve a push rule. + * * * \param scope * ``global`` to specify global rules. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. */ @@ -76,19 +68,12 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~GetPushRuleJob() override; // Result properties - /// The push rule. - const PushRule& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// The specific push rule. This will also include keys specific to the + /// rule itself such as the rule's ``actions`` and ``conditions`` if set. + PushRule data() const { return fromJson(jsonData()); } }; /*! \brief Delete a push rule. @@ -98,11 +83,14 @@ private: class DeletePushRuleJob : public BaseJob { public: /*! \brief Delete a push rule. + * * * \param scope * ``global`` to specify global rules. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. */ @@ -129,29 +117,38 @@ public: class SetPushRuleJob : public BaseJob { public: /*! \brief Add or change a push rule. + * * * \param scope * ``global`` to specify global rules. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. + * * \param actions * The action(s) to perform when the conditions for this rule are met. + * * \param before * Use 'before' with a ``rule_id`` as its value to make the new rule the * next-most important rule with respect to the given user defined rule. * It is not possible to add a rule relative to a predefined server rule. + * * \param after * This makes the new rule the next-less important rule relative to the * given user defined rule. It is not possible to add a rule relative * to a predefined server rule. + * * \param conditions * The conditions that must hold true for an event in order for a * rule to be applied to an event. A rule with no conditions - * always matches. Only applicable to ``underride`` and ``override`` - * rules. \param pattern Only applicable to ``content`` rules. The - * glob-style pattern to match against. + * always matches. Only applicable to ``underride`` and ``override`` rules. + * + * \param pattern + * Only applicable to ``content`` rules. The glob-style pattern to match + * against. */ explicit SetPushRuleJob(const QString& scope, const QString& kind, const QString& ruleId, const QStringList& actions, @@ -168,12 +165,15 @@ public: class IsPushRuleEnabledJob : public BaseJob { public: /*! \brief Get whether a push rule is enabled + * * * \param scope * Either ``global`` or ``device/`` to specify global * rules or device rules for the given ``profile_tag``. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. */ @@ -187,19 +187,11 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~IsPushRuleEnabledJob() override; // Result properties /// Whether the push rule is enabled or not. - bool enabled() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + bool enabled() const { return loadFromJson("enabled"_ls); } }; /*! \brief Enable or disable a push rule. @@ -209,13 +201,17 @@ private: class SetPushRuleEnabledJob : public BaseJob { public: /*! \brief Enable or disable a push rule. + * * * \param scope * ``global`` to specify global rules. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. + * * \param enabled * Whether the push rule is enabled or not. */ @@ -230,12 +226,15 @@ public: class GetPushRuleActionsJob : public BaseJob { public: /*! \brief The actions for a push rule + * * * \param scope * Either ``global`` or ``device/`` to specify global * rules or device rules for the given ``profile_tag``. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. */ @@ -249,19 +248,14 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& scope, const QString& kind, const QString& ruleId); - ~GetPushRuleActionsJob() override; // Result properties /// The action(s) to perform for this rule. - const QStringList& actions() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QStringList actions() const + { + return loadFromJson("actions"_ls); + } }; /*! \brief Set the actions for a push rule. @@ -272,13 +266,17 @@ private: class SetPushRuleActionsJob : public BaseJob { public: /*! \brief Set the actions for a push rule. + * * * \param scope * ``global`` to specify global rules. + * * \param kind * The kind of rule + * * \param ruleId * The identifier for the rule. + * * \param actions * The action(s) to perform for this rule. */ diff --git a/lib/csapi/read_markers.cpp b/lib/csapi/read_markers.cpp index 76145808..39e4d148 100644 --- a/lib/csapi/read_markers.cpp +++ b/lib/csapi/read_markers.cpp @@ -4,22 +4,19 @@ #include "read_markers.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SetReadMarkerJob::SetReadMarkerJob(const QString& roomId, const QString& mFullyRead, const QString& mRead) : BaseJob(HttpVerb::Post, QStringLiteral("SetReadMarkerJob"), - basePath % "/rooms/" % roomId % "/read_markers") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/read_markers") { QJsonObject _data; addParam<>(_data, QStringLiteral("m.fully_read"), mFullyRead); addParam(_data, QStringLiteral("m.read"), mRead); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h index 539aa5e4..addb9123 100644 --- a/lib/csapi/read_markers.h +++ b/lib/csapi/read_markers.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Set the position of the read marker for a room. * * Sets the position of the read marker for a given room, and optionally @@ -18,12 +16,15 @@ namespace Quotient { class SetReadMarkerJob : public BaseJob { public: /*! \brief Set the position of the read marker for a room. + * * * \param roomId * The room ID to set the read marker in for the user. + * * \param mFullyRead * The event ID the read marker should be located at. The * event MUST belong to the room. + * * \param mRead * The event ID to set the read receipt location at. This is * equivalent to calling ``/receipt/m.read/$elsewhere:example.org`` diff --git a/lib/csapi/receipts.cpp b/lib/csapi/receipts.cpp index 87264074..00d1c28a 100644 --- a/lib/csapi/receipts.cpp +++ b/lib/csapi/receipts.cpp @@ -4,20 +4,16 @@ #include "receipts.h" -#include "converters.h" - #include using namespace Quotient; -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, QStringLiteral("PostReceiptJob"), - basePath % "/rooms/" % roomId % "/receipt/" % receiptType % "/" - % eventId) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/receipt/" % receiptType % "/" % eventId) { setRequestData(Data(toJson(receipt))); } diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h index eb82fc28..fdec3b88 100644 --- a/lib/csapi/receipts.h +++ b/lib/csapi/receipts.h @@ -6,12 +6,8 @@ #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Send a receipt for the given event ID. * * This API updates the marker for the given receipt type to the event ID @@ -20,13 +16,17 @@ namespace Quotient { class PostReceiptJob : public BaseJob { public: /*! \brief Send a receipt for the given event ID. + * * * \param roomId * The room in which to send the event. + * * \param receiptType * The type of receipt to send. + * * \param eventId * The event ID to acknowledge up to. + * * \param receipt * Extra receipt information to attach to ``content`` if any. The * server will automatically set the ``ts`` field. diff --git a/lib/csapi/redaction.cpp b/lib/csapi/redaction.cpp index 2b6417ea..91497064 100644 --- a/lib/csapi/redaction.cpp +++ b/lib/csapi/redaction.cpp @@ -4,38 +4,17 @@ #include "redaction.h" -#include "converters.h" - #include using namespace Quotient; -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, QStringLiteral("RedactEventJob"), - basePath % "/rooms/" % roomId % "/redact/" % eventId % "/" % txnId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/redact/" % eventId % "/" % txnId) { QJsonObject _data; addParam(_data, QStringLiteral("reason"), 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(); - fromJson(json.value("event_id"_ls), d->eventId); - - return Success; + setRequestData(std::move(_data)); } diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h index 0e9095d1..c737de41 100644 --- a/lib/csapi/redaction.h +++ b/lib/csapi/redaction.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Strips all non-integrity-critical information out of an event. * * Strips all information out of an event which isn't critical to the @@ -18,38 +16,35 @@ namespace Quotient { * This cannot be undone. * * Users may redact their own events, and any user with a power level - * greater than or equal to the `redact` power level of the room may + * greater than or equal to the ``redact`` power level of the room may * redact events there. */ class RedactEventJob : public BaseJob { public: /*! \brief Strips all non-integrity-critical information out of an event. + * * * \param roomId * The room from which to redact the event. + * * \param eventId * The ID of the event to redact + * * \param txnId * The transaction ID for this event. Clients should generate a * unique ID; it will be used by the server to ensure idempotency of - * requests. \param reason The reason for the event being redacted. + * requests. + * + * \param reason + * The reason for the event being redacted. */ explicit RedactEventJob(const QString& roomId, const QString& eventId, const QString& txnId, const QString& reason = {}); - ~RedactEventJob() override; - // Result properties /// A unique identifier for the event. - const QString& eventId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString eventId() const { return loadFromJson("event_id"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 768617cc..9f88ef28 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -4,23 +4,11 @@ #include "registration.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class RegisterJob::Private { -public: - QString userId; - QString accessToken; - QString homeServer; - QString deviceId; -}; - -BaseJob::Query queryToRegister(const QString& kind) +auto queryToRegister(const QString& kind) { BaseJob::Query _q; addParam(_q, QStringLiteral("kind"), kind); @@ -29,220 +17,94 @@ BaseJob::Query queryToRegister(const QString& kind) RegisterJob::RegisterJob(const QString& kind, const Omittable& auth, - Omittable bindEmail, const QString& username, - const QString& password, const QString& deviceId, + const QString& username, const QString& password, + const QString& deviceId, const QString& initialDeviceDisplayName, Omittable inhibitLogin) : BaseJob(HttpVerb::Post, QStringLiteral("RegisterJob"), - basePath % "/register", queryToRegister(kind), {}, false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/register", + queryToRegister(kind), {}, false) { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); - addParam(_data, QStringLiteral("bind_email"), bindEmail); addParam(_data, QStringLiteral("username"), username); addParam(_data, QStringLiteral("password"), password); addParam(_data, QStringLiteral("device_id"), deviceId); addParam(_data, QStringLiteral("initial_device_display_name"), initialDeviceDisplayName); addParam(_data, QStringLiteral("inhibit_login"), inhibitLogin); - setRequestData(_data); -} - -RegisterJob::~RegisterJob() = default; - -const QString& RegisterJob::userId() const { return d->userId; } - -const QString& RegisterJob::accessToken() const { return d->accessToken; } - -const QString& RegisterJob::homeServer() const { return d->homeServer; } - -const QString& RegisterJob::deviceId() const { return d->deviceId; } - -BaseJob::Status RegisterJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("user_id"_ls)) - return { IncorrectResponse, - "The key 'user_id' not found in the response" }; - fromJson(json.value("user_id"_ls), d->userId); - fromJson(json.value("access_token"_ls), d->accessToken); - fromJson(json.value("home_server"_ls), d->homeServer); - fromJson(json.value("device_id"_ls), d->deviceId); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("user_id"); } -class RequestTokenToRegisterEmailJob::Private { -public: - Sid data; -}; - RequestTokenToRegisterEmailJob::RequestTokenToRegisterEmailJob( - const QString& clientSecret, const QString& email, int sendAttempt, - const QString& idServer, const QString& nextLink) + const EmailValidationData& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToRegisterEmailJob"), - basePath % "/register/email/requestToken", false) - , d(new Private) -{ - QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("email"), email); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); -} - -RequestTokenToRegisterEmailJob::~RequestTokenToRegisterEmailJob() = default; - -const Sid& RequestTokenToRegisterEmailJob::data() const { return d->data; } - -BaseJob::Status -RequestTokenToRegisterEmailJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") + % "/register/email/requestToken", + false) { - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } -class RequestTokenToRegisterMSISDNJob::Private { -public: - Sid data; -}; - RequestTokenToRegisterMSISDNJob::RequestTokenToRegisterMSISDNJob( - const QString& clientSecret, const QString& country, - const QString& phoneNumber, int sendAttempt, const QString& idServer, - const QString& nextLink) + const MsisdnValidationData& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToRegisterMSISDNJob"), - basePath % "/register/msisdn/requestToken", false) - , d(new Private) + QStringLiteral("/_matrix/client/r0") + % "/register/msisdn/requestToken", + false) { - QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("country"), country); - addParam<>(_data, QStringLiteral("phone_number"), phoneNumber); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); -} - -RequestTokenToRegisterMSISDNJob::~RequestTokenToRegisterMSISDNJob() = default; - -const Sid& RequestTokenToRegisterMSISDNJob::data() const { return d->data; } - -BaseJob::Status -RequestTokenToRegisterMSISDNJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } ChangePasswordJob::ChangePasswordJob(const QString& newPassword, + Omittable logoutDevices, const Omittable& auth) : BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"), - basePath % "/account/password") + QStringLiteral("/_matrix/client/r0") % "/account/password") { QJsonObject _data; addParam<>(_data, QStringLiteral("new_password"), newPassword); + addParam(_data, QStringLiteral("logout_devices"), logoutDevices); addParam(_data, QStringLiteral("auth"), auth); - setRequestData(_data); + setRequestData(std::move(_data)); } -class RequestTokenToResetPasswordEmailJob::Private { -public: - Sid data; -}; - RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob( - const QString& clientSecret, const QString& email, int sendAttempt, - const QString& idServer, const QString& nextLink) + const EmailValidationData& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToResetPasswordEmailJob"), - basePath % "/account/password/email/requestToken", false) - , d(new Private) -{ - QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("email"), email); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); -} - -RequestTokenToResetPasswordEmailJob::~RequestTokenToResetPasswordEmailJob() = - default; - -const Sid& RequestTokenToResetPasswordEmailJob::data() const { return d->data; } - -BaseJob::Status -RequestTokenToResetPasswordEmailJob::parseJson(const QJsonDocument& data) + QStringLiteral("/_matrix/client/r0") + % "/account/password/email/requestToken", + false) { - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } -class RequestTokenToResetPasswordMSISDNJob::Private { -public: - Sid data; -}; - RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob( - const QString& clientSecret, const QString& country, - const QString& phoneNumber, int sendAttempt, const QString& idServer, - const QString& nextLink) + const RequestMsisdnValidation& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToResetPasswordMSISDNJob"), - basePath % "/account/password/msisdn/requestToken", false) - , d(new Private) -{ - QJsonObject _data; - addParam<>(_data, QStringLiteral("client_secret"), clientSecret); - addParam<>(_data, QStringLiteral("country"), country); - addParam<>(_data, QStringLiteral("phone_number"), phoneNumber); - addParam<>(_data, QStringLiteral("send_attempt"), sendAttempt); - addParam(_data, QStringLiteral("next_link"), nextLink); - addParam<>(_data, QStringLiteral("id_server"), idServer); - setRequestData(_data); -} - -RequestTokenToResetPasswordMSISDNJob::~RequestTokenToResetPasswordMSISDNJob() = - default; - -const Sid& RequestTokenToResetPasswordMSISDNJob::data() const + QStringLiteral("/_matrix/client/r0") + % "/account/password/msisdn/requestToken", + false) { - return d->data; -} - -BaseJob::Status -RequestTokenToResetPasswordMSISDNJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; + setRequestData(Data(toJson(body))); } DeactivateAccountJob::DeactivateAccountJob( - const Omittable& auth) + const Omittable& auth, const QString& idServer) : BaseJob(HttpVerb::Post, QStringLiteral("DeactivateAccountJob"), - basePath % "/account/deactivate") + QStringLiteral("/_matrix/client/r0") % "/account/deactivate") { QJsonObject _data; addParam(_data, QStringLiteral("auth"), auth); - setRequestData(_data); + addParam(_data, QStringLiteral("id_server"), idServer); + setRequestData(std::move(_data)); + addExpectedKey("id_server_unbind_result"); } -class CheckUsernameAvailabilityJob::Private { -public: - Omittable available; -}; - -BaseJob::Query queryToCheckUsernameAvailability(const QString& username) +auto queryToCheckUsernameAvailability(const QString& username) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("username"), username); @@ -253,28 +115,13 @@ QUrl CheckUsernameAvailabilityJob::makeRequestUrl(QUrl baseUrl, const QString& username) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/register/available", + QStringLiteral("/_matrix/client/r0") + % "/register/available", queryToCheckUsernameAvailability(username)); } CheckUsernameAvailabilityJob::CheckUsernameAvailabilityJob(const QString& username) : BaseJob(HttpVerb::Get, QStringLiteral("CheckUsernameAvailabilityJob"), - basePath % "/register/available", + QStringLiteral("/_matrix/client/r0") % "/register/available", queryToCheckUsernameAvailability(username), {}, false) - , d(new Private) {} - -CheckUsernameAvailabilityJob::~CheckUsernameAvailabilityJob() = default; - -Omittable CheckUsernameAvailabilityJob::available() const -{ - return d->available; -} - -BaseJob::Status CheckUsernameAvailabilityJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("available"_ls), d->available); - - return Success; -} diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index 2619dd87..9d96db32 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -4,20 +4,20 @@ #pragma once -#include "converters.h" - -#include "csapi/../identity/definitions/sid.h" +#include "csapi/../identity/definitions/request_msisdn_validation.h" +#include "csapi/./definitions/request_email_validation.h" +#include "csapi/./definitions/request_msisdn_validation.h" #include "csapi/definitions/auth_data.h" +#include "csapi/definitions/request_token_response.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Register for an account on this homeserver. * - * This API endpoint uses the `User-Interactive Authentication API`_. + * This API endpoint uses the `User-Interactive Authentication API`_, except in + * the cases where a guest account is being registered. * * Register for an account on this homeserver. * @@ -49,36 +49,46 @@ namespace Quotient { * supplied by the client or generated by the server. The server may * invalidate any access token previously associated with that device. See * `Relationship between access tokens and devices`_. + * + * When registering a guest account, all parameters in the request body + * with the exception of ``initial_device_display_name`` MUST BE ignored + * by the server. The server MUST pick a ``device_id`` for the account + * regardless of input. + * + * Any user ID returned by this API must conform to the grammar given in the + * `Matrix specification <../appendices.html#user-identifiers>`_. */ class RegisterJob : public BaseJob { public: /*! \brief Register for an account on this homeserver. + * * * \param kind - * The kind of account to register. Defaults to `user`. + * The kind of account to register. Defaults to ``user``. + * * \param auth * Additional authentication information for the * user-interactive authentication API. Note that this * information is *not* used to define how the registered user * should be authenticated, but is instead used to - * authenticate the ``register`` call itself. It should be - * left empty, or omitted, unless an earlier call returned an - * response with status code 401. - * \param bindEmail - * If true, the server binds the email used for authentication to - * the Matrix ID with the identity server. + * authenticate the ``register`` call itself. + * * \param username * The basis for the localpart of the desired Matrix ID. If omitted, * the homeserver MUST generate a Matrix ID local part. + * * \param password * The desired password for the account. + * * \param deviceId * ID of the client device. If this does not correspond to a * known client device, a new device will be created. The server * will auto-generate a device_id if this is not specified. + * * \param initialDeviceDisplayName * A display name to assign to the newly-created device. Ignored * if ``device_id`` corresponds to a known device. + * * \param inhibitLogin * If true, an ``access_token`` and ``device_id`` should not be * returned from this call, therefore preventing an automatic @@ -86,28 +96,27 @@ public: */ explicit RegisterJob(const QString& kind = QStringLiteral("user"), const Omittable& auth = none, - Omittable bindEmail = none, const QString& username = {}, const QString& password = {}, const QString& deviceId = {}, const QString& initialDeviceDisplayName = {}, Omittable inhibitLogin = none); - ~RegisterJob() override; - // Result properties /// The fully-qualified Matrix user ID (MXID) that has been registered. /// /// Any user ID returned by this API must conform to the grammar given in - /// the `Matrix specification - /// `_. - const QString& userId() const; + /// the `Matrix specification <../appendices.html#user-identifiers>`_. + QString userId() const { return loadFromJson("user_id"_ls); } /// An access token for the account. /// This access token can then be used to authorize other requests. /// Required if the ``inhibit_login`` option is false. - const QString& accessToken() const; + QString accessToken() const + { + return loadFromJson("access_token"_ls); + } /// The server_name of the homeserver on which the account has /// been registered. @@ -115,180 +124,141 @@ public: /// **Deprecated**. Clients should extract the server_name from /// ``user_id`` (by splitting at the first colon) if they require /// it. Note also that ``homeserver`` is not spelt this way. - const QString& homeServer() const; + QString homeServer() const + { + return loadFromJson("home_server"_ls); + } /// ID of the registered device. Will be the same as the /// corresponding parameter in the request, if one was specified. /// Required if the ``inhibit_login`` option is false. - const QString& deviceId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString deviceId() const { return loadFromJson("device_id"_ls); } }; /*! \brief Begins the validation process for an email to be used during * registration. * - * Proxies the Identity Service API ``validate/email/requestToken``, but - * first checks that the given email address is not already associated - * with an account on this homeserver. See the Identity Service API for - * further information. + * The homeserver must check that the given email address is **not** + * already associated with an account on this homeserver. The homeserver + * should validate the email itself, either by sending a validation email + * itself or by using a service it has control over. */ class RequestTokenToRegisterEmailJob : public BaseJob { public: /*! \brief Begins the validation process for an email to be used during * registration. * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given email address is **not** + * already associated with an account on this homeserver. The homeserver + * should validate the email itself, either by sending a validation email + * itself or by using a service it has control over. */ - explicit RequestTokenToRegisterEmailJob(const QString& clientSecret, - const QString& email, - int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenToRegisterEmailJob() override; + explicit RequestTokenToRegisterEmailJob(const EmailValidationData& body); // Result properties - /// An email has been sent to the specified address. - /// Note that this may be an email containing the validation token or it may - /// be informing the user of an error. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// An email has been sent to the specified address. Note that this + /// may be an email containing the validation token or it may be + /// informing the user of an error. + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Requests a validation token be sent to the given phone number for the * purpose of registering an account * - * Proxies the Identity Service API ``validate/msisdn/requestToken``, but - * first checks that the given phone number is not already associated - * with an account on this homeserver. See the Identity Service API for - * further information. + * The homeserver must check that the given phone number is **not** + * already associated with an account on this homeserver. The homeserver + * should validate the phone number itself, either by sending a validation + * message itself or by using a service it has control over. */ class RequestTokenToRegisterMSISDNJob : public BaseJob { public: /*! \brief Requests a validation token be sent to the given phone number for * the purpose of registering an account * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given phone number is **not** + * already associated with an account on this homeserver. The homeserver + * should validate the phone number itself, either by sending a validation + * message itself or by using a service it has control over. */ - explicit RequestTokenToRegisterMSISDNJob(const QString& clientSecret, - const QString& country, - const QString& phoneNumber, - int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenToRegisterMSISDNJob() override; + explicit RequestTokenToRegisterMSISDNJob(const MsisdnValidationData& body); // Result properties - /// An SMS message has been sent to the specified phone number. - /// Note that this may be an SMS message containing the validation token or + /// An SMS message has been sent to the specified phone number. Note + /// that this may be an SMS message containing the validation token or /// it may be informing the user of an error. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Changes a user's password. * * Changes the password for an account on this homeserver. * - * This API endpoint uses the `User-Interactive Authentication API`_. + * This API endpoint uses the `User-Interactive Authentication API`_ to + * ensure the user changing the password is actually the owner of the + * account. * * An access token should be submitted to this endpoint if the client has * an active session. * * The homeserver may change the flows available depending on whether a - * valid access token is provided. + * valid access token is provided. The homeserver SHOULD NOT revoke the + * access token provided in the request. Whether other access tokens for + * the user are revoked depends on the request parameters. */ class ChangePasswordJob : public BaseJob { public: /*! \brief Changes a user's password. + * * * \param newPassword * The new password for the account. + * + * \param logoutDevices + * Whether the user's other access tokens, and their associated devices, + * should be revoked if the request succeeds. Defaults to true. + * + * When ``false``, the server can still take advantage of `the soft logout + * method <#soft-logout>`_ for the user's remaining devices. + * * \param auth * Additional authentication information for the user-interactive * authentication API. */ explicit ChangePasswordJob(const QString& newPassword, + Omittable logoutDevices = none, const Omittable& auth = none); }; /*! \brief Requests a validation token be sent to the given email address for * the purpose of resetting a user's password * - * Proxies the Identity Service API ``validate/email/requestToken``, but - * first checks that the given email address **is** associated with an account - * on this homeserver. This API should be used to request - * validation tokens when authenticating for the - * `account/password` endpoint. This API's parameters and response are - * identical to that of the HS API |/register/email/requestToken|_ except that - * `M_THREEPID_NOT_FOUND` may be returned if no account matching the + * The homeserver must check that the given email address **is + * associated** with an account on this homeserver. This API should be + * used to request validation tokens when authenticating for the + * ``/account/password`` endpoint. + * + * This API's parameters and response are identical to that of the + * |/register/email/requestToken|_ endpoint, except that + * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the * given email address could be found. The server may instead send an * email to the given address prompting the user to create an account. - * `M_THREEPID_IN_USE` may not be returned. + * ``M_THREEPID_IN_USE`` may not be returned. + * + * The homeserver should validate the email itself, either by sending a + * validation email itself or by using a service it has control over. + * * * .. |/register/email/requestToken| replace:: ``/register/email/requestToken`` * @@ -300,62 +270,58 @@ public: /*! \brief Requests a validation token be sent to the given email address * for the purpose of resetting a user's password * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param email - * The email address to validate. - * \param sendAttempt - * The server will only send an email if the ``send_attempt`` - * is a number greater than the most recent one which it has seen, - * scoped to that ``email`` + ``client_secret`` pair. This is to - * avoid repeatedly sending the same email in the case of request - * retries between the POSTing user and the identity server. - * The client should increment this value if they desire a new - * email (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given email address **is + * associated** with an account on this homeserver. This API should be + * used to request validation tokens when authenticating for the + * ``/account/password`` endpoint. + * + * This API's parameters and response are identical to that of the + * |/register/email/requestToken|_ endpoint, except that + * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the + * given email address could be found. The server may instead send an + * email to the given address prompting the user to create an account. + * ``M_THREEPID_IN_USE`` may not be returned. + * + * The homeserver should validate the email itself, either by sending a + * validation email itself or by using a service it has control over. + * + * + * .. |/register/email/requestToken| replace:: + * ``/register/email/requestToken`` + * + * .. _/register/email/requestToken: + * #post-matrix-client-r0-register-email-requesttoken */ - explicit RequestTokenToResetPasswordEmailJob(const QString& clientSecret, - const QString& email, - int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenToResetPasswordEmailJob() override; + explicit RequestTokenToResetPasswordEmailJob(const EmailValidationData& body); // Result properties /// An email was sent to the given address. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Requests a validation token be sent to the given phone number for the * purpose of resetting a user's password. * - * Proxies the Identity Service API ``validate/msisdn/requestToken``, but - * first checks that the given phone number **is** associated with an account - * on this homeserver. This API should be used to request - * validation tokens when authenticating for the - * `account/password` endpoint. This API's parameters and response are - * identical to that of the HS API |/register/msisdn/requestToken|_ except that - * `M_THREEPID_NOT_FOUND` may be returned if no account matching the - * given phone number could be found. The server may instead send an - * SMS message to the given address prompting the user to create an account. - * `M_THREEPID_IN_USE` may not be returned. + * The homeserver must check that the given phone number **is + * associated** with an account on this homeserver. This API should be + * used to request validation tokens when authenticating for the + * ``/account/password`` endpoint. + * + * This API's parameters and response are identical to that of the + * |/register/msisdn/requestToken|_ endpoint, except that + * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the + * given phone number could be found. The server may instead send the SMS + * to the given phone number prompting the user to create an account. + * ``M_THREEPID_IN_USE`` may not be returned. + * + * The homeserver should validate the phone number itself, either by sending a + * validation message itself or by using a service it has control over. * * .. |/register/msisdn/requestToken| replace:: ``/register/msisdn/requestToken`` * @@ -367,51 +333,39 @@ public: /*! \brief Requests a validation token be sent to the given phone number for * the purpose of resetting a user's password. * - * \param clientSecret - * A unique string generated by the client, and used to identify the - * validation attempt. It must be a string consisting of the characters - * ``[0-9a-zA-Z.=_-]``. Its length must not exceed 255 characters and it - * must not be empty. - * \param country - * The two-letter uppercase ISO country code that the number in - * ``phone_number`` should be parsed as if it were dialled from. - * \param phoneNumber - * The phone number to validate. - * \param sendAttempt - * The server will only send an SMS if the ``send_attempt`` is a - * number greater than the most recent one which it has seen, - * scoped to that ``country`` + ``phone_number`` + ``client_secret`` - * triple. This is to avoid repeatedly sending the same SMS in - * the case of request retries between the POSTing user and the - * identity server. The client should increment this value if - * they desire a new SMS (e.g. a reminder) to be sent. - * \param idServer - * The hostname of the identity server to communicate with. May - * optionally include a port. - * \param nextLink - * Optional. When the validation is completed, the identity - * server will redirect the user to this URL. + * + * \param body + * The homeserver must check that the given phone number **is + * associated** with an account on this homeserver. This API should be + * used to request validation tokens when authenticating for the + * ``/account/password`` endpoint. + * + * This API's parameters and response are identical to that of the + * |/register/msisdn/requestToken|_ endpoint, except that + * ``M_THREEPID_NOT_FOUND`` may be returned if no account matching the + * given phone number could be found. The server may instead send the SMS + * to the given phone number prompting the user to create an account. + * ``M_THREEPID_IN_USE`` may not be returned. + * + * The homeserver should validate the phone number itself, either by sending + * a validation message itself or by using a service it has control over. + * + * .. |/register/msisdn/requestToken| replace:: + * ``/register/msisdn/requestToken`` + * + * .. _/register/msisdn/requestToken: + * #post-matrix-client-r0-register-email-requesttoken */ - explicit RequestTokenToResetPasswordMSISDNJob(const QString& clientSecret, - const QString& country, - const QString& phoneNumber, - int sendAttempt, - const QString& idServer, - const QString& nextLink = {}); - - ~RequestTokenToResetPasswordMSISDNJob() override; + explicit RequestTokenToResetPasswordMSISDNJob( + const RequestMsisdnValidation& body); // Result properties /// An SMS message was sent to the given phone number. - const Sid& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + RequestTokenResponse data() const + { + return fromJson(jsonData()); + } }; /*! \brief Deactivate a user's account. @@ -426,17 +380,45 @@ private: * * The homeserver may change the flows available depending on whether a * valid access token is provided. + * + * Unlike other endpoints, this endpoint does not take an ``id_access_token`` + * parameter because the homeserver is expected to sign the request to the + * identity server instead. */ class DeactivateAccountJob : public BaseJob { public: /*! \brief Deactivate a user's account. + * * * \param auth * Additional authentication information for the user-interactive * authentication API. + * + * \param idServer + * The identity server to unbind all of the user's 3PIDs from. + * If not provided, the homeserver MUST use the ``id_server`` + * that was originally use to bind each identifier. If the + * homeserver does not know which ``id_server`` that was, + * it must return an ``id_server_unbind_result`` of + * ``no-support``. */ - explicit DeactivateAccountJob( - const Omittable& auth = none); + explicit DeactivateAccountJob(const Omittable& auth = none, + const QString& idServer = {}); + + // Result properties + + /// An indicator as to whether or not the homeserver was able to unbind + /// the user's 3PIDs from the identity server(s). ``success`` indicates + /// that all identifiers have been unbound from the identity server while + /// ``no-support`` indicates that one or more identifiers failed to unbind + /// due to the identity server refusing the request or the homeserver + /// being unable to determine an identity server to unbind from. This + /// must be ``success`` if the homeserver has no identifiers to unbind + /// for the user. + QString idServerUnbindResult() const + { + return loadFromJson("id_server_unbind_result"_ls); + } }; /*! \brief Checks to see if a username is available on the server. @@ -457,6 +439,7 @@ public: class CheckUsernameAvailabilityJob : public BaseJob { public: /*! \brief Checks to see if a username is available on the server. + * * * \param username * The username to check the availability of. @@ -469,20 +452,15 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& username); - ~CheckUsernameAvailabilityJob() override; // Result properties /// A flag to indicate that the username is available. This should always /// be ``true`` when the server replies with 200 OK. - Omittable available() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + Omittable available() const + { + return loadFromJson>("available"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/report_content.cpp b/lib/csapi/report_content.cpp index f38b7a9a..0a41625f 100644 --- a/lib/csapi/report_content.cpp +++ b/lib/csapi/report_content.cpp @@ -4,21 +4,18 @@ #include "report_content.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - ReportContentJob::ReportContentJob(const QString& roomId, const QString& eventId, int score, const QString& reason) : BaseJob(HttpVerb::Post, QStringLiteral("ReportContentJob"), - basePath % "/rooms/" % roomId % "/report/" % eventId) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/report/" % eventId) { QJsonObject _data; addParam<>(_data, QStringLiteral("score"), score); addParam<>(_data, QStringLiteral("reason"), reason); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/report_content.h b/lib/csapi/report_content.h index 0f3cc3d5..13dc9138 100644 --- a/lib/csapi/report_content.h +++ b/lib/csapi/report_content.h @@ -4,14 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Reports an event as inappropriate. * * Reports an event as inappropriate to the server, which may then notify @@ -20,14 +16,18 @@ namespace Quotient { class ReportContentJob : public BaseJob { public: /*! \brief Reports an event as inappropriate. + * * * \param roomId * The room in which the event being reported is located. + * * \param eventId * The event to report. + * * \param score * The score to rate this content as where -100 is most offensive * and 0 is inoffensive. + * * \param reason * The reason the content is being reported. May be blank. */ diff --git a/lib/csapi/room_send.cpp b/lib/csapi/room_send.cpp index d278433b..63986c56 100644 --- a/lib/csapi/room_send.cpp +++ b/lib/csapi/room_send.cpp @@ -4,36 +4,16 @@ #include "room_send.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class SendMessageJob::Private { -public: - QString eventId; -}; - SendMessageJob::SendMessageJob(const QString& roomId, const QString& eventType, const QString& txnId, const QJsonObject& body) : BaseJob(HttpVerb::Put, QStringLiteral("SendMessageJob"), - basePath % "/rooms/" % roomId % "/send/" % eventType % "/" % txnId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/send/" % eventType % "/" % txnId) { setRequestData(Data(toJson(body))); -} - -SendMessageJob::~SendMessageJob() = default; - -const QString& SendMessageJob::eventId() const { return d->eventId; } - -BaseJob::Status SendMessageJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("event_id"_ls), d->eventId); - - return Success; + addExpectedKey("event_id"); } diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h index 96b111ff..b1905e53 100644 --- a/lib/csapi/room_send.h +++ b/lib/csapi/room_send.h @@ -6,12 +6,8 @@ #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Send a message event to the given room. * * This endpoint is used to send a message event to a room. Message events @@ -25,15 +21,19 @@ namespace Quotient { class SendMessageJob : public BaseJob { public: /*! \brief Send a message event to the given room. + * * * \param roomId * The room to send the event to. + * * \param eventType * The type of event to send. + * * \param txnId * The transaction ID for this event. Clients should generate an * ID unique across requests with the same access token; it will be * used by the server to ensure idempotency of requests. + * * \param body * This endpoint is used to send a message event to a room. Message events * allow access to historical events and pagination, making them suited @@ -46,19 +46,10 @@ public: explicit SendMessageJob(const QString& roomId, const QString& eventType, const QString& txnId, const QJsonObject& body = {}); - ~SendMessageJob() override; - // Result properties /// A unique identifier for the event. - const QString& eventId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString eventId() const { return loadFromJson("event_id"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/room_state.cpp b/lib/csapi/room_state.cpp index 5973b06b..e18108ac 100644 --- a/lib/csapi/room_state.cpp +++ b/lib/csapi/room_state.cpp @@ -4,65 +4,18 @@ #include "room_state.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class SetRoomStateWithKeyJob::Private { -public: - QString eventId; -}; - SetRoomStateWithKeyJob::SetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey, const QJsonObject& body) : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomStateWithKeyJob"), - basePath % "/rooms/" % roomId % "/state/" % eventType % "/" - % stateKey) - , d(new Private) -{ - setRequestData(Data(toJson(body))); -} - -SetRoomStateWithKeyJob::~SetRoomStateWithKeyJob() = default; - -const QString& SetRoomStateWithKeyJob::eventId() const { return d->eventId; } - -BaseJob::Status SetRoomStateWithKeyJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("event_id"_ls), d->eventId); - - return Success; -} - -class SetRoomStateJob::Private { -public: - QString eventId; -}; - -SetRoomStateJob::SetRoomStateJob(const QString& roomId, const QString& eventType, - const QJsonObject& body) - : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomStateJob"), - basePath % "/rooms/" % roomId % "/state/" % eventType) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/state/" % eventType % "/" % stateKey) { setRequestData(Data(toJson(body))); -} - -SetRoomStateJob::~SetRoomStateJob() = default; - -const QString& SetRoomStateJob::eventId() const { return d->eventId; } - -BaseJob::Status SetRoomStateJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("event_id"_ls), d->eventId); - - return Success; + addExpectedKey("event_id"); } diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h index 7843cff4..eeb2b33c 100644 --- a/lib/csapi/room_state.h +++ b/lib/csapi/room_state.h @@ -6,13 +6,12 @@ #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Send a state event to the given room. + * + * .. For backwards compatibility with older links... + * .. _`put-matrix-client-r0-rooms-roomid-state-eventtype`: * * State events can be sent using this endpoint. These events will be * overwritten if ````, ```` and ```` all @@ -25,18 +24,32 @@ namespace Quotient { * The body of the request should be the content object of the event; the * fields in this object will vary depending on the type of event. See * `Room Events`_ for the ``m.`` event specification. + * + * If the event type being sent is ``m.room.canonical_alias`` servers + * SHOULD ensure that any new aliases being listed in the event are valid + * per their grammar/syntax and that they point to the room ID where the + * state event is to be sent. Servers do not validate aliases which are + * being removed or are already present in the state event. */ class SetRoomStateWithKeyJob : public BaseJob { public: /*! \brief Send a state event to the given room. + * * * \param roomId * The room to set the state in + * * \param eventType * The type of event to send. + * * \param stateKey - * The state_key for the state to send. Defaults to the empty string. + * The state_key for the state to send. Defaults to the empty string. When + * an empty string, the trailing slash on this endpoint is optional. + * * \param body + * .. For backwards compatibility with older links... + * .. _`put-matrix-client-r0-rooms-roomid-state-eventtype`: + * * State events can be sent using this endpoint. These events will be * overwritten if ````, ```` and ```` all * match. @@ -48,81 +61,22 @@ public: * The body of the request should be the content object of the event; the * fields in this object will vary depending on the type of event. See * `Room Events`_ for the ``m.`` event specification. + * + * If the event type being sent is ``m.room.canonical_alias`` servers + * SHOULD ensure that any new aliases being listed in the event are valid + * per their grammar/syntax and that they point to the room ID where the + * state event is to be sent. Servers do not validate aliases which are + * being removed or are already present in the state event. */ explicit SetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey, const QJsonObject& body = {}); - ~SetRoomStateWithKeyJob() override; - - // Result properties - - /// A unique identifier for the event. - const QString& eventId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; -}; - -/*! \brief Send a state event to the given room. - * - * State events can be sent using this endpoint. This endpoint is - * equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` - * with an empty `stateKey`. Previous state events with matching - * `` and ``, and empty ``, will be overwritten. - * - * Requests to this endpoint **cannot use transaction IDs** - * like other ``PUT`` paths because they cannot be differentiated from the - * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - * - * The body of the request should be the content object of the event; the - * fields in this object will vary depending on the type of event. See - * `Room Events`_ for the ``m.`` event specification. - */ -class SetRoomStateJob : public BaseJob { -public: - /*! \brief Send a state event to the given room. - * - * \param roomId - * The room to set the state in - * \param eventType - * The type of event to send. - * \param body - * State events can be sent using this endpoint. This endpoint is - * equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}` - * with an empty `stateKey`. Previous state events with matching - * `` and ``, and empty ``, will be - * overwritten. - * - * Requests to this endpoint **cannot use transaction IDs** - * like other ``PUT`` paths because they cannot be differentiated from the - * ``state_key``. Furthermore, ``POST`` is unsupported on state paths. - * - * The body of the request should be the content object of the event; the - * fields in this object will vary depending on the type of event. See - * `Room Events`_ for the ``m.`` event specification. - */ - explicit SetRoomStateJob(const QString& roomId, const QString& eventType, - const QJsonObject& body = {}); - - ~SetRoomStateJob() override; - // Result properties /// A unique identifier for the event. - const QString& eventId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString eventId() const { return loadFromJson("event_id"_ls); } }; } // namespace Quotient diff --git a/lib/csapi/room_upgrades.cpp b/lib/csapi/room_upgrades.cpp index 244f5b74..e3791b08 100644 --- a/lib/csapi/room_upgrades.cpp +++ b/lib/csapi/room_upgrades.cpp @@ -4,43 +4,17 @@ #include "room_upgrades.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class UpgradeRoomJob::Private { -public: - QString replacementRoom; -}; - UpgradeRoomJob::UpgradeRoomJob(const QString& roomId, const QString& newVersion) : BaseJob(HttpVerb::Post, QStringLiteral("UpgradeRoomJob"), - basePath % "/rooms/" % roomId % "/upgrade") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/upgrade") { QJsonObject _data; addParam<>(_data, QStringLiteral("new_version"), newVersion); - setRequestData(_data); -} - -UpgradeRoomJob::~UpgradeRoomJob() = default; - -const QString& UpgradeRoomJob::replacementRoom() const -{ - return d->replacementRoom; -} - -BaseJob::Status UpgradeRoomJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("replacement_room"_ls)) - return { IncorrectResponse, - "The key 'replacement_room' not found in the response" }; - fromJson(json.value("replacement_room"_ls), d->replacementRoom); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("replacement_room"); } diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index f13a9af4..53601816 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Upgrades a room to a new room version. * * Upgrades the given room to a particular room version. @@ -17,27 +15,23 @@ namespace Quotient { class UpgradeRoomJob : public BaseJob { public: /*! \brief Upgrades a room to a new room version. + * * * \param roomId * The ID of the room to upgrade. + * * \param newVersion * The new version for the room. */ explicit UpgradeRoomJob(const QString& roomId, const QString& newVersion); - ~UpgradeRoomJob() override; - // Result properties /// The ID of the new room. - const QString& replacementRoom() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString replacementRoom() const + { + return loadFromJson("replacement_room"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/rooms.cpp b/lib/csapi/rooms.cpp index 234e33df..724d941f 100644 --- a/lib/csapi/rooms.cpp +++ b/lib/csapi/rooms.cpp @@ -4,51 +4,33 @@ #include "rooms.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetOneRoomEventJob::Private { -public: - EventPtr data; -}; - QUrl GetOneRoomEventJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/rooms/" - % roomId % "/event/" - % eventId); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/event/" + % eventId); } GetOneRoomEventJob::GetOneRoomEventJob(const QString& roomId, const QString& eventId) : BaseJob(HttpVerb::Get, QStringLiteral("GetOneRoomEventJob"), - basePath % "/rooms/" % roomId % "/event/" % eventId) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/event/" % eventId) {} -GetOneRoomEventJob::~GetOneRoomEventJob() = default; - -EventPtr&& GetOneRoomEventJob::data() { return std::move(d->data); } - -BaseJob::Status GetOneRoomEventJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - QUrl GetRoomStateWithKeyJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventType, const QString& stateKey) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/state/" + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/state/" % eventType % "/" % stateKey); } @@ -56,60 +38,25 @@ GetRoomStateWithKeyJob::GetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, const QString& stateKey) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateWithKeyJob"), - basePath % "/rooms/" % roomId % "/state/" % eventType % "/" - % stateKey) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/state/" % eventType % "/" % stateKey) {} -QUrl GetRoomStateByTypeJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, - const QString& eventType) -{ - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/rooms/" - % roomId % "/state/" - % eventType); -} - -GetRoomStateByTypeJob::GetRoomStateByTypeJob(const QString& roomId, - const QString& eventType) - : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateByTypeJob"), - basePath % "/rooms/" % roomId % "/state/" % eventType) -{} - -class GetRoomStateJob::Private { -public: - StateEvents data; -}; - QUrl GetRoomStateJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/state"); + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/state"); } GetRoomStateJob::GetRoomStateJob(const QString& roomId) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomStateJob"), - basePath % "/rooms/" % roomId % "/state") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/state") {} -GetRoomStateJob::~GetRoomStateJob() = default; - -StateEvents&& GetRoomStateJob::data() { return std::move(d->data); } - -BaseJob::Status GetRoomStateJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class GetMembersByRoomJob::Private { -public: - EventsArray chunk; -}; - -BaseJob::Query queryToGetMembersByRoom(const QString& at, - const QString& membership, - const QString& notMembership) +auto queryToGetMembersByRoom(const QString& at, const QString& membership, + const QString& notMembership) { BaseJob::Query _q; addParam(_q, QStringLiteral("at"), at); @@ -123,10 +70,10 @@ QUrl GetMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& membership, const QString& notMembership) { - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/rooms/" % roomId % "/members", - queryToGetMembersByRoom(at, membership, - notMembership)); + return BaseJob::makeRequestUrl( + std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId % "/members", + queryToGetMembersByRoom(at, membership, notMembership)); } GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, @@ -134,72 +81,21 @@ GetMembersByRoomJob::GetMembersByRoomJob(const QString& roomId, const QString& membership, const QString& notMembership) : BaseJob(HttpVerb::Get, QStringLiteral("GetMembersByRoomJob"), - basePath % "/rooms/" % roomId % "/members", + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/members", queryToGetMembersByRoom(at, membership, notMembership)) - , d(new Private) {} -GetMembersByRoomJob::~GetMembersByRoomJob() = default; - -EventsArray&& GetMembersByRoomJob::chunk() -{ - return std::move(d->chunk); -} - -BaseJob::Status GetMembersByRoomJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("chunk"_ls), d->chunk); - - return Success; -} - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - GetJoinedMembersByRoomJob::RoomMember& result) - { - fromJson(jo.value("display_name"_ls), result.displayName); - fromJson(jo.value("avatar_url"_ls), result.avatarUrl); - } -}; - -} // namespace Quotient - -class GetJoinedMembersByRoomJob::Private { -public: - QHash joined; -}; - QUrl GetJoinedMembersByRoomJob::makeRequestUrl(QUrl baseUrl, const QString& roomId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/rooms/" - % roomId - % "/joined_members"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") + % "/rooms/" % roomId % "/joined_members"); } GetJoinedMembersByRoomJob::GetJoinedMembersByRoomJob(const QString& roomId) : BaseJob(HttpVerb::Get, QStringLiteral("GetJoinedMembersByRoomJob"), - basePath % "/rooms/" % roomId % "/joined_members") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/joined_members") {} - -GetJoinedMembersByRoomJob::~GetJoinedMembersByRoomJob() = default; - -const QHash& -GetJoinedMembersByRoomJob::joined() const -{ - return d->joined; -} - -BaseJob::Status GetJoinedMembersByRoomJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("joined"_ls), d->joined); - - return Success; -} diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 05c5b82a..6137bcbd 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -4,18 +4,12 @@ #pragma once -#include "converters.h" - #include "events/eventloader.h" #include "events/roommemberevent.h" #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Get a single event by event ID. * * Get a single event based on ``roomId/eventId``. You must have permission to @@ -24,9 +18,11 @@ namespace Quotient { class GetOneRoomEventJob : public BaseJob { public: /*! \brief Get a single event by event ID. + * * * \param roomId * The ID of the room the event is in. + * * \param eventId * The event ID to get. */ @@ -39,22 +35,17 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, const QString& eventId); - ~GetOneRoomEventJob() override; // Result properties /// The full event. - EventPtr&& data(); - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + EventPtr data() { return fromJson(jsonData()); } }; /*! \brief Get the state identified by the type and key. + * + * .. For backwards compatibility with older links... + * .. _`get-matrix-client-r0-rooms-roomid-state-eventtype`: * * Looks up the contents of a state event in a room. If the user is * joined to the room then the state is taken from the current @@ -64,13 +55,17 @@ private: class GetRoomStateWithKeyJob : public BaseJob { public: /*! \brief Get the state identified by the type and key. + * * * \param roomId * The room to look up the state in. + * * \param eventType * The type of state to look up. + * * \param stateKey - * The key of the state to look up. + * The key of the state to look up. Defaults to an empty string. When + * an empty string, the trailing slash on this endpoint is optional. */ explicit GetRoomStateWithKeyJob(const QString& roomId, const QString& eventType, @@ -86,36 +81,6 @@ public: const QString& stateKey); }; -/*! \brief Get the state identified by the type, with the empty state key. - * - * Looks up the contents of a state event in a room. If the user is - * joined to the room then the state is taken from the current - * state of the room. If the user has left the room then the state is - * taken from the state of the room when they left. - * - * This looks up the state event with the empty state key. - */ -class GetRoomStateByTypeJob : public BaseJob { -public: - /*! \brief Get the state identified by the type, with the empty state key. - * - * \param roomId - * The room to look up the state in. - * \param eventType - * The type of state to look up. - */ - explicit GetRoomStateByTypeJob(const QString& roomId, - const QString& eventType); - - /*! \brief Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for GetRoomStateByTypeJob - * is necessary but the job itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId, - const QString& eventType); -}; - /*! \brief Get all state events in the current state of a room. * * Get the state events for the current state of a room. @@ -123,6 +88,7 @@ public: class GetRoomStateJob : public BaseJob { public: /*! \brief Get all state events in the current state of a room. + * * * \param roomId * The room to look up the state for. @@ -135,22 +101,11 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetRoomStateJob() override; // Result properties - /// If the user is a member of the room this will be the - /// current state of the room as a list of events. If the user - /// has left the room then this will be the state of the room - /// when they left as a list of events. - StateEvents&& data(); - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + /// The current state of the room + StateEvents data() { return fromJson(jsonData()); } }; /*! \brief Get the m.room.member events for the room. @@ -160,18 +115,27 @@ private: class GetMembersByRoomJob : public BaseJob { public: /*! \brief Get the m.room.member events for the room. + * * * \param roomId * The room to get the member events for. + * * \param at - * The token defining the timeline position as-of which to return - * the list of members. This token can be obtained from a batch token - * returned for each room by the sync API, or from - * a ``start``/``end`` token returned by a ``/messages`` request. + * The point in time (pagination token) to return members for in the room. + * This token can be obtained from a ``prev_batch`` token returned for + * each room by the sync API. Defaults to the current state of the room, + * as determined by the server. + * * \param membership - * Only return users with the specified membership + * The kind of membership to filter for. Defaults to no filtering if + * unspecified. When specified alongside ``not_membership``, the two + * parameters create an 'or' condition: either the membership *is* + * the same as ``membership`` **or** *is not* the same as + * ``not_membership``. + * * \param notMembership - * Only return users with membership state other than specified + * The kind of membership to exclude from the results. Defaults to no + * filtering if unspecified. */ explicit GetMembersByRoomJob(const QString& roomId, const QString& at = {}, const QString& membership = {}, @@ -186,19 +150,14 @@ public: const QString& at = {}, const QString& membership = {}, const QString& notMembership = {}); - ~GetMembersByRoomJob() override; // Result properties /// Get the list of members for this room. - EventsArray&& chunk(); - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + EventsArray chunk() + { + return takeFromJson>("chunk"_ls); + } }; /*! \brief Gets the list of currently joined users and their profile data. @@ -230,6 +189,7 @@ public: // Construction/destruction /*! \brief Gets the list of currently joined users and their profile data. + * * * \param roomId * The room to get the members of. @@ -242,19 +202,24 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& roomId); - ~GetJoinedMembersByRoomJob() override; // Result properties /// A map from user ID to a RoomMember object. - const QHash& joined() const; - -protected: - Status parseJson(const QJsonDocument& data) override; + QHash joined() const + { + return loadFromJson>("joined"_ls); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + GetJoinedMembersByRoomJob::RoomMember& result) + { + fromJson(jo.value("display_name"_ls), result.displayName); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/sso_login_redirect.cpp b/lib/csapi/sso_login_redirect.cpp index 21fe646e..85a18560 100644 --- a/lib/csapi/sso_login_redirect.cpp +++ b/lib/csapi/sso_login_redirect.cpp @@ -4,15 +4,11 @@ #include "sso_login_redirect.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -BaseJob::Query queryToRedirectToSSO(const QString& redirectUrl) +auto queryToRedirectToSSO(const QString& redirectUrl) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("redirectUrl"), redirectUrl); @@ -22,13 +18,13 @@ BaseJob::Query queryToRedirectToSSO(const QString& redirectUrl) QUrl RedirectToSSOJob::makeRequestUrl(QUrl baseUrl, const QString& redirectUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/login/sso/redirect", + QStringLiteral("/_matrix/client/r0") + % "/login/sso/redirect", queryToRedirectToSSO(redirectUrl)); } RedirectToSSOJob::RedirectToSSOJob(const QString& redirectUrl) : BaseJob(HttpVerb::Get, QStringLiteral("RedirectToSSOJob"), - basePath % "/login/sso/redirect", + QStringLiteral("/_matrix/client/r0") % "/login/sso/redirect", queryToRedirectToSSO(redirectUrl), {}, false) - {} diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h index 8978c7f7..20f33da2 100644 --- a/lib/csapi/sso_login_redirect.h +++ b/lib/csapi/sso_login_redirect.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Redirect the user's browser to the SSO interface. * * A web-based Matrix client should instruct the user's browser to @@ -20,6 +18,7 @@ namespace Quotient { class RedirectToSSOJob : public BaseJob { public: /*! \brief Redirect the user's browser to the SSO interface. + * * * \param redirectUrl * URI to which the user will be redirected after the homeserver has diff --git a/lib/csapi/tags.cpp b/lib/csapi/tags.cpp index 0e2d122a..dc22dc18 100644 --- a/lib/csapi/tags.cpp +++ b/lib/csapi/tags.cpp @@ -4,82 +4,49 @@ #include "tags.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(QJsonObject jo, GetRoomTagsJob::Tag& result) - { - fromJson(jo.take("order"_ls), result.order); - fromJson(jo, result.additionalProperties); - } -}; - -} // namespace Quotient - -class GetRoomTagsJob::Private { -public: - QHash tags; -}; - QUrl GetRoomTagsJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/user/" - % userId % "/rooms/" - % roomId % "/tags"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client/r0") % "/user/" + % userId % "/rooms/" % roomId % "/tags"); } GetRoomTagsJob::GetRoomTagsJob(const QString& userId, const QString& roomId) : BaseJob(HttpVerb::Get, QStringLiteral("GetRoomTagsJob"), - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/rooms/" % roomId % "/tags") {} -GetRoomTagsJob::~GetRoomTagsJob() = default; - -const QHash& GetRoomTagsJob::tags() const -{ - return d->tags; -} - -BaseJob::Status GetRoomTagsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - fromJson(json.value("tags"_ls), d->tags); - - return Success; -} - SetRoomTagJob::SetRoomTagJob(const QString& userId, const QString& roomId, - const QString& tag, Omittable order) + const QString& tag, Omittable order, + const QVariantHash& additionalProperties) : BaseJob(HttpVerb::Put, QStringLiteral("SetRoomTagJob"), - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/rooms/" % roomId % "/tags/" % tag) { QJsonObject _data; + fillJson(_data, additionalProperties); addParam(_data, QStringLiteral("order"), order); - setRequestData(_data); + setRequestData(std::move(_data)); } QUrl DeleteRoomTagJob::makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId, const QString& tag) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/user/" % userId % "/rooms/" - % roomId % "/tags/" % tag); + QStringLiteral("/_matrix/client/r0") + % "/user/" % userId % "/rooms/" % roomId + % "/tags/" % tag); } DeleteRoomTagJob::DeleteRoomTagJob(const QString& userId, const QString& roomId, const QString& tag) : BaseJob(HttpVerb::Delete, QStringLiteral("DeleteRoomTagJob"), - basePath % "/user/" % userId % "/rooms/" % roomId % "/tags/" % tag) + QStringLiteral("/_matrix/client/r0") % "/user/" % userId + % "/rooms/" % roomId % "/tags/" % tag) {} diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h index 1ffb0f81..e6eb9dda 100644 --- a/lib/csapi/tags.h +++ b/lib/csapi/tags.h @@ -4,17 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief List the tags for a room. * * List the tags set by a user on a room. @@ -35,10 +28,12 @@ public: // Construction/destruction /*! \brief List the tags for a room. + * * * \param userId * The id of the user to get tags for. The access token must be * authorized to make requests for this user ID. + * * \param roomId * The ID of the room to get tags for. */ @@ -51,19 +46,23 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userId, const QString& roomId); - ~GetRoomTagsJob() override; // Result properties /// List the tags set by a user on a room. - const QHash& tags() const; - -protected: - Status parseJson(const QJsonDocument& data) override; + QHash tags() const + { + return loadFromJson>("tags"_ls); + } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter { + static void fillFrom(QJsonObject jo, GetRoomTagsJob::Tag& result) + { + fromJson(jo.take("order"_ls), result.order); + fromJson(jo, result.additionalProperties); + } }; /*! \brief Add a tag to a room. @@ -73,20 +72,28 @@ private: class SetRoomTagJob : public BaseJob { public: /*! \brief Add a tag to a room. + * * * \param userId * The id of the user to add a tag for. The access token must be * authorized to make requests for this user ID. + * * \param roomId * The ID of the room to add a tag to. + * * \param tag * The tag to add. + * * \param order * A number in a range ``[0,1]`` describing a relative * position of the room under the given tag. + * + * \param additionalProperties + * Add a tag to the room. */ explicit SetRoomTagJob(const QString& userId, const QString& roomId, - const QString& tag, Omittable order = none); + const QString& tag, Omittable order = none, + const QVariantHash& additionalProperties = {}); }; /*! \brief Remove a tag from the room. @@ -96,12 +103,15 @@ public: class DeleteRoomTagJob : public BaseJob { public: /*! \brief Remove a tag from the room. + * * * \param userId * The id of the user to remove a tag for. The access token must be * authorized to make requests for this user ID. + * * \param roomId * The ID of the room to remove a tag from. + * * \param tag * The tag to remove. */ diff --git a/lib/csapi/third_party_lookup.cpp b/lib/csapi/third_party_lookup.cpp index 9d92e407..baf1fab5 100644 --- a/lib/csapi/third_party_lookup.cpp +++ b/lib/csapi/third_party_lookup.cpp @@ -4,83 +4,37 @@ #include "third_party_lookup.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetProtocolsJob::Private { -public: - QHash data; -}; - QUrl GetProtocolsJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/protocols"); + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/protocols"); } GetProtocolsJob::GetProtocolsJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetProtocolsJob"), - basePath % "/thirdparty/protocols") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/thirdparty/protocols") {} -GetProtocolsJob::~GetProtocolsJob() = default; - -const QHash& GetProtocolsJob::data() const -{ - return d->data; -} - -BaseJob::Status GetProtocolsJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class GetProtocolMetadataJob::Private { -public: - ThirdPartyProtocol data; -}; - QUrl GetProtocolMetadataJob::makeRequestUrl(QUrl baseUrl, const QString& protocol) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/protocol/" % protocol); + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/protocol/" % protocol); } GetProtocolMetadataJob::GetProtocolMetadataJob(const QString& protocol) : BaseJob(HttpVerb::Get, QStringLiteral("GetProtocolMetadataJob"), - basePath % "/thirdparty/protocol/" % protocol) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/thirdparty/protocol/" + % protocol) {} -GetProtocolMetadataJob::~GetProtocolMetadataJob() = default; - -const ThirdPartyProtocol& GetProtocolMetadataJob::data() const -{ - return d->data; -} - -BaseJob::Status GetProtocolMetadataJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class QueryLocationByProtocolJob::Private { -public: - QVector data; -}; - -BaseJob::Query queryToQueryLocationByProtocol(const QString& searchFields) +auto queryToQueryLocationByProtocol(const QString& searchFields) { BaseJob::Query _q; addParam(_q, QStringLiteral("searchFields"), searchFields); @@ -92,38 +46,20 @@ QUrl QueryLocationByProtocolJob::makeRequestUrl(QUrl baseUrl, const QString& searchFields) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/location/" % protocol, + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/location/" % protocol, queryToQueryLocationByProtocol(searchFields)); } QueryLocationByProtocolJob::QueryLocationByProtocolJob( const QString& protocol, const QString& searchFields) : BaseJob(HttpVerb::Get, QStringLiteral("QueryLocationByProtocolJob"), - basePath % "/thirdparty/location/" % protocol, + QStringLiteral("/_matrix/client/r0") % "/thirdparty/location/" + % protocol, queryToQueryLocationByProtocol(searchFields)) - , d(new Private) {} -QueryLocationByProtocolJob::~QueryLocationByProtocolJob() = default; - -const QVector& QueryLocationByProtocolJob::data() const -{ - return d->data; -} - -BaseJob::Status QueryLocationByProtocolJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class QueryUserByProtocolJob::Private { -public: - QVector data; -}; - -BaseJob::Query queryToQueryUserByProtocol(const QString& fields) +auto queryToQueryUserByProtocol(const QString& fields) { BaseJob::Query _q; addParam(_q, QStringLiteral("fields..."), fields); @@ -135,38 +71,20 @@ QUrl QueryUserByProtocolJob::makeRequestUrl(QUrl baseUrl, const QString& fields) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/user/" % protocol, + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/user/" % protocol, queryToQueryUserByProtocol(fields)); } QueryUserByProtocolJob::QueryUserByProtocolJob(const QString& protocol, const QString& fields) : BaseJob(HttpVerb::Get, QStringLiteral("QueryUserByProtocolJob"), - basePath % "/thirdparty/user/" % protocol, + QStringLiteral("/_matrix/client/r0") % "/thirdparty/user/" + % protocol, queryToQueryUserByProtocol(fields)) - , d(new Private) {} -QueryUserByProtocolJob::~QueryUserByProtocolJob() = default; - -const QVector& QueryUserByProtocolJob::data() const -{ - return d->data; -} - -BaseJob::Status QueryUserByProtocolJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class QueryLocationByAliasJob::Private { -public: - QVector data; -}; - -BaseJob::Query queryToQueryLocationByAlias(const QString& alias) +auto queryToQueryLocationByAlias(const QString& alias) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("alias"), alias); @@ -176,37 +94,18 @@ BaseJob::Query queryToQueryLocationByAlias(const QString& alias) QUrl QueryLocationByAliasJob::makeRequestUrl(QUrl baseUrl, const QString& alias) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/location", + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/location", queryToQueryLocationByAlias(alias)); } QueryLocationByAliasJob::QueryLocationByAliasJob(const QString& alias) : BaseJob(HttpVerb::Get, QStringLiteral("QueryLocationByAliasJob"), - basePath % "/thirdparty/location", + QStringLiteral("/_matrix/client/r0") % "/thirdparty/location", queryToQueryLocationByAlias(alias)) - , d(new Private) {} -QueryLocationByAliasJob::~QueryLocationByAliasJob() = default; - -const QVector& QueryLocationByAliasJob::data() const -{ - return d->data; -} - -BaseJob::Status QueryLocationByAliasJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} - -class QueryUserByIDJob::Private { -public: - QVector data; -}; - -BaseJob::Query queryToQueryUserByID(const QString& userid) +auto queryToQueryUserByID(const QString& userid) { BaseJob::Query _q; addParam<>(_q, QStringLiteral("userid"), userid); @@ -216,26 +115,13 @@ BaseJob::Query queryToQueryUserByID(const QString& userid) QUrl QueryUserByIDJob::makeRequestUrl(QUrl baseUrl, const QString& userid) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/thirdparty/user", + QStringLiteral("/_matrix/client/r0") + % "/thirdparty/user", queryToQueryUserByID(userid)); } QueryUserByIDJob::QueryUserByIDJob(const QString& userid) : BaseJob(HttpVerb::Get, QStringLiteral("QueryUserByIDJob"), - basePath % "/thirdparty/user", queryToQueryUserByID(userid)) - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/thirdparty/user", + queryToQueryUserByID(userid)) {} - -QueryUserByIDJob::~QueryUserByIDJob() = default; - -const QVector& QueryUserByIDJob::data() const -{ - return d->data; -} - -BaseJob::Status QueryUserByIDJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} diff --git a/lib/csapi/third_party_lookup.h b/lib/csapi/third_party_lookup.h index c8ca8cbb..9329b7a7 100644 --- a/lib/csapi/third_party_lookup.h +++ b/lib/csapi/third_party_lookup.h @@ -4,21 +4,14 @@ #pragma once -#include "converters.h" - #include "csapi/../application-service/definitions/location.h" #include "csapi/../application-service/definitions/protocol.h" #include "csapi/../application-service/definitions/user.h" #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief Retrieve metadata about all protocols that a homeserver supports. * * Fetches the overall metadata about protocols supported by the @@ -36,19 +29,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetProtocolsJob() override; // Result properties /// The protocols supported by the homeserver. - const QHash& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QHash data() const + { + return fromJson>(jsonData()); + } }; /*! \brief Retrieve metadata about a specific protocol that the homeserver @@ -62,6 +50,7 @@ public: /*! \brief Retrieve metadata about a specific protocol that the homeserver * supports. * + * * \param protocol * The name of the protocol. */ @@ -73,19 +62,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol); - ~GetProtocolMetadataJob() override; // Result properties /// The protocol was found and metadata returned. - const ThirdPartyProtocol& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + ThirdPartyProtocol data() const + { + return fromJson(jsonData()); + } }; /*! \brief Retrieve Matrix-side portals rooms leading to a third party location. @@ -104,8 +88,10 @@ public: /*! \brief Retrieve Matrix-side portals rooms leading to a third party * location. * + * * \param protocol * The protocol used to communicate to the third party network. + * * \param searchFields * One or more custom fields to help identify the third party * location. @@ -120,19 +106,14 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& searchFields = {}); - ~QueryLocationByProtocolJob() override; // Result properties /// At least one portal room was found. - const QVector& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QVector data() const + { + return fromJson>(jsonData()); + } }; /*! \brief Retrieve the Matrix User ID of a corresponding third party user. @@ -143,9 +124,11 @@ private: class QueryUserByProtocolJob : public BaseJob { public: /*! \brief Retrieve the Matrix User ID of a corresponding third party user. + * * * \param protocol * The name of the protocol. + * * \param fields * One or more custom fields that are passed to the AS to help identify * the user. @@ -160,19 +143,14 @@ public: */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& protocol, const QString& fields = {}); - ~QueryUserByProtocolJob() override; // Result properties /// The Matrix User IDs found with the given parameters. - const QVector& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QVector data() const + { + return fromJson>(jsonData()); + } }; /*! \brief Reverse-lookup third party locations given a Matrix room alias. @@ -183,6 +161,7 @@ private: class QueryLocationByAliasJob : public BaseJob { public: /*! \brief Reverse-lookup third party locations given a Matrix room alias. + * * * \param alias * The Matrix room alias to look up. @@ -195,19 +174,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& alias); - ~QueryLocationByAliasJob() override; // Result properties /// All found third party locations. - const QVector& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QVector data() const + { + return fromJson>(jsonData()); + } }; /*! \brief Reverse-lookup third party users given a Matrix User ID. @@ -217,6 +191,7 @@ private: class QueryUserByIDJob : public BaseJob { public: /*! \brief Reverse-lookup third party users given a Matrix User ID. + * * * \param userid * The Matrix User ID to look up. @@ -229,19 +204,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl, const QString& userid); - ~QueryUserByIDJob() override; // Result properties /// An array of third party users. - const QVector& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QVector data() const + { + return fromJson>(jsonData()); + } }; } // namespace Quotient diff --git a/lib/csapi/third_party_membership.cpp b/lib/csapi/third_party_membership.cpp index 15da6b3c..fda772d2 100644 --- a/lib/csapi/third_party_membership.cpp +++ b/lib/csapi/third_party_membership.cpp @@ -4,22 +4,21 @@ #include "third_party_membership.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - InviteBy3PIDJob::InviteBy3PIDJob(const QString& roomId, const QString& idServer, + const QString& idAccessToken, const QString& medium, const QString& address) : BaseJob(HttpVerb::Post, QStringLiteral("InviteBy3PIDJob"), - basePath % "/rooms/" % roomId % "/invite") + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/invite") { QJsonObject _data; addParam<>(_data, QStringLiteral("id_server"), idServer); + addParam<>(_data, QStringLiteral("id_access_token"), idAccessToken); addParam<>(_data, QStringLiteral("medium"), medium); addParam<>(_data, QStringLiteral("address"), address); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h index c85e8ab4..f8196f6f 100644 --- a/lib/csapi/third_party_membership.h +++ b/lib/csapi/third_party_membership.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Invite a user to participate in a particular room. * * .. _invite-by-third-party-id-endpoint: @@ -60,16 +58,29 @@ namespace Quotient { class InviteBy3PIDJob : public BaseJob { public: /*! \brief Invite a user to participate in a particular room. + * * * \param roomId * The room identifier (not alias) to which to invite the user. + * * \param idServer * The hostname+port of the identity server which should be used for third - * party identifier lookups. \param medium The kind of address being passed - * in the address field, for example ``email``. \param address The invitee's - * third party identifier. + * party identifier lookups. + * + * \param idAccessToken + * An access token previously registered with the identity server. Servers + * can treat this as optional to distinguish between r0.5-compatible + * clients and this specification version. + * + * \param medium + * The kind of address being passed in the address field, for example + * ``email``. + * + * \param address + * The invitee's third party identifier. */ explicit InviteBy3PIDJob(const QString& roomId, const QString& idServer, + const QString& idAccessToken, const QString& medium, const QString& address); }; diff --git a/lib/csapi/to_device.cpp b/lib/csapi/to_device.cpp index e277503c..28c4115a 100644 --- a/lib/csapi/to_device.cpp +++ b/lib/csapi/to_device.cpp @@ -4,21 +4,18 @@ #include "to_device.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SendToDeviceJob::SendToDeviceJob( const QString& eventType, const QString& txnId, const QHash>& messages) : BaseJob(HttpVerb::Put, QStringLiteral("SendToDeviceJob"), - basePath % "/sendToDevice/" % eventType % "/" % txnId) + QStringLiteral("/_matrix/client/r0") % "/sendToDevice/" + % eventType % "/" % txnId) { QJsonObject _data; addParam(_data, QStringLiteral("messages"), messages); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/to_device.h b/lib/csapi/to_device.h index 48f0ec83..8c82af45 100644 --- a/lib/csapi/to_device.h +++ b/lib/csapi/to_device.h @@ -6,13 +6,8 @@ #include "jobs/basejob.h" -#include -#include - namespace Quotient { -// Operations - /*! \brief Send an event to a given set of devices. * * This endpoint is used to send send-to-device events to a set of @@ -21,13 +16,16 @@ namespace Quotient { class SendToDeviceJob : public BaseJob { public: /*! \brief Send an event to a given set of devices. + * * * \param eventType * The type of event to send. + * * \param txnId * The transaction ID for this event. Clients should generate an * ID unique across requests with the same access token; it will be * used by the server to ensure idempotency of requests. + * * \param messages * The messages to send. A map from user ID, to a map from * device ID to message body. The device ID may also be `*`, diff --git a/lib/csapi/typing.cpp b/lib/csapi/typing.cpp index 261622c4..8e214053 100644 --- a/lib/csapi/typing.cpp +++ b/lib/csapi/typing.cpp @@ -4,21 +4,18 @@ #include "typing.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - SetTypingJob::SetTypingJob(const QString& userId, const QString& roomId, bool typing, Omittable timeout) : BaseJob(HttpVerb::Put, QStringLiteral("SetTypingJob"), - basePath % "/rooms/" % roomId % "/typing/" % userId) + QStringLiteral("/_matrix/client/r0") % "/rooms/" % roomId + % "/typing/" % userId) { QJsonObject _data; addParam<>(_data, QStringLiteral("typing"), typing); addParam(_data, QStringLiteral("timeout"), timeout); - setRequestData(_data); + setRequestData(std::move(_data)); } diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h index dbf17a3e..f5b67baf 100644 --- a/lib/csapi/typing.h +++ b/lib/csapi/typing.h @@ -4,14 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Informs the server that the user has started or stopped typing. * * This tells the server that the user is typing for the next N @@ -22,14 +18,18 @@ namespace Quotient { class SetTypingJob : public BaseJob { public: /*! \brief Informs the server that the user has started or stopped typing. + * * * \param userId * The user who has started to type. + * * \param roomId * The room in which the user is typing. + * * \param typing * Whether the user is typing or not. If ``false``, the ``timeout`` * key can be omitted. + * * \param timeout * The length of time in milliseconds to mark this user as typing. */ diff --git a/lib/csapi/users.cpp b/lib/csapi/users.cpp index 5d2a6133..a0279d7e 100644 --- a/lib/csapi/users.cpp +++ b/lib/csapi/users.cpp @@ -4,68 +4,19 @@ #include "users.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -// Converters -namespace Quotient { - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - SearchUserDirectoryJob::User& result) - { - fromJson(jo.value("user_id"_ls), result.userId); - fromJson(jo.value("display_name"_ls), result.displayName); - fromJson(jo.value("avatar_url"_ls), result.avatarUrl); - } -}; - -} // namespace Quotient - -class SearchUserDirectoryJob::Private { -public: - QVector results; - bool limited; -}; - SearchUserDirectoryJob::SearchUserDirectoryJob(const QString& searchTerm, Omittable limit) : BaseJob(HttpVerb::Post, QStringLiteral("SearchUserDirectoryJob"), - basePath % "/user_directory/search") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/user_directory/search") { QJsonObject _data; addParam<>(_data, QStringLiteral("search_term"), searchTerm); addParam(_data, QStringLiteral("limit"), limit); - setRequestData(_data); -} - -SearchUserDirectoryJob::~SearchUserDirectoryJob() = default; - -const QVector& SearchUserDirectoryJob::results() const -{ - return d->results; -} - -bool SearchUserDirectoryJob::limited() const { return d->limited; } - -BaseJob::Status SearchUserDirectoryJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("results"_ls)) - return { IncorrectResponse, - "The key 'results' not found in the response" }; - fromJson(json.value("results"_ls), d->results); - if (!json.contains("limited"_ls)) - return { IncorrectResponse, - "The key 'limited' not found in the response" }; - fromJson(json.value("limited"_ls), d->limited); - - return Success; + setRequestData(std::move(_data)); + addExpectedKey("results"); + addExpectedKey("limited"); } diff --git a/lib/csapi/users.h b/lib/csapi/users.h index de4eb529..adb2a33d 100644 --- a/lib/csapi/users.h +++ b/lib/csapi/users.h @@ -4,19 +4,13 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Searches the user directory. * - * Performs a search for users on the homeserver. The homeserver may + * Performs a search for users. The homeserver may * determine which subset of users are searched, however the homeserver * MUST at a minimum consider the users the requesting user shares a * room with and those who reside in public rooms (known to the homeserver). @@ -31,7 +25,7 @@ class SearchUserDirectoryJob : public BaseJob { public: // Inner data structures - /// Performs a search for users on the homeserver. The homeserver may + /// Performs a search for users. The homeserver may /// determine which subset of users are searched, however the homeserver /// MUST at a minimum consider the users the requesting user shares a /// room with and those who reside in public rooms (known to the @@ -41,7 +35,7 @@ public: /// The search is performed case-insensitively on user IDs and display /// names preferably using a collation determined based upon the /// ``Accept-Language`` header provided in the request, if present. - struct User { + struct SearchUserDirectory200ThirdPartyUser { /// The user's matrix user ID. QString userId; /// The display name of the user, if one exists. @@ -53,31 +47,41 @@ public: // Construction/destruction /*! \brief Searches the user directory. + * * * \param searchTerm * The term to search for + * * \param limit * The maximum number of results to return. Defaults to 10. */ explicit SearchUserDirectoryJob(const QString& searchTerm, Omittable limit = none); - ~SearchUserDirectoryJob() override; - // Result properties /// Ordered by rank and then whether or not profile info is available. - const QVector& results() const; + QVector results() const + { + return loadFromJson>( + "results"_ls); + } /// Indicates if the result list has been truncated by the limit. - bool limited() const; - -protected: - Status parseJson(const QJsonDocument& data) override; + bool limited() const { return loadFromJson("limited"_ls); } +}; -private: - class Private; - QScopedPointer d; +template <> +struct JsonObjectConverter< + SearchUserDirectoryJob::SearchUserDirectory200ThirdPartyUser> { + static void + fillFrom(const QJsonObject& jo, + SearchUserDirectoryJob::SearchUserDirectory200ThirdPartyUser& result) + { + fromJson(jo.value("user_id"_ls), result.userId); + fromJson(jo.value("display_name"_ls), result.displayName); + fromJson(jo.value("avatar_url"_ls), result.avatarUrl); + } }; } // namespace Quotient diff --git a/lib/csapi/versions.cpp b/lib/csapi/versions.cpp index 8cce03b9..9003e27f 100644 --- a/lib/csapi/versions.cpp +++ b/lib/csapi/versions.cpp @@ -4,48 +4,20 @@ #include "versions.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client"); - -class GetVersionsJob::Private { -public: - QStringList versions; - QHash unstableFeatures; -}; - QUrl GetVersionsJob::makeRequestUrl(QUrl baseUrl) { - return BaseJob::makeRequestUrl(std::move(baseUrl), basePath % "/versions"); + return BaseJob::makeRequestUrl(std::move(baseUrl), + QStringLiteral("/_matrix/client") + % "/versions"); } GetVersionsJob::GetVersionsJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetVersionsJob"), - basePath % "/versions", false) - , d(new Private) -{} - -GetVersionsJob::~GetVersionsJob() = default; - -const QStringList& GetVersionsJob::versions() const { return d->versions; } - -const QHash& GetVersionsJob::unstableFeatures() const + QStringLiteral("/_matrix/client") % "/versions", false) { - return d->unstableFeatures; -} - -BaseJob::Status GetVersionsJob::parseJson(const QJsonDocument& data) -{ - auto json = data.object(); - if (!json.contains("versions"_ls)) - return { IncorrectResponse, - "The key 'versions' not found in the response" }; - fromJson(json.value("versions"_ls), d->versions); - fromJson(json.value("unstable_features"_ls), d->unstableFeatures); - - return Success; + addExpectedKey("versions"); } diff --git a/lib/csapi/versions.h b/lib/csapi/versions.h index 4c4f8109..828a7eb9 100644 --- a/lib/csapi/versions.h +++ b/lib/csapi/versions.h @@ -4,16 +4,10 @@ #pragma once -#include "converters.h" - #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Gets the versions of the specification supported by the server. * * Gets the versions of the specification supported by the server. @@ -48,24 +42,22 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetVersionsJob() override; // Result properties /// The supported versions. - const QStringList& versions() const; + QStringList versions() const + { + return loadFromJson("versions"_ls); + } /// Experimental features the server supports. Features not listed here, /// or the lack of this property all together, indicate that a feature is /// not supported. - const QHash& unstableFeatures() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QHash unstableFeatures() const + { + return loadFromJson>("unstable_features"_ls); + } }; } // namespace Quotient diff --git a/lib/csapi/voip.cpp b/lib/csapi/voip.cpp index c98824b3..43170057 100644 --- a/lib/csapi/voip.cpp +++ b/lib/csapi/voip.cpp @@ -4,38 +4,18 @@ #include "voip.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetTurnServerJob::Private { -public: - QJsonObject data; -}; - QUrl GetTurnServerJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/voip/turnServer"); + QStringLiteral("/_matrix/client/r0") + % "/voip/turnServer"); } GetTurnServerJob::GetTurnServerJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetTurnServerJob"), - basePath % "/voip/turnServer") - , d(new Private) + QStringLiteral("/_matrix/client/r0") % "/voip/turnServer") {} - -GetTurnServerJob::~GetTurnServerJob() = default; - -const QJsonObject& GetTurnServerJob::data() const { return d->data; } - -BaseJob::Status GetTurnServerJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} diff --git a/lib/csapi/voip.h b/lib/csapi/voip.h index d3eb3c4b..087ebbbd 100644 --- a/lib/csapi/voip.h +++ b/lib/csapi/voip.h @@ -6,12 +6,8 @@ #include "jobs/basejob.h" -#include - namespace Quotient { -// Operations - /*! \brief Obtain TURN server credentials. * * This API provides credentials for the client to use when initiating @@ -28,19 +24,11 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTurnServerJob() override; // Result properties /// The TURN server credentials. - const QJsonObject& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QJsonObject data() const { return fromJson(jsonData()); } }; } // namespace Quotient diff --git a/lib/csapi/wellknown.cpp b/lib/csapi/wellknown.cpp index 464068e7..1aa0a90b 100644 --- a/lib/csapi/wellknown.cpp +++ b/lib/csapi/wellknown.cpp @@ -4,38 +4,18 @@ #include "wellknown.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/.well-known"); - -class GetWellknownJob::Private { -public: - DiscoveryInformation data; -}; - QUrl GetWellknownJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/matrix/client"); + QStringLiteral("/.well-known") + % "/matrix/client"); } GetWellknownJob::GetWellknownJob() : BaseJob(HttpVerb::Get, QStringLiteral("GetWellknownJob"), - basePath % "/matrix/client", false) - , d(new Private) + QStringLiteral("/.well-known") % "/matrix/client", false) {} - -GetWellknownJob::~GetWellknownJob() = default; - -const DiscoveryInformation& GetWellknownJob::data() const { return d->data; } - -BaseJob::Status GetWellknownJob::parseJson(const QJsonDocument& data) -{ - fromJson(data, d->data); - - return Success; -} diff --git a/lib/csapi/wellknown.h b/lib/csapi/wellknown.h index 94d3631c..b21d9fc7 100644 --- a/lib/csapi/wellknown.h +++ b/lib/csapi/wellknown.h @@ -4,16 +4,12 @@ #pragma once -#include "converters.h" - #include "csapi/definitions/wellknown/full.h" #include "jobs/basejob.h" namespace Quotient { -// Operations - /*! \brief Gets Matrix server discovery information about the domain. * * Gets discovery information about the domain. The file may include @@ -36,19 +32,14 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetWellknownJob() override; // Result properties /// Server discovery information. - const DiscoveryInformation& data() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + DiscoveryInformation data() const + { + return fromJson(jsonData()); + } }; } // namespace Quotient diff --git a/lib/csapi/whoami.cpp b/lib/csapi/whoami.cpp index 8d4681e4..73f0298e 100644 --- a/lib/csapi/whoami.cpp +++ b/lib/csapi/whoami.cpp @@ -4,42 +4,20 @@ #include "whoami.h" -#include "converters.h" - #include using namespace Quotient; -static const auto basePath = QStringLiteral("/_matrix/client/r0"); - -class GetTokenOwnerJob::Private { -public: - QString userId; -}; - QUrl GetTokenOwnerJob::makeRequestUrl(QUrl baseUrl) { return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath % "/account/whoami"); + QStringLiteral("/_matrix/client/r0") + % "/account/whoami"); } GetTokenOwnerJob::GetTokenOwnerJob() : BaseJob(HttpVerb::Get, QStringLiteral("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) + QStringLiteral("/_matrix/client/r0") % "/account/whoami") { - auto json = data.object(); - if (!json.contains("user_id"_ls)) - return { IncorrectResponse, - "The key 'user_id' not found in the response" }; - fromJson(json.value("user_id"_ls), d->userId); - - return Success; + addExpectedKey("user_id"); } diff --git a/lib/csapi/whoami.h b/lib/csapi/whoami.h index dda8d8c8..af8f1e8a 100644 --- a/lib/csapi/whoami.h +++ b/lib/csapi/whoami.h @@ -8,8 +8,6 @@ namespace Quotient { -// Operations - /*! \brief Gets information about the owner of an access token. * * Gets information about the owner of a given access token. @@ -32,19 +30,11 @@ public: * is necessary but the job itself isn't. */ static QUrl makeRequestUrl(QUrl baseUrl); - ~GetTokenOwnerJob() override; // Result properties /// The user id that owns the access token. - const QString& userId() const; - -protected: - Status parseJson(const QJsonDocument& data) override; - -private: - class Private; - QScopedPointer d; + QString userId() const { return loadFromJson("user_id"_ls); } }; } // namespace Quotient diff --git a/lib/identity/definitions/request_email_validation.cpp b/lib/identity/definitions/request_email_validation.cpp deleted file mode 100644 index 22cb0072..00000000 --- a/lib/identity/definitions/request_email_validation.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "request_email_validation.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const RequestEmailValidation& pod) -{ - addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); - addParam<>(jo, QStringLiteral("email"), pod.email); - addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); - addParam(jo, QStringLiteral("next_link"), pod.nextLink); -} - -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RequestEmailValidation& result) -{ - fromJson(jo.value("client_secret"_ls), result.clientSecret); - fromJson(jo.value("email"_ls), result.email); - fromJson(jo.value("send_attempt"_ls), result.sendAttempt); - fromJson(jo.value("next_link"_ls), result.nextLink); -} diff --git a/lib/identity/definitions/request_email_validation.h b/lib/identity/definitions/request_email_validation.h index 32c6eaaf..079da953 100644 --- a/lib/identity/definitions/request_email_validation.h +++ b/lib/identity/definitions/request_email_validation.h @@ -8,8 +8,6 @@ namespace Quotient { -// Data structures - struct RequestEmailValidation { /// A unique string generated by the client, and used to identify the /// validation attempt. It must be a string consisting of the characters @@ -26,18 +24,32 @@ struct RequestEmailValidation { /// avoid repeatedly sending the same email in the case of request /// retries between the POSTing user and the identity server. /// The client should increment this value if they desire a new - /// email (e.g. a reminder) to be sent. + /// email (e.g. a reminder) to be sent. If they do not, the server + /// should respond with success but not resend the email. int sendAttempt; - /// Optional. When the validation is completed, the identity - /// server will redirect the user to this URL. + /// Optional. When the validation is completed, the identity server will + /// redirect the user to this URL. This option is ignored when submitting + /// 3PID validation information through a POST request. QString nextLink; }; template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const RequestEmailValidation& pod); - static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod); + static void dumpTo(QJsonObject& jo, const RequestEmailValidation& pod) + { + addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); + addParam<>(jo, QStringLiteral("email"), pod.email); + addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); + addParam(jo, QStringLiteral("next_link"), pod.nextLink); + } + static void fillFrom(const QJsonObject& jo, RequestEmailValidation& pod) + { + fromJson(jo.value("client_secret"_ls), pod.clientSecret); + fromJson(jo.value("email"_ls), pod.email); + fromJson(jo.value("send_attempt"_ls), pod.sendAttempt); + fromJson(jo.value("next_link"_ls), pod.nextLink); + } }; } // namespace Quotient diff --git a/lib/identity/definitions/request_msisdn_validation.cpp b/lib/identity/definitions/request_msisdn_validation.cpp deleted file mode 100644 index 6024bf61..00000000 --- a/lib/identity/definitions/request_msisdn_validation.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "request_msisdn_validation.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo( - QJsonObject& jo, const RequestMsisdnValidation& pod) -{ - addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); - addParam<>(jo, QStringLiteral("country"), pod.country); - addParam<>(jo, QStringLiteral("phone_number"), pod.phoneNumber); - addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); - addParam(jo, QStringLiteral("next_link"), pod.nextLink); -} - -void JsonObjectConverter::fillFrom( - const QJsonObject& jo, RequestMsisdnValidation& result) -{ - fromJson(jo.value("client_secret"_ls), result.clientSecret); - fromJson(jo.value("country"_ls), result.country); - fromJson(jo.value("phone_number"_ls), result.phoneNumber); - fromJson(jo.value("send_attempt"_ls), result.sendAttempt); - fromJson(jo.value("next_link"_ls), result.nextLink); -} diff --git a/lib/identity/definitions/request_msisdn_validation.h b/lib/identity/definitions/request_msisdn_validation.h index 90aed7b8..a29fd0de 100644 --- a/lib/identity/definitions/request_msisdn_validation.h +++ b/lib/identity/definitions/request_msisdn_validation.h @@ -8,8 +8,6 @@ namespace Quotient { -// Data structures - struct RequestMsisdnValidation { /// A unique string generated by the client, and used to identify the /// validation attempt. It must be a string consisting of the characters @@ -17,8 +15,8 @@ struct RequestMsisdnValidation { /// must not be empty. QString clientSecret; - /// The two-letter uppercase ISO country code that the number in - /// ``phone_number`` should be parsed as if it were dialled from. + /// The two-letter uppercase ISO-3166-1 alpha-2 country code that the + /// number in ``phone_number`` should be parsed as if it were dialled from. QString country; /// The phone number to validate. @@ -33,15 +31,30 @@ struct RequestMsisdnValidation { /// they desire a new SMS (e.g. a reminder) to be sent. int sendAttempt; - /// Optional. When the validation is completed, the identity - /// server will redirect the user to this URL. + /// Optional. When the validation is completed, the identity server will + /// redirect the user to this URL. This option is ignored when submitting + /// 3PID validation information through a POST request. QString nextLink; }; template <> struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const RequestMsisdnValidation& pod); - static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod); + static void dumpTo(QJsonObject& jo, const RequestMsisdnValidation& pod) + { + addParam<>(jo, QStringLiteral("client_secret"), pod.clientSecret); + addParam<>(jo, QStringLiteral("country"), pod.country); + addParam<>(jo, QStringLiteral("phone_number"), pod.phoneNumber); + addParam<>(jo, QStringLiteral("send_attempt"), pod.sendAttempt); + addParam(jo, QStringLiteral("next_link"), pod.nextLink); + } + static void fillFrom(const QJsonObject& jo, RequestMsisdnValidation& pod) + { + fromJson(jo.value("client_secret"_ls), pod.clientSecret); + fromJson(jo.value("country"_ls), pod.country); + fromJson(jo.value("phone_number"_ls), pod.phoneNumber); + fromJson(jo.value("send_attempt"_ls), pod.sendAttempt); + fromJson(jo.value("next_link"_ls), pod.nextLink); + } }; } // namespace Quotient diff --git a/lib/identity/definitions/sid.cpp b/lib/identity/definitions/sid.cpp deleted file mode 100644 index 99fe9b59..00000000 --- a/lib/identity/definitions/sid.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#include "sid.h" - -using namespace Quotient; - -void JsonObjectConverter::dumpTo(QJsonObject& jo, const Sid& pod) -{ - addParam<>(jo, QStringLiteral("sid"), pod.sid); -} - -void JsonObjectConverter::fillFrom(const QJsonObject& jo, Sid& result) -{ - fromJson(jo.value("sid"_ls), result.sid); -} diff --git a/lib/identity/definitions/sid.h b/lib/identity/definitions/sid.h deleted file mode 100644 index 981826f6..00000000 --- a/lib/identity/definitions/sid.h +++ /dev/null @@ -1,27 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ - -#pragma once - -#include "converters.h" - -namespace Quotient { - -// Data structures - -struct Sid { - /// The session ID. Session IDs are opaque strings generated by the identity - /// server. They must consist entirely of the characters - /// ``[0-9a-zA-Z.=_-]``. Their length must not exceed 255 characters and - /// they must not be empty. - QString sid; -}; - -template <> -struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const Sid& pod); - static void fillFrom(const QJsonObject& jo, Sid& pod); -}; - -} // namespace Quotient -- cgit v1.2.3 From 0898550adcc5e6fe2648fcd4e181ecab9b5befea Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 6 Jun 2020 20:49:17 +0200 Subject: Small updates to match the new generated definitions --- lib/connection.cpp | 4 ++-- lib/encryptionmanager.cpp | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 26de0c5f..50e31d52 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -480,8 +480,8 @@ void Connection::sync(int timeout) d->syncTimeout = timeout; Filter filter; - filter.room.edit().timeline.edit().limit.emplace(100); - filter.room.edit().state.edit().lazyLoadMembers.emplace(d->lazyLoading); + filter.room.timeline.limit.emplace(100); + filter.room.state.lazyLoadMembers.emplace(d->lazyLoading); auto job = d->syncJob = callApi(BackgroundRequest, d->data->lastEvent(), filter, timeout); diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 0895fae9..c50459f3 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -89,13 +89,11 @@ public: // A map from senderKey to InboundSession QMap sessions; // TODO: cache void updateDeviceKeys( - const QHash>& - deviceKeys) + const QHash>& deviceKeys) { for (auto userId : deviceKeys.keys()) { for (auto deviceId : deviceKeys.value(userId).keys()) { - QueryKeysJob::DeviceInformation info = - deviceKeys.value(userId).value(deviceId); + auto info = deviceKeys.value(userId).value(deviceId); // TODO: ed25519Verify, etc } } -- cgit v1.2.3 From 2ddab7a6faab58d8e911012e8a827a3a51504621 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 9 Jun 2020 16:31:26 +0200 Subject: Comments refresh [skip ci] --- lib/util.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/util.h b/lib/util.h index 902b4bfc..81add1ac 100644 --- a/lib/util.h +++ b/lib/util.h @@ -199,7 +199,7 @@ using fn_arg_t = std::tuple_element_t::arg_types>; // TODO: get rid of it as soon as Apple Clang gets proper deduction guides -// for std::function<> +// for std::function<> template inline auto wrap_in_function(FnT&& f) { @@ -217,7 +217,7 @@ inline auto operator"" _ls(const char* s, std::size_t size) */ template class Range { - // Looking forward for Ranges TS to produce something (in C++23?..) + // Looking forward to C++23 ranges using iterator = typename ArrayT::iterator; using const_iterator = typename ArrayT::const_iterator; using size_type = typename ArrayT::size_type; -- cgit v1.2.3 From 533f0af3c5c101c7c4d9ed3260fb66baf78304fa Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 10 Jun 2020 07:32:01 +0200 Subject: More comments refresh [ci skip] --- lib/qt_connection_util.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 3c81e3ac..04dc6b27 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -90,8 +90,9 @@ namespace _impl { } } // namespace _impl -/// Create a connection that self-disconnects when its "slot" returns true -/*! A slot accepted by connectUntil() is different from classic Qt slots +/*! \brief Create a connection that self-disconnects when its "slot" returns true + * + * A slot accepted by connectUntil() is different from classic Qt slots * in that its return value must be bool, not void. The slot's return value * controls whether the connection should be kept; if the slot returns false, * the connection remains; upon returning true, the slot is disconnected from @@ -135,8 +136,9 @@ inline auto connectSingleShot(SenderT* sender, SignalT signal, connType); } -/// A guard pointer that disconnects an interested object upon destruction -/*! It's almost QPointer<> except that you have to initialise it with one +/*! \brief A guard pointer that disconnects an interested object upon destruction + * + * It's almost QPointer<> except that you have to initialise it with one * more additional parameter - a pointer to a QObject that will be * disconnected from signals of the underlying pointer upon the guard's * destruction. Note that destructing the guide doesn't destruct either QObject. -- cgit v1.2.3 From f3b8dbe01a43c5334a371edda833173468d99dc4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 10 Jun 2020 08:31:43 +0200 Subject: Room: fix messageSent() being emitted too early Closes #406. --- lib/room.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 23e07cae..22ec1c82 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1565,18 +1565,17 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) std::bind(&Room::Private::onEventSendingFailure, this, txnId, call)); Room::connect(call, &BaseJob::success, q, [this, call, txnId] { - emit q->messageSent(txnId, call->eventId()); auto it = q->findPendingEvent(txnId); - if (it == unsyncedEvents.end()) { + if (it != unsyncedEvents.end()) { + if (it->deliveryStatus() != EventStatus::ReachedServer) { + it->setReachedServer(call->eventId()); + emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); + } + } else qCDebug(EVENTS) << "Pending event for transaction" << txnId << "already merged"; - return; - } - if (it->deliveryStatus() != EventStatus::ReachedServer) { - it->setReachedServer(call->eventId()); - emit q->pendingEventChanged(int(it - unsyncedEvents.begin())); - } + emit q->messageSent(txnId, call->eventId()); }); } else onEventSendingFailure(txnId); -- cgit v1.2.3 From 9fc2c15dc021b2fddcb24bbc367c02639ec1256b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 10 Jun 2020 21:27:17 +0200 Subject: syncdata.h: fix an old warning about operator<< The warning has been that it is declared as a friend but not exactly in namespace Quotient (though all compilers still could find and link it but only as long as it is defined in the namespace). Now instead of being declared as a friend it's just declared in the namespace :) --- lib/syncdata.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/syncdata.h b/lib/syncdata.h index 752dd485..67d04557 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -41,9 +41,9 @@ struct RoomSummary { /// Merge the contents of another RoomSummary object into this one /// \return true, if the current object has changed; false otherwise bool merge(const RoomSummary& other); - - friend QDebug operator<<(QDebug dbg, const RoomSummary& rs); }; +QDebug operator<<(QDebug dbg, const RoomSummary& rs); + template <> struct JsonObjectConverter { -- cgit v1.2.3 From 0db2db1d247a0a4e6e6545ec0d72879416e6226b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 11 Jun 2020 19:24:39 +0200 Subject: BaseJob::prepareError(): be more tolerant to empty error payloads TooManyRequests can come without a payload, apparently. --- lib/jobs/basejob.cpp | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 3978dbcb..d7daa170 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -508,17 +508,13 @@ BaseJob::Status BaseJob::prepareResult() { return Success; } BaseJob::Status BaseJob::prepareError() { - // Since it's an error, the expected content type is of no help; - // check the actually advertised content type instead - if (reply()->rawHeader("Content-Type") != "application/json") - return NoError; // Retain the status if the error payload is not JSON - - if (const auto status = d->parseJson(); !status.good()) - return status; - - if (d->jsonResponse.isArray()) - return { IncorrectResponse, - tr("Malformed error JSON: an array instead of an object") }; + if (!d->rawResponse.isEmpty()) { + if (const auto status = d->parseJson(); !status.good()) + return status; // If there's anything there, it should be JSON + if (d->jsonResponse.isArray()) // ...specifically, a JSON object + return { IncorrectResponse, + tr("Malformed error JSON: an array instead of an object") }; + } const auto& errorJson = jsonData(); const auto errCode = errorJson.value("errcode"_ls).toString(); @@ -534,6 +530,7 @@ BaseJob::Status BaseJob::prepareError() return { TooManyRequestsError, msg }; } + if (errCode == "M_CONSENT_NOT_GIVEN") { d->errorUrl = errorJson.value("consent_uri"_ls).toString(); return { UserConsentRequiredError }; @@ -552,10 +549,10 @@ BaseJob::Status BaseJob::prepareError() return { UserDeactivated }; // Not localisable on the client side - if (errorJson.contains("error"_ls)) - d->status.message = errorJson.value("error"_ls).toString(); + if (errorJson.contains("error"_ls)) // Keep the code, update the message + return { d->status.code, errorJson.value("error"_ls).toString() }; - return d->status; + return NoError; // Retain the status if the error payload is not recognised } QJsonValue BaseJob::takeValueFromJson(const QString& key) -- cgit v1.2.3 From 0b8c5d4180b305d7cfcd94bba34d1a1324fb2a38 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jun 2020 19:28:32 +0200 Subject: Connection: add (STORED false) to derived Q_PROPERTYs --- lib/connection.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index 12b0756d..d5fa9eac 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -136,15 +136,15 @@ class Connection : public QObject { Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) Q_PROPERTY(QString localUserId READ userId NOTIFY stateChanged) + Q_PROPERTY(QString domain READ domain NOTIFY stateChanged STORED false) Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded) Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) - Q_PROPERTY(QString domain READ domain NOTIFY homeserverChanged) Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) - Q_PROPERTY(bool isUsable READ isUsable NOTIFY loginFlowsChanged) - Q_PROPERTY(bool supportsSso READ supportsSso NOTIFY loginFlowsChanged) - Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged) + Q_PROPERTY(bool isUsable READ isUsable NOTIFY loginFlowsChanged STORED false) + Q_PROPERTY(bool supportsSso READ supportsSso NOTIFY loginFlowsChanged STORED false) + Q_PROPERTY(bool supportsPasswordAuth READ supportsPasswordAuth NOTIFY loginFlowsChanged STORED false) Q_PROPERTY(bool cacheState READ cacheState WRITE setCacheState NOTIFY cacheStateChanged) Q_PROPERTY(bool lazyLoading READ lazyLoading WRITE setLazyLoading NOTIFY lazyLoadingChanged) @@ -307,7 +307,7 @@ public: QUrl homeserver() const; /** Get the domain name used for ids/aliases on the server */ QString domain() const; - /** Whether the configured homeserver is known to be reachable and working */ + /** Check if the homeserver is known to be reachable and working */ bool isUsable() const; /** Get the list of supported login flows */ QVector loginFlows() const; -- cgit v1.2.3 From ebdb2ba9d15e6cdfb1458e7895032afd641aafe3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jun 2020 20:20:10 +0200 Subject: BaseJob: fail early if the job needs token and there's none --- lib/jobs/basejob.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index d7daa170..7774a6e2 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -341,7 +341,9 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) d->connection = connData; doPrepare(); - if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) + if (d->needsToken && d->connection->accessToken().isEmpty()) + setStatus(Unauthorised); + else if ((d->verb == HttpVerb::Post || d->verb == HttpVerb::Put) && d->requestData.source() && !d->requestData.source()->isReadable()) { setStatus(FileError, "Request data not ready"); -- cgit v1.2.3 From b1071cf34b86685c3cdb5004d6112881966a7ce6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jun 2020 20:29:46 +0200 Subject: Connection::syncLoop: give a pause between syncs As it's observed now, Synapse responds almost immediately on /sync requests - even if there are no events to return. This downgrades long-polling to simply polling, and since clients don't expect it, polling loops become pretty violent. To alleviate that somehow, syncLoop now accepts the second parameter, msecBetween (500 msecs by default), to configure waiting between the previous sync response and the next sync request. This is only for syncLoop(); Connection::sync() fires instantly, as before. --- lib/connection.cpp | 13 ++++++++----- lib/connection.h | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 50e31d52..d53c308f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -506,21 +507,23 @@ void Connection::sync(int timeout) }); } -void Connection::syncLoop(int timeout) +void Connection::syncLoop(int timeout, int msecBetween) { if (d->syncLoopConnection && d->syncTimeout == timeout) { qCInfo(MAIN) << "Attempt to run sync loop but there's one already " "running; nothing will be done"; return; } - std::swap(d->syncTimeout, timeout); + std::swap(d->syncTimeout, timeout); // swap() is for the nice log below if (d->syncLoopConnection) { qCInfo(MAIN) << "Timeout for next syncs changed from" << timeout << "to" << d->syncTimeout; } else { - d->syncLoopConnection = connect(this, &Connection::syncDone, - this, &Connection::syncLoopIteration, - Qt::QueuedConnection); + d->syncLoopConnection = + connect(this, &Connection::syncDone, this, [this, msecBetween] { + QTimer::singleShot(msecBetween, this, + &Connection::syncLoopIteration); + }); syncLoopIteration(); // initial sync to start the loop } } diff --git a/lib/connection.h b/lib/connection.h index d5fa9eac..fb76cf3d 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -544,7 +544,7 @@ public slots: void logout(); void sync(int timeout = -1); - void syncLoop(int timeout = -1); + void syncLoop(int timeout = -1, int msecBetween = 500); void stopSync(); QString nextBatchToken() const; -- cgit v1.2.3 From d257f0b6155d6d1450cf0c82d1538e553380c44e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jun 2020 20:36:29 +0200 Subject: Connection::isLoggedIn() and more robust logout() isLoggedIn() is just a wrapper around Connection::accessToken() that returns whether it's not empty. Now, Connection::accessToken() and Connection::logout() are changed in such a way that if there's a logout job ongoing Connection::accessToken() will return an empty value even though the access token is still stored in ConnectionData. This gives a hint to the rest of Connection and to the client code that the user is not quite authenticated anymore. Finally, syncLoop() and sync() have been altered to check isLoggedIn() before proceeding with their network request. --- lib/connection.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index fb76cf3d..e8373a10 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -139,6 +139,7 @@ class Connection : public QObject { Q_PROPERTY(QString domain READ domain NOTIFY stateChanged STORED false) Q_PROPERTY(QString deviceId READ deviceId NOTIFY stateChanged) Q_PROPERTY(QByteArray accessToken READ accessToken NOTIFY stateChanged) + Q_PROPERTY(bool isLoggedIn READ isLoggedIn NOTIFY stateChanged STORED false) Q_PROPERTY(QString defaultRoomVersion READ defaultRoomVersion NOTIFY capabilitiesLoaded) Q_PROPERTY(QUrl homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(QVector loginFlows READ loginFlows NOTIFY loginFlowsChanged) @@ -338,6 +339,7 @@ public: QString userId() const; QString deviceId() const; QByteArray accessToken() const; + bool isLoggedIn() const; #ifdef Quotient_E2EE_ENABLED QtOlm::Account* olmAccount() const; #endif // Quotient_E2EE_ENABLED -- cgit v1.2.3 From f85badbd7bae173871b7681322745c73b23aa512 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 12 Jun 2020 20:37:33 +0200 Subject: The previous commit is incomplete, this completes it --- lib/connection.cpp | 48 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 36 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index d53c308f..0fb301d1 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -125,6 +125,7 @@ public: QPointer loginFlowsJob = nullptr; SyncJob* syncJob = nullptr; + QPointer logoutJob = nullptr; bool cacheState = true; bool cacheToBinary = @@ -451,24 +452,30 @@ void Connection::checkAndConnect(const QString& userId, void Connection::logout() { - // If there's an ongoing sync job, stop it but don't break the sync loop yet - const auto syncWasRunning = bool(d->syncJob); - if (syncWasRunning) + // If there's an ongoing sync job, stop it (this also suspends sync loop) + const auto wasSyncing = bool(d->syncJob); + if (wasSyncing) { d->syncJob->abandon(); d->syncJob = nullptr; } - const auto* job = callApi(); - connect(job, &LogoutJob::finished, this, [this, job, syncWasRunning] { - if (job->status().good() || job->error() == BaseJob::Unauthorised - || job->error() == BaseJob::ContentAccessError) { + + d->logoutJob = callApi(); + emit stateChanged(); // isLoggedIn() == false from now + + connect(d->logoutJob, &LogoutJob::finished, this, [this, wasSyncing] { + if (d->logoutJob->status().good() + || d->logoutJob->error() == BaseJob::Unauthorised + || d->logoutJob->error() == BaseJob::ContentAccessError) { if (d->syncLoopConnection) disconnect(d->syncLoopConnection); d->data->setToken({}); - emit stateChanged(); emit loggedOut(); - } else if (syncWasRunning) - syncLoopIteration(); // Resume sync loop (or a single sync) + } else { // logout() somehow didn't proceed - restore the session state + emit stateChanged(); + if (wasSyncing) + syncLoopIteration(); // Resume sync loop (or a single sync) + } }); } @@ -478,6 +485,10 @@ void Connection::sync(int timeout) qCInfo(MAIN) << d->syncJob << "is already running"; return; } + if (!isLoggedIn()) { + qCWarning(MAIN) << "Not logged in, not going to sync"; + return; + } d->syncTimeout = timeout; Filter filter; @@ -528,7 +539,13 @@ void Connection::syncLoop(int timeout, int msecBetween) } } -void Connection::syncLoopIteration() { sync(d->syncTimeout); } +void Connection::syncLoopIteration() +{ + if (isLoggedIn()) + sync(d->syncTimeout); + else + qCInfo(MAIN) << "Logged out, sync loop will stop now"; +} QJsonObject toJson(const DirectChatsMap& directChats) { @@ -1155,7 +1172,14 @@ QString Connection::userId() const { return d->data->userId(); } QString Connection::deviceId() const { return d->data->deviceId(); } -QByteArray Connection::accessToken() const { return d->data->accessToken(); } +QByteArray Connection::accessToken() const +{ + // The logout job needs access token to do its job; so the token is + // kept inside d->data but no more exposed to the outside world. + return isJobRunning(d->logoutJob) ? QByteArray() : d->data->accessToken(); +} + +bool Connection::isLoggedIn() const { return !accessToken().isEmpty(); } #ifdef Quotient_E2EE_ENABLED QtOlm::Account* Connection::olmAccount() const -- cgit v1.2.3 From a2520bae3677be9df0abcef7681a138a6c4ad849 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 14 Jun 2020 17:23:54 +0200 Subject: quotient_common.h for common namespace things The two main cases for this header file are: * namespace QMatrixClient = Quotient should occur exactly once, to respect ODR. * Q_NAMESPACE for namespace Quotient (to enable Q_ENUM_NS, particularly) must be defined exactly once, for the same reason. --- lib/connection.h | 11 +---------- lib/quotient_common.h | 29 +++++++++++++++++++++++++++++ lib/util.h | 2 -- 3 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 lib/quotient_common.h (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index e8373a10..47d1844e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -21,6 +21,7 @@ #include "ssosession.h" #include "joinstate.h" #include "qt_connection_util.h" +#include "quotient_common.h" #include "csapi/login.h" #include "csapi/create_room.h" @@ -41,7 +42,6 @@ class Account; Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) namespace Quotient { -Q_NAMESPACE class Room; class User; @@ -114,15 +114,6 @@ static inline user_factory_t defaultUserFactory() return [](Connection* c, const QString& id) { return new T(id, c); }; } -/** Enumeration with flags defining the network job running policy - * So far only background/foreground flags are available. - * - * \sa Connection::callApi, Connection::run - */ -enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; - -Q_ENUM_NS(RunningPolicy) - // Room ids, rather than room pointers, are used in the direct chat // map types because the library keeps Invite rooms separate from // rooms in Join and Leave state; and direct chats in account data diff --git a/lib/quotient_common.h b/lib/quotient_common.h new file mode 100644 index 00000000..44541b42 --- /dev/null +++ b/lib/quotient_common.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace Quotient { +Q_NAMESPACE + +/** Enumeration with flags defining the network job running policy + * So far only background/foreground flags are available. + * + * \sa Connection::callApi, Connection::run + */ +enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; + +Q_ENUM_NS(RunningPolicy) + +enum ResourceResolveResult : short { + StillResolving = -1, + Resolved = 0, + UnknownMatrixId, + MalformedMatrixId, + NoAccount, + EmptyMatrixId +}; +Q_ENUM_NS(ResourceResolveResult) + +} // namespace Quotient +/// \deprecated Use namespace Quotient instead +namespace QMatrixClient = Quotient; diff --git a/lib/util.h b/lib/util.h index 81add1ac..943ff8db 100644 --- a/lib/util.h +++ b/lib/util.h @@ -294,5 +294,3 @@ qreal stringToHueF(const QString& s); /** Extract the serverpart from MXID */ QString serverPart(const QString& mxId); } // namespace Quotient -/// \deprecated Use namespace Quotient instead -namespace QMatrixClient = Quotient; -- cgit v1.2.3 From e17764a1ae81393968dfb747c7b67353c109bc71 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 19 Jun 2020 15:01:33 +0200 Subject: csapi/: generated using the latest GTAD and matrix-doc For matrix-doc, specifically, it is master (5cb4b086) merged with https://github.com/matrix-org/matrix-doc/pull/2518. --- lib/csapi/account-data.h | 4 -- lib/csapi/admin.h | 1 - lib/csapi/administrative_contact.h | 15 ++--- lib/csapi/appservice_room_directory.h | 1 - lib/csapi/banning.h | 2 - lib/csapi/content-repo.h | 37 +++++------- lib/csapi/create_room.h | 1 - lib/csapi/definitions/request_email_validation.h | 2 +- lib/csapi/definitions/request_msisdn_validation.h | 2 +- lib/csapi/device_management.h | 6 +- lib/csapi/directory.h | 4 -- lib/csapi/event_context.h | 1 - lib/csapi/filter.h | 6 +- lib/csapi/inviting.h | 1 - lib/csapi/joining.h | 42 ++------------ lib/csapi/keys.h | 17 ++---- lib/csapi/kicking.h | 1 - lib/csapi/leaving.h | 2 - lib/csapi/list_public_rooms.cpp | 4 +- lib/csapi/list_public_rooms.h | 71 +++++++---------------- lib/csapi/login.h | 14 ++++- lib/csapi/message_pagination.h | 1 - lib/csapi/notifications.h | 1 - lib/csapi/openid.h | 5 +- lib/csapi/peeking_events.h | 1 - lib/csapi/presence.h | 2 - lib/csapi/profile.h | 5 -- lib/csapi/pusher.h | 1 - lib/csapi/pushrules.h | 9 +-- lib/csapi/read_markers.h | 1 - lib/csapi/receipts.h | 1 - lib/csapi/redaction.h | 1 - lib/csapi/registration.cpp | 2 +- lib/csapi/registration.h | 23 +++----- lib/csapi/report_content.h | 1 - lib/csapi/room_send.h | 1 - lib/csapi/room_state.h | 1 - lib/csapi/room_upgrades.h | 1 - lib/csapi/rooms.h | 9 +-- lib/csapi/sso_login_redirect.h | 1 - lib/csapi/tags.h | 3 - lib/csapi/third_party_lookup.h | 7 +-- lib/csapi/third_party_membership.h | 1 - lib/csapi/to_device.h | 1 - lib/csapi/typing.h | 1 - lib/csapi/users.h | 16 ++--- 46 files changed, 89 insertions(+), 241 deletions(-) (limited to 'lib') diff --git a/lib/csapi/account-data.h b/lib/csapi/account-data.h index 1c7b555b..9a31596f 100644 --- a/lib/csapi/account-data.h +++ b/lib/csapi/account-data.h @@ -17,7 +17,6 @@ namespace Quotient { class SetAccountDataJob : public BaseJob { public: /*! \brief Set some account_data for the user. - * * * \param userId * The ID of the user to set account_data for. The access token must be @@ -42,7 +41,6 @@ public: class GetAccountDataJob : public BaseJob { public: /*! \brief Get some account_data for the user. - * * * \param userId * The ID of the user to get account_data for. The access token must be @@ -72,7 +70,6 @@ public: class SetAccountDataPerRoomJob : public BaseJob { public: /*! \brief Set some account_data for the user. - * * * \param userId * The ID of the user to set account_data for. The access token must be @@ -101,7 +98,6 @@ public: class GetAccountDataPerRoomJob : public BaseJob { public: /*! \brief Get some account_data for the user. - * * * \param userId * The ID of the user to set account_data for. The access token must be diff --git a/lib/csapi/admin.h b/lib/csapi/admin.h index 6ad7d08d..570bf24a 100644 --- a/lib/csapi/admin.h +++ b/lib/csapi/admin.h @@ -58,7 +58,6 @@ public: // Construction/destruction /*! \brief Gets information about a particular user. - * * * \param userId * The user to look up. diff --git a/lib/csapi/administrative_contact.h b/lib/csapi/administrative_contact.h index 53c89272..1966d533 100644 --- a/lib/csapi/administrative_contact.h +++ b/lib/csapi/administrative_contact.h @@ -4,9 +4,9 @@ #pragma once -#include "csapi/./definitions/request_email_validation.h" -#include "csapi/./definitions/request_msisdn_validation.h" #include "csapi/definitions/auth_data.h" +#include "csapi/definitions/request_email_validation.h" +#include "csapi/definitions/request_msisdn_validation.h" #include "csapi/definitions/request_token_response.h" #include "jobs/basejob.h" @@ -123,7 +123,6 @@ public: // Construction/destruction /*! \brief Adds contact information to the user's account. - * * * \param threePidCreds * The third party credentials to associate with the account. @@ -157,7 +156,6 @@ struct JsonObjectConverter { class Add3PIDJob : public BaseJob { public: /*! \brief Adds contact information to the user's account. - * * * \param clientSecret * The client secret used in the session with the homeserver. @@ -186,7 +184,6 @@ public: class Bind3PIDJob : public BaseJob { public: /*! \brief Binds a 3PID to the user's account through an Identity Service. - * * * \param clientSecret * The client secret used in the session with the identity server. @@ -216,7 +213,6 @@ public: class Delete3pidFromAccountJob : public BaseJob { public: /*! \brief Deletes a third party identifier from the user's account - * * * \param medium * The medium of the third party identifier being removed. @@ -260,7 +256,6 @@ public: class Unbind3pidFromAccountJob : public BaseJob { public: /*! \brief Removes a user's third party identifier from an identity server. - * * * \param medium * The medium of the third party identifier being removed. @@ -307,7 +302,6 @@ public: /*! \brief Begins the validation process for an email address for * association with the user's account. * - * * \param body * The homeserver must check that the given email address is **not** * already associated with an account on this homeserver. This API should @@ -324,7 +318,7 @@ public: /// An email was sent to the given address. Note that this may be an /// email containing the validation token or it may be informing the /// user of an error. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } @@ -346,7 +340,6 @@ public: /*! \brief Begins the validation process for a phone number for association * with the user's account. * - * * \param body * The homeserver must check that the given phone number is **not** * already associated with an account on this homeserver. This API should @@ -361,7 +354,7 @@ public: // Result properties /// An SMS message was sent to the given phone number. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } diff --git a/lib/csapi/appservice_room_directory.h b/lib/csapi/appservice_room_directory.h index d103177c..3fa02a07 100644 --- a/lib/csapi/appservice_room_directory.h +++ b/lib/csapi/appservice_room_directory.h @@ -26,7 +26,6 @@ public: /*! \brief Updates a room's visibility in the application service's room * directory. * - * * \param networkId * The protocol (network) ID to update the room list for. This would * have been provided by the application service as being listed as diff --git a/lib/csapi/banning.h b/lib/csapi/banning.h index 4faaaa81..37ae91ee 100644 --- a/lib/csapi/banning.h +++ b/lib/csapi/banning.h @@ -21,7 +21,6 @@ namespace Quotient { class BanJob : public BaseJob { public: /*! \brief Ban a user in the room. - * * * \param roomId * The room identifier (not alias) from which the user should be banned. @@ -49,7 +48,6 @@ public: class UnbanJob : public BaseJob { public: /*! \brief Unban a user from the room. - * * * \param roomId * The room identifier (not alias) from which the user should be unbanned. diff --git a/lib/csapi/content-repo.h b/lib/csapi/content-repo.h index 0eb273bc..ed67485c 100644 --- a/lib/csapi/content-repo.h +++ b/lib/csapi/content-repo.h @@ -17,7 +17,6 @@ namespace Quotient { class UploadContentJob : public BaseJob { public: /*! \brief Upload some content to the content repository. - * * * \param content * The content to be uploaded. @@ -46,7 +45,6 @@ public: class GetContentJob : public BaseJob { public: /*! \brief Download content from the content repository. - * * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) @@ -85,15 +83,16 @@ public: QIODevice* data() { return reply(); } }; -/*! \brief Download content from the content repository. This is the same as -the download endpoint above, except permitting a desired file name. +/*! \brief Download content from the content repository overriding the file name * + * This will download content from the content repository (same as + * the previous endpoint) but replace the target file name with the one + * provided by the caller. */ class GetContentOverrideNameJob : public BaseJob { public: - /*! \brief Download content from the content repository. This is the same as -the download endpoint above, except permitting a desired file name. - * + /*! \brief Download content from the content repository overriding the file + * name * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) @@ -106,10 +105,8 @@ the download endpoint above, except permitting a desired file name. * * \param allowRemote * Indicates to the server that it should not attempt to fetch the media -if it is deemed - * remote. This is to prevent routing loops where the server contacts -itself. Defaults to - * true if not provided. + * if it is deemed remote. This is to prevent routing loops where the server + * contacts itself. Defaults to true if not provided. */ explicit GetContentOverrideNameJob(const QString& serverName, const QString& mediaId, @@ -141,15 +138,14 @@ itself. Defaults to QIODevice* data() { return reply(); } }; -/*! \brief Download a thumbnail of content from the content repository. See the -`thumbnailing <#thumbnails>`_ section for more information. +/*! \brief Download a thumbnail of content from the content repository * + * Download a thumbnail of content from the content repository. + * See the `thumbnailing <#thumbnails>`_ section for more information. */ class GetContentThumbnailJob : public BaseJob { public: - /*! \brief Download a thumbnail of content from the content repository. See -the `thumbnailing <#thumbnails>`_ section for more information. - * + /*! \brief Download a thumbnail of content from the content repository * * \param serverName * The server name from the ``mxc://`` URI (the authoritory component) @@ -170,11 +166,9 @@ the `thumbnailing <#thumbnails>`_ section for more information. * section for more information. * * \param allowRemote - * Indicates to the server that it should not attempt to fetch the media -if it is deemed - * remote. This is to prevent routing loops where the server contacts -itself. Defaults to - * true if not provided. + * Indicates to the server that it should not attempt to fetch + * the media if it is deemed remote. This is to prevent routing loops + * where the server contacts itself. Defaults to true if not provided. */ explicit GetContentThumbnailJob(const QString& serverName, const QString& mediaId, int width, @@ -214,7 +208,6 @@ itself. Defaults to class GetUrlPreviewJob : public BaseJob { public: /*! \brief Get information about a URL for a client - * * * \param url * The URL to get a preview of. diff --git a/lib/csapi/create_room.h b/lib/csapi/create_room.h index 82db9e59..6a718ff4 100644 --- a/lib/csapi/create_room.h +++ b/lib/csapi/create_room.h @@ -189,7 +189,6 @@ public: // Construction/destruction /*! \brief Create a new room - * * * \param visibility * A ``public`` visibility indicates that the room will be shown diff --git a/lib/csapi/definitions/request_email_validation.h b/lib/csapi/definitions/request_email_validation.h index e6ab261d..ab34862e 100644 --- a/lib/csapi/definitions/request_email_validation.h +++ b/lib/csapi/definitions/request_email_validation.h @@ -6,7 +6,7 @@ #include "converters.h" -#include "csapi/./definitions/../../identity/definitions/request_email_validation.h" +#include "csapi/definitions/../../identity/definitions/request_email_validation.h" namespace Quotient { diff --git a/lib/csapi/definitions/request_msisdn_validation.h b/lib/csapi/definitions/request_msisdn_validation.h index 6f161f00..8539cd98 100644 --- a/lib/csapi/definitions/request_msisdn_validation.h +++ b/lib/csapi/definitions/request_msisdn_validation.h @@ -6,7 +6,7 @@ #include "converters.h" -#include "csapi/./definitions/../../identity/definitions/request_msisdn_validation.h" +#include "csapi/definitions/../../identity/definitions/request_msisdn_validation.h" namespace Quotient { diff --git a/lib/csapi/device_management.h b/lib/csapi/device_management.h index 0abd5b84..47dc7ec8 100644 --- a/lib/csapi/device_management.h +++ b/lib/csapi/device_management.h @@ -43,7 +43,6 @@ public: class GetDeviceJob : public BaseJob { public: /*! \brief Get a single device - * * * \param deviceId * The device to retrieve. @@ -60,7 +59,7 @@ public: // Result properties /// Device information - Device data() const { return fromJson(jsonData()); } + Device device() const { return fromJson(jsonData()); } }; /*! \brief Update a device @@ -70,7 +69,6 @@ public: class UpdateDeviceJob : public BaseJob { public: /*! \brief Update a device - * * * \param deviceId * The device to update. @@ -92,7 +90,6 @@ public: class DeleteDeviceJob : public BaseJob { public: /*! \brief Delete a device - * * * \param deviceId * The device to delete. @@ -115,7 +112,6 @@ public: class DeleteDevicesJob : public BaseJob { public: /*! \brief Bulk deletion of devices - * * * \param devices * The list of device IDs to delete. diff --git a/lib/csapi/directory.h b/lib/csapi/directory.h index 7ae44d1d..9b109aad 100644 --- a/lib/csapi/directory.h +++ b/lib/csapi/directory.h @@ -14,7 +14,6 @@ namespace Quotient { class SetRoomAliasJob : public BaseJob { public: /*! \brief Create a new mapping from room alias to room ID. - * * * \param roomAlias * The room alias to set. @@ -36,7 +35,6 @@ public: class GetRoomIdByAliasJob : public BaseJob { public: /*! \brief Get the room ID corresponding to this room alias. - * * * \param roomAlias * The room alias. @@ -81,7 +79,6 @@ public: class DeleteRoomAliasJob : public BaseJob { public: /*! \brief Remove a mapping of room alias to room ID. - * * * \param roomAlias * The room alias to remove. @@ -118,7 +115,6 @@ public: class GetLocalAliasesJob : public BaseJob { public: /*! \brief Get a list of local aliases on a given room. - * * * \param roomId * The room ID to find local aliases of. diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index 2f9c66d8..d82d16ab 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -22,7 +22,6 @@ namespace Quotient { class GetEventContextJob : public BaseJob { public: /*! \brief Get events and state around the specified event. - * * * \param roomId * The room to get events from. diff --git a/lib/csapi/filter.h b/lib/csapi/filter.h index 9aa7dc79..f07b489c 100644 --- a/lib/csapi/filter.h +++ b/lib/csapi/filter.h @@ -19,7 +19,6 @@ namespace Quotient { class DefineFilterJob : public BaseJob { public: /*! \brief Upload a new filter. - * * * \param userId * The id of the user uploading the filter. The access token must be @@ -45,7 +44,6 @@ public: class GetFilterJob : public BaseJob { public: /*! \brief Download a filter - * * * \param userId * The user ID to download a filter for. @@ -65,8 +63,8 @@ public: // Result properties - /// "The filter defintion" - Filter data() const { return fromJson(jsonData()); } + /// The filter definition. + Filter filter() const { return fromJson(jsonData()); } }; } // namespace Quotient diff --git a/lib/csapi/inviting.h b/lib/csapi/inviting.h index a7ec6093..59a61b89 100644 --- a/lib/csapi/inviting.h +++ b/lib/csapi/inviting.h @@ -32,7 +32,6 @@ namespace Quotient { class InviteUserJob : public BaseJob { public: /*! \brief Invite a user to participate in a particular room. - * * * \param roomId * The room identifier (not alias) to which to invite the user. diff --git a/lib/csapi/joining.h b/lib/csapi/joining.h index 6822fbdf..dd936f92 100644 --- a/lib/csapi/joining.h +++ b/lib/csapi/joining.h @@ -12,8 +12,8 @@ namespace Quotient { /*! \brief Start the requesting user participating in a particular room. * - * *Note that this API requires a room ID, not alias.* ``/join/{roomIdOrAlias}`` - * *exists if you have a room alias.* + * *Note that this API requires a room ID, not alias.* + * ``/join/{roomIdOrAlias}`` *exists if you have a room alias.* * * This API starts a user participating in a particular room, if that user * is allowed to participate in that room. After this call, the client is @@ -22,34 +22,18 @@ namespace Quotient { * * After a user has joined a room, the room will appear as an entry in the * response of the |/initialSync|_ and |/sync|_ APIs. - * - * If a ``third_party_signed`` was supplied, the homeserver must verify - * that it matches a pending ``m.room.third_party_invite`` event in the - * room, and perform key validity checking if required by the event. */ class JoinRoomByIdJob : public BaseJob { public: /*! \brief Start the requesting user participating in a particular room. - * * * \param roomId * The room identifier (not alias) to join. * * \param thirdPartySigned - * *Note that this API requires a room ID, not alias.* - * ``/join/{roomIdOrAlias}`` *exists if you have a room alias.* - * - * This API starts a user participating in a particular room, if that user - * is allowed to participate in that room. After this call, the client is - * allowed to see all current state events in the room, and all subsequent - * events associated with the room until the user leaves the room. - * - * After a user has joined a room, the room will appear as an entry in the - * response of the |/initialSync|_ and |/sync|_ APIs. - * - * If a ``third_party_signed`` was supplied, the homeserver must verify - * that it matches a pending ``m.room.third_party_invite`` event in the - * room, and perform key validity checking if required by the event. + * If supplied, the homeserver must verify that it matches a pending + * ``m.room.third_party_invite`` event in the room, and perform + * key validity checking if required by the event. */ explicit JoinRoomByIdJob( const QString& roomId, @@ -73,15 +57,10 @@ public: * * After a user has joined a room, the room will appear as an entry in the * response of the |/initialSync|_ and |/sync|_ APIs. - * - * If a ``third_party_signed`` was supplied, the homeserver must verify - * that it matches a pending ``m.room.third_party_invite`` event in the - * room, and perform key validity checking if required by the event. */ class JoinRoomJob : public BaseJob { public: /*! \brief Start the requesting user participating in a particular room. - * * * \param roomIdOrAlias * The room identifier or alias to join. @@ -91,17 +70,6 @@ public: * must be participating in the room. * * \param thirdPartySigned - * *Note that this API takes either a room ID or alias, unlike* - * ``/room/{roomId}/join``. - * - * This API starts a user participating in a particular room, if that user - * is allowed to participate in that room. After this call, the client is - * allowed to see all current state events in the room, and all subsequent - * events associated with the room until the user leaves the room. - * - * After a user has joined a room, the room will appear as an entry in the - * response of the |/initialSync|_ and |/sync|_ APIs. - * * If a ``third_party_signed`` was supplied, the homeserver must verify * that it matches a pending ``m.room.third_party_invite`` event in the * room, and perform key validity checking if required by the event. diff --git a/lib/csapi/keys.h b/lib/csapi/keys.h index b7a8dc71..8f6c8cc9 100644 --- a/lib/csapi/keys.h +++ b/lib/csapi/keys.h @@ -17,7 +17,6 @@ namespace Quotient { class UploadKeysJob : public BaseJob { public: /*! \brief Upload end-to-end encryption keys. - * * * \param deviceKeys * Identity keys for the device. May be absent if no new @@ -61,9 +60,7 @@ public: }; /// Returns the current devices and identity keys for the given users. - struct DeviceKeys : - - Quotient::DeviceKeys { + struct DeviceInformation : DeviceKeys { /// Additional data added to the device key information /// by intermediate servers, and not covered by the /// signatures. @@ -73,7 +70,6 @@ public: // Construction/destruction /*! \brief Download device identity keys. - * * * \param deviceKeys * The keys to be downloaded. A map from user ID, to a list of @@ -113,9 +109,9 @@ public: /// the information returned will be the same as uploaded via /// ``/keys/upload``, with the addition of an ``unsigned`` /// property. - QHash> deviceKeys() const + QHash> deviceKeys() const { - return loadFromJson>>( + return loadFromJson>>( "device_keys"_ls); } }; @@ -130,8 +126,9 @@ struct JsonObjectConverter { }; template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, QueryKeysJob::DeviceKeys& result) +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + QueryKeysJob::DeviceInformation& result) { fillFromJson(jo, result); fromJson(jo.value("unsigned"_ls), result.unsignedData); @@ -145,7 +142,6 @@ struct JsonObjectConverter { class ClaimKeysJob : public BaseJob { public: /*! \brief Claim one-time encryption keys. - * * * \param oneTimeKeys * The keys to be claimed. A map from user ID, to a map from @@ -201,7 +197,6 @@ public: class GetKeysChangesJob : public BaseJob { public: /*! \brief Query users with recent device key updates. - * * * \param from * The desired start point of the list. Should be the ``next_batch`` field diff --git a/lib/csapi/kicking.h b/lib/csapi/kicking.h index 594f5845..2645a54f 100644 --- a/lib/csapi/kicking.h +++ b/lib/csapi/kicking.h @@ -23,7 +23,6 @@ namespace Quotient { class KickJob : public BaseJob { public: /*! \brief Kick a user from the room. - * * * \param roomId * The room identifier (not alias) from which the user should be kicked. diff --git a/lib/csapi/leaving.h b/lib/csapi/leaving.h index 0a4c401c..1bea7e41 100644 --- a/lib/csapi/leaving.h +++ b/lib/csapi/leaving.h @@ -25,7 +25,6 @@ namespace Quotient { class LeaveRoomJob : public BaseJob { public: /*! \brief Stop the requesting user participating in a particular room. - * * * \param roomId * The room identifier to leave. @@ -55,7 +54,6 @@ public: class ForgetRoomJob : public BaseJob { public: /*! \brief Stop the requesting user remembering about a particular room. - * * * \param roomId * The room identifier to forget. diff --git a/lib/csapi/list_public_rooms.cpp b/lib/csapi/list_public_rooms.cpp index cdc5c716..415d816c 100644 --- a/lib/csapi/list_public_rooms.cpp +++ b/lib/csapi/list_public_rooms.cpp @@ -60,7 +60,9 @@ GetPublicRoomsJob::GetPublicRoomsJob(Omittable limit, const QString& since, : BaseJob(HttpVerb::Get, QStringLiteral("GetPublicRoomsJob"), QStringLiteral("/_matrix/client/r0") % "/publicRooms", queryToGetPublicRooms(limit, since, server), {}, false) -{} +{ + addExpectedKey("chunk"); +} auto queryToQueryPublicRooms(const QString& server) { diff --git a/lib/csapi/list_public_rooms.h b/lib/csapi/list_public_rooms.h index 5ef7c23e..b0cb79d2 100644 --- a/lib/csapi/list_public_rooms.h +++ b/lib/csapi/list_public_rooms.h @@ -17,7 +17,6 @@ namespace Quotient { class GetRoomVisibilityOnDirectoryJob : public BaseJob { public: /*! \brief Gets the visibility of a room in the directory - * * * \param roomId * The room ID. @@ -52,7 +51,6 @@ public: class SetRoomVisibilityOnDirectoryJob : public BaseJob { public: /*! \brief Sets the visibility of a room in the room directory - * * * \param roomId * The room ID. @@ -75,7 +73,6 @@ public: class GetPublicRoomsJob : public BaseJob { public: /*! \brief Lists the public rooms on the server. - * * * \param limit * Limit the number of results returned. @@ -105,10 +102,27 @@ public: // Result properties - /// A list of the rooms on the server. - PublicRoomsResponse data() const + /// A paginated chunk of public rooms. + QVector chunk() const { - return fromJson(jsonData()); + return loadFromJson>("chunk"_ls); + } + + /// A pagination token for the response. The absence of this token + /// means there are no more results to fetch and the client should + /// stop paginating. + QString nextBatch() const { return loadFromJson("next_batch"_ls); } + + /// A pagination token that allows fetching previous results. The + /// absence of this token means there are no results before this + /// batch, i.e. this is the first batch. + QString prevBatch() const { return loadFromJson("prev_batch"_ls); } + + /// An estimate on the total number of public rooms, if the + /// server has an estimate. + Omittable totalRoomCountEstimate() const + { + return loadFromJson>("total_room_count_estimate"_ls); } }; @@ -130,37 +144,9 @@ public: QString genericSearchTerm; }; - /// Lists the public rooms on the server, with optional filter. - /// - /// This API returns paginated responses. The rooms are ordered by the - /// number of joined members, with the largest rooms first. - struct PublicRoomsChunk { - /// Aliases of the room. May be empty. - QStringList aliases; - /// The canonical alias of the room, if any. - QString canonicalAlias; - /// The name of the room, if any. - QString name; - /// The number of members joined to the room. - int numJoinedMembers; - /// The ID of the room. - QString roomId; - /// The topic of the room, if any. - QString topic; - /// Whether the room may be viewed by guest users without joining. - bool worldReadable; - /// Whether guest users may join the room and participate in it. - /// If they can, they will be subject to ordinary power level - /// rules like any other user. - bool guestCanJoin; - /// The URL for the room's avatar, if one is set. - QString avatarUrl; - }; - // Construction/destruction /*! \brief Lists the public rooms on the server with optional filter. - * * * \param server * The server to fetch the public room lists from. Defaults to the @@ -228,21 +214,4 @@ struct JsonObjectConverter { } }; -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - QueryPublicRoomsJob::PublicRoomsChunk& result) - { - fromJson(jo.value("aliases"_ls), result.aliases); - fromJson(jo.value("canonical_alias"_ls), result.canonicalAlias); - fromJson(jo.value("name"_ls), result.name); - fromJson(jo.value("num_joined_members"_ls), result.numJoinedMembers); - fromJson(jo.value("room_id"_ls), result.roomId); - fromJson(jo.value("topic"_ls), result.topic); - fromJson(jo.value("world_readable"_ls), result.worldReadable); - fromJson(jo.value("guest_can_join"_ls), result.guestCanJoin); - fromJson(jo.value("avatar_url"_ls), result.avatarUrl); - } -}; - } // namespace Quotient diff --git a/lib/csapi/login.h b/lib/csapi/login.h index 0483b77c..a406fc79 100644 --- a/lib/csapi/login.h +++ b/lib/csapi/login.h @@ -75,13 +75,21 @@ struct JsonObjectConverter { class LoginJob : public BaseJob { public: /*! \brief Authenticates the user. - * * * \param type * The login type being used. * * \param identifier - * Identification information for the user. + * Authenticates the user, and issues an access token they can + * use to authorize themself in subsequent requests. + * + * If the client does not supply a ``device_id``, the server must + * auto-generate one. + * + * The returned access token must be associated with the ``device_id`` + * supplied by the client or generated by the server. The server may + * invalidate any access token previously associated with that device. See + * `Relationship between access tokens and devices`_. * * \param password * Required when ``type`` is ``m.login.password``. The user's @@ -108,7 +116,7 @@ public: // Result properties - /// The fully-qualified Matrix ID that has been registered. + /// The fully-qualified Matrix ID for the account. QString userId() const { return loadFromJson("user_id"_ls); } /// An access token for the account. diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index e9f9e6d4..286d4237 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -21,7 +21,6 @@ namespace Quotient { class GetRoomEventsJob : public BaseJob { public: /*! \brief Get a list of events for this room - * * * \param roomId * The room to get events from. diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index 095e9325..ff499c7a 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -41,7 +41,6 @@ public: // Construction/destruction /*! \brief Gets a list of events that the user has been notified about - * * * \param from * Pagination token given to retrieve the next set of events. diff --git a/lib/csapi/openid.h b/lib/csapi/openid.h index e69f9ad0..efb5f623 100644 --- a/lib/csapi/openid.h +++ b/lib/csapi/openid.h @@ -24,7 +24,6 @@ namespace Quotient { class RequestOpenIdTokenJob : public BaseJob { public: /*! \brief Get an OpenID token object to verify the requester's identity. - * * * \param userId * The user to request and OpenID token for. Should be the user who @@ -39,11 +38,11 @@ public: // Result properties /// OpenID token information. This response is nearly compatible with the - /// response documented in the `OpenID 1.0 Specification + /// response documented in the `OpenID Connect 1.0 Specification /// `_ /// with the only difference being the lack of an ``id_token``. Instead, /// the Matrix homeserver's name is provided. - OpenidToken data() const { return fromJson(jsonData()); } + OpenidToken tokenData() const { return fromJson(jsonData()); } }; } // namespace Quotient diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index bfb9e24a..cecd9f2d 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -25,7 +25,6 @@ namespace Quotient { class PeekEventsJob : public BaseJob { public: /*! \brief Listen on the event stream. - * * * \param from * The token to stream from. This token is either from a previous diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index b34028e7..a885bf4f 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -18,7 +18,6 @@ namespace Quotient { class SetPresenceJob : public BaseJob { public: /*! \brief Update this user's presence state. - * * * \param userId * The user whose presence state to update. @@ -40,7 +39,6 @@ public: class GetPresenceJob : public BaseJob { public: /*! \brief Get this user's presence state. - * * * \param userId * The user whose presence state to get. diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 5b0d06d9..88bf3166 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -16,7 +16,6 @@ namespace Quotient { class SetDisplayNameJob : public BaseJob { public: /*! \brief Set the user's display name. - * * * \param userId * The user whose display name to set. @@ -37,7 +36,6 @@ public: class GetDisplayNameJob : public BaseJob { public: /*! \brief Get the user's display name. - * * * \param userId * The user whose display name to get. @@ -68,7 +66,6 @@ public: class SetAvatarUrlJob : public BaseJob { public: /*! \brief Set the user's avatar URL. - * * * \param userId * The user whose avatar URL to set. @@ -89,7 +86,6 @@ public: class GetAvatarUrlJob : public BaseJob { public: /*! \brief Get the user's avatar URL. - * * * \param userId * The user whose avatar URL to get. @@ -119,7 +115,6 @@ public: class GetUserProfileJob : public BaseJob { public: /*! \brief Get this user's profile information. - * * * \param userId * The user whose profile information to get. diff --git a/lib/csapi/pusher.h b/lib/csapi/pusher.h index 40cd5796..ae0050d2 100644 --- a/lib/csapi/pusher.h +++ b/lib/csapi/pusher.h @@ -131,7 +131,6 @@ public: // Construction/destruction /*! \brief Modify a pusher for this user on the homeserver. - * * * \param pushkey * This is a unique identifier for this pusher. The value you diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index 0971dc6b..f6d3458a 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -47,7 +47,6 @@ public: class GetPushRuleJob : public BaseJob { public: /*! \brief Retrieve a push rule. - * * * \param scope * ``global`` to specify global rules. @@ -73,7 +72,7 @@ public: /// The specific push rule. This will also include keys specific to the /// rule itself such as the rule's ``actions`` and ``conditions`` if set. - PushRule data() const { return fromJson(jsonData()); } + PushRule pushRule() const { return fromJson(jsonData()); } }; /*! \brief Delete a push rule. @@ -83,7 +82,6 @@ public: class DeletePushRuleJob : public BaseJob { public: /*! \brief Delete a push rule. - * * * \param scope * ``global`` to specify global rules. @@ -117,7 +115,6 @@ public: class SetPushRuleJob : public BaseJob { public: /*! \brief Add or change a push rule. - * * * \param scope * ``global`` to specify global rules. @@ -165,7 +162,6 @@ public: class IsPushRuleEnabledJob : public BaseJob { public: /*! \brief Get whether a push rule is enabled - * * * \param scope * Either ``global`` or ``device/`` to specify global @@ -201,7 +197,6 @@ public: class SetPushRuleEnabledJob : public BaseJob { public: /*! \brief Enable or disable a push rule. - * * * \param scope * ``global`` to specify global rules. @@ -226,7 +221,6 @@ public: class GetPushRuleActionsJob : public BaseJob { public: /*! \brief The actions for a push rule - * * * \param scope * Either ``global`` or ``device/`` to specify global @@ -266,7 +260,6 @@ public: class SetPushRuleActionsJob : public BaseJob { public: /*! \brief Set the actions for a push rule. - * * * \param scope * ``global`` to specify global rules. diff --git a/lib/csapi/read_markers.h b/lib/csapi/read_markers.h index addb9123..0e122c63 100644 --- a/lib/csapi/read_markers.h +++ b/lib/csapi/read_markers.h @@ -16,7 +16,6 @@ namespace Quotient { class SetReadMarkerJob : public BaseJob { public: /*! \brief Set the position of the read marker for a room. - * * * \param roomId * The room ID to set the read marker in for the user. diff --git a/lib/csapi/receipts.h b/lib/csapi/receipts.h index fdec3b88..1fac0acf 100644 --- a/lib/csapi/receipts.h +++ b/lib/csapi/receipts.h @@ -16,7 +16,6 @@ namespace Quotient { class PostReceiptJob : public BaseJob { public: /*! \brief Send a receipt for the given event ID. - * * * \param roomId * The room in which to send the event. diff --git a/lib/csapi/redaction.h b/lib/csapi/redaction.h index c737de41..541e433a 100644 --- a/lib/csapi/redaction.h +++ b/lib/csapi/redaction.h @@ -22,7 +22,6 @@ namespace Quotient { class RedactEventJob : public BaseJob { public: /*! \brief Strips all non-integrity-critical information out of an event. - * * * \param roomId * The room from which to redact the event. diff --git a/lib/csapi/registration.cpp b/lib/csapi/registration.cpp index 9f88ef28..b80abc84 100644 --- a/lib/csapi/registration.cpp +++ b/lib/csapi/registration.cpp @@ -82,7 +82,7 @@ RequestTokenToResetPasswordEmailJob::RequestTokenToResetPasswordEmailJob( } RequestTokenToResetPasswordMSISDNJob::RequestTokenToResetPasswordMSISDNJob( - const RequestMsisdnValidation& body) + const MsisdnValidationData& body) : BaseJob(HttpVerb::Post, QStringLiteral("RequestTokenToResetPasswordMSISDNJob"), QStringLiteral("/_matrix/client/r0") diff --git a/lib/csapi/registration.h b/lib/csapi/registration.h index 9d96db32..62bc35f1 100644 --- a/lib/csapi/registration.h +++ b/lib/csapi/registration.h @@ -4,10 +4,9 @@ #pragma once -#include "csapi/../identity/definitions/request_msisdn_validation.h" -#include "csapi/./definitions/request_email_validation.h" -#include "csapi/./definitions/request_msisdn_validation.h" #include "csapi/definitions/auth_data.h" +#include "csapi/definitions/request_email_validation.h" +#include "csapi/definitions/request_msisdn_validation.h" #include "csapi/definitions/request_token_response.h" #include "jobs/basejob.h" @@ -61,7 +60,6 @@ namespace Quotient { class RegisterJob : public BaseJob { public: /*! \brief Register for an account on this homeserver. - * * * \param kind * The kind of account to register. Defaults to ``user``. @@ -148,7 +146,6 @@ public: /*! \brief Begins the validation process for an email to be used during * registration. * - * * \param body * The homeserver must check that the given email address is **not** * already associated with an account on this homeserver. The homeserver @@ -162,7 +159,7 @@ public: /// An email has been sent to the specified address. Note that this /// may be an email containing the validation token or it may be /// informing the user of an error. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } @@ -181,7 +178,6 @@ public: /*! \brief Requests a validation token be sent to the given phone number for * the purpose of registering an account * - * * \param body * The homeserver must check that the given phone number is **not** * already associated with an account on this homeserver. The homeserver @@ -195,7 +191,7 @@ public: /// An SMS message has been sent to the specified phone number. Note /// that this may be an SMS message containing the validation token or /// it may be informing the user of an error. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } @@ -220,7 +216,6 @@ public: class ChangePasswordJob : public BaseJob { public: /*! \brief Changes a user's password. - * * * \param newPassword * The new password for the account. @@ -270,7 +265,6 @@ public: /*! \brief Requests a validation token be sent to the given email address * for the purpose of resetting a user's password * - * * \param body * The homeserver must check that the given email address **is * associated** with an account on this homeserver. This API should be @@ -299,7 +293,7 @@ public: // Result properties /// An email was sent to the given address. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } @@ -333,7 +327,6 @@ public: /*! \brief Requests a validation token be sent to the given phone number for * the purpose of resetting a user's password. * - * * \param body * The homeserver must check that the given phone number **is * associated** with an account on this homeserver. This API should be @@ -357,12 +350,12 @@ public: * #post-matrix-client-r0-register-email-requesttoken */ explicit RequestTokenToResetPasswordMSISDNJob( - const RequestMsisdnValidation& body); + const MsisdnValidationData& body); // Result properties /// An SMS message was sent to the given phone number. - RequestTokenResponse data() const + RequestTokenResponse response() const { return fromJson(jsonData()); } @@ -388,7 +381,6 @@ public: class DeactivateAccountJob : public BaseJob { public: /*! \brief Deactivate a user's account. - * * * \param auth * Additional authentication information for the user-interactive @@ -439,7 +431,6 @@ public: class CheckUsernameAvailabilityJob : public BaseJob { public: /*! \brief Checks to see if a username is available on the server. - * * * \param username * The username to check the availability of. diff --git a/lib/csapi/report_content.h b/lib/csapi/report_content.h index 13dc9138..375e1829 100644 --- a/lib/csapi/report_content.h +++ b/lib/csapi/report_content.h @@ -16,7 +16,6 @@ namespace Quotient { class ReportContentJob : public BaseJob { public: /*! \brief Reports an event as inappropriate. - * * * \param roomId * The room in which the event being reported is located. diff --git a/lib/csapi/room_send.h b/lib/csapi/room_send.h index b1905e53..39460aca 100644 --- a/lib/csapi/room_send.h +++ b/lib/csapi/room_send.h @@ -21,7 +21,6 @@ namespace Quotient { class SendMessageJob : public BaseJob { public: /*! \brief Send a message event to the given room. - * * * \param roomId * The room to send the event to. diff --git a/lib/csapi/room_state.h b/lib/csapi/room_state.h index eeb2b33c..447605ff 100644 --- a/lib/csapi/room_state.h +++ b/lib/csapi/room_state.h @@ -34,7 +34,6 @@ namespace Quotient { class SetRoomStateWithKeyJob : public BaseJob { public: /*! \brief Send a state event to the given room. - * * * \param roomId * The room to set the state in diff --git a/lib/csapi/room_upgrades.h b/lib/csapi/room_upgrades.h index 53601816..58327587 100644 --- a/lib/csapi/room_upgrades.h +++ b/lib/csapi/room_upgrades.h @@ -15,7 +15,6 @@ namespace Quotient { class UpgradeRoomJob : public BaseJob { public: /*! \brief Upgrades a room to a new room version. - * * * \param roomId * The ID of the room to upgrade. diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 6137bcbd..f0bfa349 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -18,7 +18,6 @@ namespace Quotient { class GetOneRoomEventJob : public BaseJob { public: /*! \brief Get a single event by event ID. - * * * \param roomId * The ID of the room the event is in. @@ -39,7 +38,7 @@ public: // Result properties /// The full event. - EventPtr data() { return fromJson(jsonData()); } + EventPtr event() { return fromJson(jsonData()); } }; /*! \brief Get the state identified by the type and key. @@ -55,7 +54,6 @@ public: class GetRoomStateWithKeyJob : public BaseJob { public: /*! \brief Get the state identified by the type and key. - * * * \param roomId * The room to look up the state in. @@ -88,7 +86,6 @@ public: class GetRoomStateJob : public BaseJob { public: /*! \brief Get all state events in the current state of a room. - * * * \param roomId * The room to look up the state for. @@ -105,7 +102,7 @@ public: // Result properties /// The current state of the room - StateEvents data() { return fromJson(jsonData()); } + StateEvents events() { return fromJson(jsonData()); } }; /*! \brief Get the m.room.member events for the room. @@ -115,7 +112,6 @@ public: class GetMembersByRoomJob : public BaseJob { public: /*! \brief Get the m.room.member events for the room. - * * * \param roomId * The room to get the member events for. @@ -189,7 +185,6 @@ public: // Construction/destruction /*! \brief Gets the list of currently joined users and their profile data. - * * * \param roomId * The room to get the members of. diff --git a/lib/csapi/sso_login_redirect.h b/lib/csapi/sso_login_redirect.h index 20f33da2..d6330e38 100644 --- a/lib/csapi/sso_login_redirect.h +++ b/lib/csapi/sso_login_redirect.h @@ -18,7 +18,6 @@ namespace Quotient { class RedirectToSSOJob : public BaseJob { public: /*! \brief Redirect the user's browser to the SSO interface. - * * * \param redirectUrl * URI to which the user will be redirected after the homeserver has diff --git a/lib/csapi/tags.h b/lib/csapi/tags.h index e6eb9dda..a815d9b3 100644 --- a/lib/csapi/tags.h +++ b/lib/csapi/tags.h @@ -28,7 +28,6 @@ public: // Construction/destruction /*! \brief List the tags for a room. - * * * \param userId * The id of the user to get tags for. The access token must be @@ -72,7 +71,6 @@ struct JsonObjectConverter { class SetRoomTagJob : public BaseJob { public: /*! \brief Add a tag to a room. - * * * \param userId * The id of the user to add a tag for. The access token must be @@ -103,7 +101,6 @@ public: class DeleteRoomTagJob : public BaseJob { public: /*! \brief Remove a tag from the room. - * * * \param userId * The id of the user to remove a tag for. The access token must be diff --git a/lib/csapi/third_party_lookup.h b/lib/csapi/third_party_lookup.h index 9329b7a7..969e767c 100644 --- a/lib/csapi/third_party_lookup.h +++ b/lib/csapi/third_party_lookup.h @@ -33,7 +33,7 @@ public: // Result properties /// The protocols supported by the homeserver. - QHash data() const + QHash protocols() const { return fromJson>(jsonData()); } @@ -50,7 +50,6 @@ public: /*! \brief Retrieve metadata about a specific protocol that the homeserver * supports. * - * * \param protocol * The name of the protocol. */ @@ -88,7 +87,6 @@ public: /*! \brief Retrieve Matrix-side portals rooms leading to a third party * location. * - * * \param protocol * The protocol used to communicate to the third party network. * @@ -124,7 +122,6 @@ public: class QueryUserByProtocolJob : public BaseJob { public: /*! \brief Retrieve the Matrix User ID of a corresponding third party user. - * * * \param protocol * The name of the protocol. @@ -161,7 +158,6 @@ public: class QueryLocationByAliasJob : public BaseJob { public: /*! \brief Reverse-lookup third party locations given a Matrix room alias. - * * * \param alias * The Matrix room alias to look up. @@ -191,7 +187,6 @@ public: class QueryUserByIDJob : public BaseJob { public: /*! \brief Reverse-lookup third party users given a Matrix User ID. - * * * \param userid * The Matrix User ID to look up. diff --git a/lib/csapi/third_party_membership.h b/lib/csapi/third_party_membership.h index f8196f6f..55cab370 100644 --- a/lib/csapi/third_party_membership.h +++ b/lib/csapi/third_party_membership.h @@ -58,7 +58,6 @@ namespace Quotient { class InviteBy3PIDJob : public BaseJob { public: /*! \brief Invite a user to participate in a particular room. - * * * \param roomId * The room identifier (not alias) to which to invite the user. diff --git a/lib/csapi/to_device.h b/lib/csapi/to_device.h index 8c82af45..f5d69d65 100644 --- a/lib/csapi/to_device.h +++ b/lib/csapi/to_device.h @@ -16,7 +16,6 @@ namespace Quotient { class SendToDeviceJob : public BaseJob { public: /*! \brief Send an event to a given set of devices. - * * * \param eventType * The type of event to send. diff --git a/lib/csapi/typing.h b/lib/csapi/typing.h index f5b67baf..2c953949 100644 --- a/lib/csapi/typing.h +++ b/lib/csapi/typing.h @@ -18,7 +18,6 @@ namespace Quotient { class SetTypingJob : public BaseJob { public: /*! \brief Informs the server that the user has started or stopped typing. - * * * \param userId * The user who has started to type. diff --git a/lib/csapi/users.h b/lib/csapi/users.h index adb2a33d..6fc26f57 100644 --- a/lib/csapi/users.h +++ b/lib/csapi/users.h @@ -35,7 +35,7 @@ public: /// The search is performed case-insensitively on user IDs and display /// names preferably using a collation determined based upon the /// ``Accept-Language`` header provided in the request, if present. - struct SearchUserDirectory200ThirdPartyUser { + struct User { /// The user's matrix user ID. QString userId; /// The display name of the user, if one exists. @@ -47,7 +47,6 @@ public: // Construction/destruction /*! \brief Searches the user directory. - * * * \param searchTerm * The term to search for @@ -61,10 +60,9 @@ public: // Result properties /// Ordered by rank and then whether or not profile info is available. - QVector results() const + QVector results() const { - return loadFromJson>( - "results"_ls); + return loadFromJson>("results"_ls); } /// Indicates if the result list has been truncated by the limit. @@ -72,11 +70,9 @@ public: }; template <> -struct JsonObjectConverter< - SearchUserDirectoryJob::SearchUserDirectory200ThirdPartyUser> { - static void - fillFrom(const QJsonObject& jo, - SearchUserDirectoryJob::SearchUserDirectory200ThirdPartyUser& result) +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SearchUserDirectoryJob::User& result) { fromJson(jo.value("user_id"_ls), result.userId); fromJson(jo.value("display_name"_ls), result.displayName); -- cgit v1.2.3 From f6ad01c959e19362a0b15779b816432595153d3b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 19 Jun 2020 15:59:50 +0200 Subject: Revert adding a pause between syncs, use sane timeout defaults This reverts commit b1071cf34b86685c3cdb5004d6112881966a7ce6. Passing -1 to sync() and, respectively, to SyncJob does not add any timeout; however, careful reading of the spec reveals that the default value for the timeout (0) means to return as soon as possible, not as late as possible. As a consequence, syncLoop() without parameters initiates a sync polling frenzy, with the client sending a new request as soon as the previous returns, while the server returns the request as soon as it practically can, not as soon as another event for the client comes around. To fix this, the default value for syncLoop() is changed to 30 seconds. The recently added msecBetween parameter is abolished; we really don't want to steer people to classic polling from long polling. --- lib/connection.cpp | 13 +++++-------- lib/connection.h | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 0fb301d1..b702c1f9 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #include #include #include @@ -518,23 +517,21 @@ void Connection::sync(int timeout) }); } -void Connection::syncLoop(int timeout, int msecBetween) +void Connection::syncLoop(int timeout) { if (d->syncLoopConnection && d->syncTimeout == timeout) { qCInfo(MAIN) << "Attempt to run sync loop but there's one already " "running; nothing will be done"; return; } - std::swap(d->syncTimeout, timeout); // swap() is for the nice log below + std::swap(d->syncTimeout, timeout); if (d->syncLoopConnection) { qCInfo(MAIN) << "Timeout for next syncs changed from" << timeout << "to" << d->syncTimeout; } else { - d->syncLoopConnection = - connect(this, &Connection::syncDone, this, [this, msecBetween] { - QTimer::singleShot(msecBetween, this, - &Connection::syncLoopIteration); - }); + d->syncLoopConnection = connect(this, &Connection::syncDone, + this, &Connection::syncLoopIteration, + Qt::QueuedConnection); syncLoopIteration(); // initial sync to start the loop } } diff --git a/lib/connection.h b/lib/connection.h index 47d1844e..48ea4f5e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -537,7 +537,7 @@ public slots: void logout(); void sync(int timeout = -1); - void syncLoop(int timeout = -1, int msecBetween = 500); + void syncLoop(int timeout = 30000); void stopSync(); QString nextBatchToken() const; -- cgit v1.2.3 From 593c241661148dfd80854d3e156d9a4ee60d5ab4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 19 Jun 2020 17:56:56 +0200 Subject: Fix FTBFS and warnings with E2EE switched on --- lib/encryptionmanager.cpp | 3 ++- lib/room.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index c50459f3..4a1025b2 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -89,7 +89,8 @@ public: // A map from senderKey to InboundSession QMap sessions; // TODO: cache void updateDeviceKeys( - const QHash>& deviceKeys) + const QHash>& deviceKeys) { for (auto userId : deviceKeys.keys()) { for (auto deviceId : deviceKeys.value(userId).keys()) { diff --git a/lib/room.cpp b/lib/room.cpp index 22ec1c82..b83d805c 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1263,7 +1263,7 @@ RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) QString decrypted = d->groupSessionDecryptMessage( encryptedEvent.ciphertext(), encryptedEvent.senderKey(), encryptedEvent.sessionId(), encryptedEvent.id(), - encryptedEvent.timestamp()); + encryptedEvent.originTimestamp()); if (decrypted.isEmpty()) { return {}; } -- cgit v1.2.3 From d70b2d29c11ec7a0e177c2979beeab9ddb6eb5c3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 20 Jun 2020 19:07:46 +0200 Subject: Room: PostReadMarkersJob should be in background ...meaning - errors from it should not throw up at a user, who has no clue (they still should go to logs for investigation). --- lib/room.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index b83d805c..5a4a8611 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -623,7 +623,8 @@ Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId) emit q->readMarkerForUserMoved(u, eventId, storedId); if (isLocalUser(u)) { if (storedId != serverReadMarker) - connection->callApi(id, storedId); + connection->callApi(BackgroundRequest, id, + storedId); emit q->readMarkerMoved(eventId, storedId); return Change::ReadMarkerChange; } -- cgit v1.2.3 From b1e84568dbb70dc4fa24a915c6d15438be958378 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 26 Jun 2020 19:05:36 +0200 Subject: Disable HTTP2; enable pipelining Long run tests over 2+ days kept crashing before this commit but stopped crashing with pipelining on and HTTP2 off. --- lib/jobs/basejob.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 7774a6e2..465287c6 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -298,16 +298,17 @@ void BaseJob::Private::sendRequest() req.setAttribute(QNetworkRequest::BackgroundRequestAttribute, inBackground); req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); req.setMaximumRedirectsAllowed(10); - // Pipelining doesn't fly quite well with SSL, occasionally crashing at - // what seems like an attempt to write to a closed channel. -// req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); req.setAttribute( #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) QNetworkRequest::Http2AllowedAttribute #else QNetworkRequest::HTTP2AllowedAttribute #endif - , true); + // Qt doesn't combine HTTP2 with SSL quite right, occasionally crashing at + // what seems like an attempt to write to a closed channel. If/when that + // changes, false should be turned to true below. + , false); Q_ASSERT(req.url().isValid()); for (auto it = requestHeaders.cbegin(); it != requestHeaders.cend(); ++it) req.setRawHeader(it.key(), it.value()); -- cgit v1.2.3 From e7bf4f3e4fc059ef9ea0e0b253a1953a91fd77d8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 31 May 2020 13:24:05 +0200 Subject: ResourceResolver Introducing the uniform way to resolve Matrix URIs and identifiers to Room/User objects, passing an optional event id (if supplied) to the client-defined handler. Just call ResourceResolver::visitResource() or ResourceResolver::openResource() and you'll have that string parsed and dispatched where you need. --- lib/resourceresolver.cpp | 97 +++++++++++++++++++++++++++++++++++++++ lib/resourceresolver.h | 117 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 lib/resourceresolver.cpp create mode 100644 lib/resourceresolver.h (limited to 'lib') diff --git a/lib/resourceresolver.cpp b/lib/resourceresolver.cpp new file mode 100644 index 00000000..f910d640 --- /dev/null +++ b/lib/resourceresolver.cpp @@ -0,0 +1,97 @@ +#include "resourceresolver.h" + +#include "settings.h" + +#include + +using namespace Quotient; + +QString ResourceResolver::toMatrixId(const QString& uriOrId, + QStringList uriServers) +{ + auto id = QUrl::fromPercentEncoding(uriOrId.toUtf8()); + const auto MatrixScheme = "matrix:"_ls; + if (id.startsWith(MatrixScheme)) { + id.remove(0, MatrixScheme.size()); + for (const auto& p: { std::pair { "user/"_ls, '@' }, + { "roomid/"_ls, '!' }, + { "room/"_ls, '#' } }) + if (id.startsWith(p.first)) { + id.replace(0, p.first.size(), p.second); + break; + } + // The below assumes that /event/ cannot show up in normal Matrix ids. + id.replace("/event/"_ls, "/$"_ls); + } else { + const auto MatrixTo_ServerName = QStringLiteral("matrix.to"); + if (!uriServers.contains(MatrixTo_ServerName)) + uriServers.push_back(MatrixTo_ServerName); + id.remove( + QRegularExpression("^https://(" + uriServers.join('|') + ")/?#/")); + } + return id; +} + +ResourceResolver::Result ResourceResolver::visitResource( + Connection* account, const QString& identifier, + std::function userHandler, + std::function roomEventHandler) +{ + const auto& normalizedId = toMatrixId(identifier); + auto&& [sigil, mainId, secondaryId] = parseIdentifier(normalizedId); + Room* room = nullptr; + switch (sigil) { + case char(-1): + return MalformedMatrixId; + case char(0): + return EmptyMatrixId; + case '@': + if (auto* user = account->user(mainId)) { + userHandler(user); + return Success; + } + return MalformedMatrixId; + case '!': + if ((room = account->room(mainId))) + break; + return UnknownMatrixId; + case '#': + if ((room = account->roomByAlias(mainId))) + break; + [[fallthrough]]; + default: + return UnknownMatrixId; + } + roomEventHandler(room, secondaryId); + return Success; +} + +ResourceResolver::IdentifierParts +ResourceResolver::parseIdentifier(const QString& identifier) +{ + if (identifier.isEmpty()) + return {}; + + // The regex is quick and dirty, only intending to triage the id. + static const QRegularExpression IdRE { + "^(?
(?.)([^/]+))(/(?[^?]+))?" + }; + auto dissectedId = IdRE.match(identifier); + if (!dissectedId.hasMatch()) + return { char(-1) }; + + const auto sigil = dissectedId.captured("sigil"); + return { sigil.size() != 1 ? char(-1) : sigil.front().toLatin1(), + dissectedId.captured("main"), dissectedId.captured("sec") }; +} + +ResourceResolver::Result +ResourceResolver::openResource(Connection* account, const QString& identifier, + const QString& action) +{ + return visitResource(account, identifier, + [this, &action](User* u) { emit userAction(u, action); }, + [this, &action](Room* room, const QString& eventId) { + emit roomAction(room, eventId, action); + }); +} diff --git a/lib/resourceresolver.h b/lib/resourceresolver.h new file mode 100644 index 00000000..794b7796 --- /dev/null +++ b/lib/resourceresolver.h @@ -0,0 +1,117 @@ +#pragma once + +#include "connection.h" + +#include + +namespace Quotient { + +/*! \brief Matrix resource resolver + * TODO: rewrite + * Similar to visitResource(), this class encapsulates the logic of resolving + * a Matrix identifier or a URI into Quotient object(s) and applying an action + * to the resolved object(s). Instead of using a C++ visitor pattern, it + * announces the request through Qt's signals passing the resolved object(s) + * through those (still in a typesafe way). + * + * This class is aimed primarily at clients where invoking the resolving/action + * and handling the action are happening in decoupled parts of the code; it's + * also useful to operate on Matrix identifiers and URIs from QML/JS code + * that cannot call visitResource due to QML/C++ interface limitations. + */ +class ResourceResolver : public QObject { + Q_OBJECT +public: + enum Result : short { + StillResolving = -1, + Success = 0, + UnknownMatrixId, + MalformedMatrixId, + NoAccount, + EmptyMatrixId + }; + Q_ENUM(Result) + + explicit ResourceResolver(QObject* parent = nullptr) : QObject(parent) + { } + + /*! \brief Decode a URI to a Matrix identifier (or a room/event pair) + * + * This accepts plain Matrix ids, MSC2312 URIs (aka matrix: URIs) and + * matrix.to URIs. + * + * \return a Matrix identifier as defined by the common identifier grammars + * or a slash separated pair of Matrix identifiers if the original + * uri/id pointed to an event in a room + */ + static QString toMatrixId(const QString& uriOrId, + QStringList uriServers = {}); + + /*! \brief Resolve the resource and invoke an action on it, visitor style + * + * This template function encapsulates the logic of resolving a Matrix + * identifier or URI into a Quotient object (or objects) and applying an + * appropriate action handler from the set provided by the caller to it. + * A typical use case for that is opening a room or mentioning a user in + * response to clicking on a Matrix URI or identifier. + * + * \param account The connection used as a context to resolve the identifier + * + * \param identifier The Matrix identifier or URI. MSC2312 URIs and classic + * Matrix ID scheme are supported. + * + * \sa ResourceResolver + */ + static Result + visitResource(Connection* account, const QString& identifier, + std::function userHandler, + std::function roomEventHandler); + + /*! \brief Resolve the resource and request an action on it, signal style + * + * This method: + * 1. Resolves \p identifier into an actual object (Room or User), with + * possible additional data such as event id, in the context of + * \p account. + * 2. If the resolving is successful, depending on the type of the object, + * emits the respective signal to which the client must connect in order + * to apply the action to the resource (open a room, mention a user etc.). + * 3. Returns the result of resolving the resource. + * + * Note that the action can be applied either synchronously or entirely + * asynchronously; ResourceResolver does not restrain the client code + * to use either method. The resource resolving part is entirely synchronous + * though. If the synchronous operation is chosen, only + * direct connections to ResourceResolver signals must be used, and + * the caller should check the future's state immediately after calling + * openResource() to process any feedback from the resolver and/or action + * handler. If asynchronous operation is needed then either direct or queued + * connections to ResourceResolver's signals can be used and the caller + * must both check the ResourceFuture state right after calling openResource + * and also connect to ResourceFuture::ready() signal in order to process + * the result of resolving and action. + */ + Q_INVOKABLE Result openResource(Connection* account, + const QString& identifier, + const QString& action = {}); + +signals: + /// An action on a user has been requested + void userAction(Quotient::User* user, QString action); + + /// An action on a room has been requested, with optional event id + void roomAction(Quotient::Room* room, QString eventId, QString action); + +private: + struct IdentifierParts { + char sigil; + QString mainId {}; + QString secondaryId = {}; + }; + + static IdentifierParts parseIdentifier(const QString& identifier); +}; + +} // namespace Quotient + + -- cgit v1.2.3 From 012f6ea6a4fa63d4e48e910f829e060c19cbcb1e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 11 Jun 2020 22:48:26 +0200 Subject: Fix building with older Qt --- lib/resourceresolver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/resourceresolver.cpp b/lib/resourceresolver.cpp index f910d640..0d5c5a20 100644 --- a/lib/resourceresolver.cpp +++ b/lib/resourceresolver.cpp @@ -81,7 +81,7 @@ ResourceResolver::parseIdentifier(const QString& identifier) return { char(-1) }; const auto sigil = dissectedId.captured("sigil"); - return { sigil.size() != 1 ? char(-1) : sigil.front().toLatin1(), + return { sigil.size() != 1 ? char(-1) : sigil[0].toLatin1(), dissectedId.captured("main"), dissectedId.captured("sec") }; } -- cgit v1.2.3 From 4dd51859c9ada4660e2a9cd16c3d0a99ad13761f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 17 Jun 2020 22:13:53 +0200 Subject: class MatrixUri; support action=join and via= --- lib/quotient_common.h | 8 +- lib/resourceresolver.cpp | 275 +++++++++++++++++++++++++++++++++++------------ lib/resourceresolver.h | 172 +++++++++++++++++++---------- 3 files changed, 329 insertions(+), 126 deletions(-) (limited to 'lib') diff --git a/lib/quotient_common.h b/lib/quotient_common.h index 44541b42..446f628b 100644 --- a/lib/quotient_common.h +++ b/lib/quotient_common.h @@ -14,15 +14,15 @@ enum RunningPolicy { ForegroundRequest = 0x0, BackgroundRequest = 0x1 }; Q_ENUM_NS(RunningPolicy) -enum ResourceResolveResult : short { +enum UriResolveResult : short { StillResolving = -1, - Resolved = 0, + UriResolved = 0, UnknownMatrixId, - MalformedMatrixId, + MalformedUri, NoAccount, EmptyMatrixId }; -Q_ENUM_NS(ResourceResolveResult) +Q_ENUM_NS(UriResolveResult) } // namespace Quotient /// \deprecated Use namespace Quotient instead diff --git a/lib/resourceresolver.cpp b/lib/resourceresolver.cpp index 0d5c5a20..e7820061 100644 --- a/lib/resourceresolver.cpp +++ b/lib/resourceresolver.cpp @@ -1,97 +1,236 @@ #include "resourceresolver.h" -#include "settings.h" +#include "connection.h" +#include "logging.h" #include using namespace Quotient; -QString ResourceResolver::toMatrixId(const QString& uriOrId, - QStringList uriServers) +struct ReplacePair { QByteArray uriString; char sigil; }; +static const auto replacePairs = { ReplacePair { "user/", '@' }, + { "roomid/", '!' }, + { "room/", '#' } }; + +MatrixUri::MatrixUri(QByteArray primaryId, QByteArray secondaryId, QString query) { - auto id = QUrl::fromPercentEncoding(uriOrId.toUtf8()); - const auto MatrixScheme = "matrix:"_ls; - if (id.startsWith(MatrixScheme)) { - id.remove(0, MatrixScheme.size()); - for (const auto& p: { std::pair { "user/"_ls, '@' }, - { "roomid/"_ls, '!' }, - { "room/"_ls, '#' } }) - if (id.startsWith(p.first)) { - id.replace(0, p.first.size(), p.second); + if (primaryId.isEmpty()) + primaryType_ = Empty; + else { + setScheme("matrix"); + QString pathToBe; + primaryType_ = Invalid; + for (const auto& p: replacePairs) + if (primaryId[0] == p.sigil) { + primaryType_ = Type(p.sigil); + pathToBe = p.uriString + primaryId.mid(1); break; } - // The below assumes that /event/ cannot show up in normal Matrix ids. - id.replace("/event/"_ls, "/$"_ls); - } else { - const auto MatrixTo_ServerName = QStringLiteral("matrix.to"); - if (!uriServers.contains(MatrixTo_ServerName)) - uriServers.push_back(MatrixTo_ServerName); - id.remove( - QRegularExpression("^https://(" + uriServers.join('|') + ")/?#/")); + if (!secondaryId.isEmpty()) + pathToBe += "/event/" + secondaryId.mid(1); + setPath(pathToBe); } - return id; + setQuery(std::move(query)); } -ResourceResolver::Result ResourceResolver::visitResource( - Connection* account, const QString& identifier, - std::function userHandler, - std::function roomEventHandler) +MatrixUri::MatrixUri(QUrl url) : QUrl(std::move(url)) { - const auto& normalizedId = toMatrixId(identifier); - auto&& [sigil, mainId, secondaryId] = parseIdentifier(normalizedId); - Room* room = nullptr; - switch (sigil) { - case char(-1): - return MalformedMatrixId; - case char(0): - return EmptyMatrixId; - case '@': - if (auto* user = account->user(mainId)) { - userHandler(user); - return Success; - } - return MalformedMatrixId; - case '!': - if ((room = account->room(mainId))) - break; - return UnknownMatrixId; - case '#': - if ((room = account->roomByAlias(mainId))) + // NB: url is moved from and empty by now + if (isEmpty()) + return; // primaryType_ == None + + primaryType_ = Invalid; + if (!QUrl::isValid()) // MatrixUri::isValid() checks primaryType_ + return; + + if (scheme() == "matrix") { + // Check sanity as per https://github.com/matrix-org/matrix-doc/pull/2312 + const auto& urlPath = path(); + const auto& splitPath = urlPath.splitRef('/'); + switch (splitPath.size()) { + case 2: + break; + case 4: + if (splitPath[2] == "event") break; [[fallthrough]]; default: - return UnknownMatrixId; + return; // Invalid + } + + for (const auto& p: replacePairs) + if (urlPath.startsWith(p.uriString)) { + primaryType_ = Type(p.sigil); + return; // The only valid return path for matrix: URIs + } + qCWarning(MAIN) << "Invalid matrix: URI passed to MatrixUri"; + } + if (scheme() == "https" && authority() == "matrix.to") { + // See https://matrix.org/docs/spec/appendices#matrix-to-navigation + static const QRegularExpression MatrixToUrlRE { + R"(^/(?
[^/?]+)(/(?[^?]+))?(\?(?.+))?$)" + }; + // matrix.to accepts both literal sigils (as well as & and ? used in + // its "query" substitute) and their %-encoded forms; + // so force QUrl to decode everything. + auto f = fragment(QUrl::FullyDecoded); + if (auto&& m = MatrixToUrlRE.match(f); m.hasMatch()) + *this = MatrixUri { m.captured("main").toUtf8(), + m.captured("sec").toUtf8(), + m.captured("query") }; } - roomEventHandler(room, secondaryId); - return Success; } -ResourceResolver::IdentifierParts -ResourceResolver::parseIdentifier(const QString& identifier) +MatrixUri::MatrixUri(const QString &uriOrId) + : MatrixUri(fromUserInput(uriOrId)) +{ } + +MatrixUri MatrixUri::fromUserInput(const QString& uriOrId) { - if (identifier.isEmpty()) + if (uriOrId.isEmpty()) + return {}; // type() == None + + // A quick check if uriOrId is a plain Matrix id + if (QStringLiteral("!@#+").contains(uriOrId[0])) + return MatrixUri { uriOrId.toUtf8() }; + + // Bare event ids cannot be resolved without a room scope but are treated as + // valid anyway; in the future we might expose them as, say, + // matrix:event/eventid + if (uriOrId[0] == '$') + return MatrixUri { "", uriOrId.toUtf8() }; + + return MatrixUri { QUrl::fromUserInput(uriOrId) }; +} + +MatrixUri::Type MatrixUri::type() const { return primaryType_; } + +MatrixUri::SecondaryType MatrixUri::secondaryType() const +{ + return path().section('/', 2, 2) == "event" ? EventId : NoSecondaryId; +} + +QUrl MatrixUri::toUrl(UriForm form) const +{ + if (!isValid()) return {}; - - // The regex is quick and dirty, only intending to triage the id. - static const QRegularExpression IdRE { - "^(?
(?.)([^/]+))(/(?[^?]+))?" - }; - auto dissectedId = IdRE.match(identifier); - if (!dissectedId.hasMatch()) - return { char(-1) }; - - const auto sigil = dissectedId.captured("sigil"); - return { sigil.size() != 1 ? char(-1) : sigil[0].toLatin1(), - dissectedId.captured("main"), dissectedId.captured("sec") }; + + if (form == CanonicalUri) + return *this; + + QUrl url; + url.setScheme("https"); + url.setHost("matrix.to"); + url.setPath("/"); + auto fragment = primaryId(); + if (const auto& secId = secondaryId(); !secId.isEmpty()) + fragment += '/' + secId; + if (const auto& q = query(); !q.isEmpty()) + fragment += '?' + q; + url.setFragment(fragment); + return url; +} + +QString MatrixUri::toDisplayString(MatrixUri::UriForm form) const +{ + return toUrl(form).toDisplayString(); } -ResourceResolver::Result +QString MatrixUri::primaryId() const +{ + if (primaryType_ == Empty || primaryType_ == Invalid) + return {}; + + const auto& idStem = path().section('/', 1, 1); + return idStem.isEmpty() ? idStem : primaryType_ + idStem; +} + +QString MatrixUri::secondaryId() const +{ + const auto& idStem = path().section('/', 3); + return idStem.isEmpty() ? idStem : secondaryType() + idStem; +} + +QString MatrixUri::action() const +{ + return QUrlQuery { query() }.queryItemValue("action"); +} + +QStringList MatrixUri::viaServers() const +{ + return QUrlQuery { query() }.allQueryItemValues(QStringLiteral("via"), + QUrl::EncodeReserved); +} + +bool MatrixUri::isValid() const +{ + return primaryType_ != Empty && primaryType_ != Invalid; +} + +UriResolveResult Quotient::visitResource( + Connection* account, const MatrixUri& uri, + std::function userHandler, + std::function roomEventHandler, + std::function joinHandler) +{ + Q_ASSERT_X(account != nullptr, __FUNCTION__, + "The Connection argument passed to visit/openResource must not " + "be nullptr"); + if (uri.action() == "join") { + if (uri.type() != MatrixUri::RoomAlias + && uri.type() != MatrixUri::RoomId) + return MalformedUri; + + joinHandler(account, uri.primaryId(), uri.viaServers()); + return UriResolved; + } + + Room* room = nullptr; + switch (uri.type()) { + case MatrixUri::Invalid: + return MalformedUri; + case MatrixUri::Empty: + return EmptyMatrixId; + case MatrixUri::UserId: + if (auto* user = account->user(uri.primaryId())) { + userHandler(user); + return UriResolved; + } + return MalformedUri; + case MatrixUri::RoomId: + if ((room = account->room(uri.primaryId()))) + break; + return UnknownMatrixId; + case MatrixUri::RoomAlias: + if ((room = account->roomByAlias(uri.primaryId()))) + break; + [[fallthrough]]; + default: + return UnknownMatrixId; + } + roomEventHandler(room, uri.secondaryId()); + return UriResolved; +} + +UriResolveResult ResourceResolver::openResource(Connection* account, const QString& identifier, const QString& action) { - return visitResource(account, identifier, - [this, &action](User* u) { emit userAction(u, action); }, - [this, &action](Room* room, const QString& eventId) { - emit roomAction(room, eventId, action); + return openResource(account, MatrixUri(identifier), action); +} + +UriResolveResult ResourceResolver::openResource(Connection* account, + const MatrixUri& uri, + const QString& overrideAction) +{ + return visitResource( + account, uri, + [this, &overrideAction](User* u) { emit userAction(u, overrideAction); }, + [this, &overrideAction](Room* room, const QString& eventId) { + emit roomAction(room, eventId, overrideAction); + }, + [this](Connection* account, const QString& roomAliasOrId, + const QStringList& viaServers) { + emit joinAction(account, roomAliasOrId, viaServers); }); } diff --git a/lib/resourceresolver.h b/lib/resourceresolver.h index 794b7796..fea07e97 100644 --- a/lib/resourceresolver.h +++ b/lib/resourceresolver.h @@ -1,10 +1,118 @@ #pragma once -#include "connection.h" +#include "quotient_common.h" + +#include +#include +#include #include namespace Quotient { +class Connection; +class Room; +class User; + +/*! \brief A wrapper around a Matrix URI or identifier + * + * This class encapsulates a Matrix resource identifier, passed in either of + * 3 forms: a plain Matrix identifier (sigil, localpart, serverpart or, for + * modern event ids, sigil and base64 hash); an MSC2312 URI (aka matrix: URI); + * or a matrix.to URL. The input can be either encoded (serverparts with + * punycode, the rest with percent-encoding) or unencoded (in this case it is + * the caller's responsibility to resolve all possible ambiguities). + * + * The class provides functions to check the validity of the identifier, + * its type, and obtain components, also in either unencoded (for displaying) + * or encoded (for APIs) form. + */ +class MatrixUri : private QUrl { + Q_GADGET + Q_PROPERTY(QString primaryId READ primaryId CONSTANT) + Q_PROPERTY(QString secondaryId READ secondaryId CONSTANT) +// Q_PROPERTY(QUrlQuery query READ query CONSTANT) + Q_PROPERTY(QString action READ action CONSTANT) +// Q_PROPERTY(QStringList viaServers READ viaServers CONSTANT) +public: + enum Type : char { + Invalid = char(-1), + Empty = 0x0, + UserId = '@', + RoomId = '!', + RoomAlias = '#', + Group = '+' + }; + Q_ENUM(Type) + enum SecondaryType : char { + NoSecondaryId = 0x0, + EventId = '$' + }; + + enum UriForm : short { CanonicalUri, MatrixToUri }; + Q_ENUM(UriForm) + + /// Construct an empty Matrix URI + MatrixUri() = default; + /*! \brief Decode a user input string to a Matrix identifier + * + * Accepts plain Matrix ids, MSC2312 URIs (aka matrix: URIs) and + * matrix.to URLs. In case of URIs/URLs, it uses QUrl's TolerantMode + * parser to decode common mistakes/irregularities (see QUrl documentation + * for more details). + */ + MatrixUri(const QString& uriOrId); + + /// Construct a Matrix URI from components + explicit MatrixUri(QByteArray primaryId, QByteArray secondaryId = {}, + QString query = {}); + /// Construct a Matrix URI from matrix.to or MSC2312 (matrix:) URI + explicit MatrixUri(QUrl url); + + static MatrixUri fromUserInput(const QString& uriOrId); + static MatrixUri fromUrl(QUrl url); + + /// Get the primary type of the Matrix URI (user id, room id or alias) + /*! Note that this does not include an event as a separate type, since + * events can only be addressed inside of rooms, which, in turn, are + * addressed either by id or alias. If you need to check whether the URI + * is specifically an event URI, use secondaryType() instead. + */ + Type type() const; + SecondaryType secondaryType() const; + QUrl toUrl(UriForm form = CanonicalUri) const; + QString toDisplayString(UriForm form = CanonicalUri) const; + QString primaryId() const; + QString secondaryId() const; + QString action() const; + QStringList viaServers() const; + bool isValid() const; + using QUrl::isEmpty, QUrl::path, QUrl::query, QUrl::fragment; + +private: + + Type primaryType_ = Empty; +}; + +/*! \brief Resolve the resource and invoke an action on it, visitor style + * + * This template function encapsulates the logic of resolving a Matrix + * identifier or URI into a Quotient object (or objects) and applying an + * appropriate action handler from the set provided by the caller to it. + * A typical use case for that is opening a room or mentioning a user in + * response to clicking on a Matrix URI or identifier. + * + * \param account The connection used as a context to resolve the identifier + * + * \param uri The Matrix identifier or URI; MSC2312 URIs and classic Matrix IDs + * are supported + * + * \sa ResourceResolver + */ +UriResolveResult +visitResource(Connection* account, const MatrixUri& uri, + std::function userHandler, + std::function roomEventHandler, + std::function joinHandler); /*! \brief Matrix resource resolver * TODO: rewrite @@ -22,51 +130,9 @@ namespace Quotient { class ResourceResolver : public QObject { Q_OBJECT public: - enum Result : short { - StillResolving = -1, - Success = 0, - UnknownMatrixId, - MalformedMatrixId, - NoAccount, - EmptyMatrixId - }; - Q_ENUM(Result) - explicit ResourceResolver(QObject* parent = nullptr) : QObject(parent) { } - /*! \brief Decode a URI to a Matrix identifier (or a room/event pair) - * - * This accepts plain Matrix ids, MSC2312 URIs (aka matrix: URIs) and - * matrix.to URIs. - * - * \return a Matrix identifier as defined by the common identifier grammars - * or a slash separated pair of Matrix identifiers if the original - * uri/id pointed to an event in a room - */ - static QString toMatrixId(const QString& uriOrId, - QStringList uriServers = {}); - - /*! \brief Resolve the resource and invoke an action on it, visitor style - * - * This template function encapsulates the logic of resolving a Matrix - * identifier or URI into a Quotient object (or objects) and applying an - * appropriate action handler from the set provided by the caller to it. - * A typical use case for that is opening a room or mentioning a user in - * response to clicking on a Matrix URI or identifier. - * - * \param account The connection used as a context to resolve the identifier - * - * \param identifier The Matrix identifier or URI. MSC2312 URIs and classic - * Matrix ID scheme are supported. - * - * \sa ResourceResolver - */ - static Result - visitResource(Connection* account, const QString& identifier, - std::function userHandler, - std::function roomEventHandler); - /*! \brief Resolve the resource and request an action on it, signal style * * This method: @@ -91,9 +157,12 @@ public: * and also connect to ResourceFuture::ready() signal in order to process * the result of resolving and action. */ - Q_INVOKABLE Result openResource(Connection* account, - const QString& identifier, - const QString& action = {}); + Q_INVOKABLE UriResolveResult openResource(Connection* account, + const QString& identifier, + const QString& action = {}); + Q_INVOKABLE UriResolveResult + openResource(Connection* account, const MatrixUri& uri, + const QString& overrideAction = {}); signals: /// An action on a user has been requested @@ -102,14 +171,9 @@ signals: /// An action on a room has been requested, with optional event id void roomAction(Quotient::Room* room, QString eventId, QString action); -private: - struct IdentifierParts { - char sigil; - QString mainId {}; - QString secondaryId = {}; - }; - - static IdentifierParts parseIdentifier(const QString& identifier); + /// A join action has been requested, with optional 'via' servers + void joinAction(Quotient::Connection* account, QString roomAliasOrId, + QStringList viaServers); }; } // namespace Quotient -- cgit v1.2.3 From af329351289606f3cb1ef865cb0cbe61c1d1711b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 19 Jul 2020 16:12:09 +0200 Subject: MatrixUri->Uri: Extend to non-Matrix URIs --- lib/quotient_common.h | 8 +- lib/resourceresolver.cpp | 236 ----------------------------------------------- lib/resourceresolver.h | 181 ------------------------------------ lib/uri.cpp | 181 ++++++++++++++++++++++++++++++++++++ lib/uri.h | 85 +++++++++++++++++ lib/uriresolver.cpp | 110 ++++++++++++++++++++++ lib/uriresolver.h | 162 ++++++++++++++++++++++++++++++++ 7 files changed, 542 insertions(+), 421 deletions(-) delete mode 100644 lib/resourceresolver.cpp delete mode 100644 lib/resourceresolver.h create mode 100644 lib/uri.cpp create mode 100644 lib/uri.h create mode 100644 lib/uriresolver.cpp create mode 100644 lib/uriresolver.h (limited to 'lib') diff --git a/lib/quotient_common.h b/lib/quotient_common.h index 446f628b..bb05af05 100644 --- a/lib/quotient_common.h +++ b/lib/quotient_common.h @@ -17,10 +17,10 @@ Q_ENUM_NS(RunningPolicy) enum UriResolveResult : short { StillResolving = -1, UriResolved = 0, - UnknownMatrixId, - MalformedUri, - NoAccount, - EmptyMatrixId + CouldNotResolve, + IncorrectAction, + InvalidUri, + NoAccount }; Q_ENUM_NS(UriResolveResult) diff --git a/lib/resourceresolver.cpp b/lib/resourceresolver.cpp deleted file mode 100644 index e7820061..00000000 --- a/lib/resourceresolver.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "resourceresolver.h" - -#include "connection.h" -#include "logging.h" - -#include - -using namespace Quotient; - -struct ReplacePair { QByteArray uriString; char sigil; }; -static const auto replacePairs = { ReplacePair { "user/", '@' }, - { "roomid/", '!' }, - { "room/", '#' } }; - -MatrixUri::MatrixUri(QByteArray primaryId, QByteArray secondaryId, QString query) -{ - if (primaryId.isEmpty()) - primaryType_ = Empty; - else { - setScheme("matrix"); - QString pathToBe; - primaryType_ = Invalid; - for (const auto& p: replacePairs) - if (primaryId[0] == p.sigil) { - primaryType_ = Type(p.sigil); - pathToBe = p.uriString + primaryId.mid(1); - break; - } - if (!secondaryId.isEmpty()) - pathToBe += "/event/" + secondaryId.mid(1); - setPath(pathToBe); - } - setQuery(std::move(query)); -} - -MatrixUri::MatrixUri(QUrl url) : QUrl(std::move(url)) -{ - // NB: url is moved from and empty by now - if (isEmpty()) - return; // primaryType_ == None - - primaryType_ = Invalid; - if (!QUrl::isValid()) // MatrixUri::isValid() checks primaryType_ - return; - - if (scheme() == "matrix") { - // Check sanity as per https://github.com/matrix-org/matrix-doc/pull/2312 - const auto& urlPath = path(); - const auto& splitPath = urlPath.splitRef('/'); - switch (splitPath.size()) { - case 2: - break; - case 4: - if (splitPath[2] == "event") - break; - [[fallthrough]]; - default: - return; // Invalid - } - - for (const auto& p: replacePairs) - if (urlPath.startsWith(p.uriString)) { - primaryType_ = Type(p.sigil); - return; // The only valid return path for matrix: URIs - } - qCWarning(MAIN) << "Invalid matrix: URI passed to MatrixUri"; - } - if (scheme() == "https" && authority() == "matrix.to") { - // See https://matrix.org/docs/spec/appendices#matrix-to-navigation - static const QRegularExpression MatrixToUrlRE { - R"(^/(?
[^/?]+)(/(?[^?]+))?(\?(?.+))?$)" - }; - // matrix.to accepts both literal sigils (as well as & and ? used in - // its "query" substitute) and their %-encoded forms; - // so force QUrl to decode everything. - auto f = fragment(QUrl::FullyDecoded); - if (auto&& m = MatrixToUrlRE.match(f); m.hasMatch()) - *this = MatrixUri { m.captured("main").toUtf8(), - m.captured("sec").toUtf8(), - m.captured("query") }; - } -} - -MatrixUri::MatrixUri(const QString &uriOrId) - : MatrixUri(fromUserInput(uriOrId)) -{ } - -MatrixUri MatrixUri::fromUserInput(const QString& uriOrId) -{ - if (uriOrId.isEmpty()) - return {}; // type() == None - - // A quick check if uriOrId is a plain Matrix id - if (QStringLiteral("!@#+").contains(uriOrId[0])) - return MatrixUri { uriOrId.toUtf8() }; - - // Bare event ids cannot be resolved without a room scope but are treated as - // valid anyway; in the future we might expose them as, say, - // matrix:event/eventid - if (uriOrId[0] == '$') - return MatrixUri { "", uriOrId.toUtf8() }; - - return MatrixUri { QUrl::fromUserInput(uriOrId) }; -} - -MatrixUri::Type MatrixUri::type() const { return primaryType_; } - -MatrixUri::SecondaryType MatrixUri::secondaryType() const -{ - return path().section('/', 2, 2) == "event" ? EventId : NoSecondaryId; -} - -QUrl MatrixUri::toUrl(UriForm form) const -{ - if (!isValid()) - return {}; - - if (form == CanonicalUri) - return *this; - - QUrl url; - url.setScheme("https"); - url.setHost("matrix.to"); - url.setPath("/"); - auto fragment = primaryId(); - if (const auto& secId = secondaryId(); !secId.isEmpty()) - fragment += '/' + secId; - if (const auto& q = query(); !q.isEmpty()) - fragment += '?' + q; - url.setFragment(fragment); - return url; -} - -QString MatrixUri::toDisplayString(MatrixUri::UriForm form) const -{ - return toUrl(form).toDisplayString(); -} - -QString MatrixUri::primaryId() const -{ - if (primaryType_ == Empty || primaryType_ == Invalid) - return {}; - - const auto& idStem = path().section('/', 1, 1); - return idStem.isEmpty() ? idStem : primaryType_ + idStem; -} - -QString MatrixUri::secondaryId() const -{ - const auto& idStem = path().section('/', 3); - return idStem.isEmpty() ? idStem : secondaryType() + idStem; -} - -QString MatrixUri::action() const -{ - return QUrlQuery { query() }.queryItemValue("action"); -} - -QStringList MatrixUri::viaServers() const -{ - return QUrlQuery { query() }.allQueryItemValues(QStringLiteral("via"), - QUrl::EncodeReserved); -} - -bool MatrixUri::isValid() const -{ - return primaryType_ != Empty && primaryType_ != Invalid; -} - -UriResolveResult Quotient::visitResource( - Connection* account, const MatrixUri& uri, - std::function userHandler, - std::function roomEventHandler, - std::function joinHandler) -{ - Q_ASSERT_X(account != nullptr, __FUNCTION__, - "The Connection argument passed to visit/openResource must not " - "be nullptr"); - if (uri.action() == "join") { - if (uri.type() != MatrixUri::RoomAlias - && uri.type() != MatrixUri::RoomId) - return MalformedUri; - - joinHandler(account, uri.primaryId(), uri.viaServers()); - return UriResolved; - } - - Room* room = nullptr; - switch (uri.type()) { - case MatrixUri::Invalid: - return MalformedUri; - case MatrixUri::Empty: - return EmptyMatrixId; - case MatrixUri::UserId: - if (auto* user = account->user(uri.primaryId())) { - userHandler(user); - return UriResolved; - } - return MalformedUri; - case MatrixUri::RoomId: - if ((room = account->room(uri.primaryId()))) - break; - return UnknownMatrixId; - case MatrixUri::RoomAlias: - if ((room = account->roomByAlias(uri.primaryId()))) - break; - [[fallthrough]]; - default: - return UnknownMatrixId; - } - roomEventHandler(room, uri.secondaryId()); - return UriResolved; -} - -UriResolveResult -ResourceResolver::openResource(Connection* account, const QString& identifier, - const QString& action) -{ - return openResource(account, MatrixUri(identifier), action); -} - -UriResolveResult ResourceResolver::openResource(Connection* account, - const MatrixUri& uri, - const QString& overrideAction) -{ - return visitResource( - account, uri, - [this, &overrideAction](User* u) { emit userAction(u, overrideAction); }, - [this, &overrideAction](Room* room, const QString& eventId) { - emit roomAction(room, eventId, overrideAction); - }, - [this](Connection* account, const QString& roomAliasOrId, - const QStringList& viaServers) { - emit joinAction(account, roomAliasOrId, viaServers); - }); -} diff --git a/lib/resourceresolver.h b/lib/resourceresolver.h deleted file mode 100644 index fea07e97..00000000 --- a/lib/resourceresolver.h +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once - -#include "quotient_common.h" - -#include -#include -#include - -#include - -namespace Quotient { -class Connection; -class Room; -class User; - -/*! \brief A wrapper around a Matrix URI or identifier - * - * This class encapsulates a Matrix resource identifier, passed in either of - * 3 forms: a plain Matrix identifier (sigil, localpart, serverpart or, for - * modern event ids, sigil and base64 hash); an MSC2312 URI (aka matrix: URI); - * or a matrix.to URL. The input can be either encoded (serverparts with - * punycode, the rest with percent-encoding) or unencoded (in this case it is - * the caller's responsibility to resolve all possible ambiguities). - * - * The class provides functions to check the validity of the identifier, - * its type, and obtain components, also in either unencoded (for displaying) - * or encoded (for APIs) form. - */ -class MatrixUri : private QUrl { - Q_GADGET - Q_PROPERTY(QString primaryId READ primaryId CONSTANT) - Q_PROPERTY(QString secondaryId READ secondaryId CONSTANT) -// Q_PROPERTY(QUrlQuery query READ query CONSTANT) - Q_PROPERTY(QString action READ action CONSTANT) -// Q_PROPERTY(QStringList viaServers READ viaServers CONSTANT) -public: - enum Type : char { - Invalid = char(-1), - Empty = 0x0, - UserId = '@', - RoomId = '!', - RoomAlias = '#', - Group = '+' - }; - Q_ENUM(Type) - enum SecondaryType : char { - NoSecondaryId = 0x0, - EventId = '$' - }; - - enum UriForm : short { CanonicalUri, MatrixToUri }; - Q_ENUM(UriForm) - - /// Construct an empty Matrix URI - MatrixUri() = default; - /*! \brief Decode a user input string to a Matrix identifier - * - * Accepts plain Matrix ids, MSC2312 URIs (aka matrix: URIs) and - * matrix.to URLs. In case of URIs/URLs, it uses QUrl's TolerantMode - * parser to decode common mistakes/irregularities (see QUrl documentation - * for more details). - */ - MatrixUri(const QString& uriOrId); - - /// Construct a Matrix URI from components - explicit MatrixUri(QByteArray primaryId, QByteArray secondaryId = {}, - QString query = {}); - /// Construct a Matrix URI from matrix.to or MSC2312 (matrix:) URI - explicit MatrixUri(QUrl url); - - static MatrixUri fromUserInput(const QString& uriOrId); - static MatrixUri fromUrl(QUrl url); - - /// Get the primary type of the Matrix URI (user id, room id or alias) - /*! Note that this does not include an event as a separate type, since - * events can only be addressed inside of rooms, which, in turn, are - * addressed either by id or alias. If you need to check whether the URI - * is specifically an event URI, use secondaryType() instead. - */ - Type type() const; - SecondaryType secondaryType() const; - QUrl toUrl(UriForm form = CanonicalUri) const; - QString toDisplayString(UriForm form = CanonicalUri) const; - QString primaryId() const; - QString secondaryId() const; - QString action() const; - QStringList viaServers() const; - bool isValid() const; - using QUrl::isEmpty, QUrl::path, QUrl::query, QUrl::fragment; - -private: - - Type primaryType_ = Empty; -}; - -/*! \brief Resolve the resource and invoke an action on it, visitor style - * - * This template function encapsulates the logic of resolving a Matrix - * identifier or URI into a Quotient object (or objects) and applying an - * appropriate action handler from the set provided by the caller to it. - * A typical use case for that is opening a room or mentioning a user in - * response to clicking on a Matrix URI or identifier. - * - * \param account The connection used as a context to resolve the identifier - * - * \param uri The Matrix identifier or URI; MSC2312 URIs and classic Matrix IDs - * are supported - * - * \sa ResourceResolver - */ -UriResolveResult -visitResource(Connection* account, const MatrixUri& uri, - std::function userHandler, - std::function roomEventHandler, - std::function joinHandler); - -/*! \brief Matrix resource resolver - * TODO: rewrite - * Similar to visitResource(), this class encapsulates the logic of resolving - * a Matrix identifier or a URI into Quotient object(s) and applying an action - * to the resolved object(s). Instead of using a C++ visitor pattern, it - * announces the request through Qt's signals passing the resolved object(s) - * through those (still in a typesafe way). - * - * This class is aimed primarily at clients where invoking the resolving/action - * and handling the action are happening in decoupled parts of the code; it's - * also useful to operate on Matrix identifiers and URIs from QML/JS code - * that cannot call visitResource due to QML/C++ interface limitations. - */ -class ResourceResolver : public QObject { - Q_OBJECT -public: - explicit ResourceResolver(QObject* parent = nullptr) : QObject(parent) - { } - - /*! \brief Resolve the resource and request an action on it, signal style - * - * This method: - * 1. Resolves \p identifier into an actual object (Room or User), with - * possible additional data such as event id, in the context of - * \p account. - * 2. If the resolving is successful, depending on the type of the object, - * emits the respective signal to which the client must connect in order - * to apply the action to the resource (open a room, mention a user etc.). - * 3. Returns the result of resolving the resource. - * - * Note that the action can be applied either synchronously or entirely - * asynchronously; ResourceResolver does not restrain the client code - * to use either method. The resource resolving part is entirely synchronous - * though. If the synchronous operation is chosen, only - * direct connections to ResourceResolver signals must be used, and - * the caller should check the future's state immediately after calling - * openResource() to process any feedback from the resolver and/or action - * handler. If asynchronous operation is needed then either direct or queued - * connections to ResourceResolver's signals can be used and the caller - * must both check the ResourceFuture state right after calling openResource - * and also connect to ResourceFuture::ready() signal in order to process - * the result of resolving and action. - */ - Q_INVOKABLE UriResolveResult openResource(Connection* account, - const QString& identifier, - const QString& action = {}); - Q_INVOKABLE UriResolveResult - openResource(Connection* account, const MatrixUri& uri, - const QString& overrideAction = {}); - -signals: - /// An action on a user has been requested - void userAction(Quotient::User* user, QString action); - - /// An action on a room has been requested, with optional event id - void roomAction(Quotient::Room* room, QString eventId, QString action); - - /// A join action has been requested, with optional 'via' servers - void joinAction(Quotient::Connection* account, QString roomAliasOrId, - QStringList viaServers); -}; - -} // namespace Quotient - - diff --git a/lib/uri.cpp b/lib/uri.cpp new file mode 100644 index 00000000..e81933dc --- /dev/null +++ b/lib/uri.cpp @@ -0,0 +1,181 @@ +#include "uri.h" + +#include "logging.h" + +#include + +using namespace Quotient; + +struct ReplacePair { QByteArray uriString; char sigil; }; +static const auto replacePairs = { ReplacePair { "user/", '@' }, + { "roomid/", '!' }, + { "room/", '#' }, + // The notation for bare event ids is not + // proposed in MSC2312 (and anywhere, as yet) + { "event/", '$' } }; + +Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) +{ + if (primaryId.isEmpty()) + primaryType_ = Empty; + else { + setScheme("matrix"); + QString pathToBe; + primaryType_ = Invalid; + for (const auto& p: replacePairs) + if (primaryId[0] == p.sigil) { + primaryType_ = Type(p.sigil); + pathToBe = p.uriString + primaryId.mid(1); + break; + } + if (!secondaryId.isEmpty()) + pathToBe += "/event/" + secondaryId.mid(1); + setPath(pathToBe); + } + setQuery(std::move(query)); +} + +Uri::Uri(QUrl url) : QUrl(std::move(url)) +{ + // NB: don't try to use `url` from here on, it's moved-from and empty + if (isEmpty()) + return; // primaryType_ == Empty + + if (!QUrl::isValid()) { // MatrixUri::isValid() checks primaryType_ + primaryType_ = Invalid; + return; + } + + if (scheme() == "matrix") { + // Check sanity as per https://github.com/matrix-org/matrix-doc/pull/2312 + const auto& urlPath = path(); + const auto& splitPath = urlPath.splitRef('/'); + switch (splitPath.size()) { + case 2: + break; + case 4: + if (splitPath[2] == "event") + break; + [[fallthrough]]; + default: + return; // Invalid + } + + for (const auto& p: replacePairs) + if (urlPath.startsWith(p.uriString)) { + primaryType_ = Type(p.sigil); + return; // The only valid return path for matrix: URIs + } + qCDebug(MAIN) << "The matrix: URI is not recognised:" + << toDisplayString(); + return; + } + + primaryType_ = NonMatrix; // Default, unless overridden by the code below + if (scheme() == "https" && authority() == "matrix.to") { + // See https://matrix.org/docs/spec/appendices#matrix-to-navigation + static const QRegularExpression MatrixToUrlRE { + R"(^/(?
[^/?]+)(/(?[^?]+))?(\?(?.+))?$)" + }; + // matrix.to accepts both literal sigils (as well as & and ? used in + // its "query" substitute) and their %-encoded forms; + // so force QUrl to decode everything. + auto f = fragment(QUrl::FullyDecoded); + if (auto&& m = MatrixToUrlRE.match(f); m.hasMatch()) + *this = Uri { m.captured("main").toUtf8(), + m.captured("sec").toUtf8(), m.captured("query") }; + } +} + +Uri::Uri(const QString& uriOrId) : Uri(fromUserInput(uriOrId)) {} + +Uri Uri::fromUserInput(const QString& uriOrId) +{ + if (uriOrId.isEmpty()) + return {}; // type() == None + + // A quick check if uriOrId is a plain Matrix id + // Bare event ids cannot be resolved without a room scope as per the current + // spec but there's a movement towards making them navigable (see, e.g., + // https://github.com/matrix-org/matrix-doc/pull/2644) - so treat them + // as valid + if (QStringLiteral("!@#+$").contains(uriOrId[0])) + return Uri { uriOrId.toUtf8() }; + + return Uri { QUrl::fromUserInput(uriOrId) }; +} + +Uri::Type Uri::type() const { return primaryType_; } + +Uri::SecondaryType Uri::secondaryType() const +{ + return path().section('/', 2, 2) == "event" ? EventId : NoSecondaryId; +} + +QUrl Uri::toUrl(UriForm form) const +{ + if (!isValid()) + return {}; + + if (form == CanonicalUri || type() == NonMatrix) + return *this; + + QUrl url; + url.setScheme("https"); + url.setHost("matrix.to"); + url.setPath("/"); + auto fragment = primaryId(); + if (const auto& secId = secondaryId(); !secId.isEmpty()) + fragment += '/' + secId; + if (const auto& q = query(); !q.isEmpty()) + fragment += '?' + q; + url.setFragment(fragment); + return url; +} + +QString Uri::primaryId() const +{ + if (primaryType_ == Empty || primaryType_ == Invalid) + return {}; + + const auto& idStem = path().section('/', 1, 1); + return idStem.isEmpty() ? idStem : primaryType_ + idStem; +} + +QString Uri::secondaryId() const +{ + const auto& idStem = path().section('/', 3); + return idStem.isEmpty() ? idStem : secondaryType() + idStem; +} + +static const auto ActionKey = QStringLiteral("action"); + +QString Uri::action() const +{ + return type() == NonMatrix || !isValid() + ? QString() + : QUrlQuery { query() }.queryItemValue(ActionKey); +} + +void Uri::setAction(const QString& newAction) +{ + if (!isValid()) { + qCWarning(MAIN) << "Cannot set an action on an invalid Quotient::Uri"; + return; + } + QUrlQuery q { query() }; + q.removeQueryItem(ActionKey); + q.addQueryItem(ActionKey, newAction); + setQuery(q); +} + +QStringList Uri::viaServers() const +{ + return QUrlQuery { query() }.allQueryItemValues(QStringLiteral("via"), + QUrl::EncodeReserved); +} + +bool Uri::isValid() const +{ + return primaryType_ != Empty && primaryType_ != Invalid; +} diff --git a/lib/uri.h b/lib/uri.h new file mode 100644 index 00000000..270766dd --- /dev/null +++ b/lib/uri.h @@ -0,0 +1,85 @@ +#pragma once + +#include "quotient_common.h" + +#include +#include + +namespace Quotient { + +/*! \brief A wrapper around a Matrix URI or identifier + * + * This class encapsulates a Matrix resource identifier, passed in either of + * 3 forms: a plain Matrix identifier (sigil, localpart, serverpart or, for + * modern event ids, sigil and base64 hash); an MSC2312 URI (aka matrix: URI); + * or a matrix.to URL. The input can be either encoded (serverparts with + * punycode, the rest with percent-encoding) or unencoded (in this case it is + * the caller's responsibility to resolve all possible ambiguities). + * + * The class provides functions to check the validity of the identifier, + * its type, and obtain components, also in either unencoded (for displaying) + * or encoded (for APIs) form. + */ +class Uri : private QUrl { + Q_GADGET +public: + enum Type : char { + Invalid = char(-1), + Empty = 0x0, + UserId = '@', + RoomId = '!', + RoomAlias = '#', + Group = '+', + BareEventId = '$', // https://github.com/matrix-org/matrix-doc/pull/2644 + NonMatrix = ':' + }; + Q_ENUM(Type) + enum SecondaryType : char { NoSecondaryId = 0x0, EventId = '$' }; + Q_ENUM(SecondaryType) + + enum UriForm : short { CanonicalUri, MatrixToUri }; + Q_ENUM(UriForm) + + /// Construct an empty Matrix URI + Uri() = default; + /*! \brief Decode a user input string to a Matrix identifier + * + * Accepts plain Matrix ids, MSC2312 URIs (aka matrix: URIs) and + * matrix.to URLs. In case of URIs/URLs, it uses QUrl's TolerantMode + * parser to decode common mistakes/irregularities (see QUrl documentation + * for more details). + */ + Uri(const QString& uriOrId); + + /// Construct a Matrix URI from components + explicit Uri(QByteArray primaryId, QByteArray secondaryId = {}, + QString query = {}); + /// Construct a Matrix URI from matrix.to or MSC2312 (matrix:) URI + explicit Uri(QUrl url); + + static Uri fromUserInput(const QString& uriOrId); + static Uri fromUrl(QUrl url); + + /// Get the primary type of the Matrix URI (user id, room id or alias) + /*! Note that this does not include an event as a separate type, since + * events can only be addressed inside of rooms, which, in turn, are + * addressed either by id or alias. If you need to check whether the URI + * is specifically an event URI, use secondaryType() instead. + */ + Q_INVOKABLE Type type() const; + Q_INVOKABLE SecondaryType secondaryType() const; + Q_INVOKABLE QUrl toUrl(UriForm form = CanonicalUri) const; + Q_INVOKABLE QString primaryId() const; + Q_INVOKABLE QString secondaryId() const; + Q_INVOKABLE QString action() const; + Q_INVOKABLE void setAction(const QString& newAction); + Q_INVOKABLE QStringList viaServers() const; + Q_INVOKABLE bool isValid() const; + using QUrl::path, QUrl::query, QUrl::fragment; + using QUrl::isEmpty, QUrl::toDisplayString; + +private: + Type primaryType_ = Empty; +}; + +} diff --git a/lib/uriresolver.cpp b/lib/uriresolver.cpp new file mode 100644 index 00000000..5052890b --- /dev/null +++ b/lib/uriresolver.cpp @@ -0,0 +1,110 @@ +#include "uriresolver.h" + +#include "connection.h" +#include "user.h" + +using namespace Quotient; + +UriResolveResult UriResolverBase::visitResource(Connection* account, + const Uri& uri) +{ + switch (uri.type()) { + case Uri::NonMatrix: + return visitNonMatrix(uri.toUrl()) ? UriResolved : CouldNotResolve; + case Uri::Invalid: + case Uri::Empty: + return InvalidUri; + default:; + } + + if (!account) + return NoAccount; + + switch (uri.type()) { + case Uri::UserId: { + if (uri.action() == "join") + return IncorrectAction; + auto* user = account->user(uri.primaryId()); + Q_ASSERT(user != nullptr); + visitUser(user, uri.action()); + return UriResolved; + } + case Uri::RoomId: + case Uri::RoomAlias: { + auto* room = uri.type() == Uri::RoomId + ? account->room(uri.primaryId()) + : account->roomByAlias(uri.primaryId()); + if (room != nullptr) { + visitRoom(room, uri.secondaryId()); + return UriResolved; + } + if (uri.action() == "join") { + joinRoom(account, uri.primaryId(), uri.viaServers()); + return UriResolved; + } + [[fallthrough]]; + } + default: + return CouldNotResolve; + } +} + +template +class StaticUriDispatcher : public UriResolverBase { +public: + StaticUriDispatcher(const FnTs&... fns) : fns_(fns...) {} + +private: + void visitUser(User* user, const QString& action) override + { + std::get<0>(fns_)(user, action); + } + void visitRoom(Room* room, const QString& eventId) override + { + std::get<1>(fns_)(room, eventId); + } + void joinRoom(Connection* account, const QString& roomAliasOrId, + const QStringList& viaServers = {}) override + { + std::get<2>(fns_)(account, roomAliasOrId, viaServers); + } + bool visitNonMatrix(const QUrl& url) override + { + return std::get<3>(fns_)(url); + } + + std::tuple fns_; +}; + +UriResolveResult Quotient::visitResource( + Connection* account, const Uri& uri, + std::function userHandler, + std::function roomEventHandler, + std::function joinHandler, + std::function nonMatrixHandler) +{ + return StaticUriDispatcher(userHandler, roomEventHandler, joinHandler, + nonMatrixHandler) + .visitResource(account, uri); +} + +void UriDispatcher::visitUser(User *user, const QString &action) +{ + emit userAction(user, action); +} + +void UriDispatcher::visitRoom(Room *room, const QString &eventId) +{ + emit roomAction(room, eventId); +} + +void UriDispatcher::joinRoom(Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) +{ + emit joinAction(account, roomAliasOrId, viaServers); +} + +bool UriDispatcher::visitNonMatrix(const QUrl &url) +{ + emit nonMatrixAction(url); + return true; +} diff --git a/lib/uriresolver.h b/lib/uriresolver.h new file mode 100644 index 00000000..914ddf02 --- /dev/null +++ b/lib/uriresolver.h @@ -0,0 +1,162 @@ +#pragma once + +#include "uri.h" + +#include + +#include + +namespace Quotient { +class Connection; +class Room; +class User; + +/*! \brief Abstract class to resolve the resource and act on it + * + * This class encapsulates the logic of resolving a Matrix identifier or URI + * into a Quotient object (or objects) and calling an appropriate handler on it. + * It is a type-safe way of handling a URI with no prior context on its type + * in cases like, e.g., when a user clicks on a URI in the application. + * + * This class provides empty "handlers" for each type of URI to facilitate + * gradual implementation. Derived classes are encouraged to override as many + * of them as possible. + */ +class UriResolverBase { +public: + /*! \brief Resolve the resource and dispatch an action depending on its type + * + * This method: + * 1. Resolves \p uri into an actual object (e.g., Room or User), + * with possible additional data such as event id, in the context of + * \p account. + * 2. If the resolving is successful, depending on the type of the object, + * calls the appropriate virtual function (defined in a derived + * concrete class) to perform an action on the resource (open a room, + * mention a user etc.). + * 3. Returns the result of resolving the resource. + */ + UriResolveResult visitResource(Connection* account, const Uri& uri); + +protected: + /// Called by visitResource() when the passed URI identifies a Matrix user + virtual void visitUser(User* user, const QString& action) {} + /// Called by visitResource() when the passed URI identifies a room or + /// an event in a room + virtual void visitRoom(Room* room, const QString& eventId) {} + /// Called by visitResource() when the passed URI has `action() == "join"` + /// and identifies a room that the user defined by the Connection argument + /// is not a member of + virtual void joinRoom(Connection* account, const QString& roomAliasOrId, + const QStringList& viaServers = {}) + {} + /// Called by visitResource() when the passed URI has `type() == NonMatrix` + /*! + * Should return true if the URI is considered resolved, false otherwise. + * A basic implementation in a graphical client can look like + * `return QDesktopServices::openUrl(url);` but it's strongly advised to + * ask for a user confirmation beforehand. + */ + virtual bool visitNonMatrix(const QUrl& url) { return false; } +}; + +/*! \brief Resolve the resource and invoke an action on it, via function objects + * + * This function encapsulates the logic of resolving a Matrix identifier or URI + * into a Quotient object (or objects) and calling an appropriate handler on it. + * Unlike UriResolverBase it accepts the list of handlers from + * the caller; internally it's uses a minimal UriResolverBase class + * + * \param account The connection used as a context to resolve the identifier + * + * \param uri A URI that can represent a Matrix entity + * + * \param userHandler Called when the passed URI identifies a Matrix user + * + * \param roomEventHandler Called when the passed URI identifies a room or + * an event in a room + * + * \param joinHandler Called when the passed URI has `action() == "join"` and + * identifies a room that the user defined by + * the Connection argument is not a member of + * + * \param nonMatrixHandler Called when the passed URI has `type() == NonMatrix`; + * should return true if the URI is considered resolved, + * false otherwise + * + * \sa UriResolverBase, UriDispatcher + */ +UriResolveResult +visitResource(Connection* account, const Uri& uri, + std::function userHandler, + std::function roomEventHandler, + std::function joinHandler, + std::function nonMatrixHandler); + +/*! \brief Check that the resource is resolvable with no action on it */ +inline UriResolveResult checkResource(Connection* account, + const Uri& uri) +{ + return visitResource( + account, uri, [](auto, auto) {}, [](auto, auto) {}, + [](auto, auto, auto) {}, [](auto) { return false; }); +} + +/*! \brief Resolve the resource and invoke an action on it, via Qt signals + * + * This is an implementation of UriResolverBase that is based on + * QObject and uses Qt signals instead of virtual functions to provide an + * open-ended interface for visitors. + * + * This class is aimed primarily at clients where invoking the resolving/action + * and handling the action are happening in decoupled parts of the code; it's + * also useful to operate on Matrix identifiers and URIs from QML/JS code + * that cannot call resolveResource() due to QML/C++ interface limitations. + * + * This class does not restrain the client code to a certain type of + * connections: both direct and queued (or a mix) will work fine. One limitation + * caused by that is there's no way to indicate if a non-Matrix URI has been + * successfully resolved - a signal always returns void. + * + * Note that in case of using (non-blocking) queued connections the code that + * calls resolveResource() should not expect the action to be performed + * synchronously - the returned value is the result of resolving the URI, + * not acting on it. + */ +class UriDispatcher : public QObject, public UriResolverBase { + Q_OBJECT +public: + explicit UriDispatcher(QObject* parent = nullptr) : QObject(parent) {} + + // It's actually UriResolverBase::visitResource() but with Q_INVOKABLE + Q_INVOKABLE UriResolveResult resolveResource(Connection* account, + const Uri& uri) + { + return UriResolverBase::visitResource(account, uri); + } + +signals: + /// An action on a user has been requested + void userAction(Quotient::User* user, QString action); + + /// An action on a room has been requested, with optional event id + void roomAction(Quotient::Room* room, QString eventId); + + /// A join action has been requested, with optional 'via' servers + void joinAction(Quotient::Connection* account, QString roomAliasOrId, + QStringList viaServers); + + /// An action on a non-Matrix URL has been requested + void nonMatrixAction(QUrl url); + +private: + void visitUser(User* user, const QString& action) override; + void visitRoom(Room* room, const QString& eventId) override; + void joinRoom(Connection* account, const QString& roomAliasOrId, + const QStringList& viaServers = {}) override; + bool visitNonMatrix(const QUrl& url) override; +}; + +} // namespace Quotient + + -- cgit v1.2.3 From 1529f46e6dd457d059fb7e6e9cd10fa0b0399553 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 20 Jul 2020 19:33:19 +0200 Subject: UriResolverBase::visitUser(): add return value To enable reporting when the action is incorrect. --- lib/uriresolver.cpp | 18 ++++++++++-------- lib/uriresolver.h | 18 ++++++++++++------ 2 files changed, 22 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/uriresolver.cpp b/lib/uriresolver.cpp index 5052890b..ec30512c 100644 --- a/lib/uriresolver.cpp +++ b/lib/uriresolver.cpp @@ -6,7 +6,7 @@ using namespace Quotient; UriResolveResult UriResolverBase::visitResource(Connection* account, - const Uri& uri) + const Uri& uri) { switch (uri.type()) { case Uri::NonMatrix: @@ -26,8 +26,7 @@ UriResolveResult UriResolverBase::visitResource(Connection* account, return IncorrectAction; auto* user = account->user(uri.primaryId()); Q_ASSERT(user != nullptr); - visitUser(user, uri.action()); - return UriResolved; + return visitUser(user, uri.action()); } case Uri::RoomId: case Uri::RoomAlias: { @@ -49,15 +48,16 @@ UriResolveResult UriResolverBase::visitResource(Connection* account, } } +// This template is only instantiated once, for Quotient::visitResource() template class StaticUriDispatcher : public UriResolverBase { public: StaticUriDispatcher(const FnTs&... fns) : fns_(fns...) {} private: - void visitUser(User* user, const QString& action) override + UriResolveResult visitUser(User* user, const QString& action) override { - std::get<0>(fns_)(user, action); + return std::get<0>(fns_)(user, action); } void visitRoom(Room* room, const QString& eventId) override { @@ -78,7 +78,7 @@ private: UriResolveResult Quotient::visitResource( Connection* account, const Uri& uri, - std::function userHandler, + std::function userHandler, std::function roomEventHandler, std::function joinHandler, std::function nonMatrixHandler) @@ -88,9 +88,10 @@ UriResolveResult Quotient::visitResource( .visitResource(account, uri); } -void UriDispatcher::visitUser(User *user, const QString &action) +UriResolveResult UriDispatcher::visitUser(User *user, const QString &action) { emit userAction(user, action); + return UriResolved; } void UriDispatcher::visitRoom(Room *room, const QString &eventId) @@ -98,7 +99,8 @@ void UriDispatcher::visitRoom(Room *room, const QString &eventId) emit roomAction(room, eventId); } -void UriDispatcher::joinRoom(Connection *account, const QString &roomAliasOrId, const QStringList &viaServers) +void UriDispatcher::joinRoom(Connection* account, const QString& roomAliasOrId, + const QStringList& viaServers) { emit joinAction(account, roomAliasOrId, viaServers); } diff --git a/lib/uriresolver.h b/lib/uriresolver.h index 914ddf02..9b2ced9d 100644 --- a/lib/uriresolver.h +++ b/lib/uriresolver.h @@ -40,7 +40,14 @@ public: protected: /// Called by visitResource() when the passed URI identifies a Matrix user - virtual void visitUser(User* user, const QString& action) {} + /*! + * \return IncorrectAction if the action is not correct or not supported; + * UriResolved if it is accepted; other values are disallowed + */ + virtual UriResolveResult visitUser(User* user, const QString& action) + { + return IncorrectAction; + } /// Called by visitResource() when the passed URI identifies a room or /// an event in a room virtual void visitRoom(Room* room, const QString& eventId) {} @@ -88,17 +95,16 @@ protected: */ UriResolveResult visitResource(Connection* account, const Uri& uri, - std::function userHandler, + std::function userHandler, std::function roomEventHandler, std::function joinHandler, std::function nonMatrixHandler); /*! \brief Check that the resource is resolvable with no action on it */ -inline UriResolveResult checkResource(Connection* account, - const Uri& uri) +inline UriResolveResult checkResource(Connection* account, const Uri& uri) { return visitResource( - account, uri, [](auto, auto) {}, [](auto, auto) {}, + account, uri, [](auto, auto) { return UriResolved; }, [](auto, auto) {}, [](auto, auto, auto) {}, [](auto) { return false; }); } @@ -150,7 +156,7 @@ signals: void nonMatrixAction(QUrl url); private: - void visitUser(User* user, const QString& action) override; + UriResolveResult visitUser(User* user, const QString& action) override; void visitRoom(Room* room, const QString& eventId) override; void joinRoom(Connection* account, const QString& roomAliasOrId, const QStringList& viaServers = {}) override; -- cgit v1.2.3 From c94dc1104e1eb5e0537fcd9429626988524c8a98 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 21 Jul 2020 11:31:06 +0200 Subject: converters.*: clean away old cruft --- lib/converters.cpp | 20 ++++---------------- lib/converters.h | 28 ++-------------------------- 2 files changed, 6 insertions(+), 42 deletions(-) (limited to 'lib') diff --git a/lib/converters.cpp b/lib/converters.cpp index 9f4b9360..e5236bb9 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -32,24 +32,12 @@ QVariant JsonConverter::load(const QJsonValue& jv) return jv.toVariant(); } -QJsonObject JsonConverter::dump(const variant_map_t& map) +QJsonObject JsonConverter::dump(const QVariantHash& map) { - return -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QJsonObject::fromVariantHash -#else - QJsonObject::fromVariantMap -#endif - (map); + return QJsonObject::fromVariantHash(map); } -variant_map_t JsonConverter::load(const QJsonValue& jv) +QVariantHash JsonConverter::load(const QJsonValue& jv) { - return jv.toObject(). -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - toVariantHash -#else - toVariantMap -#endif - (); + return jv.toObject().toVariantHash(); } diff --git a/lib/converters.h b/lib/converters.h index 075af7ef..543e9496 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -30,21 +30,6 @@ #include -#if QT_VERSION < QT_VERSION_CHECK(5,14,0) -// Enable std::unordered_map -// REMOVEME in favor of UnorderedMap, once we regenerate API files -namespace std { -template <> -struct hash { - size_t operator()(const QString& s) const Q_DECL_NOEXCEPT - { - return qHash(s, uint(qGlobalQHashSeed()) - ); - } -}; -} // namespace std -#endif - class QVariant; namespace Quotient { @@ -296,18 +281,9 @@ template struct JsonObjectConverter> : public HashMapFromJson> {}; -// We could use std::conditional<> below but QT_VERSION* macros in C++ code -// cause (kinda valid but useless and noisy) compiler warnings about -// bitwise operations on signed integers; so use the preprocessor for now. -using variant_map_t = -#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) - QVariantHash; -#else - QVariantMap; -#endif template <> -struct JsonConverter { - static QJsonObject dump(const variant_map_t& vh); +struct JsonConverter { + static QJsonObject dump(const QVariantHash& vh); static QVariantHash load(const QJsonValue& jv); }; -- cgit v1.2.3 From 9f9577ccdebad84faf96766f8e5b07e2f2b605c5 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 21 Jul 2020 18:24:45 +0200 Subject: Connection: tolerate an empty set of room versions Closes #314. --- lib/connection.cpp | 72 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 38 insertions(+), 34 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index b702c1f9..46494a56 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -357,23 +357,24 @@ void Connection::assumeIdentity(const QString& userId, void Connection::reloadCapabilities() { d->capabilitiesJob = callApi(BackgroundRequest); - connect(d->capabilitiesJob, &BaseJob::finished, this, [this] { - if (d->capabilitiesJob->error() == BaseJob::Success) - d->capabilities = d->capabilitiesJob->capabilities(); - else if (d->capabilitiesJob->error() == BaseJob::IncorrectRequestError) - qCDebug(MAIN) << "Server doesn't support /capabilities"; - - if (!d->capabilities.roomVersions) { - qCWarning(MAIN) << "Pinning supported room version to 1"; - d->capabilities.roomVersions = { "1", { { "1", "stable" } } }; - } else { + connect(d->capabilitiesJob, &BaseJob::success, this, [this] { + d->capabilities = d->capabilitiesJob->capabilities(); + + if (d->capabilities.roomVersions) { qCDebug(MAIN) << "Room versions:" << defaultRoomVersion() << "is default, full list:" << availableRoomVersions(); - } - Q_ASSERT(d->capabilities.roomVersions.has_value()); - emit capabilitiesLoaded(); - for (auto* r : qAsConst(d->roomMap)) - r->checkVersion(); + emit capabilitiesLoaded(); + for (auto* r: std::as_const(d->roomMap)) + r->checkVersion(); + } else + qCWarning(MAIN) + << "The server returned an empty set of supported versions;" + " disabling version upgrade recommendations to reduce noise"; + }); + connect(d->capabilitiesJob, &BaseJob::failure, this, [this] { + if (d->capabilitiesJob->error() == BaseJob::IncorrectRequestError) + qCDebug(MAIN) << "Server doesn't support /capabilities;" + " version upgrade recommendations won't be issued"; }); } @@ -1685,18 +1686,20 @@ const QString Connection::SupportedRoomVersion::StableTag = QString Connection::defaultRoomVersion() const { - Q_ASSERT(d->capabilities.roomVersions.has_value()); - return d->capabilities.roomVersions->defaultVersion; + return d->capabilities.roomVersions + ? d->capabilities.roomVersions->defaultVersion + : QString(); } QStringList Connection::stableRoomVersions() const { - Q_ASSERT(d->capabilities.roomVersions.has_value()); QStringList l; - const auto& allVersions = d->capabilities.roomVersions->available; - for (auto it = allVersions.begin(); it != allVersions.end(); ++it) - if (it.value() == SupportedRoomVersion::StableTag) - l.push_back(it.key()); + if (d->capabilities.roomVersions) { + const auto& allVersions = d->capabilities.roomVersions->available; + for (auto it = allVersions.begin(); it != allVersions.end(); ++it) + if (it.value() == SupportedRoomVersion::StableTag) + l.push_back(it.key()); + } return l; } @@ -1711,18 +1714,19 @@ inline bool roomVersionLess(const Connection::SupportedRoomVersion& v1, QVector Connection::availableRoomVersions() const { - Q_ASSERT(d->capabilities.roomVersions.has_value()); QVector result; - result.reserve(d->capabilities.roomVersions->available.size()); - for (auto it = d->capabilities.roomVersions->available.begin(); - it != d->capabilities.roomVersions->available.end(); ++it) - result.push_back({ it.key(), it.value() }); - // Put stable versions over unstable; within each group, - // sort numeric versions as numbers, the rest as strings. - const auto mid = std::partition(result.begin(), result.end(), - std::mem_fn(&SupportedRoomVersion::isStable)); - std::sort(result.begin(), mid, roomVersionLess); - std::sort(mid, result.end(), roomVersionLess); - + if (d->capabilities.roomVersions) { + const auto& allVersions = d->capabilities.roomVersions->available; + result.reserve(allVersions.size()); + for (auto it = allVersions.begin(); it != allVersions.end(); ++it) + result.push_back({ it.key(), it.value() }); + // Put stable versions over unstable; within each group, + // sort numeric versions as numbers, the rest as strings. + const auto mid = + std::partition(result.begin(), result.end(), + std::mem_fn(&SupportedRoomVersion::isStable)); + std::sort(result.begin(), mid, roomVersionLess); + std::sort(mid, result.end(), roomVersionLess); + } return result; } -- cgit v1.2.3 From 6d804f56e570c39ea9967c66c4bdad6f530e956e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 21 Jul 2020 11:19:10 +0200 Subject: Uri: bare-sigil URIs are invalid --- lib/uri.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/uri.cpp b/lib/uri.cpp index e81933dc..f813794c 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -7,12 +7,15 @@ using namespace Quotient; struct ReplacePair { QByteArray uriString; char sigil; }; -static const auto replacePairs = { ReplacePair { "user/", '@' }, - { "roomid/", '!' }, - { "room/", '#' }, - // The notation for bare event ids is not - // proposed in MSC2312 (and anywhere, as yet) - { "event/", '$' } }; +/// Defines bi-directional mapping of path prefixes and sigils +static const auto replacePairs = { + ReplacePair { "user/", '@' }, + { "roomid/", '!' }, + { "room/", '#' }, + // The notation for bare event ids is not proposed in MSC2312 but there's + // https://github.com/matrix-org/matrix-doc/pull/2644 + { "event/", '$' } +}; Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) { @@ -22,14 +25,21 @@ Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) setScheme("matrix"); QString pathToBe; primaryType_ = Invalid; + if (primaryId.size() < 2) // There should be something after sigil + return; for (const auto& p: replacePairs) if (primaryId[0] == p.sigil) { primaryType_ = Type(p.sigil); pathToBe = p.uriString + primaryId.mid(1); break; } - if (!secondaryId.isEmpty()) + if (!secondaryId.isEmpty()) { + if (secondaryId.size() < 2) { + primaryType_ = Invalid; + return; + } pathToBe += "/event/" + secondaryId.mid(1); + } setPath(pathToBe); } setQuery(std::move(query)); -- cgit v1.2.3 From e9237a46bbdf6cbfee23c4f1acc06d90251566b0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 23 Jul 2020 11:58:54 +0200 Subject: Room::P::isEventNotable(): edits are not notable Edits are (normally) applied to some other event up the timeline, therefore not displayed. Having [1] in unread counts while seeing nothing in the timeline is quite confusing. --- lib/room.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 5a4a8611..3dd366f8 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -241,7 +241,8 @@ public: bool isEventNotable(const TimelineItem& ti) const { return !ti->isRedacted() && ti->senderId() != connection->userId() - && is(*ti); + && is(*ti) + && ti.viewAs()->replacedEvent().isEmpty(); } template @@ -700,7 +701,7 @@ Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, et.start(); unreadMessages = int(count_if(eagerMarker, timeline.cend(), - std::bind(&Room::Private::isEventNotable, this, _1))); + [this](const auto& ti) { return isEventNotable(ti); })); if (et.nsecsElapsed() > profilerMinNsecs() / 10) qCDebug(PROFILER) << "Recounting unread messages took" << et; @@ -2240,10 +2241,10 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) }); if (targetIt != it) *targetIt = makeReplaced(**targetIt, *msg); - else // FIXME: don't ignore, just show it wherever it arrived + else // FIXME: hide the replacing event when target arrives later qCDebug(EVENTS) << "Replacing event" << msg->id() - << "ignored: replaced event" << msg->replacedEvent() + << "ignored: target event" << msg->replacedEvent() << "is not found"; // Same as with redactions above, the replaced event coming // later will come already with the new content. -- cgit v1.2.3 From d1c5e514fb65bbea182b34a893bca12b321e3bac Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 24 Jul 2020 08:29:35 +0200 Subject: csapi/: refresh from the latest matrix-doc --- lib/csapi/logout.h | 11 ++++++----- lib/csapi/pushrules.cpp | 5 +++-- lib/csapi/pushrules.h | 9 +++++---- 3 files changed, 14 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/csapi/logout.h b/lib/csapi/logout.h index e9a7ef89..78f14e40 100644 --- a/lib/csapi/logout.h +++ b/lib/csapi/logout.h @@ -34,11 +34,12 @@ public: * devices for the user are also deleted. `Device keys <#device-keys>`_ for the * device are deleted alongside the device. * - * This endpoint does not require UI authorization because UI authorization is - * designed to protect against attacks where the someone gets hold of a single - * access token then takes over the account. This endpoint invalidates all - * access tokens for the user, including the token used in the request, and - * therefore the attacker is unable to take over the account in this way. + * This endpoint does not use the `User-Interactive Authentication API`_ because + * User-Interactive Authentication is designed to protect against attacks where + * the someone gets hold of a single access token then takes over the account. + * This endpoint invalidates all access tokens for the user, including the token + * used in the request, and therefore the attacker is unable to take over the + * account in this way. */ class LogoutAllJob : public BaseJob { public: diff --git a/lib/csapi/pushrules.cpp b/lib/csapi/pushrules.cpp index f2a16f1b..86165744 100644 --- a/lib/csapi/pushrules.cpp +++ b/lib/csapi/pushrules.cpp @@ -64,7 +64,8 @@ auto queryToSetPushRule(const QString& before, const QString& after) } SetPushRuleJob::SetPushRuleJob(const QString& scope, const QString& kind, - const QString& ruleId, const QStringList& actions, + const QString& ruleId, + const QVector& actions, const QString& before, const QString& after, const QVector& conditions, const QString& pattern) @@ -135,7 +136,7 @@ GetPushRuleActionsJob::GetPushRuleActionsJob(const QString& scope, SetPushRuleActionsJob::SetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId, - const QStringList& actions) + const QVector& actions) : BaseJob(HttpVerb::Put, QStringLiteral("SetPushRuleActionsJob"), QStringLiteral("/_matrix/client/r0") % "/pushrules/" % scope % "/" % kind % "/" % ruleId % "/actions") diff --git a/lib/csapi/pushrules.h b/lib/csapi/pushrules.h index f6d3458a..1c6d5c2d 100644 --- a/lib/csapi/pushrules.h +++ b/lib/csapi/pushrules.h @@ -148,7 +148,8 @@ public: * against. */ explicit SetPushRuleJob(const QString& scope, const QString& kind, - const QString& ruleId, const QStringList& actions, + const QString& ruleId, + const QVector& actions, const QString& before = {}, const QString& after = {}, const QVector& conditions = {}, @@ -246,9 +247,9 @@ public: // Result properties /// The action(s) to perform for this rule. - QStringList actions() const + QVector actions() const { - return loadFromJson("actions"_ls); + return loadFromJson>("actions"_ls); } }; @@ -275,7 +276,7 @@ public: */ explicit SetPushRuleActionsJob(const QString& scope, const QString& kind, const QString& ruleId, - const QStringList& actions); + const QVector& actions); }; } // namespace Quotient -- cgit v1.2.3 From c51a372076ed92c48a6b1853aded1473c821474b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 24 Jul 2020 11:32:02 +0200 Subject: Room: send read receipts in a background request Sending them in the foreground causes Quaternion to throw scary messages when read receipts don't go through while that's actually not a big deal. Also, network traffic deprioritisation. --- lib/room.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 3dd366f8..6f6128e0 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -735,7 +735,8 @@ Room::Changes Room::Private::markMessagesAsRead(rev_iter_t upToMarker) // until the previous last-read message, whichever comes first. for (; upToMarker < prevMarker; ++upToMarker) { if ((*upToMarker)->senderId() != q->localUser()->id()) { - connection->callApi(id, QStringLiteral("m.read"), + connection->callApi(BackgroundRequest, + id, QStringLiteral("m.read"), QUrl::toPercentEncoding( (*upToMarker)->id())); break; -- cgit v1.2.3 From 176b260bfaf8f575560bfe3fb520ee3fa0ad3a7a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 26 Jul 2020 09:26:19 +0200 Subject: RoomAvatarEvent: add constructors from content So that room avatar events could also be sent, not only received. --- lib/events/eventcontent.h | 2 ++ lib/events/roomavatarevent.h | 12 ++++++++++++ 2 files changed, 14 insertions(+) (limited to 'lib') diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index e7656de5..0d4c047e 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -214,6 +214,8 @@ namespace EventContent { template class UrlWithThumbnailContent : public UrlBasedContent { public: + // NB: when using inherited constructors, thumbnail has to be + // initialised separately using UrlBasedContent::UrlBasedContent; explicit UrlWithThumbnailContent(const QJsonObject& json) : UrlBasedContent(json), thumbnail(InfoT::originalInfoJson) diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index 4013773c..c2100eaa 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -31,6 +31,18 @@ public: DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) explicit RoomAvatarEvent(const QJsonObject& obj) : StateEvent(typeId(), obj) {} + explicit RoomAvatarEvent(const EventContent::ImageContent& avatar) + : StateEvent(typeId(), matrixTypeId(), QString(), avatar) + {} + // A replica of EventContent::ImageInfo constructor + explicit RoomAvatarEvent(const QUrl& u, qint64 fileSize = -1, + QMimeType mimeType = {}, + const QSize& imageSize = {}, + const QString& originalFilename = {}) + : RoomAvatarEvent(EventContent::ImageContent { + u, fileSize, mimeType, imageSize, originalFilename }) + {} + QUrl url() const { return content().url; } }; REGISTER_EVENT_TYPE(RoomAvatarEvent) -- cgit v1.2.3 From 776c2fd5b3caa60903a3a15db728a00753e02d67 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 29 Jul 2020 08:27:11 +0200 Subject: User: deprecate bridged() and rawName() Bridge postfixes stopped being a thing long ago; since then, bridged() has nothing but an empty string, and rawName() coincides with name(). --- lib/user.cpp | 33 ++++----------------------------- lib/user.h | 5 ++++- 2 files changed, 8 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 67cd1117..4ad74da2 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -54,7 +54,6 @@ public: QString userId; Connection* connection; - QString bridged; QString mostUsedName; QMultiHash otherNames; qreal hueF; @@ -218,11 +217,7 @@ int User::hue() const { return int(hueF() * 359); } QString User::name(const Room* room) const { return d->nameForRoom(room); } -QString User::rawName(const Room* room) const -{ - return d->bridged.isEmpty() ? name(room) - : name(room) % " (" % d->bridged % ')'; -} +QString User::rawName(const Room* room) const { return name(room); } void User::updateName(const QString& newName, const Room* room) { @@ -322,7 +317,7 @@ QString User::fullName(const Room* room) const return name.isEmpty() ? d->userId : name % " (" % d->userId % ')'; } -QString User::bridged() const { return d->bridged; } +QString User::bridged() const { return {}; } const Avatar& User::avatarObject(const Room* room) const { @@ -371,36 +366,16 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, && event.membership() != MembershipType::Join) return; - auto newName = event.displayName(); - // `bridged` value uses the same notification signal as the name; - // it is assumed that first setting of the bridge occurs together with - // the first setting of the name, and further bridge updates are - // exceptionally rare (the only reasonable case being that the bridge - // changes the naming convention). For the same reason room-specific - // bridge tags are not supported at all. - QRegularExpression reSuffix( - QStringLiteral(" \\((IRC|Gitter|Telegram)\\)$")); - auto match = reSuffix.match(newName); - if (match.hasMatch()) { - if (d->bridged != match.captured(1)) { - if (!d->bridged.isEmpty()) - qCWarning(MAIN) - << "Bridge for user" << id() << "changed:" << d->bridged - << "->" << match.captured(1); - d->bridged = match.captured(1); - } - newName.truncate(match.capturedStart(0)); - } if (event.prevContent()) { // FIXME: the hint doesn't work for bridged users auto oldNameHint = d->nameForRoom(room, event.prevContent()->displayName); - updateName(newName, oldNameHint, room); + updateName(event.displayName(), oldNameHint, room); updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room, event.prevContent()->avatarUrl), room); } else { - updateName(newName, room); + updateName(event.displayName(), room); updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room); } } diff --git a/lib/user.h b/lib/user.h index 28ec841b..fdad08bb 100644 --- a/lib/user.h +++ b/lib/user.h @@ -65,6 +65,7 @@ public: * (such as '(IRC)') to the user name. No disambiguation is done. * \sa name, displayName */ + [[deprecated("Bridge postfixes exist no more, use name() instead")]] QString rawName(const Room* room = nullptr) const; /** Get the displayed user name @@ -88,6 +89,8 @@ public: /** * Returns the name of bridge the user is connected from or empty. */ + [[deprecated("Bridged status is no more supported; this always returns" + " an empty string")]] QString bridged() const; /** Whether the user is a guest @@ -119,7 +122,7 @@ public: /// This method is for internal use and should not be called /// from client code - // FIXME: Move it away to private in lib 0.6 + // FIXME: Move it away to private void processEvent(const RoomMemberEvent& event, const Room* r, bool firstMention); -- cgit v1.2.3 From 7cba88bad28e1d3aa7a37b345b40325918030505 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 29 Jul 2020 08:34:16 +0200 Subject: User: don't spend another pointer on Connection Users are always parented to their Connection; there's no need to store a pointer to the connection on top of the one already stored by QObject. --- lib/user.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 4ad74da2..522ae197 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -45,14 +45,12 @@ class User::Private { public: static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } - Private(QString userId, Connection* connection) + Private(QString userId) : userId(move(userId)) - , connection(connection) , hueF(stringToHueF(this->userId)) {} QString userId; - Connection* connection; QString mostUsedName; QMultiHash otherNames; @@ -110,7 +108,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, et.start(); } - for (auto* r1: connection->allRooms()) + for (auto* r1: r->connection()->allRooms()) if (nameForRoom(r1) == mostUsedName) otherNames.insert(mostUsedName, r1); @@ -174,7 +172,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, nextMostUsedIt = otherAvatars.end() - 1; } std::swap(mostUsedAvatar, *nextMostUsedIt); - for (const auto* r1: connection->allRooms()) + for (const auto* r1: r->connection()->allRooms()) if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) avatarsToRooms.insert(nextMostUsedIt->url(), r1); @@ -189,15 +187,15 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, } User::User(QString userId, Connection* connection) - : QObject(connection), d(new Private(move(userId), connection)) + : QObject(connection), d(new Private(move(userId))) { setObjectName(userId); } Connection* User::connection() const { - Q_ASSERT(d->connection); - return d->connection; + Q_ASSERT(parent()); + return static_cast(parent()); } User::~User() = default; @@ -338,7 +336,7 @@ QImage User::avatar(int width, int height, const Room* room) QImage User::avatar(int width, int height, const Room* room, const Avatar::get_callback_t& callback) { - return avatarObject(room).get(d->connection, width, height, [=] { + return avatarObject(room).get(connection(), width, height, [=] { emit avatarChanged(this, room); callback(); }); -- cgit v1.2.3 From 5557b68a01f1b0f2e8e169aacdbe74f89b14cde3 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 29 Jul 2020 08:57:44 +0200 Subject: Fix FTBFS --- lib/user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 522ae197..03474b10 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -295,7 +295,7 @@ bool User::isIgnored() const { return connection()->isIgnored(this); } void User::Private::setAvatarOnServer(QString contentUri, User* q) { - auto* j = connection->callApi(userId, contentUri); + auto* j = q->connection()->callApi(userId, contentUri); connect(j, &BaseJob::success, q, [=] { q->updateAvatarUrl(contentUri, avatarUrlForRoom(nullptr)); }); } -- cgit v1.2.3 From a4bcc4c95768e919d6b37fa3593104d71ad8ae22 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 29 Jul 2020 16:11:55 +0200 Subject: User: only set object name at initialisation User::updateName() usually operates on a specific room; setting an object name from an arbitrary (whichever came last at any point in time) room member event for a given user does not look like a good idea. And having it in User::updateAvatar() seems to be a copy-paste fallout. --- lib/user.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 03474b10..6e377de8 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -230,7 +230,6 @@ void User::updateName(const QString& newName, const QString& oldName, if (newName != oldName) { emit nameAboutToChange(newName, oldName, room); d->setNameForRoom(room, newName, oldName); - setObjectName(displayname()); emit nameChanged(newName, oldName, room); } } @@ -242,7 +241,6 @@ void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, || d->avatarsToRooms.contains(oldUrl, room)); if (newUrl != oldUrl) { d->setAvatarForRoom(room, newUrl, oldUrl); - setObjectName(displayname()); emit avatarChanged(this, room); } } -- cgit v1.2.3 From 5010a78505a1a89b60cd5d3102e78717567354dd Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 30 Jul 2020 21:32:25 +0200 Subject: User: minor cleanup; doc-comments --- lib/user.cpp | 21 +++++++++------------ lib/user.h | 15 +++++++++------ 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 6e377de8..b610573d 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -45,12 +45,9 @@ class User::Private { public: static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } - Private(QString userId) - : userId(move(userId)) - , hueF(stringToHueF(this->userId)) - {} + Private(QString userId) : id(move(userId)), hueF(stringToHueF(id)) { } - QString userId; + QString id; QString mostUsedName; QMultiHash otherNames; @@ -200,14 +197,14 @@ Connection* User::connection() const User::~User() = default; -QString User::id() const { return d->userId; } +QString User::id() const { return d->id; } bool User::isGuest() const { - Q_ASSERT(!d->userId.isEmpty() && d->userId.startsWith('@')); - auto it = std::find_if_not(d->userId.begin() + 1, d->userId.end(), + Q_ASSERT(!d->id.isEmpty() && d->id.startsWith('@')); + auto it = std::find_if_not(d->id.begin() + 1, d->id.end(), [](QChar c) { return c.isDigit(); }); - Q_ASSERT(it != d->userId.end()); + Q_ASSERT(it != d->id.end()); return *it == ':'; } @@ -293,7 +290,7 @@ bool User::isIgnored() const { return connection()->isIgnored(this); } void User::Private::setAvatarOnServer(QString contentUri, User* q) { - auto* j = q->connection()->callApi(userId, contentUri); + auto* j = q->connection()->callApi(id, contentUri); connect(j, &BaseJob::success, q, [=] { q->updateAvatarUrl(contentUri, avatarUrlForRoom(nullptr)); }); } @@ -304,13 +301,13 @@ QString User::displayname(const Room* room) const return room->roomMembername(this); const auto name = d->nameForRoom(nullptr); - return name.isEmpty() ? d->userId : name; + return name.isEmpty() ? d->id : name; } QString User::fullName(const Room* room) const { const auto name = d->nameForRoom(room); - return name.isEmpty() ? d->userId : name % " (" % d->userId % ')'; + return name.isEmpty() ? id() : name % " (" % id() % ')'; } QString User::bridged() const { return {}; } diff --git a/lib/user.h b/lib/user.h index fdad08bb..e4328f1d 100644 --- a/lib/user.h +++ b/lib/user.h @@ -21,7 +21,6 @@ #include "avatar.h" #include -#include namespace Quotient { class Connection; @@ -109,6 +108,10 @@ public: int hue() const; qreal hueF() const; + /// Get a reference to a user avatar object for a given room + /*! This reference should be considered short-lived: processing the next + * room member event for this user may (or may not) invalidate it. + */ const Avatar& avatarObject(const Room* room = nullptr) const; Q_INVOKABLE QImage avatar(int dimension, const Quotient::Room* room = nullptr); @@ -135,16 +138,16 @@ public slots: bool setAvatar(const QString& fileName); /** Upload contents of the QIODevice and set that as an avatar */ bool setAvatar(QIODevice* source); - /** Create or find a direct chat with this user - * The resulting chat is returned asynchronously via + /// Create or find a direct chat with this user + /*! The resulting chat is returned asynchronously via * Connection::directChatAvailable() */ void requestDirectChat(); - /** Add the user to the ignore list */ + /// Add the user to the ignore list void ignore(); - /** Remove the user from the ignore list */ + /// Remove the user from the ignore list void unmarkIgnore(); - /** Check whether the user is in ignore list */ + /// Check whether the user is in ignore list bool isIgnored() const; signals: -- cgit v1.2.3 From 38f3f752d4cac7f155e25af36357a4c6445d885f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 2 Aug 2020 19:25:59 +0200 Subject: User: fix FTBFS and access after move --- lib/user.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index b610573d..09cf72a2 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -99,7 +99,7 @@ void User::Private::setNameForRoom(const Room* r, QString newName, Q_ASSERT(totalRooms > 1); QElapsedTimer et; if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { - qCDebug(MAIN) << "Switching the most used name of user" << userId + qCDebug(MAIN) << "Switching the most used name of user" << id << "from" << mostUsedName << "to" << newName; qCDebug(MAIN) << "The user is in" << totalRooms << "rooms"; et.start(); @@ -152,7 +152,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, QElapsedTimer et; if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { qCInfo(MAIN) - << "Switching the most used avatar of user" << userId + << "Switching the most used avatar of user" << id << "from" << mostUsedAvatar.url().toDisplayString() << "to" << newUrl.toDisplayString(); et.start(); @@ -161,7 +161,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, auto nextMostUsedIt = otherAvatar(newUrl); if (nextMostUsedIt == otherAvatars.end()) { qCCritical(MAIN) - << userId << "doesn't have" << newUrl.toDisplayString() + << id << "doesn't have" << newUrl.toDisplayString() << "in otherAvatars though it seems to be used in" << newUrlUsage << "rooms"; Q_ASSERT(false); @@ -186,7 +186,7 @@ void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, User::User(QString userId, Connection* connection) : QObject(connection), d(new Private(move(userId))) { - setObjectName(userId); + setObjectName(id()); } Connection* User::connection() const -- cgit v1.2.3 From 1c89a4be8a9862df32d9c5d0ecb716a460738ec9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 3 Aug 2020 19:10:10 +0200 Subject: Cleanup --- lib/connection.h | 6 ++++-- lib/events/roomcanonicalaliasevent.h | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index 48ea4f5e..258280a8 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -238,7 +238,8 @@ public: /** Get the list of rooms with the specified tag */ QVector roomsWithTag(const QString& tagName) const; - /** Mark the room as a direct chat with the user + /*! \brief Mark the room as a direct chat with the user + * * This function marks \p room as a direct chat with \p user. * Emits the signal synchronously, without waiting to complete * synchronisation with the server. @@ -247,7 +248,8 @@ public: */ void addToDirectChats(const Room* room, User* user); - /** Unmark the room from direct chats + /*! \brief Unmark the room from direct chats + * * This function removes the room id from direct chats either for * a specific \p user or for all users if \p user in nullptr. * The room id is used to allow removal of, e.g., ids of forgotten diff --git a/lib/events/roomcanonicalaliasevent.h b/lib/events/roomcanonicalaliasevent.h index 5d680de7..fadfece0 100644 --- a/lib/events/roomcanonicalaliasevent.h +++ b/lib/events/roomcanonicalaliasevent.h @@ -59,12 +59,14 @@ public: : StateEvent(typeId(), obj) { } - explicit RoomCanonicalAliasEvent(const QString& canonicalAlias, const QStringList& altAliases = {}) + explicit RoomCanonicalAliasEvent(const QString& canonicalAlias, + const QStringList& altAliases = {}) : StateEvent(typeId(), matrixTypeId(), QString(), canonicalAlias, altAliases) { } - explicit RoomCanonicalAliasEvent(QString&& canonicalAlias, QStringList&& altAliases = {}) + explicit RoomCanonicalAliasEvent(QString&& canonicalAlias, + QStringList&& altAliases = {}) : StateEvent(typeId(), matrixTypeId(), QString(), std::move(canonicalAlias), std::move(altAliases)) { } -- cgit v1.2.3 From 83476d31854f403b7017003a560c2d5545360487 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 4 Aug 2020 11:26:01 +0200 Subject: Room: various minor tweaks and fixes --- lib/room.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 6f6128e0..970fe56b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -346,6 +346,8 @@ public: QJsonObject toJson() const; + bool isLocalUser(const User* u) const { return u == q->localUser(); } + #ifdef Quotient_E2EE_ENABLED // A map from to QHash, QPair> @@ -436,8 +438,6 @@ private: template users_shortlist_t buildShortlist(const ContT& users) const; users_shortlist_t buildShortlist(const QStringList& userIds) const; - - bool isLocalUser(const User* u) const { return u == q->localUser(); } }; decltype(Room::Private::baseState) Room::Private::stubbedState {}; @@ -1432,19 +1432,7 @@ QString Room::roomMembername(const User* u) const if (++nextUserIt == d->membersMap.cend() || nextUserIt.key() != username) return username; // No disambiguation necessary - // Check if we can get away just attaching the bridge postfix - // (extension to the spec) - QVector bridges; - for (; namesakesIt != d->membersMap.cend() && namesakesIt.key() == username; - ++namesakesIt) { - const auto bridgeName = (*namesakesIt)->bridged(); - if (bridges.contains(bridgeName)) // Two accounts on the same bridge - return u->fullName(this); // Disambiguate fully - // Don't bother sorting, not so many bridges out there - bridges.push_back(bridgeName); - } - - return u->rawName(this); // Disambiguate using the bridge postfix only + return u->fullName(this); // Disambiguate fully } QString Room::roomMembername(const QString& userId) const @@ -2537,9 +2525,20 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return MembersChange; // clang-format off } - , [this] (const EncryptionEvent&) { - emit encryption(); // It can only be done once, so emit it here. + , [this, oldEncEvt = static_cast(oldStateEvent)]( + const EncryptionEvent&) { + // clang-format on + if (oldEncEvt + && oldEncEvt->encryption() != EncryptionEventContent::Undefined) { + qCWarning(STATE) << "The room is already encrypted but a new" + " room encryption event arrived - ignoring"; + return NoChange; + } + // As encryption can only be switched on once, emit the signal here + // instead of aggregating and emitting in updateData() + emit encryption(); return OtherChange; + // clang-format off } , [this] (const RoomTombstoneEvent& evt) { const auto successorId = evt.successorRoomId(); -- cgit v1.2.3 From 5849686e96b4a7277f2b2152ff3aa76cc6d0f18e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 4 Aug 2020 11:38:20 +0200 Subject: User: optimise names/avatars storing and updating The current mechanism relied on a complicated and fragile machinery around setNameForRoom() and setAvatarForRoom() that maintained the "most used" entity for a given user along with "other" ones. Given that per-room avatars are pretty rare in Matrix, it's also been inefficient as commit c69f100e shows. The new mechanism stores the "default" (as per user profile) name and avatar and maintains a singleton map of avatar objects across all users. Per-user profile only (normally) exists for the local user so there's yet another inefficiency - this will be fixed in 0.7 by introducing a special class for a user profile. --- lib/room.cpp | 140 +++++++++++++++-------------- lib/user.cpp | 282 +++++++++++++++++------------------------------------------ lib/user.h | 34 ++++--- 3 files changed, 176 insertions(+), 280 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 970fe56b..1e3eb603 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -192,7 +192,6 @@ public: // void inviteUser(User* u); // We might get it at some point in time. void insertMemberIntoMap(User* u); - void renameMember(User* u, const QString& oldName); void removeMemberFromMap(const QString& username, User* u); // This updates the room displayname field (which is the way a room @@ -238,6 +237,15 @@ public: return static_cast(evt); } +// template +// const auto& getCurrentStateContent(const QString& stateKey = {}) const +// { +// if (const auto* evt = +// currentState.value({ EventT::matrixTypeId(), stateKey }, nullptr)) +// return evt->content(); +// return EventT::content_type() +// } + bool isEventNotable(const TimelineItem& ti) const { return !ti->isRedacted() && ti->senderId() != connection->userId() @@ -1344,18 +1352,6 @@ void Room::Private::insertMemberIntoMap(User* u) emit q->memberRenamed(namesakes.front()); } -void Room::Private::renameMember(User* u, const QString& oldName) -{ - if (u->name(q) == oldName) { - qCWarning(MAIN) << "Room::Private::renameMember(): the user " - << u->fullName(q) - << "is already known in the room under a new name."; - } else if (membersMap.contains(oldName, u)) { - removeMemberFromMap(oldName, u); - insertMemberIntoMap(u); - } -} - void Room::Private::removeMemberFromMap(const QString& username, User* u) { User* namesake = nullptr; @@ -2396,14 +2392,69 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!e.isStateEvent()) return Change::NoChange; - const auto* oldStateEvent = - std::exchange(d->currentState[{ e.matrixType(), e.stateKey() }], - static_cast(&e)); + // Find a value (create empty if necessary) and get a reference to it + // getCurrentState<> is not used here because it (creates and) returns + // a stub if a value is not found, and what's needed here is a "real" event + // or nullptr. + const auto*& curStateEvent = + d->currentState[{ e.matrixType(), e.stateKey() }]; + // Prepare for the state change + visit(e, [this, oldRme = static_cast(curStateEvent)]( + const RoomMemberEvent& rme) { + auto* u = user(rme.userId()); + if (!u) { // ??? + qCCritical(MAIN) + << "Could not get a user object for" << rme.userId(); + return; + } + // TODO: remove along with User::processEvent() in 0.7 + const auto prevMembership = oldRme ? oldRme->membership() + : MembershipType::Leave; + u->processEvent(rme, this, oldRme == nullptr); + + switch (prevMembership) { + case MembershipType::Invite: + if (rme.membership() != prevMembership) { + d->usersInvited.removeOne(u); + Q_ASSERT(!d->usersInvited.contains(u)); + } + break; + case MembershipType::Join: + switch (rme.membership()) { + case MembershipType::Join: // rename/avatar change or no-op + if (rme.displayName() != oldRme->displayName()) { + emit memberAboutToRename(u, rme.displayName()); + d->removeMemberFromMap(u->name(this), u); + } + break; + case MembershipType::Invite: + qCWarning(MAIN) << "Membership change from Join to Invite:" + << rme; + [[fallthrough]]; + default: // whatever the new membership, it's no more Join + d->removeMemberFromMap(u->name(this), u); + emit userRemoved(u); + } + break; + default: + if (rme.membership() == MembershipType::Invite + || rme.membership() == MembershipType::Join) { + d->membersLeft.removeOne(u); + Q_ASSERT(!d->membersLeft.contains(u)); + } + } + }); + + // Change the state + const auto* const oldStateEvent = + std::exchange(curStateEvent, static_cast(&e)); Q_ASSERT(!oldStateEvent || (oldStateEvent->matrixType() == e.matrixType() && oldStateEvent->stateKey() == e.stateKey())); if (!is(e)) // Room member events are too numerous - qCDebug(STATE) << "Room state event:" << e; + qCDebug(STATE) << "Updated room state:" << e; + + // Update internal structures as per the change and work out the return value // clang-format off return visit(e @@ -2411,10 +2462,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) return NameChange; } , [] (const RoomAliasesEvent&) { - // clang-format on - // This event has been removed by MSC-2432 - return NoChange; - // clang-format off + return NoChange; // This event has been removed by MSC2432 } , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { // clang-format on @@ -2460,63 +2508,27 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) auto* u = user(evt.userId()); const auto* oldMemberEvent = static_cast(oldStateEvent); + // TODO: remove in 0.7 u->processEvent(evt, this, oldMemberEvent == nullptr); + const auto prevMembership = oldMemberEvent ? oldMemberEvent->membership() : MembershipType::Leave; - if (u == localUser() && evt.membership() == MembershipType::Invite - && evt.isDirect()) - connection()->addToDirectChats(this, user(evt.senderId())); - - switch (prevMembership) { - case MembershipType::Invite: - if (evt.membership() != prevMembership) { - d->usersInvited.removeOne(u); - Q_ASSERT(!d->usersInvited.contains(u)); - } - break; - case MembershipType::Join: - if (evt.membership() == MembershipType::Invite) - qCWarning(MAIN) << "Invalid membership change from " - "Join to Invite:" - << evt; - if (evt.membership() != prevMembership) { - disconnect(u, &User::nameAboutToChange, this, nullptr); - disconnect(u, &User::nameChanged, this, nullptr); - d->removeMemberFromMap(u->name(this), u); - emit userRemoved(u); - } - break; - default: - if (evt.membership() == MembershipType::Invite - || evt.membership() == MembershipType::Join) { - d->membersLeft.removeOne(u); - Q_ASSERT(!d->membersLeft.contains(u)); - } - } - switch (evt.membership()) { case MembershipType::Join: if (prevMembership != MembershipType::Join) { d->insertMemberIntoMap(u); - connect(u, &User::nameAboutToChange, this, - [=](QString newName, QString, const Room* context) { - if (context == this) - emit memberAboutToRename(u, newName); - }); - connect(u, &User::nameChanged, this, - [=](QString, QString oldName, const Room* context) { - if (context == this) { - d->renameMember(u, oldName); - emit memberRenamed(u); - } - }); emit userAdded(u); + } else if (oldMemberEvent->displayName() != evt.displayName()) { + d->insertMemberIntoMap(u); + emit memberRenamed(u); } break; case MembershipType::Invite: if (!d->usersInvited.contains(u)) d->usersInvited.push_back(u); + if (u == localUser() && evt.isDirect()) + connection()->addToDirectChats(this, user(evt.senderId())); break; default: if (!d->membersLeft.contains(u)) diff --git a/lib/user.cpp b/lib/user.cpp index 09cf72a2..cc6cb102 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -38,150 +38,26 @@ #include using namespace Quotient; -using namespace std::placeholders; using std::move; class User::Private { public: - static Avatar makeAvatar(QUrl url) { return Avatar(move(url)); } - Private(QString userId) : id(move(userId)), hueF(stringToHueF(id)) { } QString id; - - QString mostUsedName; - QMultiHash otherNames; qreal hueF; - Avatar mostUsedAvatar { makeAvatar({}) }; - std::vector otherAvatars; - auto otherAvatar(const QUrl& url) - { - return std::find_if(otherAvatars.begin(), otherAvatars.end(), - [&url](const auto& av) { return av.url() == url; }); - } - QMultiHash avatarsToRooms; - - mutable int totalRooms = 0; - QString nameForRoom(const Room* r, const QString& hint = {}) const; - void setNameForRoom(const Room* r, QString newName, const QString& oldName); - QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const; - void setAvatarForRoom(const Room* r, const QUrl& newUrl, const QUrl& oldUrl); - - void setAvatarOnServer(QString contentUri, User* q); + QString defaultName; + Avatar defaultAvatar; + // NB: This container is ever-growing. Even if the user no more scrolls + // the timeline that far back, historical avatars are still kept around. + // This is consistent with the rest of Quotient, as room timelines + // are never vacuumed either. This will probably change in the future. + /// Map of mediaId to Avatar objects + static UnorderedMap otherAvatars; }; -QString User::Private::nameForRoom(const Room* r, const QString& hint) const -{ - // If the hint is accurate, this function is O(1) instead of O(n) - if (!hint.isNull() && (hint == mostUsedName || otherNames.contains(hint, r))) - return hint; - return otherNames.key(r, mostUsedName); -} - -static constexpr int MIN_JOINED_ROOMS_TO_LOG = 20; - -void User::Private::setNameForRoom(const Room* r, QString newName, - const QString& oldName) -{ - Q_ASSERT(oldName != newName); - Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r)); - if (totalRooms < 2) { - Q_ASSERT_X(totalRooms > 0 && otherNames.empty(), __FUNCTION__, - "Internal structures inconsistency"); - mostUsedName = move(newName); - return; - } - otherNames.remove(oldName, r); - if (newName != mostUsedName) { - // Check if the newName is about to become most used. - if (otherNames.count(newName) >= totalRooms - otherNames.size()) { - Q_ASSERT(totalRooms > 1); - QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { - qCDebug(MAIN) << "Switching the most used name of user" << id - << "from" << mostUsedName << "to" << newName; - qCDebug(MAIN) << "The user is in" << totalRooms << "rooms"; - et.start(); - } - - for (auto* r1: r->connection()->allRooms()) - if (nameForRoom(r1) == mostUsedName) - otherNames.insert(mostUsedName, r1); - - mostUsedName = newName; - otherNames.remove(newName); - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - qCDebug(PROFILER) << et << "to switch the most used name"; - } else - otherNames.insert(newName, r); - } -} - -QUrl User::Private::avatarUrlForRoom(const Room* r, const QUrl& hint) const -{ - // If the hint is accurate, this function is O(1) instead of O(n) - if (hint == mostUsedAvatar.url() || avatarsToRooms.contains(hint, r)) - return hint; - auto it = std::find(avatarsToRooms.begin(), avatarsToRooms.end(), r); - return it == avatarsToRooms.end() ? mostUsedAvatar.url() : it.key(); -} - -void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl, - const QUrl& oldUrl) -{ - Q_ASSERT(oldUrl != newUrl); - Q_ASSERT(oldUrl == mostUsedAvatar.url() - || avatarsToRooms.contains(oldUrl, r)); - if (totalRooms < 2) { - Q_ASSERT_X(totalRooms > 0 && otherAvatars.empty(), __FUNCTION__, - "Internal structures inconsistency"); - mostUsedAvatar.updateUrl(newUrl); - return; - } - avatarsToRooms.remove(oldUrl, r); - if (!avatarsToRooms.contains(oldUrl)) { - auto it = otherAvatar(oldUrl); - if (it != otherAvatars.end()) - otherAvatars.erase(it); - } - if (newUrl != mostUsedAvatar.url()) { - // Check if the new avatar is about to become most used. - const auto newUrlUsage = avatarsToRooms.count(newUrl); - if (newUrlUsage >= totalRooms - avatarsToRooms.size()) { - QElapsedTimer et; - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) { - qCInfo(MAIN) - << "Switching the most used avatar of user" << id - << "from" << mostUsedAvatar.url().toDisplayString() << "to" - << newUrl.toDisplayString(); - et.start(); - } - avatarsToRooms.remove(newUrl); - auto nextMostUsedIt = otherAvatar(newUrl); - if (nextMostUsedIt == otherAvatars.end()) { - qCCritical(MAIN) - << id << "doesn't have" << newUrl.toDisplayString() - << "in otherAvatars though it seems to be used in" - << newUrlUsage << "rooms"; - Q_ASSERT(false); - otherAvatars.emplace_back(makeAvatar(newUrl)); - nextMostUsedIt = otherAvatars.end() - 1; - } - std::swap(mostUsedAvatar, *nextMostUsedIt); - for (const auto* r1: r->connection()->allRooms()) - if (avatarUrlForRoom(r1) == nextMostUsedIt->url()) - avatarsToRooms.insert(nextMostUsedIt->url(), r1); - - if (totalRooms > MIN_JOINED_ROOMS_TO_LOG) - qCDebug(PROFILER) << et << "to switch the most used avatar"; - } else { - if (otherAvatar(newUrl) == otherAvatars.end()) - otherAvatars.emplace_back(makeAvatar(newUrl)); - avatarsToRooms.insert(newUrl, r); - } - } -} +decltype(User::Private::otherAvatars) User::Private::otherAvatars {}; User::User(QString userId, Connection* connection) : QObject(connection), d(new Private(move(userId))) @@ -210,43 +86,34 @@ bool User::isGuest() const int User::hue() const { return int(hueF() * 359); } -QString User::name(const Room* room) const { return d->nameForRoom(room); } - -QString User::rawName(const Room* room) const { return name(room); } - -void User::updateName(const QString& newName, const Room* room) +QString User::name(const Room* room) const { - updateName(newName, d->nameForRoom(room), room); + return room ? room->getCurrentState(id())->displayName() + : d->defaultName; } -void User::updateName(const QString& newName, const QString& oldName, - const Room* room) -{ - Q_ASSERT(oldName == d->mostUsedName - || d->otherNames.contains(oldName, room)); - if (newName != oldName) { - emit nameAboutToChange(newName, oldName, room); - d->setNameForRoom(room, newName, oldName); - emit nameChanged(newName, oldName, room); - } -} +QString User::rawName(const Room* room) const { return name(room); } -void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, - const Room* room) -{ - Q_ASSERT(oldUrl == d->mostUsedAvatar.url() - || d->avatarsToRooms.contains(oldUrl, room)); - if (newUrl != oldUrl) { - d->setAvatarForRoom(room, newUrl, oldUrl); - emit avatarChanged(this, room); - } -} +void User::updateName(const QString&, const Room*) {} +void User::updateName(const QString&, const QString&, const Room*) {} +void User::updateAvatarUrl(const QUrl&, const QUrl&, const Room*) {} void User::rename(const QString& newName) { const auto actualNewName = sanitized(newName); + if (actualNewName == d->defaultName) + return; // Nothing to do + connect(connection()->callApi(id(), actualNewName), - &BaseJob::success, this, [=] { updateName(actualNewName); }); + &BaseJob::success, this, [this,actualNewName] { + if (actualNewName == d->defaultName) + return; // Check again, it could have changed meanwhile + + emit nameAboutToChange(actualNewName, d->defaultName, nullptr); + const auto& oldName = + std::exchange(d->defaultName, actualNewName); + emit nameChanged(d->defaultName, oldName, nullptr); + }); } void User::rename(const QString& newName, const Room* r) @@ -262,22 +129,39 @@ void User::rename(const QString& newName, const Room* r) const auto actualNewName = sanitized(newName); MemberEventContent evtC; evtC.displayName = actualNewName; - connect(r->setState(id(), move(evtC)), &BaseJob::success, - this, [=] { updateName(actualNewName, r); }); + r->setState(id(), move(evtC)); + // The state will be updated locally after it arrives with sync +} + +template +inline bool User::doSetAvatar(SourceT&& source) +{ + return d->defaultAvatar.upload( + connection(), source, [this](const QString& contentUri) { + auto* j = connection()->callApi(id(), contentUri); + connect(j, &BaseJob::success, this, + [this, newUrl = QUrl(contentUri)] { + if (newUrl == d->defaultAvatar.url()) { + qCWarning(MAIN) << "User" << id() + << "already has avatar URL set to" + << newUrl.toDisplayString(); + return; + } + + d->defaultAvatar.updateUrl(move(newUrl)); + emit avatarChanged(this, nullptr); + }); + }); } bool User::setAvatar(const QString& fileName) { - return avatarObject().upload(connection(), fileName, - std::bind(&Private::setAvatarOnServer, - d.data(), _1, this)); + return doSetAvatar(fileName); } bool User::setAvatar(QIODevice* source) { - return avatarObject().upload(connection(), source, - std::bind(&Private::setAvatarOnServer, - d.data(), _1, this)); + return doSetAvatar(source); } void User::requestDirectChat() { connection()->requestDirectChat(this); } @@ -288,34 +172,29 @@ void User::unmarkIgnore() { connection()->removeFromIgnoredUsers(this); } bool User::isIgnored() const { return connection()->isIgnored(this); } -void User::Private::setAvatarOnServer(QString contentUri, User* q) -{ - auto* j = q->connection()->callApi(id, contentUri); - connect(j, &BaseJob::success, q, - [=] { q->updateAvatarUrl(contentUri, avatarUrlForRoom(nullptr)); }); -} - QString User::displayname(const Room* room) const { - if (room) - return room->roomMembername(this); - - const auto name = d->nameForRoom(nullptr); - return name.isEmpty() ? d->id : name; + return room ? room->roomMembername(this) + : d->defaultName.isEmpty() ? d->id + : d->defaultName; } QString User::fullName(const Room* room) const { - const auto name = d->nameForRoom(room); - return name.isEmpty() ? id() : name % " (" % id() % ')'; + const auto displayName = name(room); + return displayName.isEmpty() ? id() : (displayName % " (" % id() % ')'); } QString User::bridged() const { return {}; } const Avatar& User::avatarObject(const Room* room) const { - auto it = d->otherAvatar(d->avatarUrlForRoom(room)); - return it != d->otherAvatars.end() ? *it : d->mostUsedAvatar; + if (!room) + return d->defaultAvatar; + + const auto& url = room->getCurrentState(id())->avatarUrl(); + const auto& mediaId = url.authority() + url.path(); + return d->otherAvatars.try_emplace(mediaId, url).first->second; } QImage User::avatar(int dimension, const Room* room) @@ -352,25 +231,22 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, { Q_ASSERT(room); - if (firstMention) - ++d->totalRooms; - - if (event.membership() != MembershipType::Invite - && event.membership() != MembershipType::Join) - return; - - if (event.prevContent()) { - // FIXME: the hint doesn't work for bridged users - auto oldNameHint = d->nameForRoom(room, - event.prevContent()->displayName); - updateName(event.displayName(), oldNameHint, room); - updateAvatarUrl(event.avatarUrl(), - d->avatarUrlForRoom(room, event.prevContent()->avatarUrl), - room); - } else { - updateName(event.displayName(), room); - updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room); + const auto& oldName = firstMention || !event.prevContent() ? QString() + : event.prevContent()->displayName; + const auto& newName = event.displayName(); + // A hacky way to find out if it's about to change or already changed + bool isAboutToChange = room->getCurrentState(id()) != &event; + if (newName != oldName) { + if (isAboutToChange) + emit nameAboutToChange(newName, oldName, room); + else { + emit nameChanged(newName, oldName, room); + } } + const auto& oldAvatarUrl = firstMention || !event.prevContent() ? QUrl() + : event.prevContent()->avatarUrl; + if (event.avatarUrl() != oldAvatarUrl && !isAboutToChange) + emit avatarChanged(this, room); } qreal User::hueF() const { return d->hueF; } diff --git a/lib/user.h b/lib/user.h index e4328f1d..ceea7c48 100644 --- a/lib/user.h +++ b/lib/user.h @@ -123,20 +123,21 @@ public: QString avatarMediaId(const Room* room = nullptr) const; QUrl avatarUrl(const Room* room = nullptr) const; - /// This method is for internal use and should not be called - /// from client code - // FIXME: Move it away to private + // TODO: This method is only there to emit obsolete signals: + // nameAboutToChange(), nameChanged() and avatarChanged() - all of these + // to be removed in 0.7 + /// \deprecated void processEvent(const RoomMemberEvent& event, const Room* r, bool firstMention); public slots: - /** Set a new name in the global user profile */ + /// Set a new name in the global user profile void rename(const QString& newName); - /** Set a new name for the user in one room */ + /// Set a new name for the user in one room void rename(const QString& newName, const Room* r); - /** Upload the file and use it as an avatar */ + /// Upload the file and use it as an avatar bool setAvatar(const QString& fileName); - /** Upload contents of the QIODevice and set that as an avatar */ + /// Upload contents of the QIODevice and set that as an avatar bool setAvatar(QIODevice* source); /// Create or find a direct chat with this user /*! The resulting chat is returned asynchronously via @@ -151,21 +152,28 @@ public slots: bool isIgnored() const; signals: + /// \deprecated Use Room::memberListChanged() for member changes void nameAboutToChange(QString newName, QString oldName, const Quotient::Room* roomContext); + /// \deprecated Use Room::memberListChanged() for member changes void nameChanged(QString newName, QString oldName, const Quotient::Room* roomContext); + /// \deprecated Use Room::memberListChanged() for member changes void avatarChanged(Quotient::User* user, const Quotient::Room* roomContext); -private slots: - void updateName(const QString& newName, const Room* room = nullptr); - void updateName(const QString& newName, const QString& oldName, - const Room* room = nullptr); - void updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl, - const Room* room = nullptr); +private slots: // TODO: remove in 0.7 + /// \deprecated + void updateName(const QString&, const Room* = nullptr); + /// \deprecated + void updateName(const QString&, const QString&, const Room* = nullptr); + /// \deprecated + void updateAvatarUrl(const QUrl&, const QUrl&, const Room* = nullptr); private: class Private; QScopedPointer d; + + template + bool doSetAvatar(SourceT&& source); }; } // namespace Quotient -- cgit v1.2.3 From 3648c6a40c585e718bcfc0068cfa431e46f24a52 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 4 Aug 2020 13:28:09 +0200 Subject: Cleanup around [BaseJob::]IncorrectResponse[Error] --- lib/jobs/basejob.cpp | 5 ++--- lib/jobs/mediathumbnailjob.cpp | 3 +-- lib/jobs/syncjob.cpp | 4 ++-- 3 files changed, 5 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 465287c6..d6d48d2f 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -401,9 +401,8 @@ BaseJob::Status BaseJob::Private::parseJson() { QJsonParseError error { 0, QJsonParseError::MissingObject }; jsonResponse = QJsonDocument::fromJson(rawResponse, &error); - return { error.error == QJsonParseError::NoError - ? BaseJob::NoError - : BaseJob::IncorrectResponse, + return { error.error == QJsonParseError::NoError ? NoError + : IncorrectResponse, error.errorString() }; } diff --git a/lib/jobs/mediathumbnailjob.cpp b/lib/jobs/mediathumbnailjob.cpp index 33f4236c..a69f00e9 100644 --- a/lib/jobs/mediathumbnailjob.cpp +++ b/lib/jobs/mediathumbnailjob.cpp @@ -53,6 +53,5 @@ BaseJob::Status MediaThumbnailJob::prepareResult() if (_thumbnail.loadFromData(data()->readAll())) return Success; - return { IncorrectResponseError, - QStringLiteral("Could not read image data") }; + return { IncorrectResponse, QStringLiteral("Could not read image data") }; } diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index 6b8cfe4b..9087fe50 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -53,9 +53,9 @@ BaseJob::Status SyncJob::prepareResult() { d.parseJson(jsonData()); if (d.unresolvedRooms().isEmpty()) - return BaseJob::Success; + return Success; qCCritical(MAIN).noquote() << "Incomplete sync response, missing rooms:" << d.unresolvedRooms().join(','); - return BaseJob::IncorrectResponseError; + return IncorrectResponse; } -- cgit v1.2.3 From eedfd50f9661149f804f169c458b6ef25f96a834 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 4 Aug 2020 13:30:21 +0200 Subject: Connection: stop the sync loop on SyncJob::failure --- lib/connection.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 46494a56..4530d95a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -508,7 +508,9 @@ void Connection::sync(int timeout) retriesTaken, nextInMilliseconds); }); connect(job, &SyncJob::failure, this, [this, job] { - d->syncJob = nullptr; + // SyncJob persists with retries on transient errors; if it fails, + // there's likely something serious enough to stop the loop. + stopSync(); if (job->error() == BaseJob::Unauthorised) { qCWarning(SYNCJOB) << "Sync job failed with Unauthorised - login expired?"; @@ -742,7 +744,8 @@ void Connection::stopSync() disconnect(d->syncLoopConnection); if (d->syncJob) // If there's an ongoing sync job, stop it too { - d->syncJob->abandon(); + if (d->syncJob->status().code == BaseJob::Pending) + d->syncJob->abandon(); d->syncJob = nullptr; } } -- cgit v1.2.3 From d88663890261c2c276cd76576655b137a8758670 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 4 Aug 2020 13:32:57 +0200 Subject: BaseJob: go for a retry on IncorrectResponse The most frequent occurence of IncorrectResponse so far is a proxy/CDN failure. This is not a grave error; there's a chance that the retry will succeed. In the worst case the job will fail after 3 identical errors (except SyncJob that will try to get through forever - but SyncJob failures should still be indicated in the client's UI in some non-intrusive way). --- lib/jobs/basejob.cpp | 55 +++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 24 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index d6d48d2f..1f0e84ba 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -586,33 +586,40 @@ void BaseJob::stop() void BaseJob::finishJob() { stop(); - if (error() == TooManyRequests) { + switch(error()) { + case TooManyRequests: emit rateLimited(); d->connection->submit(this); return; - } - if (error() == Unauthorised && !d->needsToken - && !d->connection->accessToken().isEmpty()) { - // Rerun with access token (extension of the spec while - // https://github.com/matrix-org/matrix-doc/issues/701 is pending) - d->connection->setNeedsToken(objectName()); - qCWarning(d->logCat) << this << "re-running with authentication"; - emit retryScheduled(d->retriesTaken, 0); - d->connection->submit(this); - return; - } - if ((error() == NetworkError || error() == Timeout) - && d->retriesTaken < d->maxRetries) { - // TODO: The whole retrying thing should be put to Connection(Manager) - // otherwise independently retrying jobs make a bit of notification - // storm towards the UI. - const seconds retryIn = error() == Timeout ? 0s : getNextRetryInterval(); - ++d->retriesTaken; - qCWarning(d->logCat).nospace() << this << ": retry #" << d->retriesTaken - << " in " << retryIn.count() << " s"; - d->retryTimer.start(retryIn); - emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); - return; + case Unauthorised: + if (!d->needsToken && !d->connection->accessToken().isEmpty()) { + // Rerun with access token (extension of the spec while + // https://github.com/matrix-org/matrix-doc/issues/701 is pending) + d->connection->setNeedsToken(objectName()); + qCWarning(d->logCat) << this << "re-running with authentication"; + emit retryScheduled(d->retriesTaken, 0); + d->connection->submit(this); + return; + } + break; + case NetworkError: + case IncorrectResponse: + case Timeout: + if (d->retriesTaken < d->maxRetries) { + // TODO: The whole retrying thing should be put to + // Connection(Manager) otherwise independently retrying jobs make a + // bit of notification storm towards the UI. + const seconds retryIn = error() == Timeout ? 0s + : getNextRetryInterval(); + ++d->retriesTaken; + qCWarning(d->logCat).nospace() + << this << ": retry #" << d->retriesTaken << " in " + << retryIn.count() << " s"; + d->retryTimer.start(retryIn); + emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); + return; + } + default:; } Q_ASSERT(status().code != Pending); -- cgit v1.2.3 From 812e460f2159556ad00ab838d58b349fa3542047 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 17 Aug 2020 19:38:49 +0200 Subject: Fetch user profile when needed 5849686e introduced a new way of storing user avatars and names - unfortunately it didn't fully cover the case of the user's default (profile) name and avatar. This commit fixes it; in 0.6.x branch, the fix requires a const_cast<> hack since name() and avatarObject() invocations are used as triggers to fetch the profile. 0.7 will have User::fetchProfile() method instead. --- lib/user.cpp | 114 +++++++++++++++++++++++++++++++++++++++++------------------ lib/user.h | 5 +-- 2 files changed, 81 insertions(+), 38 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index cc6cb102..673947ff 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -47,18 +47,41 @@ public: QString id; qreal hueF; + // In the following two, isNull/nullopt mean they are uninitialised; + // isEmpty/Avatar::url().isEmpty() mean they are initialised but empty. QString defaultName; - Avatar defaultAvatar; + std::optional defaultAvatar; + // NB: This container is ever-growing. Even if the user no more scrolls // the timeline that far back, historical avatars are still kept around. // This is consistent with the rest of Quotient, as room timelines - // are never vacuumed either. This will probably change in the future. + // are never rotated either. This will probably change in the future. /// Map of mediaId to Avatar objects static UnorderedMap otherAvatars; + + void fetchProfile(const User* q); + + template + bool doSetAvatar(SourceT&& source, User* q); }; decltype(User::Private::otherAvatars) User::Private::otherAvatars {}; +void User::Private::fetchProfile(const User* q) +{ + defaultAvatar.emplace(Avatar {}); + defaultName = ""; + auto* j = q->connection()->callApi(BackgroundRequest, id); + // FIXME: accepting const User* and const_cast'ing it here is only + // until we get a better User API in 0.7 + QObject::connect(j, &BaseJob::success, q, + [this, q = const_cast(q), j] { + q->updateName(j->displayname()); + defaultAvatar->updateUrl(j->avatarUrl()); + emit q->avatarChanged(q, nullptr); + }); +} + User::User(QString userId, Connection* connection) : QObject(connection), d(new Private(move(userId))) { @@ -88,13 +111,29 @@ int User::hue() const { return int(hueF() * 359); } QString User::name(const Room* room) const { - return room ? room->getCurrentState(id())->displayName() - : d->defaultName; + if (room) + return room->getCurrentState(id())->displayName(); + + if (d->defaultName.isNull()) + d->fetchProfile(this); + + return d->defaultName; } QString User::rawName(const Room* room) const { return name(room); } -void User::updateName(const QString&, const Room*) {} +void User::updateName(const QString& newName, const Room* r) +{ + Q_ASSERT(r == nullptr); + if (newName == d->defaultName) + return; + + emit nameAboutToChange(newName, d->defaultName, nullptr); + const auto& oldName = + std::exchange(d->defaultName, newName); + emit nameChanged(d->defaultName, oldName, nullptr); + +} void User::updateName(const QString&, const QString&, const Room*) {} void User::updateAvatarUrl(const QUrl&, const QUrl&, const Room*) {} @@ -105,14 +144,9 @@ void User::rename(const QString& newName) return; // Nothing to do connect(connection()->callApi(id(), actualNewName), - &BaseJob::success, this, [this,actualNewName] { - if (actualNewName == d->defaultName) - return; // Check again, it could have changed meanwhile - - emit nameAboutToChange(actualNewName, d->defaultName, nullptr); - const auto& oldName = - std::exchange(d->defaultName, actualNewName); - emit nameChanged(d->defaultName, oldName, nullptr); + &BaseJob::success, this, [this, actualNewName] { + d->fetchProfile(this); + updateName(actualNewName); }); } @@ -134,34 +168,42 @@ void User::rename(const QString& newName, const Room* r) } template -inline bool User::doSetAvatar(SourceT&& source) -{ - return d->defaultAvatar.upload( - connection(), source, [this](const QString& contentUri) { - auto* j = connection()->callApi(id(), contentUri); - connect(j, &BaseJob::success, this, - [this, newUrl = QUrl(contentUri)] { - if (newUrl == d->defaultAvatar.url()) { - qCWarning(MAIN) << "User" << id() - << "already has avatar URL set to" - << newUrl.toDisplayString(); - return; - } - - d->defaultAvatar.updateUrl(move(newUrl)); - emit avatarChanged(this, nullptr); - }); +bool User::Private::doSetAvatar(SourceT&& source, User* q) +{ + if (!defaultAvatar) { + defaultName = ""; + defaultAvatar.emplace(Avatar {}); + } + return defaultAvatar->upload( + q->connection(), source, [this, q](const QString& contentUri) { + auto* j = + q->connection()->callApi(id, contentUri); + QObject::connect(j, &BaseJob::success, q, + [this, q, newUrl = QUrl(contentUri)] { + // Fetch displayname to complete the profile + fetchProfile(q); + if (newUrl == defaultAvatar->url()) { + qCWarning(MAIN) + << "User" << id + << "already has avatar URL set to" + << newUrl.toDisplayString(); + return; + } + + defaultAvatar->updateUrl(move(newUrl)); + emit q->avatarChanged(q, nullptr); + }); }); } bool User::setAvatar(const QString& fileName) { - return doSetAvatar(fileName); + return d->doSetAvatar(fileName, this); } bool User::setAvatar(QIODevice* source) { - return doSetAvatar(source); + return d->doSetAvatar(source, this); } void User::requestDirectChat() { connection()->requestDirectChat(this); } @@ -189,8 +231,12 @@ QString User::bridged() const { return {}; } const Avatar& User::avatarObject(const Room* room) const { - if (!room) - return d->defaultAvatar; + if (!room) { + if (!d->defaultAvatar) { + d->fetchProfile(this); + } + return *d->defaultAvatar; + } const auto& url = room->getCurrentState(id())->avatarUrl(); const auto& mediaId = url.authority() + url.path(); diff --git a/lib/user.h b/lib/user.h index ceea7c48..a4985877 100644 --- a/lib/user.h +++ b/lib/user.h @@ -163,7 +163,7 @@ signals: private slots: // TODO: remove in 0.7 /// \deprecated - void updateName(const QString&, const Room* = nullptr); + void updateName(const QString& newName, const Room* r = nullptr); /// \deprecated void updateName(const QString&, const QString&, const Room* = nullptr); /// \deprecated @@ -172,8 +172,5 @@ private slots: // TODO: remove in 0.7 private: class Private; QScopedPointer d; - - template - bool doSetAvatar(SourceT&& source); }; } // namespace Quotient -- cgit v1.2.3 From 744592ee490858c464bba216cf19161aaf30d786 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 22 Aug 2020 21:37:42 +0200 Subject: Cleanup and some extra comments --- lib/qt_connection_util.h | 2 +- lib/room.cpp | 4 ++-- lib/user.cpp | 1 - lib/util.h | 6 +++++- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/qt_connection_util.h b/lib/qt_connection_util.h index 04dc6b27..699735d4 100644 --- a/lib/qt_connection_util.h +++ b/lib/qt_connection_util.h @@ -46,7 +46,7 @@ namespace _impl { // arguments are always copied (at best - COWed) to the context of // the slot. Therefore the slot decorator receives const ArgTs&... // rather than ArgTs&&... - // TODO: std::bind_front() instead of lambda. + // TODO (C++20): std::bind_front() instead of lambda. c = QObject::connect(sender, signal, context, [pc = std::move(pc), decoratedSlot = std::move(decoratedSlot)](const ArgTs&... args) { diff --git a/lib/room.cpp b/lib/room.cpp index 1e3eb603..0a4e041b 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2311,7 +2311,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) } } - qCDebug(STATE) << "Room" << q->objectName() << "received" + qCDebug(MESSAGES) << "Room" << q->objectName() << "received" << totalInserted << "new events; the last event is now" << timeline.back(); @@ -2326,7 +2326,7 @@ Room::Changes Room::Private::addNewMessageEvents(RoomEvents&& events) if (q->readMarker(firstWriter) != timeline.crend()) { roomChanges |= promoteReadMarker(firstWriter, rev_iter_t(from) - 1); - qCDebug(STATE) + qCDebug(MESSAGES) << "Auto-promoted read marker for" << senderId << "to" << *q->readMarker(firstWriter); } diff --git a/lib/user.cpp b/lib/user.cpp index 673947ff..3c61bfd6 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -132,7 +132,6 @@ void User::updateName(const QString& newName, const Room* r) const auto& oldName = std::exchange(d->defaultName, newName); emit nameChanged(d->defaultName, oldName, nullptr); - } void User::updateName(const QString&, const QString&, const Room*) {} void User::updateAvatarUrl(const QUrl&, const QUrl&, const Room*) {} diff --git a/lib/util.h b/lib/util.h index 943ff8db..8c92df74 100644 --- a/lib/util.h +++ b/lib/util.h @@ -200,6 +200,10 @@ using fn_arg_t = // TODO: get rid of it as soon as Apple Clang gets proper deduction guides // for std::function<> +// ...or consider using QtPrivate magic used by QObject::connect() +// since wrap_in_function() is actually made for qt_connection_util.h +// ...for inspiration, also check a possible std::not_fn implementation at +// https://en.cppreference.com/w/cpp/utility/functional/not_fn template inline auto wrap_in_function(FnT&& f) { @@ -217,7 +221,7 @@ inline auto operator"" _ls(const char* s, std::size_t size) */ template class Range { - // Looking forward to C++23 ranges + // Looking forward to C++20 ranges using iterator = typename ArrayT::iterator; using const_iterator = typename ArrayT::const_iterator; using size_type = typename ArrayT::size_type; -- cgit v1.2.3 From bc105c2fef5334d0654071f72e248a0892a9b20b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 23 Aug 2020 10:28:59 +0200 Subject: More cleanup; drop Qt bearer management on Qt 5.15+ Qt 5.15 deprecates bearer management. --- lib/networkaccessmanager.cpp | 5 +++-- lib/util.cpp | 30 +++++++++--------------------- 2 files changed, 12 insertions(+), 23 deletions(-) (limited to 'lib') diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 8ee080bf..b9037bcc 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -50,11 +50,12 @@ void NetworkAccessManager::clearIgnoredSslErrors() static NetworkAccessManager* createNam() { auto nam = new NetworkAccessManager(QCoreApplication::instance()); - // See #109. Once Qt bearer management gets better, this workaround - // should become unnecessary. +#if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) + // See #109; in newer Qt, bearer management is deprecated altogether nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, [nam] { nam->setNetworkAccessible(QNetworkAccessManager::Accessible); }); +#endif return nam; } diff --git a/lib/util.cpp b/lib/util.cpp index 61661de8..797f6d69 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -126,32 +126,30 @@ QString Quotient::serverPart(const QString& mxId) % ServerPartRegEx % ")$"; static QRegularExpression parser( re, - QRegularExpression::UseUnicodePropertiesOption); // Because Asian - // digits + QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits return parser.match(mxId).captured(1); } // Tests for function_traits<> -#ifdef Q_CC_CLANG -# pragma clang diagnostic push -# pragma ide diagnostic ignored "OCSimplifyInspection" -#endif using namespace Quotient; -int f(); -static_assert(std::is_same, int>::value, +int f_(); +static_assert(std::is_same, int>::value, "Test fn_return_t<>"); -void f1(int, QString); -static_assert(std::is_same, QString>::value, +void f1_(int, QString); +static_assert(std::is_same, QString>::value, "Test fn_arg_t<>"); struct Fo { int operator()(); + static constexpr auto l = [] { return 0.0f; }; }; static_assert(std::is_same, int>::value, "Test return type of function object"); +static_assert(std::is_same, float>::value, + "Test return type of lambda"); struct Fo1 { void operator()(int); @@ -159,20 +157,10 @@ struct Fo1 { static_assert(std::is_same, int>(), "Test fn_arg_t defaulting to first argument"); -#if (!defined(_MSC_VER) || _MSC_VER >= 1910) -static auto l = [] { return 1; }; -static_assert(std::is_same, int>::value, - "Test fn_return_t<> with lambda"); -#endif - template -QString ft(T&&) +static QString ft(T&&) { return {}; } static_assert(std::is_same)>, QString&&>(), "Test function templates"); - -#ifdef Q_CC_CLANG -# pragma clang diagnostic pop -#endif -- cgit v1.2.3 From c306470f21b888e0195f473c5030be40ffcdb5fc Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 23 Aug 2020 16:14:07 +0200 Subject: csapi/profile.cpp: fix setting empty name/avatar This is a temporary workaround for https://github.com/matrix-org/synapse/issues/8029 - note that regenerating CS API code will lead to overwriting this. The proper fix should be done in matrix-doc (see https://github.com/matrix-org/matrix-doc/issues/2717). --- lib/csapi/profile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/csapi/profile.cpp b/lib/csapi/profile.cpp index cb8f72be..8436b8e6 100644 --- a/lib/csapi/profile.cpp +++ b/lib/csapi/profile.cpp @@ -15,7 +15,7 @@ SetDisplayNameJob::SetDisplayNameJob(const QString& userId, % "/displayname") { QJsonObject _data; - addParam(_data, QStringLiteral("displayname"), displayname); + addParam<>(_data, QStringLiteral("displayname"), displayname); setRequestData(std::move(_data)); } @@ -39,7 +39,7 @@ SetAvatarUrlJob::SetAvatarUrlJob(const QString& userId, const QString& avatarUrl % "/avatar_url") { QJsonObject _data; - addParam(_data, QStringLiteral("avatar_url"), avatarUrl); + addParam<>(_data, QStringLiteral("avatar_url"), avatarUrl); setRequestData(std::move(_data)); } -- cgit v1.2.3 From e9d04a5f527e823232807100966ed85b5d3360db Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 23 Aug 2020 16:17:01 +0200 Subject: User: displayname() should fallback to name() As of 0.6.1, User::name() triggers fetching the user profile (whether this is right is a separate question but that's the way it works with the current library API) - displayname() should use it rather than shortcut to d->defaultName to get results consistent with User::name(). --- lib/user.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index 3c61bfd6..dac1bb68 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -215,9 +215,13 @@ bool User::isIgnored() const { return connection()->isIgnored(this); } QString User::displayname(const Room* room) const { - return room ? room->roomMembername(this) - : d->defaultName.isEmpty() ? d->id - : d->defaultName; + if (room) + return room->roomMembername(this); + + if (auto n = name(); !n.isEmpty()) + return n; + + return d->id; } QString User::fullName(const Room* room) const -- cgit v1.2.3 From 813c8e3fe0da9ee3b46fff1618d3e4ed05f2c429 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Sep 2020 22:32:33 +0200 Subject: SyndData::parseJson: use fromJson() ...instead of a complicated explicit code converting from JSON to varianthash to hash. --- lib/syncdata.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/syncdata.cpp b/lib/syncdata.cpp index a9ec5ee7..e6472e18 100644 --- a/lib/syncdata.cpp +++ b/lib/syncdata.cpp @@ -182,12 +182,8 @@ void SyncData::parseJson(const QJsonObject& json, const QString& baseDir) accountData = load(json, "account_data"_ls); toDeviceEvents = load(json, "to_device"_ls); - auto deviceOneTimeKeysCountVariantHash = - json.value("device_one_time_keys_count"_ls).toObject().toVariantHash(); - for (auto key : deviceOneTimeKeysCountVariantHash.keys()) { - deviceOneTimeKeysCount_.insert( - key, deviceOneTimeKeysCountVariantHash.value(key).toInt()); - } + fromJson(json.value("device_one_time_keys_count"_ls), + deviceOneTimeKeysCount_); auto rooms = json.value("rooms"_ls).toObject(); JoinStates::Int ii = 1; // ii is used to make a JoinState value -- cgit v1.2.3 From 787601f22025fc33bbcff768ebc6e554ec2809a2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Sep 2020 22:32:50 +0200 Subject: Address clazy warnings --- lib/room.cpp | 2 +- lib/settings.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 0a4e041b..6b2b7b23 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -553,7 +553,7 @@ QStringList Room::localAliases() const QStringList Room::remoteAliases() const { QStringList result; - for (const auto& s : d->aliasServers) + for (const auto& s : std::as_const(d->aliasServers)) result += d->getCurrentState(s)->aliases(); return result; } diff --git a/lib/settings.cpp b/lib/settings.cpp index 5413693d..dd086d9c 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -53,11 +53,12 @@ bool Settings::contains(const QString& key) const QStringList Settings::childGroups() const { - auto l = QSettings::childGroups(); - for (const auto& g: legacySettings.childGroups()) - if (!l.contains(g)) - l.push_back(g); - return l; + auto groups = QSettings::childGroups(); + const auto& legacyGroups = legacySettings.childGroups(); + for (const auto& g: legacyGroups) + if (!groups.contains(g)) + groups.push_back(g); + return groups; } void SettingsGroup::setValue(const QString& key, const QVariant& value) -- cgit v1.2.3 From 1ce9a71999fce0ad2efe1f7063316947714bab2a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Sep 2020 22:33:03 +0200 Subject: Code formatting fix --- lib/connection.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.h b/lib/connection.h index 258280a8..b925918e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -418,7 +418,7 @@ public: /*! Start a pre-created job object on this connection */ Q_INVOKABLE BaseJob* run(BaseJob* job, - RunningPolicy runningPolicy = ForegroundRequest); + RunningPolicy runningPolicy = ForegroundRequest); /*! Start a job of a specified type with specified arguments and policy * -- cgit v1.2.3 From cb47079d239b0a4bcd0346d43cccbebfc9e5a285 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 4 Sep 2020 07:59:50 +0200 Subject: csapi/profile.h: require displayname/avatar_url See https://github.com/matrix-org/matrix-doc/issues/2717 --- lib/csapi/profile.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/csapi/profile.h b/lib/csapi/profile.h index 88bf3166..3858fab2 100644 --- a/lib/csapi/profile.h +++ b/lib/csapi/profile.h @@ -24,7 +24,7 @@ public: * The new display name for this user. */ explicit SetDisplayNameJob(const QString& userId, - const QString& displayname = {}); + const QString& displayname); }; /*! \brief Get the user's display name. @@ -73,8 +73,7 @@ public: * \param avatarUrl * The new avatar URL for this user. */ - explicit SetAvatarUrlJob(const QString& userId, - const QString& avatarUrl = {}); + explicit SetAvatarUrlJob(const QString& userId, const QString& avatarUrl); }; /*! \brief Get the user's avatar URL. -- cgit v1.2.3 From 4f6df90d85adaaeb7012d2e56a2691481eed5a9b Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 4 Sep 2020 18:39:33 +0200 Subject: More fixing of clazy warnings --- lib/avatar.cpp | 4 ++-- lib/connection.cpp | 7 ++++--- lib/connection.h | 2 +- lib/room.cpp | 13 ++++++++----- 4 files changed, 15 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/avatar.cpp b/lib/avatar.cpp index cb734984..c65aa25c 100644 --- a/lib/avatar.cpp +++ b/lib/avatar.cpp @@ -45,7 +45,7 @@ public: QImage get(Connection* connection, QSize size, get_callback_t callback) const; - bool upload(UploadContentJob* job, upload_callback_t callback); + bool upload(UploadContentJob* job, upload_callback_t&& callback); bool checkUrl(const QUrl& url) const; QString localFile() const; @@ -154,7 +154,7 @@ QImage Avatar::Private::get(Connection* connection, QSize size, return result; } -bool Avatar::Private::upload(UploadContentJob* job, upload_callback_t callback) +bool Avatar::Private::upload(UploadContentJob* job, upload_callback_t &&callback) { _uploadRequest = job; if (!isJobRunning(_uploadRequest)) diff --git a/lib/connection.cpp b/lib/connection.cpp index 4530d95a..d32c7472 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -930,7 +930,7 @@ void Connection::doInDirectChat(User* u, // There can be more than one DC; find the first valid (existing and // not left), and delete inexistent (forgotten?) ones along the way. DirectChatsMap removals; - for (auto it = d->directChats.find(u); + for (auto it = std::as_const(d->directChats).find(u); it != d->directChats.end() && it.key() == u; ++it) { const auto& roomId = *it; if (auto r = room(roomId, JoinState::Join)) { @@ -1231,7 +1231,7 @@ int Connection::roomsCount(JoinStates joinStates) const { // Using int to maintain compatibility with QML // (consider also that QHash<>::size() returns int anyway). - return int(std::count_if(d->roomMap.begin(), d->roomMap.end(), + return int(std::count_if(d->roomMap.cbegin(), d->roomMap.cend(), [joinStates](Room* r) { return joinStates.testFlag(r->joinState()); })); @@ -1296,7 +1296,8 @@ QStringList Connection::tagNames() const QVector Connection::roomsWithTag(const QString& tagName) const { QVector rooms; - std::copy_if(d->roomMap.begin(), d->roomMap.end(), std::back_inserter(rooms), + std::copy_if(d->roomMap.cbegin(), d->roomMap.cend(), + std::back_inserter(rooms), [&tagName](Room* r) { return r->tags().contains(tagName); }); return rooms; } diff --git a/lib/connection.h b/lib/connection.h index b925918e..3618f284 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -326,7 +326,7 @@ public: const QStringList& previousRoomAliases, const QStringList& roomAliases); Q_INVOKABLE Quotient::Room* invitation(const QString& roomId) const; - Q_INVOKABLE Quotient::User* user(const QString& userId); + Q_INVOKABLE Quotient::User* user(const QString& uId); const User* user() const; User* user(); QString userId() const; diff --git a/lib/room.cpp b/lib/room.cpp index 6b2b7b23..90dca127 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -296,8 +296,8 @@ public: void dropDuplicateEvents(RoomEvents& events) const; Changes setLastReadEvent(User* u, QString eventId); - void updateUnreadCount(rev_iter_t from, rev_iter_t to); - Changes promoteReadMarker(User* u, rev_iter_t newMarker, bool force = false); + void updateUnreadCount(const rev_iter_t& from, const rev_iter_t& to); + Changes promoteReadMarker(User* u, const rev_iter_t& newMarker, bool force = false); Changes markMessagesAsRead(rev_iter_t upToMarker); @@ -640,7 +640,8 @@ Room::Changes Room::Private::setLastReadEvent(User* u, QString eventId) return Change::NoChange; } -void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) +void Room::Private::updateUnreadCount(const rev_iter_t& from, + const rev_iter_t& to) { Q_ASSERT(from >= timeline.crbegin() && from <= timeline.crend()); Q_ASSERT(to >= from && to <= timeline.crend()); @@ -682,7 +683,8 @@ void Room::Private::updateUnreadCount(rev_iter_t from, rev_iter_t to) } } -Room::Changes Room::Private::promoteReadMarker(User* u, rev_iter_t newMarker, +Room::Changes Room::Private::promoteReadMarker(User* u, + const rev_iter_t& newMarker, bool force) { Q_ASSERT_X(u, __FUNCTION__, "User* should not be nullptr"); @@ -1242,6 +1244,7 @@ QList Room::users() const { return d->membersMap.values(); } QStringList Room::memberNames() const { QStringList res; + res.reserve(d->membersMap.size()); for (auto u : qAsConst(d->membersMap)) res.append(roomMembername(u)); @@ -1686,7 +1689,7 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, uploadFile(txnId, localPath); // Below, the upload job is used as a context object to clean up connections connect(this, &Room::fileTransferCompleted, d->fileTransfers[txnId].job, - [this, txnId](const QString& id, QUrl, const QUrl& mxcUri) { + [this, txnId](const QString& id, const QUrl&, const QUrl& mxcUri) { if (id == txnId) { auto it = findPendingEvent(txnId); if (it != d->unsyncedEvents.end()) { -- cgit v1.2.3 From 3c85f049389dec3b0ee6406f0be2cfaf0089f1fe Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 11 Sep 2020 06:50:45 +0200 Subject: More stringent serverpart checks in user ids May lead to new crashes due to nullptr returned from Connection::user() on more utterly invalid content from the wire that the library still doesn't properly invalidate. This has long been quite a good case for exceptions, or another error-handling framework: Connection::user() can return nullptr either when out of memory or when the id is invalid or empty, and other places are likely to treat invalid ids in different ways but probably just hope that memory exhaustion "never happens", or try to handle it in a quite different way than an empty or invalid id. Something to think of in 0.7. --- lib/connection.cpp | 2 +- lib/util.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index d32c7472..b6a3198d 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1150,7 +1150,7 @@ User* Connection::user(const QString& uId) { if (uId.isEmpty()) return nullptr; - if (!uId.startsWith('@') || !uId.contains(':')) { + if (!uId.startsWith('@') || serverPart(uId).isEmpty()) { qCCritical(MAIN) << "Malformed userId:" << uId; return nullptr; } diff --git a/lib/util.cpp b/lib/util.cpp index 797f6d69..023645c1 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -116,13 +116,13 @@ qreal Quotient::stringToHueF(const QString& s) } static const auto ServerPartRegEx = QStringLiteral( - "(\\[[^]]+\\]|[^:@]+)" // Either IPv6 address or hostname/IPv4 address + "(\\[[^][:blank:]]+\\]|[-[:alnum:].]+)" // Either IPv6 address or hostname/IPv4 address "(?::(\\d{1,5}))?" // Optional port ); QString Quotient::serverPart(const QString& mxId) { - static QString re = "^[@!#$+].+?:(" // Localpart and colon + static QString re = "^[@!#$+].*?:(" // Localpart and colon % ServerPartRegEx % ")$"; static QRegularExpression parser( re, -- cgit v1.2.3 From 0e87640560343c15b0a218796509d2d94e1a5c77 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 11 Sep 2020 07:10:32 +0200 Subject: util.cpp: assert validity of regular expressions --- lib/util.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/util.cpp b/lib/util.cpp index 023645c1..ffb36068 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -60,6 +60,8 @@ void Quotient::linkifyUrls(QString& htmlEscapedText) QStringLiteral( R"((^|[^<>/])([!#@][-a-z0-9_=#/.]{1,252}:(?:\w|\.|-)+\.\w+(?::\d{1,5})?))"), RegExpOptions); + Q_ASSERT(FullUrlRegExp.isValid() && EmailAddressRegExp.isValid() + && MxIdRegExp.isValid()); // NOTE: htmlEscapedText is already HTML-escaped! No literal <,>,&," @@ -127,6 +129,7 @@ QString Quotient::serverPart(const QString& mxId) static QRegularExpression parser( re, QRegularExpression::UseUnicodePropertiesOption); // Because Asian digits + Q_ASSERT(parser.isValid()); return parser.match(mxId).captured(1); } -- cgit v1.2.3 From 948be2ef2bf04e306fbb0e2c3e0a98f4151337a7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 11 Sep 2020 07:15:01 +0200 Subject: Uri::toUrl(): fix incorrect matrix.to rep The first character inside the fragment should be / --- lib/uri.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/uri.cpp b/lib/uri.cpp index f813794c..8370d83a 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -134,7 +134,7 @@ QUrl Uri::toUrl(UriForm form) const url.setScheme("https"); url.setHost("matrix.to"); url.setPath("/"); - auto fragment = primaryId(); + auto fragment = '/' + primaryId(); if (const auto& secId = secondaryId(); !secId.isEmpty()) fragment += '/' + secId; if (const auto& q = query(); !q.isEmpty()) -- cgit v1.2.3 From 398304961570f78019ddeb545c2cc5f18b6dd706 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 8 Nov 2020 16:33:19 +0100 Subject: User: take profile data from prevContent when main content omits them Closes #412. --- lib/user.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/user.cpp b/lib/user.cpp index dac1bb68..3c9652b0 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -109,10 +109,24 @@ bool User::isGuest() const int User::hue() const { return int(hueF() * 359); } +/// \sa https://github.com/matrix-org/matrix-doc/issues/1375 +/// +/// Relies on untrusted prevContent so can't be put to RoomMemberEvent and +/// in general should rather be remade in terms of the room's eventual "state +/// time machine" +QString getBestKnownName(const RoomMemberEvent* event) +{ + const auto& jv = event->contentJson().value("displayname"_ls); + return !jv.isUndefined() + ? jv.toString() + : event->prevContent() ? event->prevContent()->displayName + : QString(); +} + QString User::name(const Room* room) const { if (room) - return room->getCurrentState(id())->displayName(); + return getBestKnownName(room->getCurrentState(id())); if (d->defaultName.isNull()) d->fetchProfile(this); @@ -232,6 +246,16 @@ QString User::fullName(const Room* room) const QString User::bridged() const { return {}; } +/// \sa getBestKnownName, https://github.com/matrix-org/matrix-doc/issues/1375 +QUrl getBestKnownAvatarUrl(const RoomMemberEvent* event) +{ + const auto& jv = event->contentJson().value("avatar_url"_ls); + return !jv.isUndefined() + ? jv.toString() + : event->prevContent() ? event->prevContent()->avatarUrl + : QUrl(); +} + const Avatar& User::avatarObject(const Room* room) const { if (!room) { @@ -241,7 +265,8 @@ const Avatar& User::avatarObject(const Room* room) const return *d->defaultAvatar; } - const auto& url = room->getCurrentState(id())->avatarUrl(); + const auto& url = + getBestKnownAvatarUrl(room->getCurrentState(id())); const auto& mediaId = url.authority() + url.path(); return d->otherAvatars.try_emplace(mediaId, url).first->second; } @@ -280,21 +305,26 @@ void User::processEvent(const RoomMemberEvent& event, const Room* room, { Q_ASSERT(room); - const auto& oldName = firstMention || !event.prevContent() ? QString() - : event.prevContent()->displayName; - const auto& newName = event.displayName(); - // A hacky way to find out if it's about to change or already changed - bool isAboutToChange = room->getCurrentState(id()) != &event; - if (newName != oldName) { - if (isAboutToChange) + // This is prone to abuse if prevContent is forged; only here until 0.7 + // (and the whole method, actually). + const auto& oldName = event.prevContent() ? event.prevContent()->displayName + : QString(); + const auto& newName = getBestKnownName(&event); + // A hacky way to find out if it's about to change or already changed; + // making it a lambda allows to omit stub event creation when unneeded + const auto& isAboutToChange = [&event, room, this] { + return room->getCurrentState(id()) != &event; + }; + if (firstMention || newName != oldName) { + if (isAboutToChange()) emit nameAboutToChange(newName, oldName, room); - else { + else emit nameChanged(newName, oldName, room); - } } - const auto& oldAvatarUrl = firstMention || !event.prevContent() ? QUrl() - : event.prevContent()->avatarUrl; - if (event.avatarUrl() != oldAvatarUrl && !isAboutToChange) + const auto& oldAvatarUrl = + event.prevContent() ? event.prevContent()->avatarUrl : QUrl(); + const auto& newAvatarUrl = getBestKnownAvatarUrl(&event); + if ((firstMention || newAvatarUrl != oldAvatarUrl) && !isAboutToChange()) emit avatarChanged(this, room); } -- cgit v1.2.3 From 2162cc8c6b026bbdf077f0b51a89a38e20963cd6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 9 Nov 2020 20:44:52 +0100 Subject: Room: be more robust in Release mode ...in one particular case, when trying to add a user to a room it's already a member of. --- lib/room.cpp | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 90dca127..2599e036 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1346,6 +1346,11 @@ void Room::Private::insertMemberIntoMap(User* u) // Callers should check they are not adding an existing user once more. Q_ASSERT(!namesakes.contains(u)); + if (namesakes.contains(u)) { // Release version whines but continues + qCCritical(STATE) << "Trying to add a user" << u->id() << "to room" + << q->objectName() << "but that's already in it"; + return; + } if (namesakes.size() == 1) emit q->memberAboutToRename(namesakes.front(), @@ -2402,8 +2407,8 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) const auto*& curStateEvent = d->currentState[{ e.matrixType(), e.stateKey() }]; // Prepare for the state change - visit(e, [this, oldRme = static_cast(curStateEvent)]( - const RoomMemberEvent& rme) { + const auto oldRme = static_cast(curStateEvent); + visit(e, [this, &oldRme](const RoomMemberEvent& rme) { auto* u = user(rme.userId()); if (!u) { // ??? qCCritical(MAIN) @@ -2506,23 +2511,20 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) emit avatarChanged(); return AvatarChange; } - , [this,oldStateEvent] (const RoomMemberEvent& evt) { + , [this,oldRme] (const RoomMemberEvent& evt) { // clang-format on auto* u = user(evt.userId()); - const auto* oldMemberEvent = - static_cast(oldStateEvent); // TODO: remove in 0.7 - u->processEvent(evt, this, oldMemberEvent == nullptr); + u->processEvent(evt, this, oldRme == nullptr); - const auto prevMembership = oldMemberEvent - ? oldMemberEvent->membership() - : MembershipType::Leave; + const auto prevMembership = oldRme ? oldRme->membership() + : MembershipType::Leave; switch (evt.membership()) { case MembershipType::Join: if (prevMembership != MembershipType::Join) { d->insertMemberIntoMap(u); emit userAdded(u); - } else if (oldMemberEvent->displayName() != evt.displayName()) { + } else if (oldRme->displayName() != evt.displayName()) { d->insertMemberIntoMap(u); emit memberRenamed(u); } -- cgit v1.2.3 From 693241d572e3e7e6b23435104a1b98c9afbec5c9 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 14 Nov 2020 19:58:58 +0100 Subject: ConnectionData: fix defunct jobs stalling the queue --- lib/connectiondata.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index e806f952..d57363d0 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -67,7 +67,8 @@ ConnectionData::ConnectionData(QUrl baseUrl) d->rateLimiter.setInterval(0); for (auto& q : d->jobs) while (!q.empty()) { - auto& job = q.front(); + const auto job = q.front(); + q.pop(); if (!job || job->error() == BaseJob::Abandoned) continue; if (job->error() != BaseJob::Pending) { @@ -79,7 +80,6 @@ ConnectionData::ConnectionData(QUrl baseUrl) } job->sendRequest(); d->rateLimiter.start(); - q.pop(); return; } qCDebug(MAIN) << d->id() << "job queues are empty"; -- cgit v1.2.3 From e81117fbd97f9ea077d377c89fdabd19726b3fbf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 15 Nov 2020 13:05:39 +0100 Subject: Room::P::getCurrentState<>(): bypass the factory chain This is to optimize a rather hot path creating stub events (for member events in bigger rooms, in particular) when the event type is known. Version 0.7 will have a completely different code based on event content rather than event that will obviate stubs creation but 0.6.x can benefit from it. --- lib/room.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 2599e036..dd95afe0 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -231,9 +231,25 @@ public: template const EventT* getCurrentState(const QString& stateKey = {}) const { - const auto* evt = getCurrentState({ EventT::matrixTypeId(), stateKey }); + const StateEventKey evtKey { EventT::matrixTypeId(), stateKey }; + const auto* evt = currentState.value(evtKey, nullptr); + if (!evt) { + if (stubbedState.find(evtKey) == stubbedState.end()) { + // In the absence of a real event, make a stub as-if an event + // with empty content has been received. Event classes should be + // prepared for empty/invalid/malicious content anyway. + stubbedState.emplace( + evtKey, makeEvent(basicStateEventJson( + EventT::matrixTypeId(), {}, evtKey.second))); + qCDebug(STATE) << "A new stub event created for key {" + << evtKey.first << evtKey.second << "}"; + } + evt = stubbedState[evtKey].get(); + Q_ASSERT(evt); + } Q_ASSERT(evt->type() == EventT::typeId() - && evt->matrixType() == EventT::matrixTypeId()); + && evt->matrixType() == EventT::matrixTypeId() + && evt->stateKey() == stateKey); return static_cast(evt); } -- cgit v1.2.3 From 6e8be18d1587af3dc82526e32fb12d3a895908f2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 10 Nov 2020 13:07:28 +0100 Subject: MembershipType: drop warning on empty values This is a usual situation when a membership type is undefined; and the current code constructs _a lot_ of stub events by loading them from empty JSON. So just silence those warnings for now. --- lib/events/roommemberevent.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 35cbdb3a..3193a54d 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -39,7 +39,8 @@ struct JsonConverter { if (membershipString == *it) return MembershipType(it - membershipStrings.begin()); - qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; + if (!membershipString.isEmpty()) + qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; return MembershipType::Undefined; } }; -- cgit v1.2.3 From 2340f3dab4f03743582099bf2d81bf4fc9976ed4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 12 Nov 2020 18:52:06 +0100 Subject: Room: add power level events to redaction rules --- lib/room.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index dd95afe0..e3603110 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2053,16 +2053,14 @@ RoomEventPtr makeRedacted(const RoomEvent& target, std::vector> keepContentKeysMap { { RoomMemberEvent::typeId(), { QStringLiteral("membership") } }, - { RoomCreateEvent::typeId(), { QStringLiteral("creator") } } - // , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } } - // , { RoomPowerLevels::typeId(), - // { QStringLiteral("ban"), QStringLiteral("events"), - // QStringLiteral("events_default"), - // QStringLiteral("kick"), QStringLiteral("redact"), - // QStringLiteral("state_default"), QStringLiteral("users"), - // QStringLiteral("users_default") } } - , + { RoomCreateEvent::typeId(), { QStringLiteral("creator") } }, + { RoomPowerLevelsEvent::typeId(), + { QStringLiteral("ban"), QStringLiteral("events"), + QStringLiteral("events_default"), QStringLiteral("kick"), + QStringLiteral("redact"), QStringLiteral("state_default"), + QStringLiteral("users"), QStringLiteral("users_default") } }, { RoomAliasesEvent::typeId(), { QStringLiteral("aliases") } } + // , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } } // , { RoomHistoryVisibility::typeId(), // { QStringLiteral("history_visibility") } } }; -- cgit v1.2.3 From a7b688c341befa0001889f5865c27ce83797bcdd Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 19 Nov 2020 14:56:09 +0100 Subject: Fix Q_ASSERT failure on sending messages Changes in e81117fb exposed a flaw in EncryptionEvent causing assertion failure when this event is default-initialised (i.e. no encryption). --- lib/events/encryptionevent.cpp | 19 +++++-------------- lib/room.cpp | 8 +++++++- 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index 073303b0..f1bde621 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -1,21 +1,14 @@ -// -// Created by rusakov on 26/09/2017. -// Contributed by andreev on 27/06/2019. -// - #include "encryptionevent.h" -#include "converters.h" #include "e2ee.h" -#include "logging.h" #include +namespace Quotient { static const std::array encryptionStrings = { - { Quotient::MegolmV1AesSha2AlgoKey } + { MegolmV1AesSha2AlgoKey } }; -namespace Quotient { template <> struct JsonConverter { static EncryptionType load(const QJsonValue& jv) @@ -26,7 +19,8 @@ struct JsonConverter { if (encryptionString == *it) return EncryptionType(it - encryptionStrings.begin()); - qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString; + if (!encryptionString.isEmpty()) + qCWarning(EVENTS) << "Unknown EncryptionType: " << encryptionString; return EncryptionType::Undefined; } }; @@ -35,7 +29,7 @@ struct JsonConverter { using namespace Quotient; EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) - : encryption(fromJson(json["algorithm"_ls])) + : encryption(fromJson(json[AlgorithmKeyL])) , algorithm(sanitized(json[AlgorithmKeyL].toString())) , rotationPeriodMs(json[RotationPeriodMsKeyL].toInt(604800000)) , rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100)) @@ -44,9 +38,6 @@ EncryptionEventContent::EncryptionEventContent(const QJsonObject& json) void EncryptionEventContent::fillJson(QJsonObject* o) const { Q_ASSERT(o); - Q_ASSERT_X( - encryption != EncryptionType::Undefined, __FUNCTION__, - "The key 'algorithm' must be explicit in EncryptionEventContent"); if (encryption != EncryptionType::Undefined) o->insert(AlgorithmKey, algorithm); o->insert(RotationPeriodMsKey, rotationPeriodMs); diff --git a/lib/room.cpp b/lib/room.cpp index e3603110..ab2932bd 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2557,8 +2557,14 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) // clang-format off } , [this, oldEncEvt = static_cast(oldStateEvent)]( - const EncryptionEvent&) { + const EncryptionEvent& ee) { // clang-format on + if (ee.algorithm().isEmpty()) { + qWarning(STATE) + << "The encryption event for room" << objectName() + << "doesn't have 'algorithm' specified - ignoring"; + return NoChange; + } if (oldEncEvt && oldEncEvt->encryption() != EncryptionEventContent::Undefined) { qCWarning(STATE) << "The room is already encrypted but a new" -- cgit v1.2.3 From e7c9ca9017eb68b1c17e352f3ee60df0e939aa5e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 3 Dec 2020 17:43:44 +0100 Subject: Room: fix breakage in internal member map The change in 39830496 led to prev_content becoming a fallback not only for displaying user names but also for storing them in the internal member map, which is really not what was intended. --- lib/room.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index ab2932bd..a5ed76a8 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -192,7 +192,7 @@ public: // void inviteUser(User* u); // We might get it at some point in time. void insertMemberIntoMap(User* u); - void removeMemberFromMap(const QString& username, User* u); + void removeMemberFromMap(User* u); // This updates the room displayname field (which is the way a room // should be shown in the room list); called whenever the list of @@ -1355,7 +1355,8 @@ Room::Changes Room::Private::setSummary(RoomSummary&& newSummary) void Room::Private::insertMemberIntoMap(User* u) { - const auto userName = u->name(q); + const auto userName = + getCurrentState(u->id())->displayName(); // If there is exactly one namesake of the added user, signal member // renaming for that other one because the two should be disambiguated now. const auto namesakes = membersMap.values(userName); @@ -1376,16 +1377,19 @@ void Room::Private::insertMemberIntoMap(User* u) emit q->memberRenamed(namesakes.front()); } -void Room::Private::removeMemberFromMap(const QString& username, User* u) +void Room::Private::removeMemberFromMap(User* u) { + const auto userName = + getCurrentState(u->id())->displayName(); + User* namesake = nullptr; - auto namesakes = membersMap.values(username); + auto namesakes = membersMap.values(userName); if (namesakes.size() == 2) { namesake = namesakes.front() == u ? namesakes.back() : namesakes.front(); Q_ASSERT_X(namesake != u, __FUNCTION__, "Room members list is broken"); - emit q->memberAboutToRename(namesake, username); + emit q->memberAboutToRename(namesake, userName); } - membersMap.remove(username, u); + membersMap.remove(userName, u); // If there was one namesake besides the removed user, signal member // renaming for it because it doesn't need to be disambiguated any more. if (namesake) @@ -2446,7 +2450,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) case MembershipType::Join: // rename/avatar change or no-op if (rme.displayName() != oldRme->displayName()) { emit memberAboutToRename(u, rme.displayName()); - d->removeMemberFromMap(u->name(this), u); + d->removeMemberFromMap(u); } break; case MembershipType::Invite: @@ -2454,7 +2458,7 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) << rme; [[fallthrough]]; default: // whatever the new membership, it's no more Join - d->removeMemberFromMap(u->name(this), u); + d->removeMemberFromMap(u); emit userRemoved(u); } break; @@ -2755,7 +2759,7 @@ QString Room::Private::calculateDisplayname() const const bool localUserIsIn = joinState == JoinState::Join; const bool emptyRoom = membersMap.isEmpty() - || (membersMap.size() == 1 && isLocalUser(*membersMap.begin())); + || (membersMap.size() == 1 && isLocalUser(*membersMap.cbegin())); const bool nonEmptySummary = summary.heroes && !summary.heroes->empty(); auto shortlist = nonEmptySummary ? buildShortlist(*summary.heroes) : !emptyRoom ? buildShortlist(membersMap) -- cgit v1.2.3 From ff2cb9f8a3a3ba01c9881ee1b196c9397c91ee6d Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Thu, 10 Dec 2020 01:53:04 +0100 Subject: Fix DELETE jobs with json data DeleteDeviceJob requires authentication, but the JSON data is not added for DELETE requests. Since QNetworkAccessManager::deleteResource does not support body data, we need to send a custom request. --- lib/jobs/basejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 1f0e84ba..b757854a 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -324,7 +324,7 @@ void BaseJob::Private::sendRequest() reply.reset(connection->nam()->put(req, requestData.source())); break; case HttpVerb::Delete: - reply.reset(connection->nam()->deleteResource(req)); + reply.reset(connection->nam()->sendCustomRequest(req, "DELETE", requestData.source())); break; } } -- cgit v1.2.3 From 97b0beca9becb0513111801ba3380f29e44383b2 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 10 Dec 2020 17:34:10 +0100 Subject: Uri: fix a few cases of insufficient escaping --- lib/uri.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 18 deletions(-) (limited to 'lib') diff --git a/lib/uri.cpp b/lib/uri.cpp index 8370d83a..0e2bcb87 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -30,7 +30,9 @@ Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) for (const auto& p: replacePairs) if (primaryId[0] == p.sigil) { primaryType_ = Type(p.sigil); - pathToBe = p.uriString + primaryId.mid(1); + auto safePrimaryId = primaryId.mid(1); + safePrimaryId.replace('/', "%2F"); + pathToBe = p.uriString + std::move(safePrimaryId); break; } if (!secondaryId.isEmpty()) { @@ -38,11 +40,40 @@ Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) primaryType_ = Invalid; return; } - pathToBe += "/event/" + secondaryId.mid(1); + auto safeSecondaryId = secondaryId.mid(1); + safeSecondaryId.replace('/', "%2F"); + pathToBe += "/event/" + std::move(safeSecondaryId); } - setPath(pathToBe); + setPath(pathToBe, QUrl::TolerantMode); } - setQuery(std::move(query)); + if (!query.isEmpty()) + setQuery(std::move(query)); +} + +static inline auto encodedPath(const QUrl& url) +{ + return url.path(QUrl::EncodeDelimiters | QUrl::EncodeUnicode); +} + +static QString pathSegment(const QUrl& url, int which) +{ + return QUrl::fromPercentEncoding( + encodedPath(url).section('/', which, which).toUtf8()); +} + +static auto decodeFragmentPart(const QStringRef& part) +{ + return QUrl::fromPercentEncoding(part.toLatin1()).toUtf8(); +} + +static auto matrixToUrlRegexInit() +{ + // See https://matrix.org/docs/spec/appendices#matrix-to-navigation + const QRegularExpression MatrixToUrlRE { + "^/(?
[^:]+:[^/?]+)(/(?(\\$|%24)[^?]+))?(\\?(?.+))?$" + }; + Q_ASSERT(MatrixToUrlRE.isValid()); + return MatrixToUrlRE; } Uri::Uri(QUrl url) : QUrl(std::move(url)) @@ -51,14 +82,13 @@ Uri::Uri(QUrl url) : QUrl(std::move(url)) if (isEmpty()) return; // primaryType_ == Empty - if (!QUrl::isValid()) { // MatrixUri::isValid() checks primaryType_ - primaryType_ = Invalid; + primaryType_ = Invalid; + if (!QUrl::isValid()) // MatrixUri::isValid() checks primaryType_ return; - } if (scheme() == "matrix") { // Check sanity as per https://github.com/matrix-org/matrix-doc/pull/2312 - const auto& urlPath = path(); + const auto& urlPath = encodedPath(*this); const auto& splitPath = urlPath.splitRef('/'); switch (splitPath.size()) { case 2: @@ -83,17 +113,15 @@ Uri::Uri(QUrl url) : QUrl(std::move(url)) primaryType_ = NonMatrix; // Default, unless overridden by the code below if (scheme() == "https" && authority() == "matrix.to") { - // See https://matrix.org/docs/spec/appendices#matrix-to-navigation - static const QRegularExpression MatrixToUrlRE { - R"(^/(?
[^/?]+)(/(?[^?]+))?(\?(?.+))?$)" - }; + static const auto MatrixToUrlRE = matrixToUrlRegexInit(); // matrix.to accepts both literal sigils (as well as & and ? used in // its "query" substitute) and their %-encoded forms; // so force QUrl to decode everything. - auto f = fragment(QUrl::FullyDecoded); + auto f = fragment(QUrl::EncodeUnicode); if (auto&& m = MatrixToUrlRE.match(f); m.hasMatch()) - *this = Uri { m.captured("main").toUtf8(), - m.captured("sec").toUtf8(), m.captured("query") }; + *this = Uri { decodeFragmentPart(m.capturedRef("main")), + decodeFragmentPart(m.capturedRef("sec")), + decodeFragmentPart(m.capturedRef("query")) }; } } @@ -119,7 +147,7 @@ Uri::Type Uri::type() const { return primaryType_; } Uri::SecondaryType Uri::secondaryType() const { - return path().section('/', 2, 2) == "event" ? EventId : NoSecondaryId; + return pathSegment(*this, 2) == "event" ? EventId : NoSecondaryId; } QUrl Uri::toUrl(UriForm form) const @@ -148,13 +176,13 @@ QString Uri::primaryId() const if (primaryType_ == Empty || primaryType_ == Invalid) return {}; - const auto& idStem = path().section('/', 1, 1); + const auto& idStem = pathSegment(*this, 1); return idStem.isEmpty() ? idStem : primaryType_ + idStem; } QString Uri::secondaryId() const { - const auto& idStem = path().section('/', 3); + const auto& idStem = pathSegment(*this, 3); return idStem.isEmpty() ? idStem : secondaryType() + idStem; } -- cgit v1.2.3 From 104356d945671762af23e346f7898a3208770d97 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 23 Dec 2020 17:18:12 +0100 Subject: Connection::resolveServer(): fix error handling Part of the fix for #421. --- lib/connection.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index b6a3198d..8c51c84c 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -270,8 +270,8 @@ void Connection::resolveServer(const QString& mxid) d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file d->resolverJob = callApi(); connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl] { - if (d->resolverJob->status() != BaseJob::NotFoundError) { - if (d->resolverJob->status() != BaseJob::Success) { + if (d->resolverJob->error() != BaseJob::NotFoundError) { + if (!d->resolverJob->status().good()) { qCWarning(MAIN) << "Fetching .well-known file failed, FAIL_PROMPT"; emit resolveError(tr("Failed resolving the homeserver")); @@ -301,8 +301,7 @@ void Connection::resolveServer(const QString& mxid) &Connection::resolved); connect(d->loginFlowsJob, &BaseJob::failure, this, [this] { qCWarning(MAIN) << "Homeserver base URL sanity check failed"; - emit resolveError( - tr("The homeserver base URL doesn't seem to work")); + emit resolveError(tr("The homeserver doesn't seem to be working")); }); }); } -- cgit v1.2.3 From 9ef83e044ed4f8409156b19d529dfc7e45f565c1 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 23 Dec 2020 17:21:01 +0100 Subject: BaseJob: tolerate unexpected error payloads Proxy servers may return arbitrary HTML, for one example; so don't expect to find a valid JSON object in whatever non-empty payload next to a non-2xx HTTP code. Fixes #421. --- lib/jobs/basejob.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index b757854a..e16ba4ef 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -510,14 +510,15 @@ BaseJob::Status BaseJob::prepareResult() { return Success; } BaseJob::Status BaseJob::prepareError() { - if (!d->rawResponse.isEmpty()) { - if (const auto status = d->parseJson(); !status.good()) - return status; // If there's anything there, it should be JSON - if (d->jsonResponse.isArray()) // ...specifically, a JSON object - return { IncorrectResponse, - tr("Malformed error JSON: an array instead of an object") }; - } - + // Try to make sense of the error payload but be prepared for all kinds + // of unexpected stuff (raw HTML, plain text, foreign JSON among those) + if (!d->rawResponse.isEmpty() + && reply()->rawHeader("Content-Type") == "application/json") + d->parseJson(); + + // By now, if d->parseJson() above succeeded then jsonData() will return + // a valid JSON object - or an empty object otherwise (in which case most + // of if's below will fall through to `return NoError` at the end const auto& errorJson = jsonData(); const auto errCode = errorJson.value("errcode"_ls).toString(); if (error() == TooManyRequestsError || errCode == "M_LIMIT_EXCEEDED") { -- cgit v1.2.3 From 1a832ae9b6a0d679b551fd644136e4bc17e7db29 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 23 Dec 2020 17:21:44 +0100 Subject: BaseJob: add [[fallthrough]] as clang-tidy says --- lib/jobs/basejob.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index e16ba4ef..3fa1cd94 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -620,6 +620,7 @@ void BaseJob::finishJob() emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); return; } + [[fallthrough]]; default:; } -- cgit v1.2.3 From 0a2acd750a4155969092be674ed3dd9a71b2354f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 24 Dec 2020 18:20:04 +0100 Subject: Fix clang-tidy/clazy warnings --- lib/connection.cpp | 4 ++-- lib/converters.cpp | 4 ++-- lib/events/typingevent.cpp | 1 + lib/jobs/downloadfilejob.cpp | 2 +- lib/networkaccessmanager.cpp | 7 +++--- lib/room.cpp | 55 ++++++++++++++++++++++---------------------- lib/uri.cpp | 8 +++---- lib/uriresolver.cpp | 2 ++ lib/user.cpp | 4 ++-- lib/util.cpp | 5 +--- 10 files changed, 46 insertions(+), 46 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 8c51c84c..b037bf49 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -929,8 +929,8 @@ void Connection::doInDirectChat(User* u, // There can be more than one DC; find the first valid (existing and // not left), and delete inexistent (forgotten?) ones along the way. DirectChatsMap removals; - for (auto it = std::as_const(d->directChats).find(u); - it != d->directChats.end() && it.key() == u; ++it) { + for (auto it = d->directChats.constFind(u); + it != d->directChats.cend() && it.key() == u; ++it) { const auto& roomId = *it; if (auto r = room(roomId, JoinState::Join)) { Q_ASSERT(r->id() == roomId); diff --git a/lib/converters.cpp b/lib/converters.cpp index e5236bb9..9f570087 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -32,9 +32,9 @@ QVariant JsonConverter::load(const QJsonValue& jv) return jv.toVariant(); } -QJsonObject JsonConverter::dump(const QVariantHash& map) +QJsonObject JsonConverter::dump(const QVariantHash& vh) { - return QJsonObject::fromVariantHash(map); + return QJsonObject::fromVariantHash(vh); } QVariantHash JsonConverter::load(const QJsonValue& jv) diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index 0c5fc6ba..a95d2f0d 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -25,6 +25,7 @@ using namespace Quotient; TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) { const auto& array = contentJson()["user_ids"_ls].toArray(); + _users.reserve(array.size()); for (const auto& user : array) _users.push_back(user.toString()); } diff --git a/lib/jobs/downloadfilejob.cpp b/lib/jobs/downloadfilejob.cpp index 7b4cf690..0011a97c 100644 --- a/lib/jobs/downloadfilejob.cpp +++ b/lib/jobs/downloadfilejob.cpp @@ -64,7 +64,7 @@ void DownloadFileJob::onSentRequest(QNetworkReply* reply) return; auto sizeHeader = reply->header(QNetworkRequest::ContentLengthHeader); if (sizeHeader.isValid()) { - auto targetSize = sizeHeader.value(); + auto targetSize = sizeHeader.toLongLong(); if (targetSize != -1) if (!d->tempFile->resize(targetSize)) { qCWarning(JOBS) << "Failed to allocate" << targetSize diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index b9037bcc..e8aa85df 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -52,9 +52,10 @@ static NetworkAccessManager* createNam() auto nam = new NetworkAccessManager(QCoreApplication::instance()); #if (QT_VERSION < QT_VERSION_CHECK(5, 15, 0)) // See #109; in newer Qt, bearer management is deprecated altogether - nam->connect(nam, &QNetworkAccessManager::networkAccessibleChanged, [nam] { - nam->setNetworkAccessible(QNetworkAccessManager::Accessible); - }); + NetworkAccessManager::connect(nam, + &QNetworkAccessManager::networkAccessibleChanged, [nam] { + nam->setNetworkAccessible(QNetworkAccessManager::Accessible); + }); #endif return nam; } diff --git a/lib/room.cpp b/lib/room.cpp index a5ed76a8..fcc22436 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -366,7 +366,7 @@ public: */ bool processReplacement(const RoomMessageEvent& newEvent); - void setTags(TagsMap newTags); + void setTags(TagsMap&& newTags); QJsonObject toJson() const; @@ -1089,11 +1089,11 @@ void Room::setTags(TagsMap newTags, ActionScope applyOn) if (propagate) { for (auto* r = this; (r = r->successor(joinStates));) - r->setTags(newTags, ActionScope::ThisRoomOnly); + r->setTags(d->tags, ActionScope::ThisRoomOnly); } } -void Room::Private::setTags(TagsMap newTags) +void Room::Private::setTags(TagsMap&& newTags) { emit q->tagsAboutToChange(); const auto keys = newTags.keys(); @@ -1208,8 +1208,8 @@ QString Room::fileNameToDownload(const QString& eventId) const FileTransferInfo Room::fileTransferInfo(const QString& id) const { - auto infoIt = d->fileTransfers.find(id); - if (infoIt == d->fileTransfers.end()) + const auto infoIt = d->fileTransfers.constFind(id); + if (infoIt == d->fileTransfers.cend()) return {}; // FIXME: Add lib tests to make sure FileTransferInfo::status stays @@ -1238,8 +1238,8 @@ QUrl Room::fileSource(const QString& id) const return url; // No urlToDownload means it's a pending or completed upload. - auto infoIt = d->fileTransfers.find(id); - if (infoIt != d->fileTransfers.end()) + auto infoIt = d->fileTransfers.constFind(id); + if (infoIt != d->fileTransfers.cend()) return QUrl::fromLocalFile(infoIt->localFileInfo.absoluteFilePath()); qCWarning(MAIN) << "File source for identifier" << id << "not found"; @@ -1285,7 +1285,7 @@ const StateEventBase* Room::getCurrentState(const QString& evtType, RoomEventPtr Room::decryptMessage(const EncryptedEvent& encryptedEvent) { #ifndef Quotient_E2EE_ENABLED - Q_UNUSED(encryptedEvent); + Q_UNUSED(encryptedEvent) qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; return {}; #else // Quotient_E2EE_ENABLED @@ -1310,10 +1310,9 @@ void Room::handleRoomKeyEvent(const RoomKeyEvent& roomKeyEvent, const QString& senderKey) { #ifndef Quotient_E2EE_ENABLED - Q_UNUSED(roomKeyEvent); - Q_UNUSED(senderKey); + Q_UNUSED(roomKeyEvent) + Q_UNUSED(senderKey) qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; - return; #else // Quotient_E2EE_ENABLED if (roomKeyEvent.algorithm() != MegolmV1AesSha2AlgoKey) { qCWarning(E2EE) << "Ignoring unsupported algorithm" @@ -1433,7 +1432,7 @@ Room::Private::moveEventsToTimeline(RoomEventsRange events, } const auto insertedSize = (index - baseIndex) * placement; Q_ASSERT(insertedSize == int(events.size())); - return insertedSize; + return Timeline::size_type(insertedSize); } QString Room::roomMembername(const User* u) const @@ -1615,8 +1614,8 @@ QString Room::retryMessage(const QString& txnId) const auto it = findPendingEvent(txnId); Q_ASSERT(it != d->unsyncedEvents.end()); qCDebug(EVENTS) << "Retrying transaction" << txnId; - const auto& transferIt = d->fileTransfers.find(txnId); - if (transferIt != d->fileTransfers.end()) { + const auto& transferIt = d->fileTransfers.constFind(txnId); + if (transferIt != d->fileTransfers.cend()) { Q_ASSERT(transferIt->isUpload); if (transferIt->status == FileTransferInfo::Completed) { qCDebug(MESSAGES) @@ -1713,7 +1712,8 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, // to enable the preview while the event is pending. uploadFile(txnId, localPath); // Below, the upload job is used as a context object to clean up connections - connect(this, &Room::fileTransferCompleted, d->fileTransfers[txnId].job, + const auto& transferJob = d->fileTransfers.value(txnId).job; + connect(this, &Room::fileTransferCompleted, transferJob, [this, txnId](const QString& id, const QUrl&, const QUrl& mxcUri) { if (id == txnId) { auto it = findPendingEvent(txnId); @@ -1732,7 +1732,7 @@ QString Room::postFile(const QString& plainText, const QUrl& localPath, } } }); - connect(this, &Room::fileTransferCancelled, d->fileTransfers[txnId].job, + connect(this, &Room::fileTransferCancelled, transferJob, [this, txnId](const QString& id) { if (id == txnId) { auto it = findPendingEvent(txnId); @@ -1940,8 +1940,8 @@ void Room::uploadFile(const QString& id, const QUrl& localFilename, void Room::downloadFile(const QString& eventId, const QUrl& localFilename) { - auto ongoingTransfer = d->fileTransfers.find(eventId); - if (ongoingTransfer != d->fileTransfers.end() + if (auto ongoingTransfer = d->fileTransfers.constFind(eventId); + ongoingTransfer != d->fileTransfers.cend() && ongoingTransfer->status == FileTransferInfo::Started) { qCWarning(MAIN) << "Transfer for" << eventId << "is ongoing; download won't start"; @@ -1998,8 +1998,8 @@ void Room::downloadFile(const QString& eventId, const QUrl& localFilename) void Room::cancelFileTransfer(const QString& id) { - auto it = d->fileTransfers.find(id); - if (it == d->fileTransfers.end()) { + const auto it = d->fileTransfers.constFind(id); + if (it == d->fileTransfers.cend()) { qCWarning(MAIN) << "No information on file transfer" << id << "in room" << d->id; return; @@ -2101,8 +2101,8 @@ bool Room::Private::processRedaction(const RedactionEvent& redaction) { // Can't use findInTimeline because it returns a const iterator, and // we need to change the underlying TimelineItem. - const auto pIdx = eventsIndex.find(redaction.redactedEvent()); - if (pIdx == eventsIndex.end()) + const auto pIdx = eventsIndex.constFind(redaction.redactedEvent()); + if (pIdx == eventsIndex.cend()) return false; Q_ASSERT(q->isValidIndex(*pIdx)); @@ -2172,8 +2172,8 @@ bool Room::Private::processReplacement(const RoomMessageEvent& newEvent) { // Can't use findInTimeline because it returns a const iterator, and // we need to change the underlying TimelineItem. - const auto pIdx = eventsIndex.find(newEvent.replacedEvent()); - if (pIdx == eventsIndex.end()) + const auto pIdx = eventsIndex.constFind(newEvent.replacedEvent()); + if (pIdx == eventsIndex.cend()) return false; Q_ASSERT(q->isValidIndex(*pIdx)); @@ -2418,12 +2418,11 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (!e.isStateEvent()) return Change::NoChange; - // Find a value (create empty if necessary) and get a reference to it - // getCurrentState<> is not used here because it (creates and) returns + // Find a value (create an empty one if necessary) and get a reference + // to it. Can't use getCurrentState<>() because it (creates and) returns // a stub if a value is not found, and what's needed here is a "real" event // or nullptr. - const auto*& curStateEvent = - d->currentState[{ e.matrixType(), e.stateKey() }]; + auto& curStateEvent = d->currentState[{ e.matrixType(), e.stateKey() }]; // Prepare for the state change const auto oldRme = static_cast(curStateEvent); visit(e, [this, &oldRme](const RoomMemberEvent& rme) { diff --git a/lib/uri.cpp b/lib/uri.cpp index 0e2bcb87..e0912eb6 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -32,7 +32,7 @@ Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) primaryType_ = Type(p.sigil); auto safePrimaryId = primaryId.mid(1); safePrimaryId.replace('/', "%2F"); - pathToBe = p.uriString + std::move(safePrimaryId); + pathToBe = p.uriString + safePrimaryId; break; } if (!secondaryId.isEmpty()) { @@ -42,12 +42,12 @@ Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) } auto safeSecondaryId = secondaryId.mid(1); safeSecondaryId.replace('/', "%2F"); - pathToBe += "/event/" + std::move(safeSecondaryId); + pathToBe += "/event/" + safeSecondaryId; } setPath(pathToBe, QUrl::TolerantMode); } if (!query.isEmpty()) - setQuery(std::move(query)); + setQuery(query); } static inline auto encodedPath(const QUrl& url) @@ -156,7 +156,7 @@ QUrl Uri::toUrl(UriForm form) const return {}; if (form == CanonicalUri || type() == NonMatrix) - return *this; + return *this; // NOLINT(cppcoreguidelines-slicing): It's intentional QUrl url; url.setScheme("https"); diff --git a/lib/uriresolver.cpp b/lib/uriresolver.cpp index ec30512c..27360bcc 100644 --- a/lib/uriresolver.cpp +++ b/lib/uriresolver.cpp @@ -75,6 +75,8 @@ private: std::tuple fns_; }; +template +StaticUriDispatcher(FnTs&&... fns) -> StaticUriDispatcher; UriResolveResult Quotient::visitResource( Connection* account, const Uri& uri, diff --git a/lib/user.cpp b/lib/user.cpp index 3c9652b0..85f9d9a7 100644 --- a/lib/user.cpp +++ b/lib/user.cpp @@ -101,7 +101,7 @@ QString User::id() const { return d->id; } bool User::isGuest() const { Q_ASSERT(!d->id.isEmpty() && d->id.startsWith('@')); - auto it = std::find_if_not(d->id.begin() + 1, d->id.end(), + auto it = std::find_if_not(d->id.cbegin() + 1, d->id.cend(), [](QChar c) { return c.isDigit(); }); Q_ASSERT(it != d->id.end()); return *it == ':'; @@ -203,7 +203,7 @@ bool User::Private::doSetAvatar(SourceT&& source, User* q) return; } - defaultAvatar->updateUrl(move(newUrl)); + defaultAvatar->updateUrl(newUrl); emit q->avatarChanged(q, nullptr); }); }); diff --git a/lib/util.cpp b/lib/util.cpp index ffb36068..875d7522 100644 --- a/lib/util.cpp +++ b/lib/util.cpp @@ -161,9 +161,6 @@ static_assert(std::is_same, int>(), "Test fn_arg_t defaulting to first argument"); template -static QString ft(T&&) -{ - return {}; -} +static QString ft(T&&); static_assert(std::is_same)>, QString&&>(), "Test function templates"); -- cgit v1.2.3 From 66972c81d018231f08f3767feda4b41ae5e1b8e0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 24 Dec 2020 18:21:41 +0100 Subject: Room: don't accept . at 0-th position in the tag Also: use a structured binding for better code readability. --- lib/room.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index fcc22436..9c932ed6 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -1026,11 +1026,11 @@ TagRecord Room::tag(const QString& name) const { return d->tags.value(name); } std::pair validatedTag(QString name) { - if (name.contains('.')) + if (name.isEmpty() || name.indexOf('.', 1) != -1) return { false, name }; qCWarning(MAIN) << "The tag" << name - << "doesn't follow the CS API conventions"; + << "doesn't follow the CS API conventions"; name.prepend("u."); qCWarning(MAIN) << "Using " << name << "instead"; @@ -1098,11 +1098,11 @@ void Room::Private::setTags(TagsMap&& newTags) emit q->tagsAboutToChange(); const auto keys = newTags.keys(); for (const auto& k : keys) - if (const auto& checkRes = validatedTag(k); checkRes.first) { - if (newTags.contains(checkRes.second)) + if (const auto& [adjusted, adjustedTag] = validatedTag(k); adjusted) { + if (newTags.contains(adjustedTag)) newTags.remove(k); else - newTags.insert(checkRes.second, newTags.take(k)); + newTags.insert(adjustedTag, newTags.take(k)); } tags = move(newTags); -- cgit v1.2.3 From d6c2622b0cdc33ad272542ab611c15de07202520 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 27 Dec 2020 18:33:51 +0100 Subject: More comments/documentation Notably, recommend using loginFlowsChanged() rather than homeserverChanged() to detect when a Connection object is ready for a login sequence. Related: #427. (cherry picked from commit 8981c5451ac378f16d5b57d7460d053e2cbc666e) --- lib/connection.cpp | 7 +++---- lib/connection.h | 30 ++++++++++++++++++++++++++---- lib/events/event.cpp | 3 +++ 3 files changed, 32 insertions(+), 8 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index b037bf49..aae4e94b 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -345,12 +345,11 @@ void Connection::loginWithToken(const QByteArray& loginToken, loginToken, deviceId, initialDeviceName); } -void Connection::assumeIdentity(const QString& userId, - const QString& accessToken, +void Connection::assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId) { - checkAndConnect(userId, - [=] { d->assumeIdentity(userId, accessToken, deviceId); }); + checkAndConnect(mxId, + [=] { d->assumeIdentity(mxId, accessToken, deviceId); }); } void Connection::reloadCapabilities() diff --git a/lib/connection.h b/lib/connection.h index 3618f284..36e20219 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -504,13 +504,35 @@ public slots: /** Determine and set the homeserver from MXID */ void resolveServer(const QString& mxid); + /** \brief Log in using a username and password pair + * + * Before logging in, this method checks if the homeserver is valid and + * supports the password login flow. If the homeserver is invalid but + * a full user MXID is provided, this method calls resolveServer() using + * this MXID. + * + * \sa resolveServer, resolveError, loginError + */ void loginWithPassword(const QString& userId, const QString& password, const QString& initialDeviceName, const QString& deviceId = {}); + /** \brief Log in using a login token + * + * One usual case for this method is the final stage of logging in via SSO. + * Unlike loginWithPassword() and assumeIdentity(), this method cannot + * resolve the server from the user name because the full user MXID is + * encoded in the login token. Callers should ensure the homeserver + * sanity in advance. + */ void loginWithToken(const QByteArray& loginToken, const QString& initialDeviceName, const QString& deviceId = {}); - void assumeIdentity(const QString& userId, const QString& accessToken, + /** \brief Use an existing access token to connect to the homeserver + * + * Similar to loginWithPassword(), this method checks that the homeserver + * URL is valid and tries to resolve it from the MXID in case it is not. + */ + void assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId); /*! \deprecated Use loginWithPassword instead */ void connectToServer(const QString& userId, const QString& password, @@ -662,9 +684,9 @@ signals: * This was a signal resulting from a successful resolveServer(). * Since Connection now provides setHomeserver(), the HS URL * may change even without resolveServer() invocation. Use - * homeserverChanged() instead of resolved(). You can also use - * connectToServer and connectWithToken without the HS URL set in - * advance (i.e. without calling resolveServer), as they now trigger + * loginFLowsChanged() instead of resolved(). You can also use + * loginWith*() and assumeIdentity() without the HS URL set in + * advance (i.e. without calling resolveServer), as they trigger * server name resolution from MXID if the server URL is not valid. */ void resolved(); diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 96e33864..7b34114d 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -61,11 +61,14 @@ QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); } QByteArray Event::originalJson() const { return QJsonDocument(_json).toJson(); } +// On const below: this is to catch accidental attempts to change event JSON +// NOLINTNEXTLINE(readability-const-return-type) const QJsonObject Event::contentJson() const { return fullJson()[ContentKeyL].toObject(); } +// NOLINTNEXTLINE(readability-const-return-type) const QJsonObject Event::unsignedJson() const { return fullJson()[UnsignedKeyL].toObject(); -- cgit v1.2.3 From f286ef4c5b3c71510d6ef15e8cc12cada84f3682 Mon Sep 17 00:00:00 2001 From: Nicolas Fella Date: Sun, 27 Dec 2020 21:24:06 +0100 Subject: Fix use-after-free of QNetworkReply in BaseJob Usually QNetworkAccessManager expects the user to delete the replies, but when the QNetworkAccessManager itself is deleted it deletes all pending replies (https://code.woboq.org/qt5/qtbase/src/network/access/qnetworkaccessmanager.cpp.html#529). This can lead to use-after-free crashes when d->reply is accessed. By putting the reply into a QPointer the exiting if(d->reply) checks can work properly. (cherry picked from commit 9d854e778d8d6ef8e03e1ea74fe958675b24fd45) --- lib/jobs/basejob.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 3fa1cd94..2ac942f5 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -76,15 +77,6 @@ QDebug BaseJob::Status::dumpToLog(QDebug dbg) const return dbg << ": " << message; } -struct NetworkReplyDeleter : public QScopedPointerDeleteLater { - static inline void cleanup(QNetworkReply* reply) - { - if (reply && reply->isRunning()) - reply->abort(); - QScopedPointerDeleteLater::cleanup(reply); - } -}; - template constexpr auto make_array(Ts&&... items) { @@ -112,6 +104,16 @@ public: retryTimer.setSingleShot(true); } + ~Private() + { + if (reply) { + if (reply->isRunning()) { + reply->abort(); + } + delete reply; + } + } + void sendRequest(); /*! \brief Parse the response byte array into JSON * @@ -140,7 +142,10 @@ public: QByteArrayList expectedKeys; - QScopedPointer reply; + // When the QNetworkAccessManager is destroyed it destroys all pending replies. + // Using QPointer allows us to know when that happend. + QPointer reply; + Status status = Unprepared; QByteArray rawResponse; /// Contains a null document in case of non-JSON body (for a successful @@ -315,16 +320,16 @@ void BaseJob::Private::sendRequest() switch (verb) { case HttpVerb::Get: - reply.reset(connection->nam()->get(req)); + reply = connection->nam()->get(req); break; case HttpVerb::Post: - reply.reset(connection->nam()->post(req, requestData.source())); + reply = connection->nam()->post(req, requestData.source()); break; case HttpVerb::Put: - reply.reset(connection->nam()->put(req, requestData.source())); + reply = connection->nam()->put(req, requestData.source()); break; case HttpVerb::Delete: - reply.reset(connection->nam()->sendCustomRequest(req, "DELETE", requestData.source())); + reply = connection->nam()->sendCustomRequest(req, "DELETE", requestData.source()); break; } } -- cgit v1.2.3 From 12c37cf3ce2aa397050fff6ce846d557524a3fe7 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 27 Dec 2020 23:32:18 +0100 Subject: BaseJob::initiate: add Q_LIKELY ...to show the sunny-day case. (cherry picked from commit 5d15e3b23649a54abdb3812c10f4a7d2ce07d7dd) --- lib/jobs/basejob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 2ac942f5..135274d6 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -342,7 +342,7 @@ void BaseJob::beforeAbandon() { } void BaseJob::initiate(ConnectionData* connData, bool inBackground) { - if (connData && connData->baseUrl().isValid()) { + if (Q_LIKELY(connData && connData->baseUrl().isValid())) { d->inBackground = inBackground; d->connection = connData; doPrepare(); @@ -355,7 +355,7 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) setStatus(FileError, "Request data not ready"); } Q_ASSERT(status().code != Pending); // doPrepare() must NOT set this - if (status().code == Unprepared) { + if (Q_LIKELY(status().code == Unprepared)) { d->connection->submit(this); return; } -- cgit v1.2.3 From 6ee20f1507b12fcb9556717eeb2784c7530ec588 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 11:24:59 +0100 Subject: Cleanup and clang-tidy/clazy fixes (cherry picked from commit 56c1db077b5da653c230432abc6c746318a77bed) --- lib/room.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/room.cpp b/lib/room.cpp index 9c932ed6..c42e618e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -2492,31 +2492,20 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) , [this, oldStateEvent] (const RoomCanonicalAliasEvent& cae) { // clang-format on setObjectName(cae.alias().isEmpty() ? d->id : cae.alias()); - QString previousCanonicalAlias = - oldStateEvent - ? static_cast(oldStateEvent) - ->alias() - : QString(); - - auto previousAltAliases = - oldStateEvent - ? static_cast(oldStateEvent) - ->altAliases() - : QStringList(); - - if (!previousCanonicalAlias.isEmpty()) { - previousAltAliases.push_back(previousCanonicalAlias); + const auto* oldCae = + static_cast(oldStateEvent); + QStringList previousAltAliases {}; + if (oldCae) { + previousAltAliases = oldCae->altAliases(); + if (!oldCae->alias().isEmpty()) + previousAltAliases.push_back(oldCae->alias()); } - const auto previousAliases = std::move(previousAltAliases); - auto newAliases = cae.altAliases(); - - if (!cae.alias().isEmpty()) { + if (!cae.alias().isEmpty()) newAliases.push_front(cae.alias()); - } - connection()->updateRoomAliases(id(), previousAliases, newAliases); + connection()->updateRoomAliases(id(), previousAltAliases, newAliases); return AliasesChange; // clang-format off } @@ -2552,7 +2541,9 @@ Room::Changes Room::processStateEvent(const RoomEvent& e) if (u == localUser() && evt.isDirect()) connection()->addToDirectChats(this, user(evt.senderId())); break; - default: + case MembershipType::Knock: + case MembershipType::Ban: + case MembershipType::Leave: if (!d->membersLeft.contains(u)) d->membersLeft.append(u); } -- cgit v1.2.3 From f10b3fadc2483986e372418725d4e50dbf85075c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 13:07:31 +0100 Subject: Connection: refactor the resolve/login code 1. resolveServer() now emits homeserverChanged() even when there's no .well-known file found. 2. checkAndConnect() entirely removed from the header file. 3. Sunny-day scenario for assumeIdentity() is now asynchronous, triggering a call to /whoami to double-check the user. (based on commit 6c9ff40dbd91cc4966f0ecf9ed817efc2495a2fb to master) --- lib/connection.cpp | 98 +++++++++++++++++++++++++++++++++++++----------------- lib/connection.h | 13 -------- 2 files changed, 68 insertions(+), 43 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index aae4e94b..677a346b 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -133,10 +133,27 @@ public: != "json"; bool lazyLoading = false; + /** \brief Check the homeserver and resolve it if needed, before connecting + * + * A single entry for functions that need to check whether the homeserver + * is valid before running. May execute connectFn either synchronously + * or asynchronously. In case of errors, emits resolveError() if + * the homeserver URL is not valid and cannot be resolved from userId, or + * the homeserver doesn't support the requested login flow. + * + * \param userId fully-qualified MXID to resolve HS from + * \param connectFn a function to execute once the HS URL is good + * \param flow optionally, a login flow that should be supported for + * connectFn to work; `none`, if there's no login flow + * requirements + * \sa resolveServer, resolveError + */ + void checkAndConnect(const QString &userId, + const std::function &connectFn, + const std::optional &flow = none); template void loginToServer(LoginArgTs&&... loginArgs); - void assumeIdentity(const QString& userId, const QString& accessToken, - const QString& deviceId); + void completeSetup(const QString& mxId); void removeRoom(const QString& roomId); void consumeRoomData(SyncDataList&& roomDataList, bool fromCache); @@ -264,12 +281,15 @@ void Connection::resolveServer(const QString& mxid) return; } - auto domain = maybeBaseUrl.host(); - qCDebug(MAIN) << "Finding the server" << domain; + qCDebug(MAIN) << "Finding the server" << maybeBaseUrl.host(); - d->data->setBaseUrl(maybeBaseUrl); // Just enough to check .well-known file + const auto& oldBaseUrl = d->data->baseUrl(); + d->data->setBaseUrl(maybeBaseUrl); // Temporarily set it for this one call d->resolverJob = callApi(); - connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl] { + connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl, oldBaseUrl] { + // Revert baseUrl so that setHomeserver() below triggers signals + // in case the base URL actually changed + d->data->setBaseUrl(oldBaseUrl); if (d->resolverJob->error() != BaseJob::NotFoundError) { if (!d->resolverJob->status().good()) { qCWarning(MAIN) @@ -297,6 +317,7 @@ void Connection::resolveServer(const QString& mxid) << "for base URL"; setHomeserver(maybeBaseUrl); } + Q_ASSERT(d->loginFlowsJob != nullptr); connect(d->loginFlowsJob, &BaseJob::success, this, &Connection::resolved); connect(d->loginFlowsJob, &BaseJob::failure, this, [this] { @@ -324,10 +345,10 @@ void Connection::loginWithPassword(const QString& userId, const QString& initialDeviceName, const QString& deviceId) { - checkAndConnect(userId, [=] { + d->checkAndConnect(userId, [=] { d->loginToServer(LoginFlows::Password.type, makeUserIdentifier(userId), password, /*token*/ "", deviceId, initialDeviceName); - }); + }, LoginFlows::Password); } SsoSession* Connection::prepareForSso(const QString& initialDeviceName, @@ -340,6 +361,7 @@ void Connection::loginWithToken(const QByteArray& loginToken, const QString& initialDeviceName, const QString& deviceId) { + Q_ASSERT(d->data->baseUrl().isValid() && d->loginFlows.contains(LoginFlows::Token)); d->loginToServer(LoginFlows::Token.type, none /*user is encoded in loginToken*/, "" /*password*/, loginToken, deviceId, initialDeviceName); @@ -348,8 +370,11 @@ void Connection::loginWithToken(const QByteArray& loginToken, void Connection::assumeIdentity(const QString& mxId, const QString& accessToken, const QString& deviceId) { - checkAndConnect(mxId, - [=] { d->assumeIdentity(mxId, accessToken, deviceId); }); + d->checkAndConnect(mxId, [this, mxId, accessToken, deviceId] { + d->data->setToken(accessToken.toLatin1()); + d->data->setDeviceId(deviceId); + d->completeSetup(mxId); + }); } void Connection::reloadCapabilities() @@ -389,8 +414,9 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs) auto loginJob = q->callApi(std::forward(loginArgs)...); connect(loginJob, &BaseJob::success, q, [this, loginJob] { - assumeIdentity(loginJob->userId(), loginJob->accessToken(), - loginJob->deviceId()); + data->setToken(loginJob->accessToken().toLatin1()); + data->setDeviceId(loginJob->deviceId()); + completeSetup(loginJob->userId()); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED @@ -403,17 +429,14 @@ void Connection::Private::loginToServer(LoginArgTs&&... loginArgs) }); } -void Connection::Private::assumeIdentity(const QString& userId, - const QString& accessToken, - const QString& deviceId) +void Connection::Private::completeSetup(const QString& mxId) { - data->setUserId(userId); + data->setUserId(mxId); q->user(); // Creates a User object for the local user - data->setToken(accessToken.toLatin1()); - data->setDeviceId(deviceId); - q->setObjectName(userId % '/' % deviceId); + q->setObjectName(data->userId() % '/' % data->deviceId()); qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() - << "by user" << userId << "from device" << deviceId; + << "by user" << data->userId() + << "from device" << data->deviceId(); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED @@ -430,22 +453,37 @@ void Connection::Private::assumeIdentity(const QString& userId, q->reloadCapabilities(); } -void Connection::checkAndConnect(const QString& userId, - std::function connectFn) +void Connection::Private::checkAndConnect(const QString& userId, + const std::function& connectFn, + const std::optional& flow) { - if (d->data->baseUrl().isValid()) { + if (data->baseUrl().isValid() && (!flow || loginFlows.contains(*flow))) { connectFn(); return; } - // Not good to go, try to fix the homeserver URL. + // Not good to go, try to ascertain the homeserver URL and flows if (userId.startsWith('@') && userId.indexOf(':') != -1) { - connectSingleShot(this, &Connection::homeserverChanged, this, connectFn); - // NB: doResolveServer can emit resolveError, so this is a part of - // checkAndConnect function contract. - resolveServer(userId); + q->resolveServer(userId); + if (flow) + connectSingleShot(q, &Connection::loginFlowsChanged, q, + [this, flow, connectFn] { + if (loginFlows.contains(*flow)) + connectFn(); + else + emit q->loginError( + tr("The homeserver at %1 does not support" + " the login flow '%2'") + .arg(data->baseUrl().toDisplayString()), + flow->type); + }); + else + connectSingleShot(q, &Connection::homeserverChanged, q, connectFn); } else - emit resolveError(tr("%1 is an invalid homeserver URL") - .arg(d->data->baseUrl().toString())); + emit q->resolveError(tr("Please provide the fully-qualified user id" + " (such as @user:example.org) so that the" + " homeserver could be resolved; the current" + " homeserver URL(%1) is not good") + .arg(data->baseUrl().toDisplayString())); } void Connection::logout() diff --git a/lib/connection.h b/lib/connection.h index 36e20219..c90cb892 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -882,19 +882,6 @@ private: class Private; QScopedPointer d; - /** - * A single entry for functions that need to check whether the - * homeserver is valid before running. May either execute connectFn - * synchronously or asynchronously (if tryResolve is true and - * a DNS lookup is initiated); in case of errors, emits resolveError - * if the homeserver URL is not valid and cannot be resolved from - * userId. - * - * @param userId - fully-qualified MXID to resolve HS from - * @param connectFn - a function to execute once the HS URL is good - */ - void checkAndConnect(const QString& userId, std::function connectFn); - static room_factory_t _roomFactory; static user_factory_t _userFactory; }; -- cgit v1.2.3 From 62feba453b9642d24084f73636ea8d6f1e3278bc Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 28 Dec 2020 19:18:34 +0100 Subject: Connection: fix FTBFS with Quotient_E2EE_ENABLED (cherry picked from commit 0f974c0f96f29035ee766e8913504fed4a4903a5) --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 677a346b..9dad3c22 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -440,7 +440,7 @@ void Connection::Private::completeSetup(const QString& mxId) #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED - AccountSettings accountSettings(userId); + AccountSettings accountSettings(data->userId()); encryptionManager.reset( new EncryptionManager(accountSettings.encryptionAccountPickle())); if (accountSettings.encryptionAccountPickle().isEmpty()) { -- cgit v1.2.3 From 90d41b697af39253483d9bcca4e57b11d2197112 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 20:16:59 +0100 Subject: Prefer connecting to BaseJob::result(), not finished() ...because finished() includes abandoning and should only be relevant when lifecycle issues are involved. --- lib/connection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index 9dad3c22..a12b1fb5 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1543,7 +1543,7 @@ void Connection::setHomeserver(const QUrl& url) // Whenever a homeserver is updated, retrieve available login flows from it d->loginFlowsJob = callApi(BackgroundRequest); - connect(d->loginFlowsJob, &BaseJob::finished, this, [this] { + connect(d->loginFlowsJob, &BaseJob::result, this, [this] { if (d->loginFlowsJob->status().good()) d->loginFlows = d->loginFlowsJob->flows(); else -- cgit v1.2.3 From 4f06d46d6d6062d6d17f69eeaddb7810edac5bbf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 21:15:28 +0100 Subject: BaseJob: more logging --- lib/jobs/basejob.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index 135274d6..da66a9f2 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -199,6 +199,7 @@ BaseJob::BaseJob(HttpVerb verb, const QString& name, const QString& endpoint, setObjectName(name); connect(&d->timer, &QTimer::timeout, this, &BaseJob::timeout); connect(&d->retryTimer, &QTimer::timeout, this, [this] { + qCDebug(d->logCat) << "Retrying" << this; d->connection->submit(this); }); } @@ -374,8 +375,11 @@ void BaseJob::initiate(ConnectionData* connData, bool inBackground) void BaseJob::sendRequest() { - if (status().code == Abandoned) + if (status().code == Abandoned) { + qCDebug(d->logCat) << "Won't proceed with the abandoned request:" + << d->dumpRequest(); return; + } Q_ASSERT(d->connection && status().code == Pending); qCDebug(d->logCat).noquote() << "Making" << d->dumpRequest(); d->needsToken |= d->connection->needsToken(objectName()); -- cgit v1.2.3 From 12e00b234e5c5f4ed57b5c400d06f780e71014f4 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 21:18:01 +0100 Subject: BaseJob: setStatus(Pending) on scheduling a retry Fixes #437. --- lib/jobs/basejob.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'lib') diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp index da66a9f2..5960203d 100644 --- a/lib/jobs/basejob.cpp +++ b/lib/jobs/basejob.cpp @@ -625,6 +625,7 @@ void BaseJob::finishJob() qCWarning(d->logCat).nospace() << this << ": retry #" << d->retriesTaken << " in " << retryIn.count() << " s"; + setStatus(Pending, "Pending retry"); d->retryTimer.start(retryIn); emit retryScheduled(d->retriesTaken, milliseconds(retryIn).count()); return; -- cgit v1.2.3 From be00308ad67286b45912202750fe49fb87f16e4a Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 21:22:55 +0100 Subject: Connection::resolveServer: abandon is not a failure So just reset the base URL and return, with no error signals. --- lib/connection.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index a12b1fb5..a61f1ada 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -286,10 +286,14 @@ void Connection::resolveServer(const QString& mxid) const auto& oldBaseUrl = d->data->baseUrl(); d->data->setBaseUrl(maybeBaseUrl); // Temporarily set it for this one call d->resolverJob = callApi(); + // Connect to finished() to make sure baseUrl is restored in any case connect(d->resolverJob, &BaseJob::finished, this, [this, maybeBaseUrl, oldBaseUrl] { // Revert baseUrl so that setHomeserver() below triggers signals // in case the base URL actually changed d->data->setBaseUrl(oldBaseUrl); + if (d->resolverJob->error() == BaseJob::Abandoned) + return; + if (d->resolverJob->error() != BaseJob::NotFoundError) { if (!d->resolverJob->status().good()) { qCWarning(MAIN) @@ -317,7 +321,7 @@ void Connection::resolveServer(const QString& mxid) << "for base URL"; setHomeserver(maybeBaseUrl); } - Q_ASSERT(d->loginFlowsJob != nullptr); + Q_ASSERT(d->loginFlowsJob != nullptr); // Ensured by setHomeserver() connect(d->loginFlowsJob, &BaseJob::success, this, &Connection::resolved); connect(d->loginFlowsJob, &BaseJob::failure, this, [this] { -- cgit v1.2.3 From 41e57c9f1e44a47694eb1fc0725a8c8985780e0c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 7 Jan 2021 21:39:28 +0100 Subject: Connection: don't explicitly reset QPointers See #437 for the discussion. (cherry picked from commit 6101971af86fdecd084759aa039b9d20a9d662a7) --- lib/connection.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index a61f1ada..3ed71bb4 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -1534,10 +1534,8 @@ void Connection::setHomeserver(const QUrl& url) { if (isJobRunning(d->resolverJob)) d->resolverJob->abandon(); - d->resolverJob = nullptr; if (isJobRunning(d->loginFlowsJob)) d->loginFlowsJob->abandon(); - d->loginFlowsJob = nullptr; d->loginFlows.clear(); if (homeserver() != url) { -- cgit v1.2.3