1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6 #ifndef DBUS_MESSAGE_HPP
7 #define DBUS_MESSAGE_HPP
8
9 #include <dbus/dbus.h>
10 #include <dbus/element.hpp>
11 #include <dbus/endpoint.hpp>
12 #include <dbus/impl/message_iterator.hpp>
13 #include <iostream>
14 #include <vector>
15 #include <boost/intrusive_ptr.hpp>
16 #include <boost/mpl/for_each.hpp>
17 #include <boost/utility/enable_if.hpp>
18
intrusive_ptr_add_ref(DBusMessage * m)19 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
20
intrusive_ptr_release(DBusMessage * m)21 inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
22
23 namespace dbus {
24
25 class message {
26 private:
27 boost::intrusive_ptr<DBusMessage> message_;
28
29 public:
30 /// Create a method call message
new_call(const endpoint & destination)31 static message new_call(const endpoint& destination) {
32 auto x = message(dbus_message_new_method_call(
33 destination.get_process_name().c_str(), destination.get_path().c_str(),
34 destination.get_interface().c_str(), destination.get_member().c_str()));
35 dbus_message_unref(x.message_.get());
36 return x;
37 }
38
39 /// Create a method call message
new_call(const endpoint & destination,const string & method_name)40 static message new_call(const endpoint& destination,
41 const string& method_name) {
42 auto x = message(dbus_message_new_method_call(
43 destination.get_process_name().c_str(), destination.get_path().c_str(),
44 destination.get_interface().c_str(), method_name.c_str()));
45 dbus_message_unref(x.message_.get());
46 return x;
47 }
48
49 /// Create a method return message
new_return(message & call)50 static message new_return(message& call) {
51 auto x = message(dbus_message_new_method_return(call));
52 dbus_message_unref(x.message_.get());
53 return x;
54 }
55
56 /// Create an error message
new_error(message & call,const string & error_name,const string & error_message)57 static message new_error(message& call, const string& error_name,
58 const string& error_message) {
59 auto x = message(dbus_message_new_error(call, error_name.c_str(),
60 error_message.c_str()));
61 dbus_message_unref(x.message_.get());
62 return x;
63 }
64
65 /// Create a signal message
new_signal(const endpoint & origin,const string & signal_name)66 static message new_signal(const endpoint& origin, const string& signal_name) {
67 auto x = message(dbus_message_new_signal(origin.get_path().c_str(),
68 origin.get_interface().c_str(),
69 signal_name.c_str()));
70 dbus_message_unref(x.message_.get());
71 return x;
72 }
73
74 message() = delete;
75
message(DBusMessage * m)76 message(DBusMessage* m) : message_(m) {}
77
operator DBusMessage*()78 operator DBusMessage*() { return message_.get(); }
79
80 operator const DBusMessage*() const { return message_.get(); }
81
get_path() const82 string get_path() const {
83 return sanitize(dbus_message_get_path(message_.get()));
84 }
85
get_interface() const86 string get_interface() const {
87 return sanitize(dbus_message_get_interface(message_.get()));
88 }
89
get_member() const90 string get_member() const {
91 return sanitize(dbus_message_get_member(message_.get()));
92 }
93
get_type() const94 string get_type() const {
95 return sanitize(
96 dbus_message_type_to_string(dbus_message_get_type(message_.get())));
97 }
98
get_signature() const99 string get_signature() const {
100 return sanitize(dbus_message_get_signature(message_.get()));
101 }
102
get_sender() const103 string get_sender() const {
104 return sanitize(dbus_message_get_sender(message_.get()));
105 }
106
get_destination() const107 string get_destination() const {
108 return sanitize(dbus_message_get_destination(message_.get()));
109 }
110
get_serial()111 uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
112
set_serial(uint32 serial)113 message& set_serial(uint32 serial) {
114 dbus_message_set_serial(message_.get(), serial);
115 return *this;
116 }
117
get_reply_serial()118 uint32 get_reply_serial() {
119 return dbus_message_get_reply_serial(message_.get());
120 }
121
set_reply_serial(uint32 reply_serial)122 message& set_reply_serial(uint32 reply_serial) {
123 dbus_message_set_reply_serial(message_.get(), reply_serial);
124 return *this;
125 }
126
127 struct packer {
128 impl::message_iterator iter_;
packerdbus::message::packer129 packer(message& m) { impl::message_iterator::init_append(m, iter_); }
packerdbus::message::packer130 packer(){};
131
132 template <typename Element, typename... Args>
packdbus::message::packer133 bool pack(const Element& e, const Args&... args) {
134 if (this->pack(e) == false) {
135 return false;
136 } else {
137 return pack(args...);
138 }
139 }
140
141 template <typename Element>
packdbus::message::packer142 typename std::enable_if<is_fixed_type<Element>::value, bool>::type pack(
143 const Element& e) {
144 return iter_.append_basic(element<Element>::code, &e);
145 }
146
147 template <typename Element>
packdbus::message::packer148 typename std::enable_if<std::is_pointer<Element>::value, bool>::type pack(
149 const Element e) {
150 return pack(*e);
151 }
152
153 template <typename Container>
154 typename std::enable_if<has_const_iterator<Container>::value &&
155 !is_string_type<Container>::value,
156 bool>::type
packdbus::message::packer157 pack(const Container& c) {
158 message::packer sub;
159
160 static const constexpr auto signature =
161 element_signature<Container>::code;
162 if (iter_.open_container(signature[0], &signature[1], sub.iter_) ==
163 false) {
164 return false;
165 }
166 for (auto& element : c) {
167 if (!sub.pack(element)) {
168 return false;
169 }
170 }
171 return iter_.close_container(sub.iter_);
172 }
173
packdbus::message::packer174 bool pack(const char* c) {
175 return iter_.append_basic(element<string>::code, &c);
176 }
177
178 // bool pack specialization
packdbus::message::packer179 bool pack(bool c) {
180 int v = c;
181 return iter_.append_basic(element<bool>::code, &v);
182 }
183
184 template <typename Key, typename Value>
packdbus::message::packer185 bool pack(const std::pair<Key, Value> element) {
186 message::packer dict_entry;
187 if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) ==
188 false) {
189 return false;
190 }
191 if (dict_entry.pack(element.first) == false) {
192 return false;
193 };
194 if (dict_entry.pack(element.second) == false) {
195 return false;
196 };
197 return iter_.close_container(dict_entry.iter_);
198 }
199
packdbus::message::packer200 bool pack(const object_path& e) {
201 const char* c = e.value.c_str();
202 return iter_.append_basic(element<object_path>::code, &c);
203 }
204
packdbus::message::packer205 bool pack(const string& e) {
206 const char* c = e.c_str();
207 return pack(c);
208 }
209
packdbus::message::packer210 bool pack(const dbus_variant& v) {
211 // Get the dbus typecode of the variant being packed
212 const char* type = boost::apply_visitor(
213 [&](auto val) {
214 static const constexpr auto sig =
215 element_signature<decltype(val)>::code;
216 return &sig[0];
217 },
218 v);
219 message::packer sub;
220 iter_.open_container(element<dbus_variant>::code, type, sub.iter_);
221 boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v);
222 iter_.close_container(sub.iter_);
223
224 return true;
225 }
226 };
227
228 template <typename... Args>
pack(const Args &...args)229 bool pack(const Args&... args) {
230 return packer(*this).pack(args...);
231 }
232
233 // noop for template functions that have no arguments
pack()234 bool pack() { return true; }
235
236 struct unpacker {
237 impl::message_iterator iter_;
unpackerdbus::message::unpacker238 unpacker(message& m) { impl::message_iterator::init(m, iter_); }
unpackerdbus::message::unpacker239 unpacker() {}
240
241 template <typename Element, typename... Args>
unpackdbus::message::unpacker242 bool unpack(Element& e, Args&... args) {
243 if (unpack(e) == false) {
244 return false;
245 }
246 return unpack(args...);
247 }
248
249 // Basic type unpack
250 template <typename Element>
unpackdbus::message::unpacker251 typename std::enable_if<is_fixed_type<Element>::value, bool>::type unpack(
252 Element& e) {
253 if (iter_.get_arg_type() != element<Element>::code) {
254 return false;
255 }
256 iter_.get_basic(&e);
257 // ignoring return code here, as we might hit last element, and don't
258 // really care because get_arg_type will return invalid if we call it
259 // after we're over the struct boundary
260 iter_.next();
261 return true;
262 }
263
264 // bool unpack specialization
unpackdbus::message::unpacker265 bool unpack(bool& s) {
266 if (iter_.get_arg_type() != element<bool>::code) {
267 return false;
268 }
269 int c;
270 iter_.get_basic(&c);
271 s = c;
272 iter_.next();
273 return true;
274 }
275
276 // std::string unpack specialization
unpackdbus::message::unpacker277 bool unpack(string& s) {
278 if (iter_.get_arg_type() != element<string>::code) {
279 return false;
280 }
281 const char* c;
282 iter_.get_basic(&c);
283 s.assign(c);
284 iter_.next();
285 return true;
286 }
287
288 // object_path unpack specialization
unpackdbus::message::unpacker289 bool unpack(object_path& s) {
290 if (iter_.get_arg_type() != element<object_path>::code) {
291 return false;
292 }
293 const char* c;
294 iter_.get_basic(&c);
295 s.value.assign(c);
296 iter_.next();
297 return true;
298 }
299
300 // object_path unpack specialization
unpackdbus::message::unpacker301 bool unpack(signature& s) {
302 if (iter_.get_arg_type() != element<signature>::code) {
303 return false;
304 }
305 const char* c;
306 iter_.get_basic(&c);
307 s.value.assign(c);
308 iter_.next();
309 return true;
310 }
311
312 // variant unpack specialization
unpackdbus::message::unpacker313 bool unpack(dbus_variant& v) {
314 if (iter_.get_arg_type() != element<dbus_variant>::code) {
315 return false;
316 }
317 message::unpacker sub;
318 iter_.recurse(sub.iter_);
319
320 char arg_type = sub.iter_.get_arg_type();
321
322 boost::mpl::for_each<dbus_variant::types>([&](auto t) {
323 if (arg_type == element<decltype(t)>::code) {
324 decltype(t) val_to_fill;
325 sub.unpack(val_to_fill);
326 v = val_to_fill;
327 }
328 });
329
330 iter_.next();
331 return true;
332 }
333
334 // dict entry unpack specialization
335 template <typename Key, typename Value>
unpackdbus::message::unpacker336 bool unpack(std::pair<Key, Value>& v) {
337 auto this_code = iter_.get_arg_type();
338 // This can't use element<std::pair> because there is a difference between
339 // the dbus type code 'e' and the dbus signature code for dict entries
340 // '{'. Element_signature will return the full signature, and will never
341 // return e, but we still want to type check it before recursing
342 if (this_code != DBUS_TYPE_DICT_ENTRY) {
343 return false;
344 }
345 message::unpacker sub;
346 iter_.recurse(sub.iter_);
347 if (!sub.unpack(v.first)) {
348 return false;
349 }
350 if (!sub.unpack(v.second)) {
351 return false;
352 }
353
354 iter_.next();
355 return true;
356 }
357
358 template <typename T>
359 struct has_emplace_method
360
361 {
362 struct dummy {};
363
364 template <typename C, typename P>
365 static auto test(P* p)
366 -> decltype(std::declval<C>().emplace(*p), std::true_type());
367
368 template <typename, typename>
369 static std::false_type test(...);
370
371 typedef decltype(test<T, dummy>(nullptr)) type;
372
373 static constexpr bool value =
374 std::is_same<std::true_type,
375 decltype(test<T, dummy>(nullptr))>::value;
376 };
377
378 template <typename T>
379 struct has_emplace_back_method
380
381 {
382 struct dummy {};
383
384 template <typename C, typename P>
385 static auto test(P* p)
386 -> decltype(std::declval<C>().emplace_back(*p), std::true_type());
387
388 template <typename, typename>
389 static std::false_type test(...);
390
391 typedef decltype(test<T, dummy>(nullptr)) type;
392
393 static constexpr bool value =
394 std::is_same<std::true_type,
395 decltype(test<T, dummy>(nullptr))>::value;
396 };
397
398 template <typename Container>
399 typename std::enable_if<has_emplace_back_method<Container>::value &&
400 !is_string_type<Container>::value,
401 bool>::type
unpackdbus::message::unpacker402 unpack(Container& c) {
403 auto top_level_arg_type = iter_.get_arg_type();
404 constexpr auto type = element_signature<Container>::code[0];
405 if (top_level_arg_type != type) {
406 return false;
407 }
408 message::unpacker sub;
409
410 iter_.recurse(sub.iter_);
411 while (sub.iter_.get_arg_type() != DBUS_TYPE_INVALID) {
412 c.emplace_back();
413 if (!sub.unpack(c.back())) {
414 return false;
415 }
416 }
417 iter_.next();
418 return true;
419 }
420
421 template <typename Container>
422 typename std::enable_if<has_emplace_method<Container>::value &&
423 !is_string_type<Container>::value,
424 bool>::type
unpackdbus::message::unpacker425 unpack(Container& c) {
426 auto top_level_arg_type = iter_.get_arg_type();
427 constexpr auto type = element_signature<Container>::code[0];
428 if (top_level_arg_type != type) {
429 return false;
430 }
431 message::unpacker sub;
432
433 iter_.recurse(sub.iter_);
434 while (sub.iter_.get_arg_type() != DBUS_TYPE_INVALID) {
435 // TODO(ed) this is done as an unpack to a stack variable, then an
436 // emplace move into the container (as the key isn't known until the
437 // unpack is done) This could be made more efficient by unpacking only
438 // the key type into a temporary, using find on the temporary, then
439 // unpacking directly into the map type, instead of unpacking both key
440 // and value.
441
442 typename Container::value_type t;
443 if (!sub.unpack(t)) {
444 return false;
445 }
446 c.emplace(std::move(t));
447 }
448 iter_.next();
449 return true;
450 }
451 };
452
453 template <typename... Args>
unpack(Args &...args)454 bool unpack(Args&... args) {
455 return unpacker(*this).unpack(args...);
456 }
457
458 private:
sanitize(const char * str)459 static std::string sanitize(const char* str) {
460 return (str == NULL) ? "(null)" : str;
461 }
462 };
463
operator <<(std::ostream & os,const message & m)464 inline std::ostream& operator<<(std::ostream& os, const message& m) {
465 os << "type='" << m.get_type() << "',"
466 << "sender='" << m.get_sender() << "',"
467 << "interface='" << m.get_interface() << "',"
468 << "member='" << m.get_member() << "',"
469 << "path='" << m.get_path() << "',"
470 << "destination='" << m.get_destination() << "'";
471 return os;
472 }
473
474 } // namespace dbus
475
476 // primary template.
477 template <class T>
478 struct function_traits : function_traits<decltype(&T::operator())> {};
479
480 // partial specialization for function type
481 template <class R, class... Args>
482 struct function_traits<R(Args...)> {
483 using result_type = R;
484 using argument_types = std::tuple<Args...>;
485 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
486 };
487
488 // partial specialization for function pointer
489 template <class R, class... Args>
490 struct function_traits<R (*)(Args...)> {
491 using result_type = R;
492 using argument_types = std::tuple<Args...>;
493 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
494 };
495
496 // partial specialization for std::function
497 template <class R, class... Args>
498 struct function_traits<std::function<R(Args...)>> {
499 using result_type = R;
500 using argument_types = std::tuple<Args...>;
501 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
502 };
503
504 // partial specialization for pointer-to-member-function (i.e., operator()'s)
505 template <class T, class R, class... Args>
506 struct function_traits<R (T::*)(Args...)> {
507 using result_type = R;
508 using argument_types = std::tuple<Args...>;
509 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
510 };
511
512 template <class T, class R, class... Args>
513 struct function_traits<R (T::*)(Args...) const> {
514 using result_type = R;
515 using argument_types = std::tuple<Args...>;
516 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>;
517 };
518
519 template <class F, size_t... Is>
index_apply_impl(F f,std::index_sequence<Is...>)520 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) {
521 return f(std::integral_constant<size_t, Is>{}...);
522 }
523
524 template <size_t N, class F>
index_apply(F f)525 constexpr auto index_apply(F f) {
526 return index_apply_impl(f, std::make_index_sequence<N>{});
527 }
528
529 template <class Tuple, class F>
apply(F f,Tuple & t)530 constexpr auto apply(F f, Tuple& t) {
531 return index_apply<std::tuple_size<Tuple>{}>(
532 [&](auto... Is) { return f(std::get<Is>(t)...); });
533 }
534
535 template <class Tuple>
unpack_into_tuple(Tuple & t,dbus::message & m)536 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) {
537 return index_apply<std::tuple_size<Tuple>{}>(
538 [&](auto... Is) { return m.unpack(std::get<Is>(t)...); });
539 }
540
541 // Specialization for empty tuples. No need to unpack if no arguments
unpack_into_tuple(std::tuple<> & t,dbus::message & m)542 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) {
543 return true;
544 }
545
546 template <typename... Args>
pack_tuple_into_msg(std::tuple<Args...> & t,dbus::message & m)547 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) {
548 return index_apply<std::tuple_size<std::tuple<Args...>>{}>(
549 [&](auto... Is) { return m.pack(std::get<Is>(t)...); });
550 }
551
552 // Specialization for empty tuples. No need to pack if no arguments
pack_tuple_into_msg(std::tuple<> & t,dbus::message & m)553 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) {
554 return true;
555 }
556
557 // Specialization for single types. Used when callbacks simply return one value
558 template <typename Element>
pack_tuple_into_msg(Element & t,dbus::message & m)559 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) {
560 return m.pack(t);
561 }
562
563 #include <dbus/impl/message_iterator.ipp>
564
565 #endif // DBUS_MESSAGE_HPP
566