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