KUnifiedPush

connector_dbus.cpp
1/*
2 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org>
3 SPDX-License-Identifier: LGPL-2.0-or-later
4*/
5
6#include "connector_p.h"
7#include "connector1adaptor.h"
8#include "connector2adaptor.h"
9#include "distributor1iface.h"
10#include "distributor2iface.h"
11#include "introspectiface.h"
12#include "logging.h"
13
14#include "../shared/unifiedpush-constants.h"
15#include "../shared/connectorutils_p.h"
16
17#include <QDBusConnection>
18#include <QDBusPendingCallWatcher>
19
20using namespace Qt::Literals;
21using namespace KUnifiedPush;
22
23void ConnectorPrivate::init()
24{
25 new Connector1Adaptor(this);
26 new Connector2Adaptor(this);
27 const auto res = QDBusConnection::sessionBus().registerObject(UP_CONNECTOR_PATH, this);
28 if (!res) {
29 qCWarning(Log) << "Failed to register connector D-Bus adapter!" << UP_CONNECTOR_PATH;
30 // TODO switch to error state?
31 }
32
33 connect(&m_serviceWatcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &serviceName) {
34 qCDebug(Log) << "Distributor" << serviceName << "became available";
35 if (!hasDistributor()) {
36 setDistributor(ConnectorUtils::selectDistributor());
37 processNextCommand();
38 }
39 });
40 connect(&m_serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &serviceName) {
41 qCDebug(Log) << "Distributor" << serviceName << "is gone";
42 const auto distributorServiceName = std::visit([](auto iface) { return iface->service(); }, m_distributor);
43 if (distributorServiceName == serviceName) {
44 std::visit([](auto iface) { return delete iface; }, m_distributor);
45 m_distributor = {};
46 m_currentCommand = Command::None;
48 }
49 });
50
51 m_serviceWatcher.setConnection(QDBusConnection::sessionBus());
53 m_serviceWatcher.addWatchedService(UP_DISTRIBUTOR_SERVICE_NAME_FILTER);
54}
55
56void ConnectorPrivate::deinit()
57{
59}
60
61void ConnectorPrivate::doSetDistributor(const QString &distServiceName)
62{
63 // QDBusInterface::isValid does not detect whether the interface actually exists on the remote side,
64 // so we have to manually introspect this
65 OrgFreedesktopDBusIntrospectableInterface introspectIface(distServiceName, UP_DISTRIBUTOR_PATH, QDBusConnection::sessionBus());
66 if (const QString reply = introspectIface.Introspect(); reply.contains(QLatin1StringView(OrgUnifiedpushDistributor2Interface::staticInterfaceName()))) {
67 auto iface2 = std::make_unique<OrgUnifiedpushDistributor2Interface>(distServiceName, UP_DISTRIBUTOR_PATH, QDBusConnection::sessionBus(), this);
68 if (iface2->isValid()) {
69 m_distributor = iface2.release();
70 qCDebug(Log) << "Using v2 distributor interface";
71 return;
72 }
73 }
74 auto iface1 = std::make_unique<OrgUnifiedpushDistributor1Interface>(distServiceName, UP_DISTRIBUTOR_PATH, QDBusConnection::sessionBus(), this);
75 if (iface1->isValid()) {
76 m_distributor = iface1.release();
77 qCDebug(Log) << "Using v1 distributor interface";
78 return;
79 }
80 qCWarning(Log) << "Invalid distributor D-Bus interface?" << distServiceName;
81}
82
83bool ConnectorPrivate::hasDistributor() const
84{
85 return std::visit([](auto iface) { return iface && iface->isValid(); }, m_distributor);
86}
87
88void ConnectorPrivate::doRegister()
89{
90 std::visit([this](auto iface) {
91 using T = std::decay_t<decltype(iface)>;
92 if constexpr (std::is_same_v<OrgUnifiedpushDistributor1Interface*, T>) {
93 handleRegisterResponse(iface->Register(m_serviceName, m_token, m_description));
94 }
95 if constexpr (std::is_same_v<OrgUnifiedpushDistributor2Interface*, T>) {
96 QVariantMap args;
97 args.insert(UP_ARG_SERVICE, m_serviceName);
98 args.insert(UP_ARG_TOKEN, m_token);
99 args.insert(UP_ARG_DESCRIPTION, m_description);
100 if (!m_vapidPublicKey.isEmpty()) {
101 args.insert(UP_ARG_VAPID, m_vapidPublicKey);
102 }
103 handleRegisterResponse(iface->Register(args));
104 }
105 }, m_distributor);
106}
107
108// Generally Qt can do that automatically, but not for the delayed replies
109// we get for Register()...
110[[nodiscard]] static QVariantMap unpackVariantMap(const QDBusArgument &arg)
111{
112 QVariantMap m;
113 arg.beginMap();
114 while (!arg.atEnd()) {
115 arg.beginMapEntry();
116 QString key;
117 QVariant value;
118 arg >> key;
119 arg >> value;
120 m.insert(key, value);
121 arg.endMapEntry();
122 }
123 arg.endMap();
124 return m;
125}
126
127void ConnectorPrivate::handleRegisterResponse(const QDBusPendingCall &reply)
128{
129 auto watcher = new QDBusPendingCallWatcher(reply, this);
130 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() {
131 if (watcher->isError()) {
132 qCWarning(Log) << watcher->error();
133 setState(Connector::Error);
134 } else {
135 QString result, reason;
136 std::visit([&result, &reason, watcher](auto iface) {
137 using T = std::decay_t<decltype(iface)>;
138 if constexpr (std::is_same_v<OrgUnifiedpushDistributor1Interface*, T>) {
139 result = watcher->reply().arguments().at(0).toString();
140 reason = watcher->reply().arguments().at(1).toString();
141 }
142 if constexpr (std::is_same_v<OrgUnifiedpushDistributor2Interface*, T>) {
143 const auto args = unpackVariantMap(watcher->reply().arguments().at(0).value<QDBusArgument>());
144 result = args.value(UP_ARG_SUCCESS).toString();
145 reason = args.value(UP_ARG_REASON).toString();
146 }
147 }, m_distributor);
148 qCDebug(Log) << result << reason;
149 if (result == UP_REGISTER_RESULT_SUCCESS) {
150 setState(m_endpoint.isEmpty() ? Connector::Registering : Connector::Registered);
151 } else {
152 registrationFailed(m_token, reason);
153 }
154 }
155 m_currentCommand = Command::None;
156 processNextCommand();
157 });
158}
159
160void ConnectorPrivate::doUnregister()
161{
162 std::visit([this](auto iface) {
163 using T = std::decay_t<decltype(iface)>;
164 if constexpr (std::is_same_v<OrgUnifiedpushDistributor1Interface*, T>) {
165 iface->Unregister(m_token);
166 }
167 if constexpr (std::is_same_v<OrgUnifiedpushDistributor2Interface*, T>) {
168 QVariantMap args;
169 args.insert(UP_ARG_TOKEN, m_token);
170 iface->Unregister(args);
171 }
172 }, m_distributor);
173}
@ Error
Any other error condition.
Definition connector.h:65
@ NoDistributor
Connector cannot find a UnifiedPush distributor to register at.
Definition connector.h:64
@ Registering
Connector is registering with the push provider.
Definition connector.h:62
@ Registered
Connector is registered and thus operational.
Definition connector.h:63
Client-side integration with UnifiedPush.
Definition connector.h:14
bool atEnd() const const
void beginMap(QMetaType keyMetaType, QMetaType valueMetaType)
void beginMapEntry()
void endMapEntry()
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
void unregisterObject(const QString &path, UnregisterMode mode)
void finished(QDBusPendingCallWatcher *self)
void serviceRegistered(const QString &serviceName)
void serviceUnregistered(const QString &serviceName)
const QChar at(qsizetype position) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 18 2025 12:16:55 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.