KUnifiedPush

gotifypushprovider.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "gotifypushprovider.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 <QWebSocket>
17
18using namespace KUnifiedPush;
19
20GotifyPushProvider::GotifyPushProvider(QObject *parent)
21 : AbstractPushProvider(Id, parent)
22{
23 qCDebug(Log);
24}
25
27{
28 m_clientToken = settings.value(QStringLiteral("ClientToken"), QString()).toString();
29 m_url = QUrl(settings.value(QStringLiteral("Url"), QString()).toString());
30 qCDebug(Log) << m_clientToken << m_url;
31 return m_url.isValid() && !m_clientToken.isEmpty();
32}
33
34void GotifyPushProvider::connectToProvider([[maybe_unused]] Urgency urgency)
35{
36 qCDebug(Log);
37 m_socket = new QWebSocket();
38 m_socket->setParent(this);
39 connect(m_socket, &QWebSocket::stateChanged, this, [this](auto state) {
40 qCDebug(Log) << m_socket->state();
43 } else if (state == QAbstractSocket::UnconnectedState) {
44 Q_EMIT disconnected(TransientNetworkError, m_socket->errorString());
45 m_socket->disconnect(); // Prevent StateChanged from being signaled again during the following deleteLater
46 m_socket->deleteLater();
47 m_socket = nullptr;
48 }
49 });
50 connect(m_socket, &QWebSocket::textMessageReceived, this, &GotifyPushProvider::wsMessageReceived);
51
52 auto wsUrl = m_url;
53 if (wsUrl.scheme() == QLatin1String("https")) {
54 wsUrl.setScheme(QStringLiteral("wss"));
55 } else if (wsUrl.scheme() == QLatin1String("http")) {
56 wsUrl.setScheme(QStringLiteral("ws"));
57 } else {
58 qCWarning(Log) << "Unknown URL scheme:" << m_url;
60 return;
61 }
62
63 auto path = wsUrl.path();
64 path += QLatin1String("/stream");
65 wsUrl.setPath(path);
66
67 QUrlQuery query(wsUrl);
68 query.addQueryItem(QStringLiteral("token"), m_clientToken);
69 wsUrl.setQuery(query);
70
71 m_socket->open(wsUrl);
72}
73
75{
76 m_socket->close();
77 m_socket = nullptr;
78}
79
80void GotifyPushProvider::wsMessageReceived(const QString &msg)
81{
82 qCDebug(Log) << msg;
83 const auto msgObj = QJsonDocument::fromJson(msg.toUtf8()).object();
84 Message m;
85 m.clientRemoteId = QString::number(msgObj.value(QLatin1String("appid")).toInt());
86 m.content = msgObj.value(QLatin1String("message")).toString().toUtf8();
88}
89
91{
92 qCDebug(Log) << client.serviceName << client.token;
93
94 QUrl url = m_url;
95 auto path = url.path();
96 path += QLatin1String("/application");
97 url.setPath(path);
98
99 QNetworkRequest req(url);
100 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
101 req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
102
103 QJsonObject content;
104 content.insert(QLatin1String("name"), client.serviceName);
105 content.insert(QLatin1String("token"), client.token);
106
107 auto reply = nam()->post(req, QJsonDocument(content).toJson(QJsonDocument::Compact));
108 connect(reply, &QNetworkReply::finished, this, [reply, this, client]() {
109 reply->deleteLater();
110 if (reply->error() != QNetworkReply::NoError) {
111 Q_EMIT clientRegistered(client, TransientNetworkError, reply->errorString());
112 return;
113 }
114
115 const auto replyObj = QJsonDocument::fromJson(reply->readAll()).object();
116 qCDebug(Log) << QJsonDocument(replyObj).toJson(QJsonDocument::Compact);
117
118 auto newClient = client;
119 newClient.remoteId = QString::number(replyObj.value(QLatin1String("id")).toInt());
120
121 QUrl endpointUrl = m_url;
122 auto path = endpointUrl.path();
123 path += QLatin1String("/message");
124 endpointUrl.setPath(path);
125 QUrlQuery query(endpointUrl);
126 query.addQueryItem(QStringLiteral("token"), replyObj.value(QLatin1String("token")).toString());
127 endpointUrl.setQuery(query);
128 newClient.endpoint = endpointUrl.toString();
129
130 Q_EMIT clientRegistered(newClient);
131 });
132}
133
135{
136 qCDebug(Log) << client.serviceName << client.token << client.remoteId;
137
138 QUrl url = m_url;
139 auto path = url.path();
140 path += QLatin1String("/application/") + client.remoteId;
141 url.setPath(path);
142
143 QNetworkRequest req(url);
144 req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
145
146 auto reply = nam()->deleteResource(req);
147 connect(reply, &QNetworkReply::finished, this, [reply, client, this]() {
148 reply->deleteLater();
149 qCDebug(Log) << reply->errorString() << reply->readAll(); // TODO
150 if (reply->error() != QNetworkReply::NoError) {
152 } else {
154 }
155 });
156}
157
158#include "moc_gotifypushprovider.cpp"
Base class for push provider protocol implementations.
void messageReceived(const KUnifiedPush::Message &msg)
Inform about a received push notification.
void connected()
Emitted after the connection to the push provider has been established successfully.
Urgency urgency() const
The urgency level currently used by this provider.
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.
@ ProviderRejected
communication worked, but the provider refused to complete the operation
@ TransientNetworkError
temporary network error, try again
Information about a registered client.
Definition client.h:20
void unregisterClient(const Client &client) override
Unregister a client from the provider.
void connectToProvider(Urgency urgency) override
Attempt to establish a connection to the push provider.
void registerClient(const Client &client) override
Register a new client with the provider.
bool loadSettings(const QSettings &settings) override
Load connection settings.
void disconnectFromProvider() override
Disconnect and existing connection to the push provider.
A received push notification message.
Definition message.h:15
Client-side integration with UnifiedPush.
Definition connector.h:14
int64_t Id
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
void setHeader(KnownHeaders header, const QVariant &value)
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QVariant value(QAnyStringView key) const const
QString number(double n, char format, int precision)
QByteArray toUtf8() 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
QString toString() const const
void stateChanged(QAbstractSocket::SocketState state)
void textMessageReceived(const QString &message)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 25 2025 12:05:39 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.