blob: 96e20bab8a0b42464388a1e8ffbda741e4fcdd6a [file] [log] [blame]
Emeric Vigier2f625822012-08-06 11:09:52 -04001/*
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++/object.h>
30#include "internalerror.h"
31
32#include <cstring>
33#include <map>
34#include <dbus/dbus.h>
35
36#include "message_p.h"
37#include "server_p.h"
38#include "connection_p.h"
39
40using namespace DBus;
41
42Object::Object(Connection &conn, const Path &path, const char *service)
43 : _conn(conn), _path(path), _service(service ? service : ""), _default_timeout(-1)
44{
45}
46
47Object::~Object()
48{
49}
50
51void Object::set_timeout(int new_timeout)
52{
53 debug_log("%s: %d millies", __PRETTY_FUNCTION__, new_timeout);
54 if (new_timeout < 0 && new_timeout != -1)
55 throw ErrorInvalidArgs("Bad timeout, cannot set it");
56 _default_timeout = new_timeout;
57}
58
59struct ObjectAdaptor::Private
60{
61 static void unregister_function_stub(DBusConnection *, void *);
62 static DBusHandlerResult message_function_stub(DBusConnection *, DBusMessage *, void *);
63};
64
65static DBusObjectPathVTable _vtable =
66{
67 ObjectAdaptor::Private::unregister_function_stub,
68 ObjectAdaptor::Private::message_function_stub,
69 NULL, NULL, NULL, NULL
70};
71
72void ObjectAdaptor::Private::unregister_function_stub(DBusConnection *conn, void *data)
73{
74 //TODO: what do we have to do here ?
75}
76
77DBusHandlerResult ObjectAdaptor::Private::message_function_stub(DBusConnection *, DBusMessage *dmsg, void *data)
78{
79 ObjectAdaptor *o = static_cast<ObjectAdaptor *>(data);
80
81 if (o)
82 {
83 Message msg(new Message::Private(dmsg));
84
85 debug_log("in object %s", o->path().c_str());
86 debug_log(" got message #%d from %s to %s",
87 msg.serial(),
88 msg.sender(),
89 msg.destination()
90 );
91
92 return o->handle_message(msg)
93 ? DBUS_HANDLER_RESULT_HANDLED
94 : DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
95 }
96 else
97 {
98 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
99 }
100}
101
102typedef std::map<Path, ObjectAdaptor *> ObjectAdaptorTable;
103static ObjectAdaptorTable _adaptor_table;
104
105ObjectAdaptor *ObjectAdaptor::from_path(const Path &path)
106{
107 ObjectAdaptorTable::iterator ati = _adaptor_table.find(path);
108
109 if (ati != _adaptor_table.end())
110 return ati->second;
111
112 return NULL;
113}
114
115ObjectAdaptorPList ObjectAdaptor::from_path_prefix(const std::string &prefix)
116{
117 ObjectAdaptorPList ali;
118
119 ObjectAdaptorTable::iterator ati = _adaptor_table.begin();
120
121 size_t plen = prefix.length();
122
123 while (ati != _adaptor_table.end())
124 {
125 if (!strncmp(ati->second->path().c_str(), prefix.c_str(), plen))
126 ali.push_back(ati->second);
127
128 ++ati;
129 }
130
131 return ali;
132}
133
134ObjectPathList ObjectAdaptor::child_nodes_from_prefix(const std::string &prefix)
135{
136 ObjectPathList ali;
137
138 ObjectAdaptorTable::iterator ati = _adaptor_table.begin();
139
140 size_t plen = prefix.length();
141
142 while (ati != _adaptor_table.end())
143 {
144 if (!strncmp(ati->second->path().c_str(), prefix.c_str(), plen))
145 {
146 std::string p = ati->second->path().substr(plen);
147 p = p.substr(0, p.find('/'));
148 ali.push_back(p);
149 }
150 ++ati;
151 }
152
153 ali.sort();
154 ali.unique();
155
156 return ali;
157}
158
159ObjectAdaptor::ObjectAdaptor(Connection &conn, const Path &path)
160 : Object(conn, path, conn.unique_name())
161{
162 register_obj();
163}
164
165ObjectAdaptor::~ObjectAdaptor()
166{
167 unregister_obj(false);
168}
169
170void ObjectAdaptor::register_obj()
171{
172 debug_log("registering local object %s", path().c_str());
173
174 if (!dbus_connection_register_object_path(conn()._pvt->conn, path().c_str(), &_vtable, this))
175 {
176 throw ErrorNoMemory("unable to register object path");
177 }
178
179 _adaptor_table[path()] = this;
180}
181
182void ObjectAdaptor::unregister_obj(bool)
183{
184 _adaptor_table.erase(path());
185
186 debug_log("unregistering local object %s", path().c_str());
187
188 dbus_connection_unregister_object_path(conn()._pvt->conn, path().c_str());
189}
190
191void ObjectAdaptor::_emit_signal(SignalMessage &sig)
192{
193 sig.path(path().c_str());
194
195 conn().send(sig);
196}
197
198struct ReturnLaterError
199{
200 const Tag *tag;
201};
202
203bool ObjectAdaptor::handle_message(const Message &msg)
204{
205 switch (msg.type())
206 {
207 case DBUS_MESSAGE_TYPE_METHOD_CALL:
208 {
209 const CallMessage &cmsg = reinterpret_cast<const CallMessage &>(msg);
210 const char *member = cmsg.member();
211 const char *interface = cmsg.interface();
212
213 debug_log(" invoking method %s.%s", interface, member);
214
215 InterfaceAdaptor *ii = find_interface(interface);
216 if (ii)
217 {
218 try
219 {
220 Message ret = ii->dispatch_method(cmsg);
221 conn().send(ret);
222 }
223 catch (Error &e)
224 {
225 ErrorMessage em(cmsg, e.name(), e.message());
226 conn().send(em);
227 }
228 catch (ReturnLaterError &rle)
229 {
230 _continuations[rle.tag] = new Continuation(conn(), cmsg, rle.tag);
231 }
232 return true;
233 }
234 else
235 {
236 return false;
237 }
238 }
239 default:
240 {
241 return false;
242 }
243 }
244}
245
246void ObjectAdaptor::return_later(const Tag *tag)
247{
248 ReturnLaterError rle = { tag };
249 throw rle;
250}
251
252void ObjectAdaptor::return_now(Continuation *ret)
253{
254 ret->_conn.send(ret->_return);
255
256 ContinuationMap::iterator di = _continuations.find(ret->_tag);
257
258 delete di->second;
259
260 _continuations.erase(di);
261}
262
263void ObjectAdaptor::return_error(Continuation *ret, const Error error)
264{
265 ret->_conn.send(ErrorMessage(ret->_call, error.name(), error.message()));
266
267 ContinuationMap::iterator di = _continuations.find(ret->_tag);
268
269 delete di->second;
270
271 _continuations.erase(di);
272}
273
274ObjectAdaptor::Continuation *ObjectAdaptor::find_continuation(const Tag *tag)
275{
276 ContinuationMap::iterator di = _continuations.find(tag);
277
278 return di != _continuations.end() ? di->second : NULL;
279}
280
281ObjectAdaptor::Continuation::Continuation(Connection &conn, const CallMessage &call, const Tag *tag)
282 : _conn(conn), _call(call), _return(_call), _tag(tag)
283{
284 _writer = _return.writer(); //todo: verify
285}
286
287/*
288*/
289
290ObjectProxy::ObjectProxy(Connection &conn, const Path &path, const char *service)
291 : Object(conn, path, service)
292{
293 register_obj();
294}
295
296ObjectProxy::~ObjectProxy()
297{
298 unregister_obj(false);
299}
300
301void ObjectProxy::register_obj()
302{
303 debug_log("registering remote object %s", path().c_str());
304
305 _filtered = new Callback<ObjectProxy, bool, const Message &>(this, &ObjectProxy::handle_message);
306
307 conn().add_filter(_filtered);
308
309 InterfaceProxyTable::const_iterator ii = _interfaces.begin();
310 while (ii != _interfaces.end())
311 {
312 std::string im = "type='signal',interface='" + ii->first + "',path='" + path() + "'";
313 conn().add_match(im.c_str());
314 ++ii;
315 }
316}
317
318void ObjectProxy::unregister_obj(bool throw_on_error)
319{
320 debug_log("unregistering remote object %s", path().c_str());
321
322 InterfaceProxyTable::const_iterator ii = _interfaces.begin();
323 while (ii != _interfaces.end())
324 {
325 std::string im = "type='signal',interface='" + ii->first + "',path='" + path() + "'";
326 conn().remove_match(im.c_str(), throw_on_error);
327 ++ii;
328 }
329 conn().remove_filter(_filtered);
330}
331
332Message ObjectProxy::_invoke_method(CallMessage &call)
333{
334 if (call.path() == NULL)
335 call.path(path().c_str());
336
337 if (call.destination() == NULL)
338 call.destination(service().c_str());
339
340 return conn().send_blocking(call, get_timeout());
341}
342
343bool ObjectProxy::_invoke_method_noreply(CallMessage &call)
344{
345 if (call.path() == NULL)
346 call.path(path().c_str());
347
348 if (call.destination() == NULL)
349 call.destination(service().c_str());
350
351 return conn().send(call);
352}
353
354bool ObjectProxy::handle_message(const Message &msg)
355{
356 switch (msg.type())
357 {
358 case DBUS_MESSAGE_TYPE_SIGNAL:
359 {
360 const SignalMessage &smsg = reinterpret_cast<const SignalMessage &>(msg);
361 const char *interface = smsg.interface();
362 const char *member = smsg.member();
363 const char *objpath = smsg.path();
364
365 if (objpath != path()) return false;
366
367 debug_log("filtered signal %s(in %s) from %s to object %s",
368 member, interface, msg.sender(), objpath);
369
370 InterfaceProxy *ii = find_interface(interface);
371 if (ii)
372 {
373 return ii->dispatch_signal(smsg);
374 }
375 else
376 {
377 return false;
378 }
379 }
380 default:
381 {
382 return false;
383 }
384 }
385}