summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Branson <andrew.branson@jolla.com>2026-02-14 16:22:38 +0100
committerAndrew Branson <andrew.branson@jolla.com>2026-02-14 16:22:38 +0100
commit5335519ab47e89316858168d4ae540c9a3c6542d (patch)
tree12a53748249a3722ee2e077544841121e22a7272
parent7ffdb036406bd15e75159597434e70cbaa542b8e (diff)
Redirect foreign servers to authorize_interaction endpoint on home serverHEADmaster
-rw-r--r--RPMS/sailfish-account-mastodon-1.0.0-1.aarch64.rpmbin0 -> 3968946 bytes
-rw-r--r--buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp30
-rw-r--r--eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml31
3 files changed, 59 insertions, 2 deletions
diff --git a/RPMS/sailfish-account-mastodon-1.0.0-1.aarch64.rpm b/RPMS/sailfish-account-mastodon-1.0.0-1.aarch64.rpm
new file mode 100644
index 0000000..604e7d1
--- /dev/null
+++ b/RPMS/sailfish-account-mastodon-1.0.0-1.aarch64.rpm
Binary files differ
diff --git a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
index d5d1766..7775841 100644
--- a/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
+++ b/buteo-plugins/buteo-sync-plugin-mastodon-notifications/mastodonnotificationssyncadaptor.cpp
@@ -143,6 +143,34 @@ namespace {
return hasActiveNotifications;
}
+ QString authorizeInteractionUrl(const QString &apiHost, const QString &targetUrl)
+ {
+ const QUrl parsedApiHost(apiHost);
+ const QUrl parsedTargetUrl(targetUrl);
+ if (!parsedApiHost.isValid()
+ || parsedApiHost.scheme().isEmpty()
+ || parsedApiHost.host().isEmpty()
+ || !parsedTargetUrl.isValid()
+ || parsedTargetUrl.scheme().isEmpty()
+ || parsedTargetUrl.host().isEmpty()) {
+ return targetUrl;
+ }
+
+ // Links on the account's own instance should open directly.
+ const bool sameScheme = QString::compare(parsedApiHost.scheme(), parsedTargetUrl.scheme(), Qt::CaseInsensitive) == 0;
+ const bool sameHost = QString::compare(parsedApiHost.host(), parsedTargetUrl.host(), Qt::CaseInsensitive) == 0;
+ const int apiPort = parsedApiHost.port(parsedApiHost.scheme() == QLatin1String("https") ? 443 : 80);
+ const int targetPort = parsedTargetUrl.port(parsedTargetUrl.scheme() == QLatin1String("https") ? 443 : 80);
+ if (sameScheme && sameHost && apiPort == targetPort) {
+ return targetUrl;
+ }
+
+ QUrl authorizeUrl(parsedApiHost);
+ authorizeUrl.setPath(QStringLiteral("/authorize_interaction"));
+ authorizeUrl.setQuery(QStringLiteral("uri=") + QString::fromUtf8(QUrl::toPercentEncoding(targetUrl)));
+ return authorizeUrl.toString();
+ }
+
}
MastodonNotificationsSyncAdaptor::MastodonNotificationsSyncAdaptor(QObject *parent)
@@ -655,7 +683,7 @@ void MastodonNotificationsSyncAdaptor::publishSystemNotification(int accountId,
&& !parsedOpenUrl.host().isEmpty()
? openUrl
: fallbackUrl;
- notification->setRemoteAction(OPEN_URL_ACTION(safeOpenUrl));
+ notification->setRemoteAction(OPEN_URL_ACTION(authorizeInteractionUrl(apiHost(accountId), safeOpenUrl)));
notification->publish();
if (notification->replacesId() == 0) {
qCWarning(lcSocialPlugin) << "failed to publish Mastodon notification"
diff --git a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
index aefaa96..c8e8713 100644
--- a/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
+++ b/eventsview-plugins/eventsview-plugin-mastodon/mastodon-delegate.qml
@@ -46,7 +46,7 @@ SocialMediaAccountDelegate {
imageList: delegateItem.variantRole(model, ["images", "mediaAttachments", "media"])
avatarSource: delegateItem.convertUrl(delegateItem.stringRole(model, ["icon", "avatar", "avatarUrl"]))
fallbackAvatarSource: delegateItem.stringRole(model, ["icon", "avatar", "avatarUrl"])
- resolvedStatusUrl: delegateItem.statusUrl(model)
+ resolvedStatusUrl: delegateItem.authorizeInteractionUrl(model)
postId: delegateItem.stringRole(model, ["mastodonId", "statusId", "id", "twitterId"])
postActions: mastodonPostActions
accountId: delegateItem.firstAccountId(model)
@@ -163,6 +163,35 @@ SocialMediaAccountDelegate {
return instanceUrl + "/explore"
}
+ function authorizeInteractionUrl(modelData) {
+ var targetUrl = statusUrl(modelData)
+ if (targetUrl.length === 0) {
+ return targetUrl
+ }
+
+ var instanceUrl = stringRole(modelData, ["instanceUrl", "serverUrl", "baseUrl"])
+ if (instanceUrl.length === 0) {
+ return targetUrl
+ }
+ while (instanceUrl.length > 0 && instanceUrl.charAt(instanceUrl.length - 1) === "/") {
+ instanceUrl = instanceUrl.slice(0, instanceUrl.length - 1)
+ }
+
+ // Links on the user's own instance should open directly.
+ var sameServer = /^([a-z][a-z0-9+.-]*):\/\/([^\/?#]+)/i
+ var targetMatch = targetUrl.match(sameServer)
+ var instanceMatch = instanceUrl.match(sameServer)
+ if (targetMatch && instanceMatch
+ && targetMatch.length > 2
+ && instanceMatch.length > 2
+ && targetMatch[1].toLowerCase() === instanceMatch[1].toLowerCase()
+ && targetMatch[2].toLowerCase() === instanceMatch[2].toLowerCase()) {
+ return targetUrl
+ }
+
+ return instanceUrl + "/authorize_interaction?uri=" + encodeURIComponent(targetUrl)
+ }
+
function convertUrl(source) {
if (source.indexOf("_normal.") !== -1) {
return source.replace("_normal.", "_bigger.")