KUnifiedPush

nextpushprovider.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "nextpushprovider.h"
7#include "client.h"
8#include "logging.h"
9#include "message.h"
10
11#include <QHostInfo>
12#include <QJsonDocument>
13#include <QJsonObject>
14#include <QNetworkReply>
15#include <QSettings>
16
17using namespace KUnifiedPush;
18
19NextPushProvider::NextPushProvider(QObject *parent)
20 : AbstractPushProvider(Id, parent)
21{
22 connect(&m_sseStream, &ServerSentEventsStream::messageReceived, this, [this](const SSEMessage &sse) {
23 qCDebug(Log) << sse.event << sse.data;
24 if (sse.event == "message") {
25 QJsonObject msgObj = QJsonDocument::fromJson(sse.data).object();
26 Message msg;
27 msg.clientRemoteId = msgObj.value(QLatin1String("token")).toString();
28 msg.content = QByteArray::fromBase64(msgObj.value(QLatin1String("message")).toString().toUtf8());
29 Q_EMIT messageReceived(msg);
30 }
31 });
32}
33
34NextPushProvider::~NextPushProvider() = default;
35
37{
38 m_appPassword = settings.value(QStringLiteral("AppPassword"), QString()).toString();
39 m_url = QUrl(settings.value(QStringLiteral("Url"), QString()).toString());
40 m_userName = settings.value(QStringLiteral("Username"), QString()).toString();
41 m_deviceId = settings.value(QStringLiteral("DeviceId"), QString()).toString();
42 qCDebug(Log) << m_url << m_userName << m_deviceId;
43 return m_url.isValid() && !m_appPassword.isEmpty() && !m_userName.isEmpty();
44}
45
47{
48 qCDebug(Log) << m_deviceId;
49
50 if (m_deviceId.isEmpty()) {
51 auto req = prepareRequest("device");
52 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
53
54 QJsonObject content;
55 content.insert(QLatin1String("deviceName"), QHostInfo::localHostName());
56 auto reply = nam()->put(req, QJsonDocument(content).toJson(QJsonDocument::Compact));
57 connect(reply, &QNetworkReply::finished, this, [reply, this]() {
58 reply->deleteLater();
59 if (reply->error() != QNetworkReply::NoError) {
60 qCWarning(Log) << reply->errorString();
61 Q_EMIT disconnected(ProviderRejected, reply->errorString());
62 return;
63 }
64
65 const auto content = QJsonDocument::fromJson(reply->readAll()).object();
66 qCDebug(Log) << QJsonDocument(content).toJson(QJsonDocument::Compact);
67 // TODO check "success" field
68 m_deviceId = content.value(QLatin1String("deviceId")).toString();
69
70 QSettings settings;
71 settings.setValue(QStringLiteral("NextPush/DeviceId"), m_deviceId);
72 waitForMessage();
73 });
74 } else {
75 waitForMessage();
76 }
77}
78
80{
81 if (m_sseReply) {
82 m_sseReply->abort();
83 } else {
85 }
86}
87
89{
90 qCDebug(Log) << client.serviceName;
91 auto req = prepareRequest("app");
92 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
93
94 QJsonObject content;
95 content.insert(QLatin1String("deviceId"), m_deviceId);
96 content.insert(QLatin1String("appName"), client.serviceName);
97 auto reply = nam()->put(req, QJsonDocument(content).toJson(QJsonDocument::Compact));
98 connect(reply, &QNetworkReply::finished, this, [reply, this, client]() {
99 reply->deleteLater();
100 if (reply->error() != QNetworkReply::NoError) {
101 Q_EMIT clientRegistered(client, TransientNetworkError, reply->errorString());
102 return;
103 }
104
105 const auto content = QJsonDocument::fromJson(reply->readAll()).object();
106 qCDebug(Log) << QJsonDocument(content).toJson(QJsonDocument::Compact);
107 if (!content.value(QLatin1String("success")).toBool()) {
108 Q_EMIT clientRegistered(client, ProviderRejected, QString()); // TODO do we get an error message in this case?
109 return;
110 }
111 auto newClient = client;
112 newClient.remoteId = content.value(QLatin1String("token")).toString();
113
114 QUrl endpointUrl = m_url;
115 auto path = endpointUrl.path();
116 path += QLatin1String("/index.php/apps/uppush/push/") + newClient.remoteId;
117 endpointUrl.setPath(path);
118 newClient.endpoint = endpointUrl.toString();
119
120 Q_EMIT clientRegistered(newClient);
121 });
122}
123
125{
126 qCDebug(Log) << client.serviceName << client.remoteId;
127 auto req = prepareRequest("app", client.remoteId);
128 auto reply = nam()->deleteResource(req);
129 connect(reply, &QNetworkReply::finished, this, [reply, this, client]() {
130 reply->deleteLater();
131 if (reply->error() != QNetworkReply::NoError) {
132 qCWarning(Log) << reply->errorString();
133 Q_EMIT clientUnregistered(client, TransientNetworkError);
134 } else {
135 qCDebug(Log) << "application deleted";
136 Q_EMIT clientUnregistered(client);
137 }
138 });
139}
140
141void NextPushProvider::waitForMessage()
142{
143 qCDebug(Log);
144 auto req = prepareRequest("device", m_deviceId);
145 auto reply = nam()->get(req);
146 connect(reply, &QNetworkReply::finished, this, [reply, this]() {
147 reply->deleteLater();
148 if (reply->error() != QNetworkReply::NoError) {
149 qCWarning(Log) << reply->errorString();
150 Q_EMIT disconnected(TransientNetworkError, reply->errorString());
151 } else {
152 qCDebug(Log) << "GET finished";
153 Q_EMIT disconnected(NoError);
154 }
155 });
156 m_sseStream.read(reply);
157 m_sseReply = reply;
159}
160
161QNetworkRequest NextPushProvider::prepareRequest(const char *restCmd, const QString &restArg) const
162{
163 QUrl url = m_url;
164 auto path = url.path();
165 path += QLatin1String("/index.php/apps/uppush/") + QLatin1String(restCmd) + QLatin1Char('/') + restArg;
166 url.setPath(path);
167
168 QNetworkRequest req(url);
169 req.setRawHeader("Authorization", "Basic " + QByteArray(m_userName.toUtf8() + ':' + m_appPassword.toUtf8()).toBase64());
170 return req;
171}
Base class for push provider protocol implementations.
void connected()
Emitted after the connection to the push provider has been established successfully.
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
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.
void disconnectFromProvider() override
Disconnect and existing connection to the push provider.
void unregisterClient(const Client &client) override
Unregister a client from the provider.
void connectToProvider() override
Attempt to establish a connection to the push provider.
bool loadSettings(const QSettings &settings) override
Load connection settings.
char * toString(const EngineQuery &query)
QString path(const QString &relativePath)
Client-side integration with UnifiedPush.
Definition connector.h:16
int64_t Id
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QByteArray toBase64(Base64Options options) const const
QString localHostName()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QByteArray toJson(JsonFormat format) const const
iterator insert(QLatin1StringView key, const QJsonValue &value)
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
QNetworkReply * deleteResource(const QNetworkRequest &request)
QNetworkReply * get(const QNetworkRequest &request)
QNetworkReply * put(const QNetworkRequest &request, QHttpMultiPart *multiPart)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void setValue(QAnyStringView key, const QVariant &value)
QVariant value(QAnyStringView key) const const
bool isEmpty() const const
QByteArray toUtf8() 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)
QString toString(FormattingOptions options) const const
QString toString() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 24 2024 12:02:24 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.