KUnifiedPush

gotifypushprovider.cpp
1 /*
2  SPDX-FileCopyrightText: 2022 Volker Krause <[email protected]>
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 
18 using namespace KUnifiedPush;
19 
20 GotifyPushProvider::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 
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();
41  if (state == QAbstractSocket::ConnectedState) {
42  Q_EMIT connected();
43  } else if (state == QAbstractSocket::UnconnectedState) {
45  m_socket->deleteLater();
46  }
47  });
48  connect(m_socket, &QWebSocket::textMessageReceived, this, &GotifyPushProvider::wsMessageReceived);
49 
50  auto wsUrl = m_url;
51  if (wsUrl.scheme() == QLatin1String("https")) {
52  wsUrl.setScheme(QStringLiteral("wss"));
53  } else if (wsUrl.scheme() == QLatin1String("http")) {
54  wsUrl.setScheme(QStringLiteral("ws"));
55  } else {
56  qCWarning(Log) << "Unknown URL scheme:" << m_url;
58  return;
59  }
60 
61  auto path = wsUrl.path();
62  path += QLatin1String("/stream");
63  wsUrl.setPath(path);
64 
65  QUrlQuery query(wsUrl);
66  query.addQueryItem(QStringLiteral("token"), m_clientToken);
67  wsUrl.setQuery(query);
68 
69  m_socket->open(wsUrl);
70 }
71 
73 {
74  m_socket->close();
75  m_socket = nullptr;
76 }
77 
78 void GotifyPushProvider::wsMessageReceived(const QString &msg)
79 {
80  qCDebug(Log) << msg;
81  const auto msgObj = QJsonDocument::fromJson(msg.toUtf8()).object();
82  Message m;
83  m.clientRemoteId = QString::number(msgObj.value(QLatin1String("appid")).toInt());
84  m.content = msgObj.value(QLatin1String("message")).toString().toUtf8();
86 }
87 
89 {
90  qCDebug(Log) << client.serviceName << client.token;
91 
92  QUrl url = m_url;
93  auto path = url.path();
94  path += QLatin1String("/application");
95  url.setPath(path);
96 
97  QNetworkRequest req(url);
98  req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
99  req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
100 
101  QJsonObject content;
102  content.insert(QLatin1String("name"), client.serviceName);
103  content.insert(QLatin1String("token"), client.token);
104 
105  auto reply = nam()->post(req, QJsonDocument(content).toJson(QJsonDocument::Compact));
106  connect(reply, &QNetworkReply::finished, this, [reply, this, client]() {
107  reply->deleteLater();
108  if (reply->error() != QNetworkReply::NoError) {
109  Q_EMIT clientRegistered(client, TransientNetworkError, reply->errorString());
110  return;
111  }
112 
113  const auto replyObj = QJsonDocument::fromJson(reply->readAll()).object();
114  qCDebug(Log) << QJsonDocument(replyObj).toJson(QJsonDocument::Compact);
115 
116  auto newClient = client;
117  newClient.remoteId = QString::number(replyObj.value(QLatin1String("id")).toInt());
118 
119  QUrl endpointUrl = m_url;
120  auto path = endpointUrl.path();
121  path += QLatin1String("/message");
122  endpointUrl.setPath(path);
123  QUrlQuery query(endpointUrl);
124  query.addQueryItem(QStringLiteral("token"), replyObj.value(QLatin1String("token")).toString());
125  endpointUrl.setQuery(query);
126  newClient.endpoint = endpointUrl.toString();
127 
128  Q_EMIT clientRegistered(newClient);
129  });
130 }
131 
133 {
134  qCDebug(Log) << client.serviceName << client.token << client.remoteId;
135 
136  QUrl url = m_url;
137  auto path = url.path();
138  path += QLatin1String("/application/") + client.remoteId;
139  url.setPath(path);
140 
141  QNetworkRequest req(url);
142  req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
143 
144  auto reply = nam()->deleteResource(req);
145  connect(reply, &QNetworkReply::finished, this, [reply, client, this]() {
146  reply->deleteLater();
147  qCDebug(Log) << reply->errorString() << reply->readAll(); // TODO
148  if (reply->error() != QNetworkReply::NoError) {
150  } else {
151  Q_EMIT clientUnregistered(client);
152  }
153  });
154 }
void disconnected(KUnifiedPush::AbstractPushProvider::Error error, const QString &errorMsg={})
Emitted after the connection to the push provider disconnected or failed to be established.
QJsonObject object() const const
Client-side integration with UnifiedPush.
Definition: connector.h:16
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QString number(int n, int base)
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
Q_EMITQ_EMIT
@ TransientNetworkError
temporary network error, try again
void clientUnregistered(const KUnifiedPush::Client &client, KUnifiedPush::AbstractPushProvider::Error error=NoError)
Emitted after successful client unregistration.
void disconnectFromProvider() override
Disconnect and existing connection to the push provider.
int64_t Id
Base class for push provider protocol implementations.
void registerClient(const Client &client) override
Register a new client with the provider.
void stateChanged(QAbstractSocket::SocketState state)
QString errorString() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void connected()
Emitted after the connection to the push provider has been established successfully.
void setScheme(const QString &scheme)
bool isValid() const const
void deleteLater()
void connectToProvider() override
Attempt to establish a connection to the push provider.
QString toString(QUrl::FormattingOptions options) const const
QNetworkReply * post(const QNetworkRequest &request, QIODevice *data)
QJsonObject::iterator insert(const QString &key, const QJsonValue &value)
void close(QWebSocketProtocol::CloseCode closeCode, const QString &reason)
bool isEmpty() const const
QByteArray toUtf8() const const
QVariant value(const QString &key, const QVariant &defaultValue) const const
void messageReceived(const KUnifiedPush::Message &msg)
Inform about a received push notification.
@ ProviderRejected
communication worked, but the provider refused to complete the operation
void setQuery(const QString &query, QUrl::ParsingMode mode)
void unregisterClient(const Client &client) override
Unregister a client from the provider.
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
QString path(QUrl::ComponentFormattingOptions options) const const
void clientRegistered(const KUnifiedPush::Client &client, KUnifiedPush::AbstractPushProvider::Error error=NoError, const QString &errorMsg={})
Emitted after successful client registration.
void setPath(const QString &path, QUrl::ParsingMode mode)
void setParent(QObject *parent)
A received push notification message.
Definition: message.h:14
void textMessageReceived(const QString &message)
bool loadSettings(const QSettings &settings) override
Load connection settings.
Information about a registered client.
Definition: client.h:18
void open(const QUrl &url)
QNetworkReply * deleteResource(const QNetworkRequest &request)
QString toString() const const
QAbstractSocket::SocketState state() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Tue Dec 5 2023 03:51:24 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.