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 "qdbusservicewatcher.h"
41 #include "qdbusconnection.h"
42 #include "qdbusutil_p.h"
43 
44 #include <QStringList>
45 
46 #include <private/qobject_p.h>
47 #include <private/qdbusconnection_p.h>
48 
49 #ifndef QT_NO_DBUS
50 
51 QT_BEGIN_NAMESPACE
52 
53 class QDBusServiceWatcherPrivate: public QObjectPrivate
54 {
55     Q_DECLARE_PUBLIC(QDBusServiceWatcher)
56 public:
QDBusServiceWatcherPrivate(const QDBusConnection & c,QDBusServiceWatcher::WatchMode wm)57     QDBusServiceWatcherPrivate(const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm)
58         : connection(c), watchMode(wm)
59     {
60     }
61 
62     QStringList servicesWatched;
63     QDBusConnection connection;
64     QDBusServiceWatcher::WatchMode watchMode;
65 
66     void _q_serviceOwnerChanged(const QString &, const QString &, const QString &);
67     void setConnection(const QStringList &services, const QDBusConnection &c, QDBusServiceWatcher::WatchMode watchMode);
68 
69     void addService(const QString &service);
70     void removeService(const QString &service);
71 };
72 
_q_serviceOwnerChanged(const QString & service,const QString & oldOwner,const QString & newOwner)73 void QDBusServiceWatcherPrivate::_q_serviceOwnerChanged(const QString &service, const QString &oldOwner, const QString &newOwner)
74 {
75     Q_Q(QDBusServiceWatcher);
76     emit q->serviceOwnerChanged(service, oldOwner, newOwner);
77     if (oldOwner.isEmpty())
78         emit q->serviceRegistered(service);
79     else if (newOwner.isEmpty())
80         emit q->serviceUnregistered(service);
81 }
82 
setConnection(const QStringList & s,const QDBusConnection & c,QDBusServiceWatcher::WatchMode wm)83 void QDBusServiceWatcherPrivate::setConnection(const QStringList &s, const QDBusConnection &c, QDBusServiceWatcher::WatchMode wm)
84 {
85     if (connection.isConnected()) {
86         // remove older rules
87         for (const QString &s : qAsConst(servicesWatched))
88             removeService(s);
89     }
90 
91     connection = c;
92     watchMode = wm;
93     servicesWatched = s;
94 
95     if (connection.isConnected()) {
96         // add new rules
97         for (const QString &s : qAsConst(servicesWatched))
98             addService(s);
99     }
100 }
101 
addService(const QString & service)102 void QDBusServiceWatcherPrivate::addService(const QString &service)
103 {
104     QDBusConnectionPrivate *d = QDBusConnectionPrivate::d(connection);
105     if (d && d->shouldWatchService(service))
106         d->watchService(service, watchMode, q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
107 }
108 
removeService(const QString & service)109 void QDBusServiceWatcherPrivate::removeService(const QString &service)
110 {
111     QDBusConnectionPrivate *d = QDBusConnectionPrivate::d(connection);
112     if (d && d->shouldWatchService(service))
113         d->unwatchService(service, watchMode, q_func(), SLOT(_q_serviceOwnerChanged(QString,QString,QString)));
114 }
115 
116 /*!
117     \class QDBusServiceWatcher
118     \since 4.6
119     \inmodule QtDBus
120 
121     \brief The QDBusServiceWatcher class allows the user to watch for a bus service change.
122 
123     A QDBusServiceWatcher object can be used to notify the application about
124     an ownership change of a service name on the bus. It has three watch
125     modes:
126 
127     \list
128       \li Watching for service registration only.
129       \li Watching for service unregistration only.
130       \li Watching for any kind of service ownership change (the default mode).
131     \endlist
132 
133     Besides being created or deleted, services may change owners without a
134     unregister/register operation happening. So the serviceRegistered()
135     and serviceUnregistered() signals may not be emitted if that
136     happens.
137 
138     This class is more efficient than using the
139     QDBusConnectionInterface::serviceOwnerChanged() signal because it allows
140     one to receive only the signals for which the class is interested in.
141 
142     Ending a service name with the character '*' will match all service names
143     within the specified namespace.
144 
145     For example "com.example.backend1*" will match
146     \list
147         \li com.example.backend1
148         \li com.example.backend1.foo
149         \li com.example.backend1.foo.bar
150     \endlist
151     Substrings in the same domain will not be matched, i.e "com.example.backend12".
152 
153     \sa QDBusConnection
154 */
155 
156 /*!
157     \enum QDBusServiceWatcher::WatchModeFlag
158 
159     QDBusServiceWatcher supports three different watch modes, which are configured by this flag:
160 
161     \value WatchForRegistration watch for service registration only, ignoring
162     any signals related to other service ownership change.
163 
164     \value WatchForUnregistration watch for service unregistration only,
165     ignoring any signals related to other service ownership change.
166 
167     \value WatchForOwnerChange watch for any kind of service ownership
168     change.
169 */
170 
171 /*!
172     \property QDBusServiceWatcher::watchMode
173 
174     The \c watchMode property holds the current watch mode for this
175     QDBusServiceWatcher object. The default value for this property is
176     QDBusServiceWatcher::WatchForOwnershipChange.
177 */
178 
179 /*!
180     \property QDBusServiceWatcher::watchedServices
181 
182     The \c servicesWatched property holds the list of services watched.
183 
184     Note that modifying this list with setServicesWatched() is an expensive
185     operation. If you can, prefer to change it by way of addWatchedService()
186     and removeWatchedService().
187 */
188 
189 /*!
190     \fn void QDBusServiceWatcher::serviceRegistered(const QString &serviceName)
191 
192     This signal is emitted whenever this object detects that the service \a
193     serviceName became available on the bus.
194 
195     \sa serviceUnregistered(), serviceOwnerChanged()
196 */
197 
198 /*!
199     \fn void QDBusServiceWatcher::serviceUnregistered(const QString &serviceName)
200 
201     This signal is emitted whenever this object detects that the service \a
202     serviceName was unregistered from the bus and is no longer available.
203 
204     \sa serviceRegistered(), serviceOwnerChanged()
205 */
206 
207 /*!
208     \fn void QDBusServiceWatcher::serviceOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner)
209 
210     This signal is emitted whenever this object detects that there was a
211     service ownership change relating to the \a serviceName service. The \a
212     oldOwner parameter contains the old owner name and \a newOwner is the new
213     owner. Both \a oldOwner and \a newOwner are unique connection names.
214 
215     Note that this signal is also emitted whenever the \a serviceName service
216     was registered or unregistered. If it was registered, \a oldOwner will
217     contain an empty string, whereas if it was unregistered, \a newOwner will
218     contain an empty string.
219 
220     If you need only to find out if the service is registered or unregistered
221     only, without being notified that the ownership changed, consider using
222     the specific modes for those operations. This class is more efficient if
223     you use the more specific modes.
224 
225     \sa serviceRegistered(), serviceUnregistered()
226 */
227 
228 /*!
229     Creates a QDBusServiceWatcher object. Note that until you set a
230     connection with setConnection(), this object will not emit any signals.
231 
232     The \a parent parameter is passed to QObject to set the parent of this
233     object.
234 */
QDBusServiceWatcher(QObject * parent)235 QDBusServiceWatcher::QDBusServiceWatcher(QObject *parent)
236     : QObject(*new QDBusServiceWatcherPrivate(QDBusConnection(QString()), WatchForOwnerChange), parent)
237 {
238 }
239 
240 /*!
241     Creates a QDBusServiceWatcher object and attaches it to the \a connection
242     connection. Also, this function immediately starts watching for \a
243     watchMode changes to service \a service.
244 
245     The \a parent parameter is passed to QObject to set the parent of this
246     object.
247 */
QDBusServiceWatcher(const QString & service,const QDBusConnection & connection,WatchMode watchMode,QObject * parent)248 QDBusServiceWatcher::QDBusServiceWatcher(const QString &service, const QDBusConnection &connection, WatchMode watchMode, QObject *parent)
249     : QObject(*new QDBusServiceWatcherPrivate(connection, watchMode), parent)
250 {
251     d_func()->setConnection(QStringList() << service, connection, watchMode);
252 }
253 
254 /*!
255     Destroys the QDBusServiceWatcher object and releases any resources
256     associated with it.
257 */
~QDBusServiceWatcher()258 QDBusServiceWatcher::~QDBusServiceWatcher()
259 {
260 }
261 
262 /*!
263     Returns the list of D-Bus services that are being watched.
264 
265     \sa setWatchedServices()
266 */
watchedServices() const267 QStringList QDBusServiceWatcher::watchedServices() const
268 {
269     return d_func()->servicesWatched;
270 }
271 
272 /*!
273     Sets the list of D-Bus services being watched to be \a services.
274 
275     Note that setting the entire list means removing all previous rules for
276     watching services and adding new ones. This is an expensive operation and
277     should be avoided, if possible. Instead, use addWatchedService() and
278     removeWatchedService() if you can to manipulate entries in the list.
279 */
setWatchedServices(const QStringList & services)280 void QDBusServiceWatcher::setWatchedServices(const QStringList &services)
281 {
282     Q_D(QDBusServiceWatcher);
283     if (services == d->servicesWatched)
284         return;
285     d->setConnection(services, d->connection, d->watchMode);
286 }
287 
288 /*!
289     Adds \a newService to the list of services to be watched by this object.
290     This function is more efficient than setWatchedServices() and should be
291     used whenever possible to add services.
292 */
addWatchedService(const QString & newService)293 void QDBusServiceWatcher::addWatchedService(const QString &newService)
294 {
295     Q_D(QDBusServiceWatcher);
296     if (d->servicesWatched.contains(newService))
297         return;
298     d->addService(newService);
299     d->servicesWatched << newService;
300 }
301 
302 /*!
303     Removes the \a service from the list of services being watched by this
304     object. Note that D-Bus notifications are asynchronous, so there may
305     still be signals pending delivery about \a service. Those signals will
306     still be emitted whenever the D-Bus messages are processed.
307 
308     This function returns \c true if any services were removed.
309 */
removeWatchedService(const QString & service)310 bool QDBusServiceWatcher::removeWatchedService(const QString &service)
311 {
312     Q_D(QDBusServiceWatcher);
313     d->removeService(service);
314     return d->servicesWatched.removeOne(service);
315 }
316 
watchMode() const317 QDBusServiceWatcher::WatchMode QDBusServiceWatcher::watchMode() const
318 {
319     return d_func()->watchMode;
320 }
321 
setWatchMode(WatchMode mode)322 void QDBusServiceWatcher::setWatchMode(WatchMode mode)
323 {
324     Q_D(QDBusServiceWatcher);
325     if (mode == d->watchMode)
326         return;
327     d->setConnection(d->servicesWatched, d->connection, mode);
328 }
329 
330 /*!
331     Returns the QDBusConnection that this object is attached to.
332 
333     \sa setConnection()
334 */
connection() const335 QDBusConnection QDBusServiceWatcher::connection() const
336 {
337     return d_func()->connection;
338 }
339 
340 /*!
341     Sets the D-Bus connection that this object is attached to be \a
342     connection. All services watched will be transferred to this connection.
343 
344     Note that QDBusConnection objects are reference counted:
345     QDBusServiceWatcher will keep a reference for this connection while it
346     exists. The connection is not closed until the reference count drops to
347     zero, so this will ensure that any notifications are received while this
348     QDBusServiceWatcher object exists.
349 
350     \sa connection()
351 */
setConnection(const QDBusConnection & connection)352 void QDBusServiceWatcher::setConnection(const QDBusConnection &connection)
353 {
354     Q_D(QDBusServiceWatcher);
355     if (connection.name() == d->connection.name())
356         return;
357     d->setConnection(d->servicesWatched, connection, d->watchMode);
358 }
359 
360 QT_END_NAMESPACE
361 
362 #endif // QT_NO_DBUS
363 
364 #include "moc_qdbusservicewatcher.cpp"
365