KUnifiedPush

selftest.cpp
1/*
2 SPDX-FileCopyrightText: 2025 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "selftest.h"
7
8#include "../notifier/notifier.h"
9
10#include <KUnifiedPush/Connector>
11
12#include <KLocalizedString>
13
14#include <QDBusConnection>
15#include <QJsonDocument>
16#include <QJsonObject>
17#include <QNetworkReply>
18#include <QUuid>
19
20constexpr inline const char VAPID_PUBLIC_KEY[] = "BCzlgilO4rGwV9yvrW8afgUJes4-wy4HuVRWH0BIt-5858aF21oSmB9agUz5eyvmxpAUruVyU7pBaQ9HvcWY0TY";
21constexpr inline const char VAPID_PRIVATE_KEY[] = "dV5WqGE33-HmKyuvabQdE0vUrin-FuZYRbkspO9Vxco";
22
23using namespace Qt::Literals;
24
25SelfTest::SelfTest(QObject *parent)
26 : QObject(parent)
27{
28 m_timer.setTimerType(Qt::VeryCoarseTimer);
29 m_timer.setInterval(std::chrono::seconds(30));
30 m_timer.setSingleShot(true);
31 connect(&m_timer, &QTimer::timeout, this, [this]() {
32 if (m_state == WaitingForEndpoint || m_state == Submitting || m_state == WaitingForMessage) {
33 setState(Error);
34 setErrorMessage(i18n("The operation timed out."));
35 }
36 });
37}
38
39SelfTest::~SelfTest()
40{
41 if (m_connector) {
42 m_connector->unregisterClient();
43 }
44}
45
46void SelfTest::start()
47{
48 setErrorMessage({});
49
50 m_connector = std::make_unique<KUnifiedPush::Connector>(QDBusConnection::sessionBus().baseService());
51 m_connector->setVapidPublicKeyRequired(true);
52 m_connector->setVapidPublicKey(QLatin1StringView(VAPID_PUBLIC_KEY));
53
54 connect(m_connector.get(), &KUnifiedPush::Connector::endpointChanged, this, &SelfTest::endpointChanged);
55 connect(m_connector.get(), &KUnifiedPush::Connector::messageReceived, this, &SelfTest::messageReceived);
56 setState(WaitingForEndpoint);
57 m_timer.start();
58
59 m_connector->registerClient(i18n("Push notification self-test."));
60}
61
62void SelfTest::reset()
63{
64 setState(Idle);
65 setErrorMessage({});
66}
67
68void SelfTest::endpointChanged()
69{
70 QUrl url(m_connector->endpoint());
71 if (!url.isValid()) {
72 setState(Error);
73 setErrorMessage(i18n("Could not obtain push notification endpoint."));
74 return;
75 }
76
77 m_notifier = std::make_unique<KUnifiedPush::Notifier>();
78 m_notifier->setEndpoint(url);
79 m_notifier->setUserAgentPublicKey(m_connector->contentEncryptionPublicKey());
80 m_notifier->setAuthSecret(m_connector->contentEncryptionAuthSecret());
81 m_notifier->setContact(u"https://invent.kde.org/libraries/kunifiedpush - KCM Self Test"_s);
82 m_notifier->setVapidPublicKey(QByteArray::fromBase64(VAPID_PUBLIC_KEY, QByteArray::Base64UrlEncoding));
83 m_notifier->setVapidPrivateKey(QByteArray::fromBase64(VAPID_PRIVATE_KEY, QByteArray::Base64UrlEncoding));
84 m_notifier->setTtl(std::chrono::seconds(30));
85
86 connect(m_notifier.get(), &KUnifiedPush::Notifier::finished, this, &SelfTest::submissionFinished);
87 setState(Submitting);
88 m_timer.start();
89
91 m_notifier->submit(m_msg, m_nam);
92}
93
94[[nodiscard]] static QString readErrorMessage(const QJsonObject &obj)
95{
96 for (const auto &key : { "message"_L1, "error"_L1 }) {
97 if (const auto msg = obj.value(key).toString(); !msg.isEmpty()) {
98 return msg;
99 }
100 }
101 return {};
102}
103
104void SelfTest::submissionFinished(QNetworkReply *reply)
105{
106 if (reply->error() != QNetworkReply::NoError) {
107 const auto obj = QJsonDocument::fromJson(reply->readAll()).object();
108 if (const auto errMsg = readErrorMessage(obj); !errMsg.isEmpty()) {
109 setErrorMessage(errMsg);
110 } else {
111 setErrorMessage(reply->errorString());
112 }
113 setState(Error); // ### careful, this invalidates reply!
114 return;
115 }
116
117 setState(WaitingForMessage);
118 m_timer.start();
119}
120
121void SelfTest::messageReceived(const QByteArray &msg)
122{
123 m_timer.stop();
124 if (msg != m_msg) {
125 setState(Error);
126 setErrorMessage(i18n("Received notification does not have the expected content."));
127 return;
128 }
129
130 setState(Success);
131}
132
133void SelfTest::setState(State state)
134{
135 if (m_state == state) {
136 return;
137 }
138
139 m_state = state;
140 Q_EMIT stateChanged();
141
142 if (m_state == Success || m_state == Error || m_state == Idle) {
143 m_notifier.reset();
144 if (m_connector) {
145 m_connector->unregisterClient();
146 }
147 m_connector.reset();
148 m_msg = {};
149 }
150}
151
152void SelfTest::setErrorMessage(const QString &errMsg)
153{
154 if (m_errorMsg == errMsg) {
155 return;
156 }
157
158 m_errorMsg = errMsg;
159 Q_EMIT errorMessageChanged();
160}
void endpointChanged(const QString &endpoint)
Emitted when a new endpoint URL has been received.
void messageReceived(const QByteArray &msg)
Emitted for each newly received push message.
QString i18n(const char *text, const TYPE &arg...)
KCRASH_EXPORT void setErrorMessage(const QString &message)
QByteArray fromBase64(const QByteArray &base64, Base64Options options)
QDBusConnection sessionBus()
QString errorString() const const
QByteArray readAll()
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
QJsonObject object() const const
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
NetworkError error() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool isEmpty() const const
VeryCoarseTimer
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
QUuid createUuid()
QByteArray toByteArray(StringFormat mode) const const
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.