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 handleRegisterResponse(iface->Register(args));
101 }
102 }, m_distributor);
103}
104
105// Generally Qt can do that automatically, but not for the delayed replies
106// we get for Register()...
107[[nodiscard]] static QVariantMap unpackVariantMap(const QDBusArgument &arg)
108{
109 QVariantMap m;
110 arg.beginMap();
111 while (!arg.atEnd()) {
112 arg.beginMapEntry();
113 QString key;
114 QVariant value;
115 arg >> key;
116 arg >> value;
117 m.insert(key, value);
118 arg.endMapEntry();
119 }
120 arg.endMap();
121 return m;
122}
123
124void ConnectorPrivate::handleRegisterResponse(const QDBusPendingCall &reply)
125{
126 auto watcher = new QDBusPendingCallWatcher(reply, this);
127 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() {
128 if (watcher->isError()) {
129 qCWarning(Log) << watcher->error();
130 setState(Connector::Error);
131 } else {
132 QString result, errorMsg;
133 std::visit([&result, &errorMsg, watcher](auto iface) {
134 using T = std::decay_t<decltype(iface)>;
135 if constexpr (std::is_same_v<OrgUnifiedpushDistributor1Interface*, T>) {
136 result = watcher->reply().arguments().at(0).toString();
137 errorMsg = watcher->reply().arguments().at(1).toString();
138 }
139 if constexpr (std::is_same_v<OrgUnifiedpushDistributor2Interface*, T>) {
140 const auto args = unpackVariantMap(watcher->reply().arguments().at(0).value<QDBusArgument>());
141 result = args.value(UP_ARG_SUCCESS).toString();
142 errorMsg = args.value(UP_ARG_REASON).toString();
143 }
144 }, m_distributor);
145 qCDebug(Log) << result << errorMsg;
146 if (result == UP_REGISTER_RESULT_SUCCESS) {
147 setState(m_endpoint.isEmpty() ? Connector::Registering : Connector::Registered);
148 } else {
149 setState(Connector::Error);
150 }
151 }
152 m_currentCommand = Command::None;
153 processNextCommand();
154 });
155}
156
157void ConnectorPrivate::doUnregister()
158{
159 std::visit([this](auto iface) {
160 using T = std::decay_t<decltype(iface)>;
161 if constexpr (std::is_same_v<OrgUnifiedpushDistributor1Interface*, T>) {
162 iface->Unregister(m_token);
163 }
164 if constexpr (std::is_same_v<OrgUnifiedpushDistributor2Interface*, T>) {
165 QVariantMap args;
166 args.insert(UP_ARG_TOKEN, m_token);
167 iface->Unregister(args);
168 }
169 }, m_distributor);
170}
@ Error
Any other error condition.
Definition connector.h:61
@ NoDistributor
Connector cannot find a UnifiedPush distributor to register at.
Definition connector.h:60
@ Registering
Connector is registering with the push provider.
Definition connector.h:58
@ Registered
Connector is registered and thus operational.
Definition connector.h:59
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 Mar 21 2025 11:55:51 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.