1 /*
2  *
3  *  D-Bus++ - C++ bindings for D-Bus
4  *
5  *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <dbus-c++/debug.h>
29 #include <dbus-c++/connection.h>
30 
31 #include <dbus/dbus.h>
32 #include <string>
33 
34 #include "internalerror.h"
35 
36 #include "connection_p.h"
37 #include "dispatcher_p.h"
38 #include "server_p.h"
39 #include "message_p.h"
40 #include "pendingcall_p.h"
41 
42 using namespace DBus;
43 
Private(DBusConnection * c,Server::Private * s)44 Connection::Private::Private(DBusConnection *c, Server::Private *s)
45   : conn(c) , dispatcher(NULL), server(s)
46 {
47   init();
48 }
49 
Private(DBusBusType type)50 Connection::Private::Private(DBusBusType type)
51   : dispatcher(NULL), server(NULL)
52 {
53   InternalError e;
54 
55   conn = dbus_bus_get_private(type, e);
56 
57   if (e) throw Error(e);
58 
59   init();
60 }
61 
~Private()62 Connection::Private::~Private()
63 {
64   debug_log("terminating connection 0x%08x", conn);
65 
66   detach_server();
67 
68   if (dbus_connection_get_is_connected(conn))
69   {
70     std::vector<std::string>::iterator i = names.begin();
71 
72     while (i != names.end())
73     {
74       debug_log("%s: releasing bus name %s", dbus_bus_get_unique_name(conn), i->c_str());
75       dbus_bus_release_name(conn, i->c_str(), NULL);
76       ++i;
77     }
78     dbus_connection_close(conn);
79   }
80   dbus_connection_unref(conn);
81 }
82 
init()83 void Connection::Private::init()
84 {
85   dbus_connection_ref(conn);
86   dbus_connection_ref(conn);	//todo: the library has to own another reference
87 
88   disconn_filter = new Callback<Connection::Private, bool, const Message &>(
89     this, &Connection::Private::disconn_filter_function
90   );
91 
92   dbus_connection_add_filter(conn, message_filter_stub, &disconn_filter, NULL); // TODO: some assert at least
93 
94   dbus_connection_set_dispatch_status_function(conn, dispatch_status_stub, this, 0);
95   dbus_connection_set_exit_on_disconnect(conn, false); //why was this set to true??
96 }
97 
detach_server()98 void Connection::Private::detach_server()
99 {
100   /*	Server::Private *tmp = server;
101 
102   	server = NULL;
103 
104   	if (tmp)
105   	{
106   		ConnectionList::iterator i;
107 
108   		for (i = tmp->connections.begin(); i != tmp->connections.end(); ++i)
109   		{
110   			if (i->_pvt.get() == this)
111   			{
112   				tmp->connections.erase(i);
113   				break;
114   			}
115   		}
116   	}*/
117 }
118 
do_dispatch()119 bool Connection::Private::do_dispatch()
120 {
121   debug_log("dispatching on %p", conn);
122 
123   if (!dbus_connection_get_is_connected(conn))
124   {
125     debug_log("connection terminated");
126 
127     detach_server();
128 
129     return true;
130   }
131 
132   return dbus_connection_dispatch(conn) != DBUS_DISPATCH_DATA_REMAINS;
133 }
134 
dispatch_status_stub(DBusConnection * dc,DBusDispatchStatus status,void * data)135 void Connection::Private::dispatch_status_stub(DBusConnection *dc, DBusDispatchStatus status, void *data)
136 {
137   Private *p = static_cast<Private *>(data);
138 
139   switch (status)
140   {
141   case DBUS_DISPATCH_DATA_REMAINS:
142     debug_log("some dispatching to do on %p", dc);
143     p->dispatcher->queue_connection(p);
144     break;
145 
146   case DBUS_DISPATCH_COMPLETE:
147     debug_log("all dispatching done on %p", dc);
148     break;
149 
150   case DBUS_DISPATCH_NEED_MEMORY: //uh oh...
151     debug_log("connection %p needs memory", dc);
152     break;
153   }
154 }
155 
message_filter_stub(DBusConnection * conn,DBusMessage * dmsg,void * data)156 DBusHandlerResult Connection::Private::message_filter_stub(DBusConnection *conn, DBusMessage *dmsg, void *data)
157 {
158   MessageSlot *slot = static_cast<MessageSlot *>(data);
159 
160   Message msg = Message(new Message::Private(dmsg));
161 
162   return slot && !slot->empty() && slot->call(msg)
163          ? DBUS_HANDLER_RESULT_HANDLED
164          : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
165 }
166 
disconn_filter_function(const Message & msg)167 bool Connection::Private::disconn_filter_function(const Message &msg)
168 {
169   if (msg.is_signal(DBUS_INTERFACE_LOCAL, "Disconnected"))
170   {
171     debug_log("%p disconnected by local bus", conn);
172     dbus_connection_close(conn);
173 
174     return true;
175   }
176   return false;
177 }
178 
dispatch_status()179 DBusDispatchStatus Connection::Private::dispatch_status()
180 {
181   return dbus_connection_get_dispatch_status(conn);
182 }
183 
has_something_to_dispatch()184 bool Connection::Private::has_something_to_dispatch()
185 {
186   return dispatch_status() == DBUS_DISPATCH_DATA_REMAINS;
187 }
188 
189 
SystemBus()190 Connection Connection::SystemBus()
191 {
192   return Connection(new Private(DBUS_BUS_SYSTEM));
193 }
194 
SessionBus()195 Connection Connection::SessionBus()
196 {
197   return Connection(new Private(DBUS_BUS_SESSION));
198 }
199 
ActivationBus()200 Connection Connection::ActivationBus()
201 {
202   return Connection(new Private(DBUS_BUS_STARTER));
203 }
204 
Connection(const char * address,bool priv)205 Connection::Connection(const char *address, bool priv)
206   : _timeout(-1)
207 {
208   InternalError e;
209   DBusConnection *conn = priv
210                          ? dbus_connection_open_private(address, e)
211                          : dbus_connection_open(address, e);
212 
213   if (e) throw Error(e);
214 
215   _pvt = new Private(conn);
216 
217   setup(default_dispatcher);
218 
219   debug_log("connected to %s", address);
220 }
221 
Connection(Connection::Private * p)222 Connection::Connection(Connection::Private *p)
223   : _pvt(p), _timeout(-1)
224 {
225   setup(default_dispatcher);
226 }
227 
Connection(const Connection & c)228 Connection::Connection(const Connection &c)
229   : _pvt(c._pvt), _timeout(c._timeout)
230 {
231   dbus_connection_ref(_pvt->conn);
232 }
233 
~Connection()234 Connection::~Connection()
235 {
236   dbus_connection_unref(_pvt->conn);
237 }
238 
setup(Dispatcher * dispatcher)239 Dispatcher *Connection::setup(Dispatcher *dispatcher)
240 {
241   debug_log("registering stubs for connection %p", _pvt->conn);
242 
243   if (!dispatcher) dispatcher = default_dispatcher;
244 
245   if (!dispatcher) throw ErrorFailed("no default dispatcher set for new connection");
246 
247   Dispatcher *prev = _pvt->dispatcher;
248 
249   _pvt->dispatcher = dispatcher;
250 
251   dispatcher->queue_connection(_pvt.get());
252 
253   dbus_connection_set_watch_functions(
254     _pvt->conn,
255     Dispatcher::Private::on_add_watch,
256     Dispatcher::Private::on_rem_watch,
257     Dispatcher::Private::on_toggle_watch,
258     dispatcher,
259     0
260   );
261 
262   dbus_connection_set_timeout_functions(
263     _pvt->conn,
264     Dispatcher::Private::on_add_timeout,
265     Dispatcher::Private::on_rem_timeout,
266     Dispatcher::Private::on_toggle_timeout,
267     dispatcher,
268     0
269   );
270 
271   return prev;
272 }
273 
operator ==(const Connection & c) const274 bool Connection::operator == (const Connection &c) const
275 {
276   return _pvt->conn == c._pvt->conn;
277 }
278 
register_bus()279 bool Connection::register_bus()
280 {
281   InternalError e;
282 
283   bool r = dbus_bus_register(_pvt->conn, e);
284 
285   if (e) throw(e);
286 
287   return r;
288 }
289 
connected() const290 bool Connection::connected() const
291 {
292   return dbus_connection_get_is_connected(_pvt->conn);
293 }
294 
disconnect()295 void Connection::disconnect()
296 {
297 //	dbus_connection_disconnect(_pvt->conn); // disappeared in 0.9x
298   dbus_connection_close(_pvt->conn);
299 }
300 
exit_on_disconnect(bool exit)301 void Connection::exit_on_disconnect(bool exit)
302 {
303   dbus_connection_set_exit_on_disconnect(_pvt->conn, exit);
304 }
305 
unique_name(const char * n)306 bool Connection::unique_name(const char *n)
307 {
308   return dbus_bus_set_unique_name(_pvt->conn, n);
309 }
310 
unique_name() const311 const char *Connection::unique_name() const
312 {
313   return dbus_bus_get_unique_name(_pvt->conn);
314 }
315 
flush()316 void Connection::flush()
317 {
318   dbus_connection_flush(_pvt->conn);
319 }
320 
add_match(const char * rule)321 void Connection::add_match(const char *rule)
322 {
323   InternalError e;
324 
325   dbus_bus_add_match(_pvt->conn, rule, e);
326 
327   debug_log("%s: added match rule %s", unique_name(), rule);
328 
329   if (e) throw Error(e);
330 }
331 
remove_match(const char * rule,bool throw_on_error)332 void Connection::remove_match(const char	*rule,
333                               bool		throw_on_error)
334 {
335   InternalError e;
336 
337   dbus_bus_remove_match(_pvt->conn, rule, e);
338 
339   debug_log("%s: removed match rule %s", unique_name(), rule);
340 
341   if (e)
342   {
343     if (throw_on_error)
344       throw Error(e);
345     else
346       debug_log("DBus::Connection::remove_match: %s (%s).",
347                 static_cast<DBusError *>(e)->message,
348                 static_cast<DBusError *>(e)->name);
349   }
350 }
351 
add_filter(MessageSlot & s)352 bool Connection::add_filter(MessageSlot &s)
353 {
354   debug_log("%s: adding filter", unique_name());
355   return dbus_connection_add_filter(_pvt->conn, Private::message_filter_stub, &s, NULL);
356 }
357 
remove_filter(MessageSlot & s)358 void Connection::remove_filter(MessageSlot &s)
359 {
360   debug_log("%s: removing filter", unique_name());
361   dbus_connection_remove_filter(_pvt->conn, Private::message_filter_stub, &s);
362 }
363 
send(const Message & msg,unsigned int * serial)364 bool Connection::send(const Message &msg, unsigned int *serial)
365 {
366   return dbus_connection_send(_pvt->conn, msg._pvt->msg, serial);
367 }
368 
send_blocking(Message & msg,int timeout)369 Message Connection::send_blocking(Message &msg, int timeout)
370 {
371   DBusMessage *reply;
372   InternalError e;
373 
374   if (this->_timeout != -1)
375   {
376     reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, this->_timeout, e);
377   }
378   else
379   {
380     reply = dbus_connection_send_with_reply_and_block(_pvt->conn, msg._pvt->msg, timeout, e);
381   }
382 
383   if (e) throw Error(e);
384 
385   return Message(new Message::Private(reply), false);
386 }
387 
send_async(Message & msg,int timeout)388 PendingCall Connection::send_async(Message &msg, int timeout)
389 {
390   DBusPendingCall *pending;
391 
392   if (!dbus_connection_send_with_reply(_pvt->conn, msg._pvt->msg, &pending, timeout))
393   {
394     throw ErrorNoMemory("Unable to start asynchronous call");
395   }
396   return PendingCall(new PendingCall::Private(pending));
397 }
398 
request_name(const char * name,int flags)399 void Connection::request_name(const char *name, int flags)
400 {
401   InternalError e;
402 
403   debug_log("%s: registering bus name %s", unique_name(), name);
404 
405   /*
406    * TODO:
407    * Think about giving back the 'ret' value. Some people on the list
408    * requested about this...
409    */
410   int ret = dbus_bus_request_name(_pvt->conn, name, flags, e);
411 
412   if (ret == -1)
413   {
414     if (e) throw Error(e);
415   }
416 
417 //	this->remove_match("destination");
418 
419   if (name)
420   {
421     _pvt->names.push_back(name);
422     std::string match = "destination='" + _pvt->names.back() + "'";
423     add_match(match.c_str());
424   }
425 }
426 
sender_unix_uid(const char * sender)427 unsigned long Connection::sender_unix_uid(const char *sender)
428 {
429   InternalError e;
430 
431   unsigned long ul = dbus_bus_get_unix_user(_pvt->conn, sender, e);
432 
433   if (e) throw Error(e);
434 
435   return ul;
436 }
437 
has_name(const char * name)438 bool Connection::has_name(const char *name)
439 {
440   InternalError e;
441 
442   bool b = dbus_bus_name_has_owner(_pvt->conn, name, e);
443 
444   if (e) throw Error(e);
445 
446   return b;
447 }
448 
names()449 const std::vector<std::string>& Connection::names()
450 {
451   return _pvt->names;
452 }
453 
start_service(const char * name,unsigned long flags)454 bool Connection::start_service(const char *name, unsigned long flags)
455 {
456   InternalError e;
457 
458   bool b = dbus_bus_start_service_by_name(_pvt->conn, name, flags, NULL, e);
459 
460   if (e) throw Error(e);
461 
462   return b;
463 }
464 
set_timeout(int timeout)465 void Connection::set_timeout(int timeout)
466 {
467   _timeout = timeout;
468 }
469 
get_timeout()470 int Connection::get_timeout()
471 {
472   return _timeout;
473 }
474 
475