From a35c9fa159173388d88ef77e1d31f53488aad094 Mon Sep 17 00:00:00 2001 From: Andrew Branson Date: Fri, 3 Apr 2026 22:55:30 +0200 Subject: Generalize for all fediverse accounts --- common/common.pri | 2 +- common/common.pro | 10 +-- common/fediverseauthutils.h | 153 ++++++++++++++++++++++++++++++++++++++ common/fediversepostsdatabase.cpp | 147 ++++++++++++++++++++++++++++++++++++ common/fediversepostsdatabase.h | 55 ++++++++++++++ common/fediversetextutils.h | 122 ++++++++++++++++++++++++++++++ common/mastodonauthutils.h | 143 ----------------------------------- common/mastodonpostsdatabase.cpp | 142 ----------------------------------- common/mastodonpostsdatabase.h | 53 ------------- common/mastodontextutils.h | 122 ------------------------------ 10 files changed, 483 insertions(+), 466 deletions(-) create mode 100644 common/fediverseauthutils.h create mode 100644 common/fediversepostsdatabase.cpp create mode 100644 common/fediversepostsdatabase.h create mode 100644 common/fediversetextutils.h delete mode 100644 common/mastodonauthutils.h delete mode 100644 common/mastodonpostsdatabase.cpp delete mode 100644 common/mastodonpostsdatabase.h delete mode 100644 common/mastodontextutils.h (limited to 'common') diff --git a/common/common.pri b/common/common.pri index 7f593db..1e75506 100644 --- a/common/common.pri +++ b/common/common.pri @@ -5,4 +5,4 @@ INCLUDEPATH += $$PWD DEPENDPATH += . -LIBS += -L$$PWD -lmastodoncommon +LIBS += -L$$PWD -lfediversecommon diff --git a/common/common.pro b/common/common.pro index c01e571..13aadb0 100644 --- a/common/common.pro +++ b/common/common.pro @@ -10,16 +10,16 @@ QT += sql CONFIG += link_pkgconfig PKGCONFIG += socialcache -TARGET = mastodoncommon +TARGET = fediversecommon TARGET = $$qtLibraryTarget($$TARGET) HEADERS += \ - $$PWD/mastodonauthutils.h \ - $$PWD/mastodontextutils.h \ - $$PWD/mastodonpostsdatabase.h + $$PWD/fediverseauthutils.h \ + $$PWD/fediversetextutils.h \ + $$PWD/fediversepostsdatabase.h SOURCES += \ - $$PWD/mastodonpostsdatabase.cpp + $$PWD/fediversepostsdatabase.cpp TARGETPATH = $$[QT_INSTALL_LIBS] target.path = $$TARGETPATH diff --git a/common/fediverseauthutils.h b/common/fediverseauthutils.h new file mode 100644 index 0000000..01f264a --- /dev/null +++ b/common/fediverseauthutils.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2013-2026 Jolla Ltd. + * + * 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 + */ + +#ifndef FEDIVERSEAUTHUTILS_H +#define FEDIVERSEAUTHUTILS_H + +#include +#include + +#include + +#include + +namespace FediverseAuthUtils { + +inline QString defaultServerHost() +{ + return QStringLiteral("mastodon.social"); +} + +inline QString defaultApiHost() +{ + return QStringLiteral("https://") + defaultServerHost(); +} + +inline QString normalizeApiHost(const QString &rawHost) +{ + QString host = rawHost.trimmed(); + if (host.isEmpty()) { + host = defaultServerHost(); + } + if (!host.startsWith(QLatin1String("https://")) + && !host.startsWith(QLatin1String("http://"))) { + host.prepend(QStringLiteral("https://")); + } + + QUrl url(host); + if (!url.isValid() || url.host().isEmpty()) { + return defaultApiHost(); + } + + QString normalized = QString::fromLatin1(url.toEncoded(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment)); + if (normalized.endsWith(QLatin1Char('/'))) { + normalized.chop(1); + } + return normalized; +} + +inline QString signOnHost(Accounts::Account *account) +{ + QString configuredHost = account->value(QStringLiteral("auth/oauth2/web_server/Host")).toString().trimmed(); + if (configuredHost.isEmpty()) { + configuredHost = normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()); + } + + if (configuredHost.startsWith(QLatin1String("https://"))) { + configuredHost.remove(0, 8); + } else if (configuredHost.startsWith(QLatin1String("http://"))) { + configuredHost.remove(0, 7); + } + + const int separator = configuredHost.indexOf(QLatin1Char('/')); + if (separator > -1) { + configuredHost.truncate(separator); + } + while (configuredHost.endsWith(QLatin1Char('/'))) { + configuredHost.chop(1); + } + if (configuredHost.isEmpty()) { + configuredHost = defaultServerHost(); + } + + return configuredHost; +} + +inline void addSignOnSessionParameters(Accounts::Account *account, QVariantMap *sessionData) +{ + sessionData->insert(QStringLiteral("Host"), signOnHost(account)); + + const QString authPath = account->value(QStringLiteral("auth/oauth2/web_server/AuthPath")).toString().trimmed(); + if (!authPath.isEmpty()) { + sessionData->insert(QStringLiteral("AuthPath"), authPath); + } + + const QString tokenPath = account->value(QStringLiteral("auth/oauth2/web_server/TokenPath")).toString().trimmed(); + if (!tokenPath.isEmpty()) { + sessionData->insert(QStringLiteral("TokenPath"), tokenPath); + } + + const QString responseType = account->value(QStringLiteral("auth/oauth2/web_server/ResponseType")).toString().trimmed(); + if (!responseType.isEmpty()) { + sessionData->insert(QStringLiteral("ResponseType"), responseType); + } + + const QString redirectUri = account->value(QStringLiteral("auth/oauth2/web_server/RedirectUri")).toString().trimmed(); + if (!redirectUri.isEmpty()) { + sessionData->insert(QStringLiteral("RedirectUri"), redirectUri); + } + + const QVariant scopeValue = account->value(QStringLiteral("auth/oauth2/web_server/Scope")); + if (scopeValue.isValid()) { + sessionData->insert(QStringLiteral("Scope"), scopeValue); + } + + const QString clientId = account->value(QStringLiteral("auth/oauth2/web_server/ClientId")).toString().trimmed(); + if (!clientId.isEmpty()) { + sessionData->insert(QStringLiteral("ClientId"), clientId); + } + + const QString clientSecret = account->value(QStringLiteral("auth/oauth2/web_server/ClientSecret")).toString().trimmed(); + if (!clientSecret.isEmpty()) { + sessionData->insert(QStringLiteral("ClientSecret"), clientSecret); + } + + sessionData->insert(QStringLiteral("UiPolicy"), SignOn::NoUserInteractionPolicy); +} + +inline QString accessToken(const QVariantMap &sessionResponseData) +{ + QString token = sessionResponseData.value(QLatin1String("AccessToken")).toString().trimmed(); + if (token.isEmpty()) { + token = sessionResponseData.value(QLatin1String("access_token")).toString().trimmed(); + } + return token; +} + +inline QVariantMap responseDataToMap(const SignOn::SessionData &responseData) +{ + QVariantMap data; + foreach (const QString &key, responseData.propertyNames()) { + data.insert(key, responseData.getProperty(key)); + } + return data; +} + +} // namespace FediverseAuthUtils + +#endif // FEDIVERSEAUTHUTILS_H diff --git a/common/fediversepostsdatabase.cpp b/common/fediversepostsdatabase.cpp new file mode 100644 index 0000000..50bdf96 --- /dev/null +++ b/common/fediversepostsdatabase.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2013-2026 Jolla Ltd. + * + * 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 "fediversepostsdatabase.h" + +namespace { + +QVariantMap postExtra(const SocialPost::ConstPtr &post) +{ + return post ? post->extra() : QVariantMap(); +} + +QString extraString(const SocialPost::ConstPtr &post, const char *key) +{ + return postExtra(post).value(QString::fromLatin1(key)).toString(); +} + +int extraInt(const SocialPost::ConstPtr &post, const char *key, int defaultValue = 0) +{ + return postExtra(post).value(QString::fromLatin1(key), defaultValue).toInt(); +} + +bool extraBool(const SocialPost::ConstPtr &post, const char *key, bool defaultValue = false) +{ + return postExtra(post).value(QString::fromLatin1(key), defaultValue).toBool(); +} + +} + +static const char *DB_NAME = "fediverse.db"; +static const char *ACCOUNT_NAME_KEY = "account_name"; +static const char *URL_KEY = "url"; +static const char *BOOSTED_BY_KEY = "boosted_by"; +static const char *REPLIES_COUNT_KEY = "replies_count"; +static const char *FAVOURITES_COUNT_KEY = "favourites_count"; +static const char *REBLOGS_COUNT_KEY = "reblogs_count"; +static const char *FAVOURITED_KEY = "favourited"; +static const char *REBLOGGED_KEY = "reblogged"; +static const char *INSTANCE_URL_KEY = "instance_url"; +static const char *INSTANCE_ICON_PATH_KEY = "instance_icon_path"; + +FediversePostsDatabase::FediversePostsDatabase() + : AbstractSocialPostCacheDatabase(QStringLiteral("fediverse"), QLatin1String(DB_NAME)) +{ +} + +FediversePostsDatabase::~FediversePostsDatabase() +{ +} + +void FediversePostsDatabase::addFediversePost( + const QString &identifier, + const QString &name, + const QString &accountName, + const QString &body, + const QDateTime ×tamp, + const QString &icon, + const QList > &images, + const QString &url, + const QString &boostedBy, + int repliesCount, + int favouritesCount, + int reblogsCount, + bool favourited, + bool reblogged, + const QString &instanceUrl, + const QString &instanceIconPath, + int account) +{ + QVariantMap extra; + extra.insert(ACCOUNT_NAME_KEY, accountName); + extra.insert(URL_KEY, url); + extra.insert(BOOSTED_BY_KEY, boostedBy); + extra.insert(REPLIES_COUNT_KEY, repliesCount); + extra.insert(FAVOURITES_COUNT_KEY, favouritesCount); + extra.insert(REBLOGS_COUNT_KEY, reblogsCount); + extra.insert(FAVOURITED_KEY, favourited); + extra.insert(REBLOGGED_KEY, reblogged); + extra.insert(INSTANCE_URL_KEY, instanceUrl); + extra.insert(INSTANCE_ICON_PATH_KEY, instanceIconPath); + addPost(identifier, name, body, timestamp, icon, images, extra, account); +} + +QString FediversePostsDatabase::accountName(const SocialPost::ConstPtr &post) +{ + return extraString(post, ACCOUNT_NAME_KEY); +} + +QString FediversePostsDatabase::url(const SocialPost::ConstPtr &post) +{ + return extraString(post, URL_KEY); +} + +QString FediversePostsDatabase::boostedBy(const SocialPost::ConstPtr &post) +{ + return extraString(post, BOOSTED_BY_KEY); +} + +int FediversePostsDatabase::repliesCount(const SocialPost::ConstPtr &post) +{ + return extraInt(post, REPLIES_COUNT_KEY); +} + +int FediversePostsDatabase::favouritesCount(const SocialPost::ConstPtr &post) +{ + return extraInt(post, FAVOURITES_COUNT_KEY); +} + +int FediversePostsDatabase::reblogsCount(const SocialPost::ConstPtr &post) +{ + return extraInt(post, REBLOGS_COUNT_KEY); +} + +bool FediversePostsDatabase::favourited(const SocialPost::ConstPtr &post) +{ + return extraBool(post, FAVOURITED_KEY); +} + +bool FediversePostsDatabase::reblogged(const SocialPost::ConstPtr &post) +{ + return extraBool(post, REBLOGGED_KEY); +} + +QString FediversePostsDatabase::instanceUrl(const SocialPost::ConstPtr &post) +{ + return extraString(post, INSTANCE_URL_KEY); +} + +QString FediversePostsDatabase::instanceIconPath(const SocialPost::ConstPtr &post) +{ + return extraString(post, INSTANCE_ICON_PATH_KEY); +} diff --git a/common/fediversepostsdatabase.h b/common/fediversepostsdatabase.h new file mode 100644 index 0000000..2c085e0 --- /dev/null +++ b/common/fediversepostsdatabase.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013-2026 Jolla Ltd. + * + * 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 + */ + +#ifndef FEDIVERSEPOSTSDATABASE_H +#define FEDIVERSEPOSTSDATABASE_H + +#include + +class FediversePostsDatabase: public AbstractSocialPostCacheDatabase +{ + Q_OBJECT +public: + FediversePostsDatabase(); + ~FediversePostsDatabase(); + + void addFediversePost(const QString &identifier, const QString &name, + const QString &accountName, const QString &body, + const QDateTime ×tamp, + const QString &icon, + const QList > &images, + const QString &url, const QString &boostedBy, + int repliesCount, int favouritesCount, int reblogsCount, + bool favourited, bool reblogged, + const QString &instanceUrl, + const QString &instanceIconPath, + int account); + + static QString accountName(const SocialPost::ConstPtr &post); + static QString url(const SocialPost::ConstPtr &post); + static QString boostedBy(const SocialPost::ConstPtr &post); + static int repliesCount(const SocialPost::ConstPtr &post); + static int favouritesCount(const SocialPost::ConstPtr &post); + static int reblogsCount(const SocialPost::ConstPtr &post); + static bool favourited(const SocialPost::ConstPtr &post); + static bool reblogged(const SocialPost::ConstPtr &post); + static QString instanceUrl(const SocialPost::ConstPtr &post); + static QString instanceIconPath(const SocialPost::ConstPtr &post); +}; + +#endif // FEDIVERSEPOSTSDATABASE_H diff --git a/common/fediversetextutils.h b/common/fediversetextutils.h new file mode 100644 index 0000000..4fa7aed --- /dev/null +++ b/common/fediversetextutils.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013-2026 Jolla Ltd. + * + * 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 + */ + +#ifndef FEDIVERSETEXTUTILS_H +#define FEDIVERSETEXTUTILS_H + +#include +#include +#include + +namespace FediverseTextUtils { + +inline QString decodeHtmlEntities(QString text) +{ + text.replace(QStringLiteral("""), QStringLiteral("\"")); + text.replace(QStringLiteral("'"), QStringLiteral("'")); + text.replace(QStringLiteral("<"), QStringLiteral("<")); + text.replace(QStringLiteral(">"), QStringLiteral(">")); + text.replace(QStringLiteral("&"), QStringLiteral("&")); + text.replace(QStringLiteral(" "), QStringLiteral(" ")); + + static const QRegularExpression decimalEntity(QStringLiteral("&#(\\d+);")); + QRegularExpressionMatch match; + int index = 0; + while ((index = text.indexOf(decimalEntity, index, &match)) != -1) { + bool ok = false; + const uint value = match.captured(1).toUInt(&ok, 10); + QString replacement; + if (ok && value > 0 && value <= 0x10FFFF) { + replacement = QString::fromUcs4(&value, 1); + } + text.replace(index, match.capturedLength(0), replacement); + index += replacement.size(); + } + + static const QRegularExpression hexEntity(QStringLiteral("&#x([0-9a-fA-F]+);")); + index = 0; + while ((index = text.indexOf(hexEntity, index, &match)) != -1) { + bool ok = false; + const uint value = match.captured(1).toUInt(&ok, 16); + QString replacement; + if (ok && value > 0 && value <= 0x10FFFF) { + replacement = QString::fromUcs4(&value, 1); + } + text.replace(index, match.capturedLength(0), replacement); + index += replacement.size(); + } + + return text; +} + +inline QString sanitizeContent(const QString &content) +{ + QString plain = content; + plain.replace(QRegularExpression(QStringLiteral("<\\s*br\\s*/?\\s*>"), QRegularExpression::CaseInsensitiveOption), + QStringLiteral("\n")); + plain.replace(QRegularExpression(QStringLiteral("<\\s*/\\s*p\\s*>"), QRegularExpression::CaseInsensitiveOption), + QStringLiteral("\n")); + plain.remove(QRegularExpression(QStringLiteral("<[^>]+>"), QRegularExpression::CaseInsensitiveOption)); + + return decodeHtmlEntities(plain).trimmed(); +} + +inline QDateTime parseTimestamp(const QString ×tampString) +{ + QDateTime timestamp; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + timestamp = QDateTime::fromString(timestampString, Qt::ISODateWithMs); + if (timestamp.isValid()) { + return timestamp; + } +#endif + + timestamp = QDateTime::fromString(timestampString, Qt::ISODate); + if (timestamp.isValid()) { + return timestamp; + } + + // Qt 5.6 cannot parse ISO-8601 timestamps with fractional seconds. + const int timeSeparator = timestampString.indexOf(QLatin1Char('T')); + const int fractionSeparator = timestampString.indexOf(QLatin1Char('.'), timeSeparator + 1); + if (timeSeparator > -1 && fractionSeparator > -1) { + int timezoneSeparator = timestampString.indexOf(QLatin1Char('Z'), fractionSeparator + 1); + if (timezoneSeparator == -1) { + timezoneSeparator = timestampString.indexOf(QLatin1Char('+'), fractionSeparator + 1); + } + if (timezoneSeparator == -1) { + timezoneSeparator = timestampString.indexOf(QLatin1Char('-'), fractionSeparator + 1); + } + + QString stripped = timestampString; + if (timezoneSeparator > -1) { + stripped.remove(fractionSeparator, timezoneSeparator - fractionSeparator); + } else { + stripped.truncate(fractionSeparator); + } + + timestamp = QDateTime::fromString(stripped, Qt::ISODate); + } + + return timestamp; +} + +} // namespace FediverseTextUtils + +#endif // FEDIVERSETEXTUTILS_H diff --git a/common/mastodonauthutils.h b/common/mastodonauthutils.h deleted file mode 100644 index 3f1fc85..0000000 --- a/common/mastodonauthutils.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2013-2026 Jolla Ltd. - * - * 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 - */ - -#ifndef MASTODONAUTHUTILS_H -#define MASTODONAUTHUTILS_H - -#include -#include - -#include - -#include - -namespace MastodonAuthUtils { - -inline QString normalizeApiHost(const QString &rawHost) -{ - QString host = rawHost.trimmed(); - if (host.isEmpty()) { - host = QStringLiteral("https://mastodon.social"); - } - if (!host.startsWith(QLatin1String("https://")) - && !host.startsWith(QLatin1String("http://"))) { - host.prepend(QStringLiteral("https://")); - } - - QUrl url(host); - if (!url.isValid() || url.host().isEmpty()) { - return QStringLiteral("https://mastodon.social"); - } - - QString normalized = QString::fromLatin1(url.toEncoded(QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment)); - if (normalized.endsWith(QLatin1Char('/'))) { - normalized.chop(1); - } - return normalized; -} - -inline QString signOnHost(Accounts::Account *account) -{ - QString configuredHost = account->value(QStringLiteral("auth/oauth2/web_server/Host")).toString().trimmed(); - if (configuredHost.isEmpty()) { - configuredHost = normalizeApiHost(account->value(QStringLiteral("api/Host")).toString()); - } - - if (configuredHost.startsWith(QLatin1String("https://"))) { - configuredHost.remove(0, 8); - } else if (configuredHost.startsWith(QLatin1String("http://"))) { - configuredHost.remove(0, 7); - } - - const int separator = configuredHost.indexOf(QLatin1Char('/')); - if (separator > -1) { - configuredHost.truncate(separator); - } - while (configuredHost.endsWith(QLatin1Char('/'))) { - configuredHost.chop(1); - } - if (configuredHost.isEmpty()) { - configuredHost = QStringLiteral("mastodon.social"); - } - - return configuredHost; -} - -inline void addSignOnSessionParameters(Accounts::Account *account, QVariantMap *sessionData) -{ - sessionData->insert(QStringLiteral("Host"), signOnHost(account)); - - const QString authPath = account->value(QStringLiteral("auth/oauth2/web_server/AuthPath")).toString().trimmed(); - if (!authPath.isEmpty()) { - sessionData->insert(QStringLiteral("AuthPath"), authPath); - } - - const QString tokenPath = account->value(QStringLiteral("auth/oauth2/web_server/TokenPath")).toString().trimmed(); - if (!tokenPath.isEmpty()) { - sessionData->insert(QStringLiteral("TokenPath"), tokenPath); - } - - const QString responseType = account->value(QStringLiteral("auth/oauth2/web_server/ResponseType")).toString().trimmed(); - if (!responseType.isEmpty()) { - sessionData->insert(QStringLiteral("ResponseType"), responseType); - } - - const QString redirectUri = account->value(QStringLiteral("auth/oauth2/web_server/RedirectUri")).toString().trimmed(); - if (!redirectUri.isEmpty()) { - sessionData->insert(QStringLiteral("RedirectUri"), redirectUri); - } - - const QVariant scopeValue = account->value(QStringLiteral("auth/oauth2/web_server/Scope")); - if (scopeValue.isValid()) { - sessionData->insert(QStringLiteral("Scope"), scopeValue); - } - - const QString clientId = account->value(QStringLiteral("auth/oauth2/web_server/ClientId")).toString().trimmed(); - if (!clientId.isEmpty()) { - sessionData->insert(QStringLiteral("ClientId"), clientId); - } - - const QString clientSecret = account->value(QStringLiteral("auth/oauth2/web_server/ClientSecret")).toString().trimmed(); - if (!clientSecret.isEmpty()) { - sessionData->insert(QStringLiteral("ClientSecret"), clientSecret); - } - - sessionData->insert(QStringLiteral("UiPolicy"), SignOn::NoUserInteractionPolicy); -} - -inline QString accessToken(const QVariantMap &sessionResponseData) -{ - QString token = sessionResponseData.value(QLatin1String("AccessToken")).toString().trimmed(); - if (token.isEmpty()) { - token = sessionResponseData.value(QLatin1String("access_token")).toString().trimmed(); - } - return token; -} - -inline QVariantMap responseDataToMap(const SignOn::SessionData &responseData) -{ - QVariantMap data; - foreach (const QString &key, responseData.propertyNames()) { - data.insert(key, responseData.getProperty(key)); - } - return data; -} - -} // namespace MastodonAuthUtils - -#endif // MASTODONAUTHUTILS_H diff --git a/common/mastodonpostsdatabase.cpp b/common/mastodonpostsdatabase.cpp deleted file mode 100644 index 7f82162..0000000 --- a/common/mastodonpostsdatabase.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2013-2026 Jolla Ltd. - * - * 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 "mastodonpostsdatabase.h" - -static const char *DB_NAME = "mastodon.db"; -static const char *ACCOUNT_NAME_KEY = "account_name"; -static const char *URL_KEY = "url"; -static const char *BOOSTED_BY_KEY = "boosted_by"; -static const char *REPLIES_COUNT_KEY = "replies_count"; -static const char *FAVOURITES_COUNT_KEY = "favourites_count"; -static const char *REBLOGS_COUNT_KEY = "reblogs_count"; -static const char *FAVOURITED_KEY = "favourited"; -static const char *REBLOGGED_KEY = "reblogged"; -static const char *INSTANCE_URL_KEY = "instance_url"; - -MastodonPostsDatabase::MastodonPostsDatabase() - : AbstractSocialPostCacheDatabase(QStringLiteral("mastodon"), QLatin1String(DB_NAME)) -{ -} - -MastodonPostsDatabase::~MastodonPostsDatabase() -{ -} - -void MastodonPostsDatabase::addMastodonPost( - const QString &identifier, - const QString &name, - const QString &accountName, - const QString &body, - const QDateTime ×tamp, - const QString &icon, - const QList > &images, - const QString &url, - const QString &boostedBy, - int repliesCount, - int favouritesCount, - int reblogsCount, - bool favourited, - bool reblogged, - const QString &instanceUrl, - int account) -{ - QVariantMap extra; - extra.insert(ACCOUNT_NAME_KEY, accountName); - extra.insert(URL_KEY, url); - extra.insert(BOOSTED_BY_KEY, boostedBy); - extra.insert(REPLIES_COUNT_KEY, repliesCount); - extra.insert(FAVOURITES_COUNT_KEY, favouritesCount); - extra.insert(REBLOGS_COUNT_KEY, reblogsCount); - extra.insert(FAVOURITED_KEY, favourited); - extra.insert(REBLOGGED_KEY, reblogged); - extra.insert(INSTANCE_URL_KEY, instanceUrl); - addPost(identifier, name, body, timestamp, icon, images, extra, account); -} - -QString MastodonPostsDatabase::accountName(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return QString(); - } - return post->extra().value(ACCOUNT_NAME_KEY).toString(); -} - -QString MastodonPostsDatabase::url(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return QString(); - } - return post->extra().value(URL_KEY).toString(); -} - -QString MastodonPostsDatabase::boostedBy(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return QString(); - } - return post->extra().value(BOOSTED_BY_KEY).toString(); -} - -int MastodonPostsDatabase::repliesCount(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return 0; - } - return post->extra().value(REPLIES_COUNT_KEY).toInt(); -} - -int MastodonPostsDatabase::favouritesCount(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return 0; - } - return post->extra().value(FAVOURITES_COUNT_KEY).toInt(); -} - -int MastodonPostsDatabase::reblogsCount(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return 0; - } - return post->extra().value(REBLOGS_COUNT_KEY).toInt(); -} - -bool MastodonPostsDatabase::favourited(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return false; - } - return post->extra().value(FAVOURITED_KEY).toBool(); -} - -bool MastodonPostsDatabase::reblogged(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return false; - } - return post->extra().value(REBLOGGED_KEY).toBool(); -} - -QString MastodonPostsDatabase::instanceUrl(const SocialPost::ConstPtr &post) -{ - if (post.isNull()) { - return QString(); - } - return post->extra().value(INSTANCE_URL_KEY).toString(); -} diff --git a/common/mastodonpostsdatabase.h b/common/mastodonpostsdatabase.h deleted file mode 100644 index 9736fa8..0000000 --- a/common/mastodonpostsdatabase.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2013-2026 Jolla Ltd. - * - * 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 - */ - -#ifndef MASTODONPOSTSDATABASE_H -#define MASTODONPOSTSDATABASE_H - -#include - -class MastodonPostsDatabase: public AbstractSocialPostCacheDatabase -{ - Q_OBJECT -public: - MastodonPostsDatabase(); - ~MastodonPostsDatabase(); - - void addMastodonPost(const QString &identifier, const QString &name, - const QString &accountName, const QString &body, - const QDateTime ×tamp, - const QString &icon, - const QList > &images, - const QString &url, const QString &boostedBy, - int repliesCount, int favouritesCount, int reblogsCount, - bool favourited, bool reblogged, - const QString &instanceUrl, - int account); - - static QString accountName(const SocialPost::ConstPtr &post); - static QString url(const SocialPost::ConstPtr &post); - static QString boostedBy(const SocialPost::ConstPtr &post); - static int repliesCount(const SocialPost::ConstPtr &post); - static int favouritesCount(const SocialPost::ConstPtr &post); - static int reblogsCount(const SocialPost::ConstPtr &post); - static bool favourited(const SocialPost::ConstPtr &post); - static bool reblogged(const SocialPost::ConstPtr &post); - static QString instanceUrl(const SocialPost::ConstPtr &post); -}; - -#endif // MASTODONPOSTSDATABASE_H diff --git a/common/mastodontextutils.h b/common/mastodontextutils.h deleted file mode 100644 index bde74c4..0000000 --- a/common/mastodontextutils.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2013-2026 Jolla Ltd. - * - * 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 - */ - -#ifndef MASTODONTEXTUTILS_H -#define MASTODONTEXTUTILS_H - -#include -#include -#include - -namespace MastodonTextUtils { - -inline QString decodeHtmlEntities(QString text) -{ - text.replace(QStringLiteral("""), QStringLiteral("\"")); - text.replace(QStringLiteral("'"), QStringLiteral("'")); - text.replace(QStringLiteral("<"), QStringLiteral("<")); - text.replace(QStringLiteral(">"), QStringLiteral(">")); - text.replace(QStringLiteral("&"), QStringLiteral("&")); - text.replace(QStringLiteral(" "), QStringLiteral(" ")); - - static const QRegularExpression decimalEntity(QStringLiteral("&#(\\d+);")); - QRegularExpressionMatch match; - int index = 0; - while ((index = text.indexOf(decimalEntity, index, &match)) != -1) { - bool ok = false; - const uint value = match.captured(1).toUInt(&ok, 10); - QString replacement; - if (ok && value > 0 && value <= 0x10FFFF) { - replacement = QString::fromUcs4(&value, 1); - } - text.replace(index, match.capturedLength(0), replacement); - index += replacement.size(); - } - - static const QRegularExpression hexEntity(QStringLiteral("&#x([0-9a-fA-F]+);")); - index = 0; - while ((index = text.indexOf(hexEntity, index, &match)) != -1) { - bool ok = false; - const uint value = match.captured(1).toUInt(&ok, 16); - QString replacement; - if (ok && value > 0 && value <= 0x10FFFF) { - replacement = QString::fromUcs4(&value, 1); - } - text.replace(index, match.capturedLength(0), replacement); - index += replacement.size(); - } - - return text; -} - -inline QString sanitizeContent(const QString &content) -{ - QString plain = content; - plain.replace(QRegularExpression(QStringLiteral("<\\s*br\\s*/?\\s*>"), QRegularExpression::CaseInsensitiveOption), - QStringLiteral("\n")); - plain.replace(QRegularExpression(QStringLiteral("<\\s*/\\s*p\\s*>"), QRegularExpression::CaseInsensitiveOption), - QStringLiteral("\n")); - plain.remove(QRegularExpression(QStringLiteral("<[^>]+>"), QRegularExpression::CaseInsensitiveOption)); - - return decodeHtmlEntities(plain).trimmed(); -} - -inline QDateTime parseTimestamp(const QString ×tampString) -{ - QDateTime timestamp; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) - timestamp = QDateTime::fromString(timestampString, Qt::ISODateWithMs); - if (timestamp.isValid()) { - return timestamp; - } -#endif - - timestamp = QDateTime::fromString(timestampString, Qt::ISODate); - if (timestamp.isValid()) { - return timestamp; - } - - // Qt 5.6 cannot parse ISO-8601 timestamps with fractional seconds. - const int timeSeparator = timestampString.indexOf(QLatin1Char('T')); - const int fractionSeparator = timestampString.indexOf(QLatin1Char('.'), timeSeparator + 1); - if (timeSeparator > -1 && fractionSeparator > -1) { - int timezoneSeparator = timestampString.indexOf(QLatin1Char('Z'), fractionSeparator + 1); - if (timezoneSeparator == -1) { - timezoneSeparator = timestampString.indexOf(QLatin1Char('+'), fractionSeparator + 1); - } - if (timezoneSeparator == -1) { - timezoneSeparator = timestampString.indexOf(QLatin1Char('-'), fractionSeparator + 1); - } - - QString stripped = timestampString; - if (timezoneSeparator > -1) { - stripped.remove(fractionSeparator, timezoneSeparator - fractionSeparator); - } else { - stripped.truncate(fractionSeparator); - } - - timestamp = QDateTime::fromString(stripped, Qt::ISODate); - } - - return timestamp; -} - -} // namespace MastodonTextUtils - -#endif // MASTODONTEXTUTILS_H -- cgit v1.2.3