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 const string& method_name) { 33 auto x = message(dbus_message_new_method_call( 34 destination.get_process_name().c_str(), destination.get_path().c_str(), 35 destination.get_interface().c_str(), method_name.c_str())); 36 dbus_message_unref(x.message_.get()); 37 return x; 38 } 39 40 /// Create a method return message 41 static message new_return(message& call) { 42 auto x = message(dbus_message_new_method_return(call)); 43 dbus_message_unref(x.message_.get()); 44 return x; 45 } 46 47 /// Create an error message 48 static message new_error(message& call, const string& error_name, 49 const string& error_message) { 50 auto x = message(dbus_message_new_error(call, error_name.c_str(), 51 error_message.c_str())); 52 dbus_message_unref(x.message_.get()); 53 return x; 54 } 55 56 /// Create a signal message 57 static message new_signal(const endpoint& origin, const string& signal_name) { 58 auto x = message(dbus_message_new_signal(origin.get_path().c_str(), 59 origin.get_interface().c_str(), 60 signal_name.c_str())); 61 dbus_message_unref(x.message_.get()); 62 return x; 63 } 64 65 message() = delete; 66 67 message(DBusMessage* m) : message_(m) {} 68 69 operator DBusMessage*() { return message_.get(); } 70 71 operator const DBusMessage*() const { return message_.get(); } 72 73 string get_path() const { 74 return sanitize(dbus_message_get_path(message_.get())); 75 } 76 77 string get_interface() const { 78 return sanitize(dbus_message_get_interface(message_.get())); 79 } 80 81 string get_member() const { 82 return sanitize(dbus_message_get_member(message_.get())); 83 } 84 85 string get_type() const { 86 return sanitize( 87 dbus_message_type_to_string(dbus_message_get_type(message_.get()))); 88 } 89 90 string get_signature() const { 91 return sanitize(dbus_message_get_signature(message_.get())); 92 } 93 94 string get_sender() const { 95 return sanitize(dbus_message_get_sender(message_.get())); 96 } 97 98 string get_destination() const { 99 return sanitize(dbus_message_get_destination(message_.get())); 100 } 101 102 uint32 get_serial() { return dbus_message_get_serial(message_.get()); } 103 104 message& set_serial(uint32 serial) { 105 dbus_message_set_serial(message_.get(), serial); 106 return *this; 107 } 108 109 uint32 get_reply_serial() { 110 return dbus_message_get_reply_serial(message_.get()); 111 } 112 113 message& set_reply_serial(uint32 reply_serial) { 114 dbus_message_set_reply_serial(message_.get(), reply_serial); 115 return *this; 116 } 117 118 struct packer { 119 impl::message_iterator iter_; 120 packer(message& m) { impl::message_iterator::init_append(m, iter_); } 121 packer(){}; 122 123 template <typename Element, typename... Args> 124 bool pack(const Element& e, const Args&... args) { 125 if (this->pack(e) == false) { 126 return false; 127 } else { 128 return pack(args...); 129 } 130 } 131 132 template <typename Element> 133 typename boost::enable_if<is_fixed_type<Element>, bool>::type pack( 134 const Element& e) { 135 return iter_.append_basic(element<Element>::code, &e); 136 } 137 138 // Specialization used to represent "dict" in dbus. 139 // TODO(ed) generalize for all "map like" types instead of using vector 140 template <typename Key, typename Value> 141 bool pack(const std::vector<std::pair<Key, Value>>& v) { 142 static const constexpr auto sig = 143 element_signature<typename std::decay<decltype(v)>::type>::code; 144 145 static_assert(std::tuple_size<decltype(sig)>::value > 2, 146 "Signature size must be greater than 2 characters long"); 147 // Skip over the array part "a" of the signature to get the element 148 // signature. 149 // Open container expects JUST the portion after the "a" in the second arg 150 message::packer sub; 151 iter_.open_container(sig[0], &sig[1], sub.iter_); 152 for (auto& element : v) { 153 sub.pack(element); 154 } 155 156 return iter_.close_container(sub.iter_); 157 } 158 159 template <typename Element> 160 bool pack(const std::vector<Element>& v) { 161 message::packer sub; 162 static const constexpr auto signature = 163 element_signature<std::vector<Element>>::code; 164 static_assert(std::tuple_size<decltype(signature)>::value > 2, 165 "Signature size must be greater than 2 characters long"); 166 iter_.open_container(signature[0], &signature[1], sub.iter_); 167 for (auto& element : v) { 168 sub.pack(element); 169 } 170 171 return iter_.close_container(sub.iter_); 172 } 173 174 bool pack(const char* c) { 175 return iter_.append_basic(element<string>::code, &c); 176 } 177 178 template <typename Key, typename Value> 179 bool pack(const std::pair<Key, Value> element) { 180 message::packer dict_entry; 181 if (iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_) == 182 false) { 183 return false; 184 } 185 if (dict_entry.pack(element.first) == false) { 186 return false; 187 }; 188 if (dict_entry.pack(element.second) == false) { 189 return false; 190 }; 191 return iter_.close_container(dict_entry.iter_); 192 } 193 194 bool pack(const string& e) { 195 const char* c = e.c_str(); 196 return pack(c); 197 } 198 199 bool pack(const dbus_variant& v) { 200 // Get the dbus typecode of the variant being packed 201 const char* type = boost::apply_visitor( 202 [&](auto val) { 203 static const constexpr auto sig = 204 element_signature<decltype(val)>::code; 205 static_assert( 206 std::tuple_size<decltype(sig)>::value == 2, 207 "Element signature for dbus_variant too long. Expected " 208 "length of 1"); 209 return &sig[0]; 210 }, 211 v); 212 message::packer sub; 213 iter_.open_container(element<dbus_variant>::code, type, sub.iter_); 214 boost::apply_visitor([&](const auto& val) { sub.pack(val); }, v); 215 iter_.close_container(sub.iter_); 216 217 return true; 218 } 219 }; 220 221 template <typename... Args> 222 bool pack(const Args&... args) { 223 return packer(*this).pack(args...); 224 } 225 226 struct unpacker { 227 impl::message_iterator iter_; 228 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 229 unpacker() {} 230 231 template <typename Element, typename... Args> 232 bool unpack(Element& e, Args&... args) { 233 if (unpack(e) == false) { 234 return false; 235 } 236 return unpack(args...); 237 } 238 239 // Basic type unpack 240 template <typename Element> 241 typename boost::enable_if<is_fixed_type<Element>, bool>::type unpack( 242 Element& e) { 243 if (iter_.get_arg_type() != element<Element>::code) { 244 return false; 245 } 246 iter_.get_basic(&e); 247 // ignoring return code here, as we might hit last element, and don't 248 // really care because get_arg_type will return invalid if we call it 249 // after we're over the struct boundary 250 iter_.next(); 251 return true; 252 } 253 254 // std::string unpack specialization 255 bool unpack(string& s) { 256 if (iter_.get_arg_type() != element<string>::code) { 257 return false; 258 } 259 const char* c; 260 iter_.get_basic(&c); 261 s.assign(c); 262 iter_.next(); 263 return true; 264 } 265 266 // object_path unpack specialization 267 bool unpack(object_path& s) { 268 if (iter_.get_arg_type() != element<object_path>::code) { 269 return false; 270 } 271 const char* c; 272 iter_.get_basic(&c); 273 s.value.assign(c); 274 iter_.next(); 275 return true; 276 } 277 278 // object_path unpack specialization 279 bool unpack(signature& s) { 280 if (iter_.get_arg_type() != element<signature>::code) { 281 return false; 282 } 283 const char* c; 284 iter_.get_basic(&c); 285 s.value.assign(c); 286 iter_.next(); 287 return true; 288 } 289 290 // variant unpack specialization 291 bool unpack(dbus_variant& v) { 292 if (iter_.get_arg_type() != element<dbus_variant>::code) { 293 return false; 294 } 295 message::unpacker sub; 296 iter_.recurse(sub.iter_); 297 298 char arg_type = sub.iter_.get_arg_type(); 299 300 boost::mpl::for_each<dbus_variant::types>([&](auto t) { 301 if (arg_type == element<decltype(t)>::code) { 302 decltype(t) val_to_fill; 303 sub.unpack(val_to_fill); 304 v = val_to_fill; 305 } 306 }); 307 308 iter_.next(); 309 return true; 310 } 311 312 // dict entry unpack specialization 313 template <typename Key, typename Value> 314 bool unpack(std::pair<Key, Value>& v) { 315 auto this_code = iter_.get_arg_type(); 316 // This can't use element<std::pair> because there is a difference between 317 // the dbus type code 'e' and the dbus signature code for dict entries 318 // '{'. Element_signature will return the full signature, and will never 319 // return e, but we still want to type check it before recursing 320 if (this_code != DBUS_TYPE_DICT_ENTRY) { 321 return false; 322 } 323 message::unpacker sub; 324 iter_.recurse(sub.iter_); 325 if (!sub.unpack(v.first)) { 326 return false; 327 } 328 if (!sub.unpack(v.second)) { 329 return false; 330 } 331 332 iter_.next(); 333 return true; 334 } 335 336 // dbus array / c++ vector unpack specialization 337 template <typename Element> 338 bool unpack(std::vector<Element>& s) { 339 auto top_level_arg_type = iter_.get_arg_type(); 340 if (top_level_arg_type != element<std::vector<Element>>::code) { 341 return false; 342 } 343 message::unpacker sub; 344 345 iter_.recurse(sub.iter_); 346 auto arg_type = sub.iter_.get_arg_type(); 347 while (arg_type != DBUS_TYPE_INVALID) { 348 s.emplace_back(); 349 if (!sub.unpack(s.back())) { 350 return false; 351 } 352 arg_type = sub.iter_.get_arg_type(); 353 } 354 iter_.next(); 355 return true; 356 } 357 }; 358 359 template <typename... Args> 360 bool unpack(Args&... args) { 361 return unpacker(*this).unpack(args...); 362 } 363 364 private: 365 static std::string sanitize(const char* str) { 366 return (str == NULL) ? "(null)" : str; 367 } 368 }; 369 370 inline std::ostream& operator<<(std::ostream& os, const message& m) { 371 os << "type='" << m.get_type() << "'," 372 << "sender='" << m.get_sender() << "'," 373 << "interface='" << m.get_interface() << "'," 374 << "member='" << m.get_member() << "'," 375 << "path='" << m.get_path() << "'," 376 << "destination='" << m.get_destination() << "'"; 377 return os; 378 } 379 380 } // namespace dbus 381 382 #include <dbus/impl/message_iterator.ipp> 383 384 #endif // DBUS_MESSAGE_HPP 385