diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/accountregistry.cpp | 73 | ||||
-rw-r--r-- | lib/accountregistry.h | 49 | ||||
-rw-r--r-- | lib/connection.cpp | 4 | ||||
-rw-r--r-- | lib/connection.h | 11 | ||||
-rw-r--r-- | lib/converters.h | 2 | ||||
-rw-r--r-- | lib/events/accountdataevents.h | 29 | ||||
-rw-r--r-- | lib/events/event.h | 6 | ||||
-rw-r--r-- | lib/events/eventcontent.h | 4 | ||||
-rw-r--r-- | lib/events/roommessageevent.cpp | 66 | ||||
-rw-r--r-- | lib/function_traits.cpp | 2 | ||||
-rw-r--r-- | lib/jobs/basejob.h | 4 | ||||
-rw-r--r-- | lib/networkaccessmanager.cpp | 2 | ||||
-rw-r--r-- | lib/networkaccessmanager.h | 2 | ||||
-rw-r--r-- | lib/quotient_common.h | 8 | ||||
-rw-r--r-- | lib/room.cpp | 14 | ||||
-rw-r--r-- | lib/settings.cpp | 10 | ||||
-rw-r--r-- | lib/ssosession.cpp | 42 | ||||
-rw-r--r-- | lib/ssosession.h | 6 | ||||
-rw-r--r-- | lib/uri.cpp | 25 | ||||
-rw-r--r-- | lib/util.h | 8 |
20 files changed, 179 insertions, 188 deletions
diff --git a/lib/accountregistry.cpp b/lib/accountregistry.cpp index a292ed45..616b54b4 100644 --- a/lib/accountregistry.cpp +++ b/lib/accountregistry.cpp @@ -8,90 +8,59 @@ using namespace Quotient; -void AccountRegistry::add(Connection* c) +void AccountRegistry::add(Connection* a) { - if (m_accounts.contains(c)) + if (contains(a)) return; - beginInsertRows(QModelIndex(), m_accounts.size(), m_accounts.size()); - m_accounts += c; + beginInsertRows(QModelIndex(), size(), size()); + push_back(a); endInsertRows(); } -void AccountRegistry::drop(Connection* c) +void AccountRegistry::drop(Connection* a) { - beginRemoveRows(QModelIndex(), m_accounts.indexOf(c), m_accounts.indexOf(c)); - m_accounts.removeOne(c); + const auto idx = indexOf(a); + beginRemoveRows(QModelIndex(), idx, idx); + remove(idx); endRemoveRows(); - Q_ASSERT(!m_accounts.contains(c)); + Q_ASSERT(!contains(a)); } bool AccountRegistry::isLoggedIn(const QString &userId) const { - return std::any_of(m_accounts.cbegin(), m_accounts.cend(), - [&userId](Connection* a) { return a->userId() == userId; }); + return std::any_of(cbegin(), cend(), [&userId](const Connection* a) { + return a->userId() == userId; + }); } -bool AccountRegistry::contains(Connection *c) const +QVariant AccountRegistry::data(const QModelIndex& index, int role) const { - return m_accounts.contains(c); -} - -AccountRegistry::AccountRegistry() = default; - -QVariant AccountRegistry::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) { - return {}; - } - - if (index.row() >= m_accounts.count()) { + if (!index.isValid() || index.row() >= count()) return {}; - } - auto account = m_accounts[index.row()]; - - if (role == ConnectionRole) { - return QVariant::fromValue(account); - } + if (role == AccountRole) + return QVariant::fromValue(at(index.row())); return {}; } -int AccountRegistry::rowCount(const QModelIndex &parent) const +int AccountRegistry::rowCount(const QModelIndex& parent) const { - if (parent.isValid()) { - return 0; - } - - return m_accounts.count(); + return parent.isValid() ? 0 : count(); } QHash<int, QByteArray> AccountRegistry::roleNames() const { - return {{ConnectionRole, "connection"}}; + return { { AccountRole, "connection" } }; } -bool AccountRegistry::isEmpty() const -{ - return m_accounts.isEmpty(); -} -int AccountRegistry::count() const -{ - return m_accounts.count(); -} - -const QVector<Connection*> AccountRegistry::accounts() const -{ - return m_accounts; -} Connection* AccountRegistry::get(const QString& userId) { - for (const auto &connection : m_accounts) { - if(connection->userId() == userId) { + for (const auto &connection : *this) { + if (connection->userId() == userId) return connection; - } } return nullptr; } diff --git a/lib/accountregistry.h b/lib/accountregistry.h index f7a864df..2f6dffdf 100644 --- a/lib/accountregistry.h +++ b/lib/accountregistry.h @@ -6,42 +6,55 @@ #include "quotient_export.h" -#include <QtCore/QObject> -#include <QtCore/QList> #include <QtCore/QAbstractListModel> namespace Quotient { class Connection; -class QUOTIENT_API AccountRegistry : public QAbstractListModel { +class QUOTIENT_API AccountRegistry : public QAbstractListModel, + private QVector<Connection*> { Q_OBJECT public: + using const_iterator = QVector::const_iterator; + using const_reference = QVector::const_reference; + enum EventRoles { - ConnectionRole = Qt::UserRole + 1, + AccountRole = Qt::UserRole + 1, + ConnectionRole = AccountRole }; - static AccountRegistry &instance() { - static AccountRegistry _instance; - return _instance; - } + [[deprecated("Use Accounts variable instead")]] // + static AccountRegistry& instance(); + + // Expose most of QVector's const-API but only provide add() and drop() + // for changing it. In theory other changing operations could be supported + // too; but then boilerplate begin/end*() calls has to be tucked into each + // and this class gives no guarantees on the order of entries, so why care. - const QVector<Connection*> accounts() const; + const QVector<Connection*>& accounts() const { return *this; } void add(Connection* a); void drop(Connection* a); + const_iterator begin() const { return QVector::begin(); } + const_iterator end() const { return QVector::end(); } + const_reference front() const { return QVector::front(); } + const_reference back() const { return QVector::back(); } bool isLoggedIn(const QString& userId) const; - bool isEmpty() const; - int count() const; - bool contains(Connection*) const; Connection* get(const QString& userId); - [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; + using QVector::isEmpty, QVector::empty; + using QVector::size, QVector::count, QVector::capacity; + using QVector::cbegin, QVector::cend, QVector::contains; + + // QAbstractItemModel interface implementation + [[nodiscard]] QVariant data(const QModelIndex& index, + int role) const override; + [[nodiscard]] int rowCount( + const QModelIndex& parent = QModelIndex()) const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override; +}; -private: - AccountRegistry(); +inline QUOTIENT_API AccountRegistry Accounts {}; - QVector<Connection *> m_accounts; -}; +inline AccountRegistry& AccountRegistry::instance() { return Accounts; } } diff --git a/lib/connection.cpp b/lib/connection.cpp index 1915c2a9..67bc83f9 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -258,7 +258,7 @@ Connection::~Connection() { qCDebug(MAIN) << "deconstructing connection object for" << userId(); stopSync(); - AccountRegistry::instance().drop(this); + Accounts.drop(this); } void Connection::resolveServer(const QString& mxid) @@ -438,7 +438,7 @@ void Connection::Private::completeSetup(const QString& mxId) qCDebug(MAIN) << "Using server" << data->baseUrl().toDisplayString() << "by user" << data->userId() << "from device" << data->deviceId(); - AccountRegistry::instance().add(q); + Accounts.add(q); #ifndef Quotient_E2EE_ENABLED qCWarning(E2EE) << "End-to-end encryption (E2EE) support is turned off."; #else // Quotient_E2EE_ENABLED diff --git a/lib/connection.h b/lib/connection.h index b4476347..28688cc1 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -443,6 +443,17 @@ public: std::forward<JobArgTs>(jobArgs)...); } + //! \brief Start a local HTTP server and generate a single sign-on URL + //! + //! This call does the preparatory steps to carry out single sign-on + //! sequence + //! \sa https://matrix.org/docs/guides/sso-for-client-developers + //! \return A proxy object holding two URLs: one for SSO on the chosen + //! homeserver and another for the local callback address. Normally + //! you won't need the callback URL unless you proxy the response + //! with a custom UI. You do not need to delete the SsoSession + //! object; the Connection that issued it will dispose of it once + //! the login sequence completes (with any outcome). Q_INVOKABLE SsoSession* prepareForSso(const QString& initialDeviceName, const QString& deviceId = {}); diff --git a/lib/converters.h b/lib/converters.h index a6028f1b..8eea1cd3 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -355,7 +355,7 @@ namespace _impl { }; } // namespace _impl -static constexpr bool IfNotEmpty = false; +constexpr bool IfNotEmpty = false; /*! Add a key-value pair to QJsonObject or QUrlQuery * diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index c0f2202d..12f1f00b 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -4,27 +4,24 @@ #pragma once #include "event.h" +#include "util.h" namespace Quotient { -constexpr const char* FavouriteTag = "m.favourite"; -constexpr const char* LowPriorityTag = "m.lowpriority"; -constexpr const char* ServerNoticeTag = "m.server_notice"; +constexpr auto FavouriteTag [[maybe_unused]] = "m.favourite"_ls; +constexpr auto LowPriorityTag [[maybe_unused]] = "m.lowpriority"_ls; +constexpr auto ServerNoticeTag [[maybe_unused]] = "m.server_notice"_ls; struct TagRecord { - using order_type = Omittable<float>; - - order_type 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, - // against std::optional<>::operator<() convention. - return order && (!other.order || *order < *other.order); - } + Omittable<float> order = none; }; +inline bool operator<(TagRecord lhs, TagRecord rhs) +{ + // Per The Spec, rooms with no order should be after those with order, + // against std::optional<>::operator<() convention. + return lhs.order && (!rhs.order || *lhs.order < *rhs.order); +} + template <> struct JsonObjectConverter<TagRecord> { static void fillFrom(const QJsonObject& jo, TagRecord& rec) @@ -41,7 +38,7 @@ struct JsonObjectConverter<TagRecord> { rec.order = none; } } - static void dumpTo(QJsonObject& jo, const TagRecord& rec) + static void dumpTo(QJsonObject& jo, TagRecord rec) { addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order); } diff --git a/lib/events/event.h b/lib/events/event.h index f12e525e..f10f6a8d 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -264,9 +264,9 @@ using Events = EventsArray<Event>; // provide matrixTypeId() and typeId(). #define DEFINE_EVENT_TYPEID(Id_, Type_) \ static constexpr event_type_t TypeId = Id_##_ls; \ - [[deprecated("Use _Type::TypeId directly instead")]] \ + [[deprecated("Use " #Type_ "::TypeId directly instead")]] \ static constexpr event_mtype_t matrixTypeId() { return Id_; } \ - [[deprecated("Use _Type::TypeId directly instead")]] \ + [[deprecated("Use " #Type_ "::TypeId directly instead")]] \ static event_type_t typeId() { return TypeId; } \ // End of macro @@ -311,7 +311,7 @@ inline auto switchOnType(const BaseEventT& event, FnT&& fn) namespace _impl { // Using bool instead of auto below because auto apparently upsets MSVC template <class BaseT, typename FnT> - inline constexpr bool needs_downcast = + constexpr bool needs_downcast = std::is_base_of_v<BaseT, std::decay_t<fn_arg_t<FnT>>> && !std::is_same_v<BaseT, std::decay_t<fn_arg_t<FnT>>>; } diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 87ea3672..de9a792b 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -149,10 +149,8 @@ namespace EventContent { */ class QUOTIENT_API Thumbnail : public ImageInfo { public: - Thumbnail() = default; // Allow empty thumbnails - Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file = none); - Thumbnail(const ImageInfo& info) : ImageInfo(info) {} using ImageInfo::ImageInfo; + Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file = none); /** * Writes thumbnail information to "thumbnail_info" subobject diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 2b7b4166..5ab0f845 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -19,15 +19,15 @@ using namespace EventContent; using MsgType = RoomMessageEvent::MsgType; -static const auto RelatesToKeyL = "m.relates_to"_ls; -static const auto MsgTypeKeyL = "msgtype"_ls; -static const auto FormattedBodyKeyL = "formatted_body"_ls; +namespace { // Supporting internal definitions -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"); +constexpr auto RelatesToKey = "m.relates_to"_ls; +constexpr auto MsgTypeKey = "msgtype"_ls; +constexpr auto FormattedBodyKey = "formatted_body"_ls; +constexpr auto TextTypeKey = "m.text"_ls; +constexpr auto EmoteTypeKey = "m.emote"_ls; +constexpr auto NoticeTypeKey = "m.notice"_ls; +constexpr auto HtmlContentTypeId = "org.matrix.custom.html"_ls; template <typename ContentT> TypedBase* make(const QJsonObject& json) @@ -38,13 +38,13 @@ TypedBase* make(const QJsonObject& json) template <> TypedBase* make<TextContent>(const QJsonObject& json) { - return json.contains(FormattedBodyKeyL) || json.contains(RelatesToKeyL) + return json.contains(FormattedBodyKey) || json.contains(RelatesToKey) ? new TextContent(json) : nullptr; } struct MsgTypeDesc { - QString matrixType; + QLatin1String matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); }; @@ -53,11 +53,11 @@ const std::vector<MsgTypeDesc> msgTypes = { { TextTypeKey, MsgType::Text, make<TextContent> }, { EmoteTypeKey, MsgType::Emote, make<TextContent> }, { NoticeTypeKey, MsgType::Notice, make<TextContent> }, - { QStringLiteral("m.image"), MsgType::Image, make<ImageContent> }, - { QStringLiteral("m.file"), MsgType::File, make<FileContent> }, - { QStringLiteral("m.location"), MsgType::Location, make<LocationContent> }, - { QStringLiteral("m.video"), MsgType::Video, make<VideoContent> }, - { QStringLiteral("m.audio"), MsgType::Audio, make<AudioContent> } + { "m.image"_ls, MsgType::Image, make<ImageContent> }, + { "m.file"_ls, MsgType::File, make<FileContent> }, + { "m.location"_ls, MsgType::Location, make<LocationContent> }, + { "m.video"_ls, MsgType::Video, make<VideoContent> }, + { "m.audio"_ls, MsgType::Audio, make<AudioContent> } }; QString msgTypeToJson(MsgType enumType) @@ -89,17 +89,19 @@ inline bool isReplacement(const Omittable<RelatesTo>& rel) return rel && rel->type == RelatesTo::ReplacementTypeId(); } +} // anonymous namespace + QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, const QString& jsonMsgType, TypedBase* content) { auto json = content ? content->toJson() : QJsonObject(); - if (json.contains(RelatesToKeyL)) { + if (json.contains(RelatesToKey)) { if (jsonMsgType != TextTypeKey && jsonMsgType != NoticeTypeKey && jsonMsgType != EmoteTypeKey) { - json.remove(RelatesToKeyL); + json.remove(RelatesToKey); qCWarning(EVENTS) - << RelatesToKeyL << "cannot be used in" << jsonMsgType + << 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 @@ -109,9 +111,9 @@ 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(MsgTypeKeyL, jsonMsgType); + newContentJson.insert(MsgTypeKey, jsonMsgType); json.insert(QStringLiteral("m.new_content"), newContentJson); - json[MsgTypeKeyL] = jsonMsgType; + json[MsgTypeKey] = jsonMsgType; json[BodyKeyL] = "* " + plainBody; return json; } @@ -177,8 +179,8 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) if (isRedacted()) return; const QJsonObject content = contentJson(); - if (content.contains(MsgTypeKeyL) && content.contains(BodyKeyL)) { - auto msgtype = content[MsgTypeKeyL].toString(); + if (content.contains(MsgTypeKey) && content.contains(BodyKeyL)) { + auto msgtype = content[MsgTypeKey].toString(); bool msgTypeFound = false; for (const auto& mt : msgTypes) if (mt.matrixType == msgtype) { @@ -204,7 +206,7 @@ RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const QString RoomMessageEvent::rawMsgtype() const { - return contentPart<QString>(MsgTypeKeyL); + return contentPart<QString>(MsgTypeKey); } QString RoomMessageEvent::plainBody() const @@ -295,7 +297,7 @@ Omittable<RelatesTo> fromJson(const QJsonValue& jv) } // namespace Quotient TextContent::TextContent(const QJsonObject& json) - : relatesTo(fromJson<Omittable<RelatesTo>>(json[RelatesToKeyL])) + : relatesTo(fromJson<Omittable<RelatesTo>>(json[RelatesToKey])) { QMimeDatabase db; static const auto PlainTextMimeType = db.mimeTypeForName("text/plain"); @@ -308,7 +310,7 @@ TextContent::TextContent(const QJsonObject& json) // of sending HTML messages. if (actualJson["format"_ls].toString() == HtmlContentTypeId) { mimeType = HtmlMimeType; - body = actualJson[FormattedBodyKeyL].toString(); + body = actualJson[FormattedBodyKey].toString(); } else { // Falling back to plain text, as there's no standard way to describe // rich text in messages. @@ -320,7 +322,6 @@ TextContent::TextContent(const QJsonObject& json) void TextContent::fillJson(QJsonObject* json) const { static const auto FormatKey = QStringLiteral("format"); - static const auto FormattedBodyKey = QStringLiteral("formatted_body"); Q_ASSERT(json); if (mimeType.inherits("text/html")) { @@ -328,11 +329,14 @@ void TextContent::fillJson(QJsonObject* json) const json->insert(FormattedBodyKey, body); } if (relatesTo) { - json->insert(QStringLiteral("m.relates_to"), - relatesTo->type == RelatesTo::ReplyTypeId() ? - QJsonObject { { relatesTo->type, QJsonObject{ { EventIdKey, relatesTo->eventId } } } } : - QJsonObject { { "rel_type", relatesTo->type }, { EventIdKey, relatesTo->eventId } } - ); + json->insert( + QStringLiteral("m.relates_to"), + relatesTo->type == RelatesTo::ReplyTypeId() + ? QJsonObject { { relatesTo->type, + QJsonObject { + { EventIdKey, relatesTo->eventId } } } } + : QJsonObject { { "rel_type", relatesTo->type }, + { EventIdKey, relatesTo->eventId } }); if (relatesTo->type == RelatesTo::ReplacementTypeId()) { QJsonObject newContentJson; if (mimeType.inherits("text/html")) { diff --git a/lib/function_traits.cpp b/lib/function_traits.cpp index 20bcf30e..6542101a 100644 --- a/lib/function_traits.cpp +++ b/lib/function_traits.cpp @@ -47,7 +47,7 @@ static_assert(std::is_same_v<fn_arg_t<Fo1>, int>, "Test fn_arg_t defaulting to first argument"); template <typename T> -static void ft(const std::vector<T>&); +[[maybe_unused]] static void ft(const std::vector<T>&); static_assert( std::is_same<fn_arg_t<decltype(ft<double>)>, const std::vector<double>&>(), "Test function templates"); diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index c899170d..9ed58ba8 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -28,7 +28,7 @@ class QUOTIENT_API BaseJob : public QObject { static QByteArray encodeIfParam(const QString& paramPart); template <int N> - static inline auto encodeIfParam(const char (&constPart)[N]) + static auto encodeIfParam(const char (&constPart)[N]) { return constPart; } @@ -248,7 +248,7 @@ public: } public Q_SLOTS: - void initiate(ConnectionData* connData, bool inBackground); + void initiate(Quotient::ConnectionData* connData, bool inBackground); /** * Abandons the result of this job, arrived or unarrived. diff --git a/lib/networkaccessmanager.cpp b/lib/networkaccessmanager.cpp index 2c0f716b..58c3cc3a 100644 --- a/lib/networkaccessmanager.cpp +++ b/lib/networkaccessmanager.cpp @@ -97,7 +97,7 @@ QNetworkReply* NetworkAccessManager::createRequest( // TODO: Make the best effort with a direct unauthenticated request // to the media server } else { - auto* const connection = AccountRegistry::instance().get(accountId); + auto* const connection = Accounts.get(accountId); if (!connection) { qCWarning(NETWORK) << "Connection" << accountId << "not found"; return new MxcReply(); diff --git a/lib/networkaccessmanager.h b/lib/networkaccessmanager.h index 5a9c134c..8ff1c6b5 100644 --- a/lib/networkaccessmanager.h +++ b/lib/networkaccessmanager.h @@ -8,8 +8,6 @@ #include <QtNetwork/QNetworkAccessManager> namespace Quotient { -class Room; -class Connection; class QUOTIENT_API NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT diff --git a/lib/quotient_common.h b/lib/quotient_common.h index 3d38ce1f..02a9f0cd 100644 --- a/lib/quotient_common.h +++ b/lib/quotient_common.h @@ -77,7 +77,7 @@ enum class Membership : unsigned int { }; QUO_DECLARE_FLAGS_NS(MembershipMask, Membership) -constexpr inline auto MembershipStrings = make_array( +constexpr auto MembershipStrings = make_array( // The order MUST be the same as the order in the original enum "join", "leave", "invite", "knock", "ban"); @@ -95,7 +95,7 @@ enum class JoinState : std::underlying_type_t<Membership> { }; QUO_DECLARE_FLAGS_NS(JoinStates, JoinState) -constexpr inline auto JoinStateStrings = make_array( +[[maybe_unused]] constexpr auto JoinStateStrings = make_array( MembershipStrings[0], MembershipStrings[1], MembershipStrings[2], MembershipStrings[3] /* same as MembershipStrings, sans "ban" */ ); @@ -125,9 +125,7 @@ enum RoomType { }; Q_ENUM_NS(RoomType) -constexpr inline auto RoomTypeStrings = make_array( - "m.space" -); +[[maybe_unused]] constexpr auto RoomTypeStrings = make_array("m.space"); } // namespace Quotient Q_DECLARE_OPERATORS_FOR_FLAGS(Quotient::MembershipMask) diff --git a/lib/room.cpp b/lib/room.cpp index edf5dcd9..55efb5b9 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -562,16 +562,14 @@ QStringList Room::pinnedEventIds() const { return d->getCurrentState<RoomPinnedEvent>()->pinnedEvents(); } -QVector< const Quotient::RoomEvent* > Quotient::Room::pinnedEvents() const +QVector<const Quotient::RoomEvent*> Quotient::Room::pinnedEvents() const { - QStringList events = d->getCurrentState<RoomPinnedEvent>()->pinnedEvents(); + const auto& pinnedIds = d->getCurrentState<RoomPinnedEvent>()->pinnedEvents(); QVector<const RoomEvent*> pinnedEvents; - QStringList::iterator i; - for (i = events.begin(); i != events.end(); ++i) { - auto timelineItem = findInTimeline(*i); - if (timelineItem != historyEdge()) - pinnedEvents.append(timelineItem->event()); - } + for (auto&& evtId: pinnedIds) + if (const auto& it = findInTimeline(evtId); it != historyEdge()) + pinnedEvents.append(it->event()); + return pinnedEvents; } diff --git a/lib/settings.cpp b/lib/settings.cpp index 5549e4de..2491d89d 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -3,6 +3,7 @@ #include "settings.h" +#include "util.h" #include "logging.h" #include <QtCore/QUrl> @@ -109,10 +110,11 @@ QUO_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, QUO_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) -static const auto HomeserverKey = QStringLiteral("homeserver"); -static const auto AccessTokenKey = QStringLiteral("access_token"); -static const auto EncryptionAccountPickleKey = - QStringLiteral("encryption_account_pickle"); +namespace { +constexpr auto HomeserverKey = "homeserver"_ls; +constexpr auto AccessTokenKey = "access_token"_ls; +constexpr auto EncryptionAccountPickleKey = "encryption_account_pickle"_ls; +} QUrl AccountSettings::homeserver() const { diff --git a/lib/ssosession.cpp b/lib/ssosession.cpp index 5f3479b8..93e252cc 100644 --- a/lib/ssosession.cpp +++ b/lib/ssosession.cpp @@ -15,10 +15,10 @@ using namespace Quotient; class SsoSession::Private { public: - Private(SsoSession* q, const QString& initialDeviceName = {}, - const QString& deviceId = {}, Connection* connection = nullptr) - : initialDeviceName(initialDeviceName) - , deviceId(deviceId) + Private(SsoSession* q, QString initialDeviceName = {}, + QString deviceId = {}, Connection* connection = nullptr) + : initialDeviceName(std::move(initialDeviceName)) + , deviceId(std::move(deviceId)) , connection(connection) { auto* server = new QTcpServer(q); @@ -29,7 +29,7 @@ public: .arg(server->serverPort()); ssoUrl = connection->getUrlForApi<RedirectToSSOJob>(callbackUrl); - QObject::connect(server, &QTcpServer::newConnection, q, [this, server] { + QObject::connect(server, &QTcpServer::newConnection, q, [this, q, server] { qCDebug(MAIN) << "SSO callback initiated"; socket = server->nextPendingConnection(); server->close(); @@ -43,8 +43,14 @@ public: }); QObject::connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); + QObject::connect(socket, &QObject::destroyed, q, + &QObject::deleteLater); }); + qCDebug(MAIN) << "SSO session constructed"; } + ~Private() { qCDebug(MAIN) << "SSO session deconstructed"; } + Q_DISABLE_COPY_MOVE(Private) + void processCallback(); void sendHttpResponse(const QByteArray& code, const QByteArray& msg); void onError(const QByteArray& code, const QString& errorMsg); @@ -62,14 +68,7 @@ SsoSession::SsoSession(Connection* connection, const QString& initialDeviceName, const QString& deviceId) : QObject(connection) , d(makeImpl<Private>(this, initialDeviceName, deviceId, connection)) -{ - qCDebug(MAIN) << "SSO session constructed"; -} - -SsoSession::~SsoSession() -{ - qCDebug(MAIN) << "SSO session deconstructed"; -} +{} QUrl SsoSession::ssoUrl() const { return d->ssoUrl; } @@ -82,29 +81,29 @@ void SsoSession::Private::processCallback() // (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")); + onError("400 Bad Request", tr("Malformed single sign-on 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")); + onError("400 Bad Request", tr("No login token in SSO callback")); + return; } 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"; + const auto msg = + tr("The application '%1' has successfully logged in as a user %2 " + "with device id %3. This window can be closed. Thank you.\r\n") + .arg(QCoreApplication::applicationName(), connection->userId(), + connection->deviceId()); sendHttpResponse("200 OK", msg.toHtmlEscaped().toUtf8()); socket->disconnectFromHost(); }); connect(connection, &Connection::loginError, socket, [this] { onError("401 Unauthorised", tr("Login failed")); - socket->disconnectFromHost(); }); } @@ -128,4 +127,5 @@ void SsoSession::Private::onError(const QByteArray& code, // [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); + socket->disconnectFromHost(); } diff --git a/lib/ssosession.h b/lib/ssosession.h index 0f3fc3b8..e6a3f8fb 100644 --- a/lib/ssosession.h +++ b/lib/ssosession.h @@ -8,9 +8,6 @@ #include <QtCore/QUrl> #include <QtCore/QObject> -class QTcpServer; -class QTcpSocket; - namespace Quotient { class Connection; @@ -36,7 +33,8 @@ class QUOTIENT_API SsoSession : public QObject { public: SsoSession(Connection* connection, const QString& initialDeviceName, const QString& deviceId = {}); - ~SsoSession() override; + ~SsoSession() override = default; + QUrl ssoUrl() const; QUrl callbackUrl() const; diff --git a/lib/uri.cpp b/lib/uri.cpp index c8843dda..6b7d1d20 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -3,29 +3,34 @@ #include "uri.h" +#include "util.h" #include "logging.h" #include <QtCore/QRegularExpression> using namespace Quotient; -struct ReplacePair { QByteArray uriString; char sigil; }; +namespace { + +struct ReplacePair { QLatin1String uriString; char sigil; }; /// \brief Defines bi-directional mapping of path prefixes and sigils /// /// When there are two prefixes for the same sigil, the first matching /// entry for a given sigil is used. -static const auto replacePairs = { - ReplacePair { "u/", '@' }, - { "user/", '@' }, - { "roomid/", '!' }, - { "r/", '#' }, - { "room/", '#' }, +const ReplacePair replacePairs[] = { + { "u/"_ls, '@' }, + { "user/"_ls, '@' }, + { "roomid/"_ls, '!' }, + { "r/"_ls, '#' }, + { "room/"_ls, '#' }, // The notation for bare event ids is not proposed in MSC2312 but there's // https://github.com/matrix-org/matrix-doc/pull/2644 - { "e/", '$' }, - { "event/", '$' } + { "e/"_ls, '$' }, + { "event/"_ls, '$' } }; +} + Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) { if (primaryId.isEmpty()) @@ -75,7 +80,7 @@ static auto decodeFragmentPart(QStringView part) return QUrl::fromPercentEncoding(part.toLatin1()).toUtf8(); } -static auto matrixToUrlRegexInit() +static inline auto matrixToUrlRegexInit() { // See https://matrix.org/docs/spec/appendices#matrix-to-navigation const QRegularExpression MatrixToUrlRE { @@ -54,9 +54,9 @@ using UnorderedMap = std::unordered_map<KeyT, ValT, HashQ<KeyT>>; namespace _impl { template <typename TT> - constexpr inline auto IsOmittableValue = false; + constexpr auto IsOmittableValue = false; template <typename TT> - constexpr inline auto IsOmittable = IsOmittableValue<std::decay_t<TT>>; + constexpr auto IsOmittable = IsOmittableValue<std::decay_t<TT>>; } constexpr auto none = std::nullopt; @@ -165,7 +165,7 @@ Omittable(T&&) -> Omittable<T>; namespace _impl { template <typename T> - constexpr inline auto IsOmittableValue<Omittable<T>> = true; + constexpr auto IsOmittableValue<Omittable<T>> = true; } template <typename T1, typename T2> @@ -191,7 +191,7 @@ inline auto merge(T1& lhs, const Omittable<T2>& rhs) return true; } -inline constexpr auto operator"" _ls(const char* s, std::size_t size) +constexpr auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); } |