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 19 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); } 20 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 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 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 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 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 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 76 message(DBusMessage* m) : message_(m) {} 77 78 operator DBusMessage*() { return message_.get(); } 79 80 operator const DBusMessage*() const { return message_.get(); } 81 82 string get_path() const { 83 return sanitize(dbus_message_get_path(message_.get())); 84 } 85 86 string get_interface() const { 87 return sanitize(dbus_message_get_interface(message_.get())); 88 } 89 90 string get_member() const { 91 return sanitize(dbus_message_get_member(message_.get())); 92 } 93 94 string get_type() const { 95 return sanitize( 96 dbus_message_type_to_string(dbus_message_get_type(message_.get()))); 97 } 98 99 string get_signature() const { 100 return sanitize(dbus_message_get_signature(message_.get())); 101 } 102 103 string get_sender() const { 104 return sanitize(dbus_message_get_sender(message_.get())); 105 } 106 107 string get_destination() const { 108 return sanitize(dbus_message_get_destination(message_.get())); 109 } 110 111 uint32 get_serial() { return dbus_message_get_serial(message_.get()); } 112 113 message& set_serial(uint32 serial) { 114 dbus_message_set_serial(message_.get(), serial); 115 return *this; 116 } 117 118 uint32 get_reply_serial() { 119 return dbus_message_get_reply_serial(message_.get()); 120 } 121 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_; 129 packer(message& m) { impl::message_iterator::init_append(m, iter_); } 130 packer(){}; 131 132 template <typename Element, typename... Args> 133 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> 142 typename boost::enable_if<is_fixed_type<Element>, bool>::type pack( 143 const Element& e) { 144 return iter_.append_basic(element<Element>::code, &e); 145 } 146 147 template <typename Element> 148 typename boost::enable_if<std::is_pointer<Element>, bool>::type pack( 149 const Element e) { 150 return iter_.append_basic( 151 element<typename std::remove_pointer<Element>::type>::code, e); 152 } 153 154 template <typename Container> 155 typename std::enable_if<has_const_iterator<Container>::value && 156 !is_string_type<Container>::value, 157 bool>::type 158 pack(const Container& c) { 159 message::packer sub; 160 161 static const constexpr auto signature = 162 element_signature<Container>::code; 163 if (iter_.open_container(signature[0], &signature[1], sub.iter_) == 164 false) { 165 return false; 166 } 167 for (auto& element : c) { 168 if (!sub.pack(element)) { 169 return false; 170 } 171 } 172 return iter_.close_container(sub.iter_); 173 } 174 175 bool pack(const char* c) { 176 return iter_.append_basic(element<string>::code, &c); 177 } 178 179 // bool pack specialization 180 bool pack(bool c) { 181 int v = c; 182 return iter_.append_basic(element<bool>::code, &v); 183 } 184 185 template <typename Key, typename Value> 186 bool pack(const std::pair<Key, Value> element) { 187 message::packer dict_entry; 188 if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) == 189 false) { 190 return false; 191 } 192 if (dict_entry.pack(element.first) == false) { 193 return false; 194 }; 195 if (dict_entry.pack(element.second) == false) { 196 return false; 197 }; 198 return iter_.close_container(dict_entry.iter_); 199 } 200 201 bool pack(const object_path& e) { 202 const char* c = e.value.c_str(); 203 return iter_.append_basic(element<object_path>::code, &c); 204 } 205 206 bool pack(const string& e) { 207 const char* c = e.c_str(); 208 return pack(c); 209 } 210 211 bool pack(const dbus_variant& v) { 212 // Get the dbus typecode of the variant being packed 213 const char* type = boost::apply_visitor( 214 [&](auto val) { 215 static const constexpr auto sig = 216 element_signature<decltype(val)>::code; 217 static_assert( 218 std::tuple_size<decltype(sig)>::value == 2, 219 "Element signature for dbus_variant too long. Expected " 220 "length of 1"); 221 return &sig[0]; 222 }, 223 v); 224 message::packer sub; 225 iter_.open_container(element<dbus_variant>::code, type, sub.iter_); 226 boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v); 227 iter_.close_container(sub.iter_); 228 229 return true; 230 } 231 }; 232 233 template <typename... Args> 234 bool pack(const Args&... args) { 235 return packer(*this).pack(args...); 236 } 237 238 // noop for template functions that have no arguments 239 bool pack() { return true; } 240 241 struct unpacker { 242 impl::message_iterator iter_; 243 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 244 unpacker() {} 245 246 template <typename Element, typename... Args> 247 bool unpack(Element& e, Args&... args) { 248 if (unpack(e) == false) { 249 return false; 250 } 251 return unpack(args...); 252 } 253 254 // Basic type unpack 255 template <typename Element> 256 typename boost::enable_if<is_fixed_type<Element>, bool>::type unpack( 257 Element& e) { 258 if (iter_.get_arg_type() != element<Element>::code) { 259 return false; 260 } 261 iter_.get_basic(&e); 262 // ignoring return code here, as we might hit last element, and don't 263 // really care because get_arg_type will return invalid if we call it 264 // after we're over the struct boundary 265 iter_.next(); 266 return true; 267 } 268 269 // bool unpack specialization 270 bool unpack(bool& s) { 271 if (iter_.get_arg_type() != element<bool>::code) { 272 return false; 273 } 274 int c; 275 iter_.get_basic(&c); 276 s = c; 277 iter_.next(); 278 return true; 279 } 280 281 // std::string unpack specialization 282 bool unpack(string& s) { 283 if (iter_.get_arg_type() != element<string>::code) { 284 return false; 285 } 286 const char* c; 287 iter_.get_basic(&c); 288 s.assign(c); 289 iter_.next(); 290 return true; 291 } 292 293 // object_path unpack specialization 294 bool unpack(object_path& s) { 295 if (iter_.get_arg_type() != element<object_path>::code) { 296 return false; 297 } 298 const char* c; 299 iter_.get_basic(&c); 300 s.value.assign(c); 301 iter_.next(); 302 return true; 303 } 304 305 // object_path unpack specialization 306 bool unpack(signature& s) { 307 if (iter_.get_arg_type() != element<signature>::code) { 308 return false; 309 } 310 const char* c; 311 iter_.get_basic(&c); 312 s.value.assign(c); 313 iter_.next(); 314 return true; 315 } 316 317 // variant unpack specialization 318 bool unpack(dbus_variant& v) { 319 if (iter_.get_arg_type() != element<dbus_variant>::code) { 320 return false; 321 } 322 message::unpacker sub; 323 iter_.recurse(sub.iter_); 324 325 char arg_type = sub.iter_.get_arg_type(); 326 327 boost::mpl::for_each<dbus_variant::types>([&](auto t) { 328 if (arg_type == element<decltype(t)>::code) { 329 decltype(t) val_to_fill; 330 sub.unpack(val_to_fill); 331 v = val_to_fill; 332 } 333 }); 334 335 iter_.next(); 336 return true; 337 } 338 339 // dict entry unpack specialization 340 template <typename Key, typename Value> 341 bool unpack(std::pair<Key, Value>& v) { 342 auto this_code = iter_.get_arg_type(); 343 // This can't use element<std::pair> because there is a difference between 344 // the dbus type code 'e' and the dbus signature code for dict entries 345 // '{'. Element_signature will return the full signature, and will never 346 // return e, but we still want to type check it before recursing 347 if (this_code != DBUS_TYPE_DICT_ENTRY) { 348 return false; 349 } 350 message::unpacker sub; 351 iter_.recurse(sub.iter_); 352 if (!sub.unpack(v.first)) { 353 return false; 354 } 355 if (!sub.unpack(v.second)) { 356 return false; 357 } 358 359 iter_.next(); 360 return true; 361 } 362 363 template <typename Container> 364 typename std::enable_if<has_const_iterator<Container>::value && 365 !is_string_type<Container>::value, 366 bool>::type 367 unpack(Container& c) { 368 auto top_level_arg_type = iter_.get_arg_type(); 369 constexpr auto type = element_signature<Container>::code[0]; 370 if (top_level_arg_type != type) { 371 return false; 372 } 373 message::unpacker sub; 374 375 iter_.recurse(sub.iter_); 376 auto arg_type = sub.iter_.get_arg_type(); 377 while (arg_type != DBUS_TYPE_INVALID) { 378 c.emplace_back(); 379 if (!sub.unpack(c.back())) { 380 return false; 381 } 382 arg_type = sub.iter_.get_arg_type(); 383 } 384 iter_.next(); 385 return true; 386 } 387 }; 388 389 template <typename... Args> 390 bool unpack(Args&... args) { 391 return unpacker(*this).unpack(args...); 392 } 393 394 private: 395 static std::string sanitize(const char* str) { 396 return (str == NULL) ? "(null)" : str; 397 } 398 }; 399 400 inline std::ostream& operator<<(std::ostream& os, const message& m) { 401 os << "type='" << m.get_type() << "'," 402 << "sender='" << m.get_sender() << "'," 403 << "interface='" << m.get_interface() << "'," 404 << "member='" << m.get_member() << "'," 405 << "path='" << m.get_path() << "'," 406 << "destination='" << m.get_destination() << "'"; 407 return os; 408 } 409 410 } // namespace dbus 411 412 // primary template. 413 template <class T> 414 struct function_traits : function_traits<decltype(&T::operator())> {}; 415 416 // partial specialization for function type 417 template <class R, class... Args> 418 struct function_traits<R(Args...)> { 419 using result_type = R; 420 using argument_types = std::tuple<Args...>; 421 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 422 }; 423 424 // partial specialization for function pointer 425 template <class R, class... Args> 426 struct function_traits<R (*)(Args...)> { 427 using result_type = R; 428 using argument_types = std::tuple<Args...>; 429 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 430 }; 431 432 // partial specialization for std::function 433 template <class R, class... Args> 434 struct function_traits<std::function<R(Args...)>> { 435 using result_type = R; 436 using argument_types = std::tuple<Args...>; 437 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 438 }; 439 440 // partial specialization for pointer-to-member-function (i.e., operator()'s) 441 template <class T, class R, class... Args> 442 struct function_traits<R (T::*)(Args...)> { 443 using result_type = R; 444 using argument_types = std::tuple<Args...>; 445 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 446 }; 447 448 template <class T, class R, class... Args> 449 struct function_traits<R (T::*)(Args...) const> { 450 using result_type = R; 451 using argument_types = std::tuple<Args...>; 452 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 453 }; 454 455 template <class F, size_t... Is> 456 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) { 457 return f(std::integral_constant<size_t, Is>{}...); 458 } 459 460 template <size_t N, class F> 461 constexpr auto index_apply(F f) { 462 return index_apply_impl(f, std::make_index_sequence<N>{}); 463 } 464 465 template <class Tuple, class F> 466 constexpr auto apply(F f, Tuple& t) { 467 return index_apply<std::tuple_size<Tuple>{}>( 468 [&](auto... Is) { return f(std::get<Is>(t)...); }); 469 } 470 471 template <class Tuple> 472 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) { 473 return index_apply<std::tuple_size<Tuple>{}>( 474 [&](auto... Is) { return m.unpack(std::get<Is>(t)...); }); 475 } 476 477 // Specialization for empty tuples. No need to unpack if no arguments 478 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) { 479 return true; 480 } 481 482 template <typename... Args> 483 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) { 484 return index_apply<std::tuple_size<std::tuple<Args...>>{}>( 485 [&](auto... Is) { return m.pack(std::get<Is>(t)...); }); 486 } 487 488 // Specialization for empty tuples. No need to pack if no arguments 489 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) { 490 return true; 491 } 492 493 // Specialization for single types. Used when callbacks simply return one value 494 template <typename Element> 495 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) { 496 return m.pack(t); 497 } 498 499 #include <dbus/impl/message_iterator.ipp> 500 501 #endif // DBUS_MESSAGE_HPP 502