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 string& e) { 202 const char* c = e.c_str(); 203 return pack(c); 204 } 205 206 bool pack(const dbus_variant& v) { 207 // Get the dbus typecode of the variant being packed 208 const char* type = boost::apply_visitor( 209 [&](auto val) { 210 static const constexpr auto sig = 211 element_signature<decltype(val)>::code; 212 static_assert( 213 std::tuple_size<decltype(sig)>::value == 2, 214 "Element signature for dbus_variant too long. Expected " 215 "length of 1"); 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> 229 bool pack(const Args&... args) { 230 return packer(*this).pack(args...); 231 } 232 233 // noop for template functions that have no arguments 234 bool pack() { return true; } 235 236 struct unpacker { 237 impl::message_iterator iter_; 238 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 239 unpacker() {} 240 241 template <typename Element, typename... Args> 242 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> 251 typename boost::enable_if<is_fixed_type<Element>, 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 265 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 277 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 289 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 301 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 313 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> 336 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 Container> 359 typename std::enable_if<has_const_iterator<Container>::value && 360 !is_string_type<Container>::value, 361 bool>::type 362 unpack(Container& c) { 363 std::cout << "test\n"; 364 auto top_level_arg_type = iter_.get_arg_type(); 365 constexpr auto type = element_signature<Container>::code[0]; 366 if (top_level_arg_type != type) { 367 return false; 368 } 369 message::unpacker sub; 370 371 iter_.recurse(sub.iter_); 372 auto arg_type = sub.iter_.get_arg_type(); 373 while (arg_type != DBUS_TYPE_INVALID) { 374 c.emplace_back(); 375 if (!sub.unpack(c.back())) { 376 return false; 377 } 378 arg_type = sub.iter_.get_arg_type(); 379 } 380 iter_.next(); 381 return true; 382 } 383 }; 384 385 template <typename... Args> 386 bool unpack(Args&... args) { 387 return unpacker(*this).unpack(args...); 388 } 389 390 private: 391 static std::string sanitize(const char* str) { 392 return (str == NULL) ? "(null)" : str; 393 } 394 }; 395 396 inline std::ostream& operator<<(std::ostream& os, const message& m) { 397 os << "type='" << m.get_type() << "'," 398 << "sender='" << m.get_sender() << "'," 399 << "interface='" << m.get_interface() << "'," 400 << "member='" << m.get_member() << "'," 401 << "path='" << m.get_path() << "'," 402 << "destination='" << m.get_destination() << "'"; 403 return os; 404 } 405 406 } // namespace dbus 407 408 // primary template. 409 template <class T> 410 struct function_traits : function_traits<decltype(&T::operator())> {}; 411 412 // partial specialization for function type 413 template <class R, class... Args> 414 struct function_traits<R(Args...)> { 415 using result_type = R; 416 using argument_types = std::tuple<Args...>; 417 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 418 }; 419 420 // partial specialization for function pointer 421 template <class R, class... Args> 422 struct function_traits<R (*)(Args...)> { 423 using result_type = R; 424 using argument_types = std::tuple<Args...>; 425 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 426 }; 427 428 // partial specialization for std::function 429 template <class R, class... Args> 430 struct function_traits<std::function<R(Args...)>> { 431 using result_type = R; 432 using argument_types = std::tuple<Args...>; 433 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 434 }; 435 436 // partial specialization for pointer-to-member-function (i.e., operator()'s) 437 template <class T, class R, class... Args> 438 struct function_traits<R (T::*)(Args...)> { 439 using result_type = R; 440 using argument_types = std::tuple<Args...>; 441 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 442 }; 443 444 template <class T, class R, class... Args> 445 struct function_traits<R (T::*)(Args...) const> { 446 using result_type = R; 447 using argument_types = std::tuple<Args...>; 448 using decayed_arg_types = std::tuple<typename std::decay<Args>::type...>; 449 }; 450 451 template <class F, size_t... Is> 452 constexpr auto index_apply_impl(F f, std::index_sequence<Is...>) { 453 return f(std::integral_constant<size_t, Is>{}...); 454 } 455 456 template <size_t N, class F> 457 constexpr auto index_apply(F f) { 458 return index_apply_impl(f, std::make_index_sequence<N>{}); 459 } 460 461 template <class Tuple, class F> 462 constexpr auto apply(F f, Tuple& t) { 463 return index_apply<std::tuple_size<Tuple>{}>( 464 [&](auto... Is) { return f(std::get<Is>(t)...); }); 465 } 466 467 template <class Tuple> 468 constexpr bool unpack_into_tuple(Tuple& t, dbus::message& m) { 469 return index_apply<std::tuple_size<Tuple>{}>( 470 [&](auto... Is) { return m.unpack(std::get<Is>(t)...); }); 471 } 472 473 // Specialization for empty tuples. No need to unpack if no arguments 474 constexpr bool unpack_into_tuple(std::tuple<>& t, dbus::message& m) { 475 return true; 476 } 477 478 template <typename... Args> 479 constexpr bool pack_tuple_into_msg(std::tuple<Args...>& t, dbus::message& m) { 480 return index_apply<std::tuple_size<std::tuple<Args...>>{}>( 481 [&](auto... Is) { return m.pack(std::get<Is>(t)...); }); 482 } 483 484 // Specialization for empty tuples. No need to pack if no arguments 485 constexpr bool pack_tuple_into_msg(std::tuple<>& t, dbus::message& m) { 486 return true; 487 } 488 489 // Specialization for single types. Used when callbacks simply return one value 490 template <typename Element> 491 constexpr bool pack_tuple_into_msg(Element& t, dbus::message& m) { 492 return m.pack(t); 493 } 494 495 #include <dbus/impl/message_iterator.ipp> 496 497 #endif // DBUS_MESSAGE_HPP 498