File indexing completed on 2025-02-16 04:23:12

0001 /*
0002     SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "gotifypushprovider.h"
0007 #include "client.h"
0008 #include "logging.h"
0009 #include "message.h"
0010 
0011 #include <QJsonDocument>
0012 #include <QJsonObject>
0013 #include <QNetworkReply>
0014 #include <QSettings>
0015 #include <QUrlQuery>
0016 #include <QWebSocket>
0017 
0018 using namespace KUnifiedPush;
0019 
0020 GotifyPushProvider::GotifyPushProvider(QObject *parent)
0021     : AbstractPushProvider(Id, parent)
0022 {
0023     qCDebug(Log);
0024 }
0025 
0026 bool GotifyPushProvider::loadSettings(const QSettings &settings)
0027 {
0028     m_clientToken = settings.value(QStringLiteral("ClientToken"), QString()).toString();
0029     m_url = QUrl(settings.value(QStringLiteral("Url"), QString()).toString());
0030     qCDebug(Log) << m_clientToken << m_url;
0031     return m_url.isValid() && !m_clientToken.isEmpty();
0032 }
0033 
0034 void GotifyPushProvider::connectToProvider()
0035 {
0036     qCDebug(Log);
0037     m_socket = new QWebSocket();
0038     m_socket->setParent(this);
0039     connect(m_socket, &QWebSocket::stateChanged, this, [this](auto state) {
0040         qCDebug(Log) << m_socket->state();
0041         if (state == QAbstractSocket::ConnectedState) {
0042             Q_EMIT connected();
0043         } else if (state == QAbstractSocket::UnconnectedState) {
0044             Q_EMIT disconnected(TransientNetworkError, m_socket->errorString());
0045             m_socket->deleteLater();
0046         }
0047     });
0048     connect(m_socket, &QWebSocket::textMessageReceived, this, &GotifyPushProvider::wsMessageReceived);
0049 
0050     auto wsUrl = m_url;
0051     if (wsUrl.scheme() == QLatin1String("https")) {
0052         wsUrl.setScheme(QStringLiteral("wss"));
0053     } else if (wsUrl.scheme() == QLatin1String("http")) {
0054         wsUrl.setScheme(QStringLiteral("ws"));
0055     } else {
0056         qCWarning(Log) << "Unknown URL scheme:" << m_url;
0057         Q_EMIT disconnected(ProviderRejected);
0058         return;
0059     }
0060 
0061     auto path = wsUrl.path();
0062     path += QLatin1String("/stream");
0063     wsUrl.setPath(path);
0064 
0065     QUrlQuery query(wsUrl);
0066     query.addQueryItem(QStringLiteral("token"), m_clientToken);
0067     wsUrl.setQuery(query);
0068 
0069     m_socket->open(wsUrl);
0070 }
0071 
0072 void GotifyPushProvider::disconnectFromProvider()
0073 {
0074     m_socket->close();
0075     m_socket = nullptr;
0076 }
0077 
0078 void GotifyPushProvider::wsMessageReceived(const QString &msg)
0079 {
0080     qCDebug(Log) << msg;
0081     const auto msgObj = QJsonDocument::fromJson(msg.toUtf8()).object();
0082     Message m;
0083     m.clientRemoteId = QString::number(msgObj.value(QLatin1String("appid")).toInt());
0084     m.content = msgObj.value(QLatin1String("message")).toString().toUtf8();
0085     Q_EMIT messageReceived(m);
0086 }
0087 
0088 void GotifyPushProvider::registerClient(const Client &client)
0089 {
0090     qCDebug(Log) << client.serviceName << client.token;
0091 
0092     QUrl url = m_url;
0093     auto path = url.path();
0094     path += QLatin1String("/application");
0095     url.setPath(path);
0096 
0097     QNetworkRequest req(url);
0098     req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json"));
0099     req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
0100 
0101     QJsonObject content;
0102     content.insert(QLatin1String("name"), client.serviceName);
0103     content.insert(QLatin1String("token"), client.token);
0104 
0105     auto reply = nam()->post(req, QJsonDocument(content).toJson(QJsonDocument::Compact));
0106     connect(reply, &QNetworkReply::finished, this, [reply, this, client]() {
0107         reply->deleteLater();
0108         if (reply->error() != QNetworkReply::NoError) {
0109             Q_EMIT clientRegistered(client, TransientNetworkError, reply->errorString());
0110             return;
0111         }
0112 
0113         const auto replyObj = QJsonDocument::fromJson(reply->readAll()).object();
0114         qCDebug(Log) << QJsonDocument(replyObj).toJson(QJsonDocument::Compact);
0115 
0116         auto newClient = client;
0117         newClient.remoteId = QString::number(replyObj.value(QLatin1String("id")).toInt());
0118 
0119         QUrl endpointUrl = m_url;
0120         auto path = endpointUrl.path();
0121         path += QLatin1String("/message");
0122         endpointUrl.setPath(path);
0123         QUrlQuery query(endpointUrl);
0124         query.addQueryItem(QStringLiteral("token"), replyObj.value(QLatin1String("token")).toString());
0125         endpointUrl.setQuery(query);
0126         newClient.endpoint = endpointUrl.toString();
0127 
0128         Q_EMIT clientRegistered(newClient);
0129     });
0130 }
0131 
0132 void GotifyPushProvider::unregisterClient(const Client &client)
0133 {
0134     qCDebug(Log) << client.serviceName << client.token << client.remoteId;
0135 
0136     QUrl url = m_url;
0137     auto path = url.path();
0138     path += QLatin1String("/application/") + client.remoteId;
0139     url.setPath(path);
0140 
0141     QNetworkRequest req(url);
0142     req.setRawHeader("X-Gotify-Key", m_clientToken.toUtf8());
0143 
0144     auto reply = nam()->deleteResource(req);
0145     connect(reply, &QNetworkReply::finished, this, [reply, client, this]() {
0146         reply->deleteLater();
0147         qCDebug(Log) << reply->errorString() << reply->readAll(); // TODO
0148         if (reply->error() != QNetworkReply::NoError) {
0149             Q_EMIT clientUnregistered(client, TransientNetworkError);
0150         } else {
0151             Q_EMIT clientUnregistered(client);
0152         }
0153     });
0154 }