File indexing completed on 2024-11-17 04:18:46

0001 /*
0002     SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
0003     SPDX-License-Identifier: LGPL-2.0-or-later
0004 */
0005 
0006 #include "connector.h"
0007 #include "connector_p.h"
0008 #include "logging.h"
0009 
0010 #include "../shared/unifiedpush-constants.h"
0011 #include "../shared/connectorutils_p.h"
0012 
0013 #include <QFile>
0014 #include <QSettings>
0015 #include <QStandardPaths>
0016 #include <QUuid>
0017 
0018 using namespace KUnifiedPush;
0019 
0020 ConnectorPrivate::ConnectorPrivate(Connector *qq)
0021     : QObject(qq)
0022     , q(qq)
0023 {
0024     init();
0025 }
0026 
0027 ConnectorPrivate::~ConnectorPrivate()
0028 {
0029     deinit();
0030 }
0031 
0032 void ConnectorPrivate::Message(const QString &token, const QByteArray &message, const QString &messageIdentifier)
0033 {
0034     qCDebug(Log) << token << message << messageIdentifier;
0035     if (token != m_token) {
0036         qCWarning(Log) << "Got message for a different token??";
0037         return;
0038     }
0039     Q_EMIT q->messageReceived(message);
0040 }
0041 
0042 void ConnectorPrivate::NewEndpoint(const QString &token, const QString &endpoint)
0043 {
0044     qCDebug(Log) << token << endpoint;
0045     if (token != m_token) {
0046         qCWarning(Log) << "Got new endpoint for a different token??";
0047         return;
0048     }
0049 
0050     // ### Gotify workaround...
0051     QString actuallyWorkingEndpoint(endpoint);
0052     actuallyWorkingEndpoint.replace(QLatin1String("/UP?"), QLatin1String("/message?"));
0053 
0054     if (m_endpoint != actuallyWorkingEndpoint) {
0055         m_endpoint = actuallyWorkingEndpoint;
0056         Q_EMIT q->endpointChanged(m_endpoint);
0057     }
0058     storeState();
0059     setState(Connector::Registered);
0060 }
0061 
0062 void ConnectorPrivate::Unregistered(const QString &token)
0063 {
0064     qCDebug(Log) << token;
0065 
0066     // confirmation of our unregistration request
0067     if (token.isEmpty()) {
0068         m_token.clear();
0069         m_endpoint.clear();
0070         Q_EMIT q->endpointChanged(m_endpoint);
0071         const auto res = QFile::remove(stateFile());
0072         qCDebug(Log) << "Removing" << stateFile() << res;
0073         setState(Connector::Unregistered);
0074     }
0075 
0076     // we got unregistered by the distributor
0077     else if (token == m_token) {
0078         m_endpoint.clear();
0079         Q_EMIT q->endpointChanged(m_endpoint);
0080         setState(Connector::Unregistered);
0081         storeState();
0082     }
0083 
0084     if (m_currentCommand == Command::Unregister) {
0085         m_currentCommand = Command::None;
0086     }
0087     processNextCommand();
0088 }
0089 
0090 QString ConnectorPrivate::stateFile() const
0091 {
0092     return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1String("/kunifiedpush-") + m_serviceName;
0093 }
0094 
0095 void ConnectorPrivate::loadState()
0096 {
0097     QSettings settings(stateFile(), QSettings::IniFormat);
0098     settings.beginGroup(QStringLiteral("Client"));
0099     m_token = settings.value(QStringLiteral("Token"), QString()).toString();
0100     m_endpoint = settings.value(QStringLiteral("Endpoint"), QString()).toString();
0101     m_description = settings.value(QStringLiteral("Description"), QString()).toString();
0102 }
0103 
0104 void ConnectorPrivate::storeState() const
0105 {
0106     QSettings settings(stateFile(), QSettings::IniFormat);
0107     settings.beginGroup(QStringLiteral("Client"));
0108     settings.setValue(QStringLiteral("Token"), m_token);
0109     settings.setValue(QStringLiteral("Endpoint"), m_endpoint);
0110     settings.setValue(QStringLiteral("Description"), m_description);
0111 }
0112 
0113 void ConnectorPrivate::setDistributor(const QString &distServiceName)
0114 {
0115     if (distServiceName.isEmpty()) {
0116         qCWarning(Log) << "No UnifiedPush distributor found.";
0117         setState(Connector::NoDistributor);
0118         return;
0119     }
0120 
0121     doSetDistributor(distServiceName);
0122     qCDebug(Log) << "Selected distributor" << distServiceName;
0123     setState(Connector::Unregistered);
0124 
0125     if (!m_token.isEmpty()) { // re-register if we have been registered before
0126         q->registerClient(m_description);
0127     }
0128 }
0129 
0130 void ConnectorPrivate::setState(Connector::State state)
0131 {
0132     qCDebug(Log) << state;
0133     if (m_state == state) {
0134         return;
0135     }
0136 
0137     m_state = state;
0138     Q_EMIT q->stateChanged(m_state);
0139 }
0140 
0141 void ConnectorPrivate::addCommand(ConnectorPrivate::Command cmd)
0142 {
0143     // ignore new commands that are already in the queue or cancel each other out
0144     if (!m_commandQueue.empty()) {
0145         if (m_commandQueue.back() == cmd) {
0146             return;
0147         }
0148         if ((m_commandQueue.back() == Command::Register && cmd == Command::Unregister) || (m_commandQueue.back() == Command::Unregister && cmd == Command::Register)) {
0149             m_commandQueue.pop_back();
0150             return;
0151         }
0152     } else if (m_currentCommand == cmd) {
0153         return;
0154     }
0155 
0156     m_commandQueue.push_back(cmd);
0157     processNextCommand();
0158 }
0159 
0160 void ConnectorPrivate::processNextCommand()
0161 {
0162     if (m_currentCommand != Command::None || !hasDistributor() || m_commandQueue.empty()) {
0163         return;
0164     }
0165 
0166     m_currentCommand = m_commandQueue.front();
0167     m_commandQueue.pop_front();
0168 
0169     switch (m_currentCommand) {
0170         case Command::None:
0171             break;
0172         case Command::Register:
0173         {
0174             if (m_state == Connector::Registered) {
0175                 m_currentCommand = Command::None;
0176                 break;
0177             }
0178             setState(Connector::Registering);
0179             if (m_token.isEmpty()) {
0180                 m_token = QUuid::createUuid().toString();
0181             }
0182             qCDebug(Log) << "Registering";
0183             doRegister();
0184             break;
0185         }
0186         case Command::Unregister:
0187             if (m_state == Connector::Unregistered) {
0188                 m_currentCommand = Command::None;
0189                 break;
0190             }
0191             qCDebug(Log) << "Unregistering";
0192             doUnregister();
0193             break;
0194     }
0195 
0196     processNextCommand();
0197 }
0198 
0199 
0200 Connector::Connector(const QString &serviceName, QObject *parent)
0201     : QObject(parent)
0202     , d(new ConnectorPrivate(this))
0203 {
0204     d->m_serviceName = serviceName;
0205     if (d->m_serviceName.isEmpty()) {
0206         qCWarning(Log) << "empty D-Bus service name!";
0207         return;
0208     }
0209 
0210     d->loadState();
0211     d->setDistributor(ConnectorUtils::selectDistributor());
0212 }
0213 
0214 Connector::~Connector() = default;
0215 
0216 QString Connector::endpoint() const
0217 {
0218     return d->m_endpoint;
0219 }
0220 
0221 void Connector::registerClient(const QString &description)
0222 {
0223     qCDebug(Log) << d->m_state;
0224     d->m_description = description;
0225     d->addCommand(ConnectorPrivate::Command::Register);
0226 }
0227 
0228 void Connector::unregisterClient()
0229 {
0230     qCDebug(Log) << d->m_state;
0231     d->addCommand(ConnectorPrivate::Command::Unregister);
0232 }
0233 
0234 Connector::State Connector::state() const
0235 {
0236     return d->m_state;
0237 }