File indexing completed on 2025-02-16 04:23:13
0001 /* 0002 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: LGPL-2.0-or-later 0004 */ 0005 0006 #include "nextpushprovider.h" 0007 #include "client.h" 0008 #include "logging.h" 0009 #include "message.h" 0010 0011 #include <QHostInfo> 0012 #include <QJsonDocument> 0013 #include <QJsonObject> 0014 #include <QNetworkReply> 0015 #include <QSettings> 0016 0017 using namespace KUnifiedPush; 0018 0019 NextPushProvider::NextPushProvider(QObject *parent) 0020 : AbstractPushProvider(Id, parent) 0021 { 0022 connect(&m_sseStream, &ServerSentEventsStream::messageReceived, this, [this](const SSEMessage &sse) { 0023 qCDebug(Log) << sse.event << sse.data; 0024 if (sse.event == "message") { 0025 QJsonObject msgObj = QJsonDocument::fromJson(sse.data).object(); 0026 Message msg; 0027 msg.clientRemoteId = msgObj.value(QLatin1String("token")).toString(); 0028 msg.content = QByteArray::fromBase64(msgObj.value(QLatin1String("message")).toString().toUtf8()); 0029 Q_EMIT messageReceived(msg); 0030 } 0031 }); 0032 } 0033 0034 NextPushProvider::~NextPushProvider() = default; 0035 0036 bool NextPushProvider::loadSettings(const QSettings &settings) 0037 { 0038 m_appPassword = settings.value(QStringLiteral("AppPassword"), QString()).toString(); 0039 m_url = QUrl(settings.value(QStringLiteral("Url"), QString()).toString()); 0040 m_userName = settings.value(QStringLiteral("Username"), QString()).toString(); 0041 m_deviceId = settings.value(QStringLiteral("DeviceId"), QString()).toString(); 0042 qCDebug(Log) << m_url << m_userName << m_deviceId; 0043 return m_url.isValid() && !m_appPassword.isEmpty() && !m_userName.isEmpty(); 0044 } 0045 0046 void NextPushProvider::connectToProvider() 0047 { 0048 qCDebug(Log) << m_deviceId; 0049 0050 if (m_deviceId.isEmpty()) { 0051 auto req = prepareRequest("device"); 0052 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0053 0054 QJsonObject content; 0055 content.insert(QLatin1String("deviceName"), QHostInfo::localHostName()); 0056 auto reply = nam()->put(req, QJsonDocument(content).toJson(QJsonDocument::Compact)); 0057 connect(reply, &QNetworkReply::finished, this, [reply, this]() { 0058 reply->deleteLater(); 0059 if (reply->error() != QNetworkReply::NoError) { 0060 qCWarning(Log) << reply->errorString(); 0061 Q_EMIT disconnected(ProviderRejected, reply->errorString()); 0062 return; 0063 } 0064 0065 const auto content = QJsonDocument::fromJson(reply->readAll()).object(); 0066 qCDebug(Log) << QJsonDocument(content).toJson(QJsonDocument::Compact); 0067 // TODO check "success" field 0068 m_deviceId = content.value(QLatin1String("deviceId")).toString(); 0069 0070 QSettings settings; 0071 settings.setValue(QStringLiteral("NextPush/DeviceId"), m_deviceId); 0072 waitForMessage(); 0073 }); 0074 } else { 0075 waitForMessage(); 0076 } 0077 } 0078 0079 void NextPushProvider::disconnectFromProvider() 0080 { 0081 if (m_sseReply) { 0082 m_sseReply->abort(); 0083 } else { 0084 Q_EMIT disconnected(NoError); 0085 } 0086 } 0087 0088 void NextPushProvider::registerClient(const Client &client) 0089 { 0090 qCDebug(Log) << client.serviceName; 0091 auto req = prepareRequest("app"); 0092 req.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/json")); 0093 0094 QJsonObject content; 0095 content.insert(QLatin1String("deviceId"), m_deviceId); 0096 content.insert(QLatin1String("appName"), client.serviceName); 0097 auto reply = nam()->put(req, QJsonDocument(content).toJson(QJsonDocument::Compact)); 0098 connect(reply, &QNetworkReply::finished, this, [reply, this, client]() { 0099 reply->deleteLater(); 0100 if (reply->error() != QNetworkReply::NoError) { 0101 Q_EMIT clientRegistered(client, TransientNetworkError, reply->errorString()); 0102 return; 0103 } 0104 0105 const auto content = QJsonDocument::fromJson(reply->readAll()).object(); 0106 qCDebug(Log) << QJsonDocument(content).toJson(QJsonDocument::Compact); 0107 if (!content.value(QLatin1String("success")).toBool()) { 0108 Q_EMIT clientRegistered(client, ProviderRejected, QString()); // TODO do we get an error message in this case? 0109 return; 0110 } 0111 auto newClient = client; 0112 newClient.remoteId = content.value(QLatin1String("token")).toString(); 0113 0114 QUrl endpointUrl = m_url; 0115 auto path = endpointUrl.path(); 0116 path += QLatin1String("/index.php/apps/uppush/push/") + newClient.remoteId; 0117 endpointUrl.setPath(path); 0118 newClient.endpoint = endpointUrl.toString(); 0119 0120 Q_EMIT clientRegistered(newClient); 0121 }); 0122 } 0123 0124 void NextPushProvider::unregisterClient(const Client &client) 0125 { 0126 qCDebug(Log) << client.serviceName << client.remoteId; 0127 auto req = prepareRequest("app", client.remoteId); 0128 auto reply = nam()->deleteResource(req); 0129 connect(reply, &QNetworkReply::finished, this, [reply, this, client]() { 0130 reply->deleteLater(); 0131 if (reply->error() != QNetworkReply::NoError) { 0132 qCWarning(Log) << reply->errorString(); 0133 Q_EMIT clientUnregistered(client, TransientNetworkError); 0134 } else { 0135 qCDebug(Log) << "application deleted"; 0136 Q_EMIT clientUnregistered(client); 0137 } 0138 }); 0139 } 0140 0141 void NextPushProvider::waitForMessage() 0142 { 0143 qCDebug(Log); 0144 auto req = prepareRequest("device", m_deviceId); 0145 auto reply = nam()->get(req); 0146 connect(reply, &QNetworkReply::finished, this, [reply, this]() { 0147 reply->deleteLater(); 0148 if (reply->error() != QNetworkReply::NoError) { 0149 qCWarning(Log) << reply->errorString(); 0150 Q_EMIT disconnected(TransientNetworkError, reply->errorString()); 0151 } else { 0152 qCDebug(Log) << "GET finished"; 0153 Q_EMIT disconnected(NoError); 0154 } 0155 }); 0156 m_sseStream.read(reply); 0157 m_sseReply = reply; 0158 Q_EMIT connected(); 0159 } 0160 0161 QNetworkRequest NextPushProvider::prepareRequest(const char *restCmd, const QString &restArg) const 0162 { 0163 QUrl url = m_url; 0164 auto path = url.path(); 0165 path += QLatin1String("/index.php/apps/uppush/") + QLatin1String(restCmd) + QLatin1Char('/') + restArg; 0166 url.setPath(path); 0167 0168 QNetworkRequest req(url); 0169 req.setRawHeader("Authorization", "Basic " + QByteArray(m_userName.toUtf8() + ':' + m_appPassword.toUtf8()).toBase64()); 0170 return req; 0171 }