KUnifiedPush

ntfypushprovider.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "ntfypushprovider.h"
7#include "client.h"
8#include "logging.h"
9#include "message.h"
10
11#include <QJsonDocument>
12#include <QJsonObject>
13#include <QNetworkReply>
14#include <QSettings>
15#include <QUrlQuery>
16#include <QUuid>
17
18using namespace KUnifiedPush;
19
20NtfyPushProvider::NtfyPushProvider(QObject *parent)
21 : AbstractPushProvider(Id, parent)
22{
23 connect(&m_sseStream, &ServerSentEventsStream::messageReceived, this, [this](const SSEMessage &sse) {
24 qCDebug(Log) << sse.event << sse.data;
25 if (sse.event.isEmpty()) {
26 QJsonObject msgObj = QJsonDocument::fromJson(sse.data).object();
27 Message msg;
28 msg.clientRemoteId = msgObj.value(QLatin1String("topic")).toString();
29 msg.content = msgObj.value(QLatin1String("message")).toString().toUtf8();
30 if (msgObj.value(QLatin1String("encoding")).toString() == QLatin1String("base64")) {
31 msg.content = QByteArray::fromBase64(msg.content);
32 }
33 m_lastMessageId = msgObj.value(QLatin1String("id")).toString();
34 Q_EMIT messageReceived(msg);
35 storeState();
36 }
37 });
38}
39
40NtfyPushProvider::~NtfyPushProvider() = default;
41
43{
44 m_url = settings.value(QStringLiteral("Url"), QUrl()).toUrl();
45
46 QSettings internal;
47 internal.beginGroup(QLatin1String(providerId()) + QLatin1String("-internal"));
48 m_topics = internal.value(QStringLiteral("Topics"), QStringList()).toStringList();
49 m_lastMessageId = internal.value(QStringLiteral("LastMessageId"), QString()).toString();
50
51 return m_url.isValid();
52}
53
55{
56 doConnectToProvider();
58}
59
61{
62 if (m_sseReply) {
63 m_sseReply->abort();
64 }
66}
67
69{
71 auto newClient = client;
72 newClient.remoteId = topic;
73
74 QUrl endpoint = m_url;
75 auto path = endpoint.path();
76 if (!path.endsWith(QLatin1Char('/'))) {
77 path += QLatin1Char('/');
78 }
79 path += topic;
80 endpoint.setPath(path);
81 newClient.endpoint = endpoint.toString();
82
83 m_topics.push_back(topic);
84 storeState();
85 doConnectToProvider();
86 Q_EMIT clientRegistered(newClient);
87}
88
90{
91 m_topics.removeAll(client.remoteId);
92 storeState();
93 doConnectToProvider();
95}
96
97void NtfyPushProvider::doConnectToProvider()
98{
99 if (m_sseReply) {
100 m_sseReply->abort();
101 }
102
103 if (m_topics.empty()) {
104 return;
105 }
106
107 QUrl url = m_url;
108 QString path = url.path();
109 path += QLatin1Char('/') + m_topics.join(QLatin1Char(',')) + QLatin1String("/sse");
110 url.setPath(path);
112 query.addQueryItem(QStringLiteral("up"), QStringLiteral("1"));
113 query.addQueryItem(QStringLiteral("since"), m_lastMessageId.isEmpty() ? QStringLiteral("all") : m_lastMessageId);
114 url.setQuery(query);
115 qCDebug(Log) << url;
116
117 auto reply = nam()->get(QNetworkRequest(url));
118 connect(reply, &QNetworkReply::finished, this, [reply, this]() {
119 reply->deleteLater();
120 if (reply->error() == QNetworkReply::OperationCanceledError) {
121 return; // we triggered this ourselves
122 }
123 qCDebug(Log) << reply->error() << reply->errorString();
124 Q_EMIT disconnected(TransientNetworkError, reply->errorString());
125 });
126
127 m_sseReply = reply;
128 m_sseStream.read(reply);
129}
130
131void NtfyPushProvider::storeState()
132{
133 QSettings settings;
134 settings.beginGroup(QLatin1String(providerId()) + QLatin1String("-internal"));
135 settings.setValue(QStringLiteral("Topics"), m_topics);
136 settings.setValue(QStringLiteral("LastMessageId"), m_lastMessageId);
137}
138
139#include "moc_ntfypushprovider.cpp"
Base class for push provider protocol implementations.
void connected()
Emitted after the connection to the push provider has been established successfully.
const char * providerId() const
Provider id used e.g.
void clientUnregistered(const KUnifiedPush::Client &client, KUnifiedPush::AbstractPushProvider::Error error=NoError)
Emitted after successful client unregistration.
void disconnected(KUnifiedPush::AbstractPushProvider::Error error, const QString &errorMsg={})
Emitted after the connection to the push provider disconnected or failed to be established.
void clientRegistered(const KUnifiedPush::Client &client, KUnifiedPush::AbstractPushProvider::Error error=NoError, const QString &errorMsg={})
Emitted after successful client registration.
@ TransientNetworkError
temporary network error, try again
Information about a registered client.
Definition client.h:19
A received push notification message.
Definition message.h:15
void registerClient(const Client &client) override
Register a new client with the provider.
bool loadSettings(const QSettings &settings) override
Load connection settings.
void connectToProvider() override
Attempt to establish a connection to the push provider.
void unregisterClient(const Client &client) override
Unregister a client from the provider.
void disconnectFromProvider() override
Disconnect and existing connection to the push provider.
char * toString(const EngineQuery &query)
KSERVICE_EXPORT KService::List query(FilterFunc filterFunc)
QString path(const QString &relativePath)
Client-side integration with UnifiedPush.
Definition connector.h:16
int64_t Id
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
bool isEmpty() const const
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
bool empty() const const
void push_back(parameter_type value)
qsizetype removeAll(const AT &t)
QNetworkReply * get(const QNetworkRequest &request)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void beginGroup(QAnyStringView prefix)
void setValue(QAnyStringView key, const QVariant &value)
QVariant value(QAnyStringView key) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QByteArray toUtf8() const const
QString join(QChar separator) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isValid() const const
QString path(ComponentFormattingOptions options) const const
void setPath(const QString &path, ParsingMode mode)
void setQuery(const QString &query, ParsingMode mode)
QString toString(FormattingOptions options) const const
QUuid createUuid()
QString toString(StringFormat mode) const const
QString toString() const const
QStringList toStringList() const const
QUrl toUrl() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:19:35 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.