1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtDBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qdbusconnectioninterface.h"
41
42#include <QtCore/QByteArray>
43#include <QtCore/QList>
44#include <QtCore/QMap>
45#include <QtCore/QMetaMethod>
46#include <QtCore/QString>
47#include <QtCore/QStringList>
48#include <QtCore/QVariant>
49#include <QtCore/QDebug>
50
51#include "qdbusutil_p.h" // for the DBUS_* constants
52
53#ifndef QT_NO_DBUS
54
55QT_BEGIN_NAMESPACE
56
57/*
58 * Implementation of interface class QDBusConnectionInterface
59 */
60
61/*!
62 \class QDBusConnectionInterface
63 \inmodule QtDBus
64 \since 4.2
65
66 \brief The QDBusConnectionInterface class provides access to the D-Bus bus daemon service.
67
68 The D-Bus bus server daemon provides one special interface \c
69 org.freedesktop.DBus that allows clients to access certain
70 properties of the bus, such as the current list of clients
71 connected. The QDBusConnectionInterface class provides access to that
72 interface.
73
74 The most common uses of this class are to register and unregister
75 service names on the bus using the registerService() and
76 unregisterService() functions, query about existing names using
77 the isServiceRegistered(), registeredServiceNames() and
78 serviceOwner() functions, and to receive notification that a
79 client has registered or de-registered through the
80 serviceRegistered(), serviceUnregistered() and serviceOwnerChanged()
81 signals.
82*/
83
84/*!
85 \enum QDBusConnectionInterface::ServiceQueueOptions
86
87 Flags for determining how a service registration should behave, in
88 case the service name is already registered.
89
90 \value DontQueueService If an application requests a name that
91 is already owned, no queueing will be
92 performed. The registeredService()
93 call will simply fail.
94 This is the default.
95
96 \value QueueService Attempts to register the requested
97 service, but do not try to replace it
98 if another application already has it
99 registered. Instead, simply put this
100 application in queue, until it is
101 given up. The serviceRegistered()
102 signal will be emitted when that
103 happens.
104
105 \value ReplaceExistingService If another application already has
106 the service name registered, attempt
107 to replace it.
108
109 \sa ServiceReplacementOptions
110*/
111
112/*!
113 \enum QDBusConnectionInterface::ServiceReplacementOptions
114
115 Flags for determining if the D-Bus server should allow another
116 application to replace a name that this application has registered
117 with the ReplaceExistingService option.
118
119 The possible values are:
120
121 \value DontAllowReplacement Do not allow another application to
122 replace us. The service must be
123 explicitly unregistered with
124 unregisterService() for another
125 application to acquire it.
126 This is the default.
127
128 \value AllowReplacement Allow other applications to replace us
129 with the ReplaceExistingService option
130 to registerService() without
131 intervention. If that happens, the
132 serviceUnregistered() signal will be
133 emitted.
134
135 \sa ServiceQueueOptions
136*/
137
138/*!
139 \enum QDBusConnectionInterface::RegisterServiceReply
140
141 The possible return values from registerService():
142
143 \value ServiceNotRegistered The call failed and the service name was not registered.
144 \value ServiceRegistered The caller is now the owner of the service name.
145 \value ServiceQueued The caller specified the QueueService flag and the
146 service was already registered, so we are in queue.
147
148 The serviceRegistered() signal will be emitted when the service is
149 acquired by this application.
150*/
151
152/*!
153 \internal
154*/
155const char *QDBusConnectionInterface::staticInterfaceName()
156{ return "org.freedesktop.DBus"; }
157
158/*!
159 \internal
160*/
161QDBusConnectionInterface::QDBusConnectionInterface(const QDBusConnection &connection,
162 QObject *parent)
163 : QDBusAbstractInterface(QDBusUtil::dbusService(),
164 QDBusUtil::dbusPath(),
165 DBUS_INTERFACE_DBUS, connection, parent)
166{
167 connect(this, &QDBusConnectionInterface::NameAcquired, this, emit &QDBusConnectionInterface::serviceRegistered);
168 connect(this, &QDBusConnectionInterface::NameLost, this, emit &QDBusConnectionInterface::serviceUnregistered);
169 connect(this, &QDBusConnectionInterface::NameOwnerChanged,
170 this, emit &QDBusConnectionInterface::serviceOwnerChanged);
171}
172
173/*!
174 \internal
175*/
176QDBusConnectionInterface::~QDBusConnectionInterface()
177{
178}
179
180/*!
181 Returns the unique connection name of the primary owner of the
182 name \a name. If the requested name doesn't have an owner, returns
183 a \c org.freedesktop.DBus.Error.NameHasNoOwner error.
184*/
185QDBusReply<QString> QDBusConnectionInterface::serviceOwner(const QString &name) const
186{
187 return internalConstCall(QDBus::AutoDetect, QLatin1String("GetNameOwner"), QList<QVariant>() << name);
188}
189
190/*!
191 \property QDBusConnectionInterface::registeredServiceNames
192 \brief holds the registered service names
193
194 Lists all names currently registered on the bus.
195*/
196QDBusReply<QStringList> QDBusConnectionInterface::registeredServiceNames() const
197{
198 return internalConstCall(QDBus::AutoDetect, QLatin1String("ListNames"));
199}
200
201/*!
202 \property QDBusConnectionInterface::activatableServiceNames
203 \brief holds the activatable service names
204 \since 5.14
205
206 Lists all names that can be activated on the bus.
207*/
208QDBusReply<QStringList> QDBusConnectionInterface::activatableServiceNames() const
209{
210 return internalConstCall(QDBus::AutoDetect, QLatin1String("ListActivatableNames"));
211}
212
213/*!
214 Returns \c true if the service name \a serviceName has is currently
215 registered.
216*/
217QDBusReply<bool> QDBusConnectionInterface::isServiceRegistered(const QString &serviceName) const
218{
219 return internalConstCall(QDBus::AutoDetect, QLatin1String("NameHasOwner"),
220 QList<QVariant>() << serviceName);
221}
222
223/*!
224 Returns the Unix Process ID (PID) for the process currently
225 holding the bus service \a serviceName.
226*/
227QDBusReply<uint> QDBusConnectionInterface::servicePid(const QString &serviceName) const
228{
229 return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixProcessID"),
230 QList<QVariant>() << serviceName);
231}
232
233/*!
234 Returns the Unix User ID (UID) for the process currently holding
235 the bus service \a serviceName.
236*/
237QDBusReply<uint> QDBusConnectionInterface::serviceUid(const QString &serviceName) const
238{
239 return internalConstCall(QDBus::AutoDetect, QLatin1String("GetConnectionUnixUser"),
240 QList<QVariant>() << serviceName);
241}
242
243/*!
244 Requests that the bus start the service given by the name \a name.
245*/
246QDBusReply<void> QDBusConnectionInterface::startService(const QString &name)
247{
248 return call(QLatin1String("StartServiceByName"), name, uint(0));
249}
250
251/*!
252 Requests to register the service name \a serviceName on the
253 bus. The \a qoption flag specifies how the D-Bus server should behave
254 if \a serviceName is already registered. The \a roption flag
255 specifies if the server should allow another application to
256 replace our registered name.
257
258 If the service registration succeeds, the serviceRegistered()
259 signal will be emitted. If we are placed in queue, the signal will
260 be emitted when we obtain the name. If \a roption is
261 AllowReplacement, the serviceUnregistered() signal will be emitted
262 if another application replaces this one.
263
264 \sa unregisterService()
265*/
266QDBusReply<QDBusConnectionInterface::RegisterServiceReply>
267QDBusConnectionInterface::registerService(const QString &serviceName,
268 ServiceQueueOptions qoption,
269 ServiceReplacementOptions roption)
270{
271 // reconstruct the low-level flags
272 uint flags = 0;
273 switch (qoption) {
274 case DontQueueService:
275 flags = DBUS_NAME_FLAG_DO_NOT_QUEUE;
276 break;
277 case QueueService:
278 flags = 0;
279 break;
280 case ReplaceExistingService:
281 flags = DBUS_NAME_FLAG_DO_NOT_QUEUE | DBUS_NAME_FLAG_REPLACE_EXISTING;
282 break;
283 }
284
285 switch (roption) {
286 case DontAllowReplacement:
287 break;
288 case AllowReplacement:
289 flags |= DBUS_NAME_FLAG_ALLOW_REPLACEMENT;
290 break;
291 }
292
293 QDBusMessage reply = call(QLatin1String("RequestName"), serviceName, flags);
294// qDebug() << "QDBusConnectionInterface::registerService" << serviceName << "Reply:" << reply;
295
296 // convert the low-level flags to something that we can use
297 if (reply.type() == QDBusMessage::ReplyMessage) {
298 uint code = 0;
299
300 switch (reply.arguments().at(0).toUInt()) {
301 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
302 case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
303 code = uint(ServiceRegistered);
304 break;
305
306 case DBUS_REQUEST_NAME_REPLY_EXISTS:
307 code = uint(ServiceNotRegistered);
308 break;
309
310 case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
311 code = uint(ServiceQueued);
312 break;
313 }
314
315 reply.setArguments(QVariantList() << code);
316 }
317
318 return reply;
319}
320
321/*!
322 Releases the claim on the bus service name \a serviceName, that
323 had been previously registered with registerService(). If this
324 application had ownership of the name, it will be released for
325 other applications to claim. If it only had the name queued, it
326 gives up its position in the queue.
327*/
328QDBusReply<bool>
329QDBusConnectionInterface::unregisterService(const QString &serviceName)
330{
331 QDBusMessage reply = call(QLatin1String("ReleaseName"), serviceName);
332 if (reply.type() == QDBusMessage::ReplyMessage) {
333 bool success = reply.arguments().at(0).toUInt() == DBUS_RELEASE_NAME_REPLY_RELEASED;
334 reply.setArguments(QVariantList() << success);
335 }
336 return reply;
337}
338
339/*!
340 \internal
341*/
342void QDBusConnectionInterface::connectNotify(const QMetaMethod &signal)
343{
344 // translate the signal names to what we really want
345 // this avoids setting hooks for signals that don't exist on the bus
346 static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceRegistered);
347 static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceUnregistered);
348 static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceOwnerChanged);
349 static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameAcquired);
350 static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameLost);
351 static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameOwnerChanged);
352 if (signal == serviceRegisteredSignal)
353 QDBusAbstractInterface::connectNotify(NameAcquiredSignal);
354
355 else if (signal == serviceUnregisteredSignal)
356 QDBusAbstractInterface::connectNotify(NameLostSignal);
357
358 else if (signal == serviceOwnerChangedSignal) {
359 static bool warningPrinted = false;
360 if (!warningPrinted) {
361 qWarning("Connecting to deprecated signal QDBusConnectionInterface::serviceOwnerChanged(QString,QString,QString)");
362 warningPrinted = true;
363 }
364 QDBusAbstractInterface::connectNotify(NameOwnerChangedSignal);
365 }
366}
367
368/*!
369 \internal
370*/
371void QDBusConnectionInterface::disconnectNotify(const QMetaMethod &signal)
372{
373 // translate the signal names to what we really want
374 // this avoids setting hooks for signals that don't exist on the bus
375 static const QMetaMethod serviceRegisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceRegistered);
376 static const QMetaMethod serviceUnregisteredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceUnregistered);
377 static const QMetaMethod serviceOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::serviceOwnerChanged);
378 static const QMetaMethod NameAcquiredSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameAcquired);
379 static const QMetaMethod NameLostSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameLost);
380 static const QMetaMethod NameOwnerChangedSignal = QMetaMethod::fromSignal(&QDBusConnectionInterface::NameOwnerChanged);
381 if (signal == serviceRegisteredSignal)
382 QDBusAbstractInterface::disconnectNotify(NameAcquiredSignal);
383
384 else if (signal == serviceUnregisteredSignal)
385 QDBusAbstractInterface::disconnectNotify(NameLostSignal);
386
387 else if (signal == serviceOwnerChangedSignal)
388 QDBusAbstractInterface::disconnectNotify(NameOwnerChangedSignal);
389}
390
391// signals
392/*!
393 \fn QDBusConnectionInterface::serviceRegistered(const QString &service)
394
395 This signal is emitted by the D-Bus server when the bus service
396 name (unique connection name or well-known service name) given by
397 \a service is acquired by this application.
398
399 Acquisition happens after this application has requested a name using
400 registerService().
401*/
402
403/*!
404 \fn QDBusConnectionInterface::serviceUnregistered(const QString &service)
405
406 This signal is emitted by the D-Bus server when this application
407 loses ownership of the bus service name given by \a service.
408*/
409
410/*!
411 \fn QDBusConnectionInterface::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner)
412 \deprecated
413
414 Use QDBusServiceWatcher instead.
415
416 This signal is emitted by the D-Bus server whenever a service
417 ownership change happens in the bus, including apparition and
418 disparition of names.
419
420 This signal means the application \a oldOwner lost ownership of
421 bus name \a name to application \a newOwner. If \a oldOwner is an
422 empty string, it means the name \a name has just been created; if
423 \a newOwner is empty, the name \a name has no current owner and is
424 no longer available.
425
426 \note connecting to this signal will make the application listen for and
427 receive every single service ownership change on the bus. Depending on
428 how many services are running, this make the application be activated to
429 receive more signals than it needs. To avoid this problem, use the
430 QDBusServiceWatcher class, which can listen for specific changes.
431*/
432
433/*!
434 \fn void QDBusConnectionInterface::callWithCallbackFailed(const QDBusError &error, const QDBusMessage &call)
435
436 This signal is emitted when there is an error during a
437 QDBusConnection::callWithCallback(). \a error specifies the error.
438 \a call is the message that couldn't be delivered.
439
440 \sa QDBusConnection::callWithCallback()
441 */
442
443QT_END_NAMESPACE
444
445#endif // QT_NO_DBUS
446