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/utility/enable_if.hpp> 17 18 inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); } 19 20 inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); } 21 22 namespace dbus { 23 24 class message { 25 boost::intrusive_ptr<DBusMessage> message_; 26 27 public: 28 /// Create a method call message 29 static message new_call(const endpoint& destination, 30 const string& method_name) { 31 return dbus_message_new_method_call( 32 destination.get_process_name().c_str(), destination.get_path().c_str(), 33 destination.get_interface().c_str(), method_name.c_str()); 34 } 35 36 /// Create a method return message 37 static message new_return(message& call) { 38 return dbus_message_new_method_return(call); 39 } 40 41 /// Create an error message 42 static message new_error(message& call, const string& error_name, 43 const string& error_message) { 44 return dbus_message_new_error(call, error_name.c_str(), 45 error_message.c_str()); 46 } 47 48 /// Create a signal message 49 static message new_signal(const endpoint& origin, const string& signal_name) { 50 return dbus_message_new_signal(origin.get_path().c_str(), 51 origin.get_interface().c_str(), 52 signal_name.c_str()); 53 } 54 55 message() {} 56 57 message(DBusMessage* m) : message_(dbus_message_ref(m)) {} 58 59 operator DBusMessage*() { return message_.get(); } 60 61 operator const DBusMessage*() const { return message_.get(); } 62 63 string get_path() const { 64 return sanitize(dbus_message_get_path(message_.get())); 65 } 66 67 string get_interface() const { 68 return sanitize(dbus_message_get_interface(message_.get())); 69 } 70 71 string get_member() const { 72 return sanitize(dbus_message_get_member(message_.get())); 73 } 74 75 string get_type() const { 76 return sanitize( 77 dbus_message_type_to_string(dbus_message_get_type(message_.get()))); 78 } 79 80 string get_signature() const { 81 return sanitize(dbus_message_get_signature(message_.get())); 82 } 83 84 string get_sender() const { 85 return sanitize(dbus_message_get_sender(message_.get())); 86 } 87 88 string get_destination() const { 89 return sanitize(dbus_message_get_destination(message_.get())); 90 } 91 92 uint32 get_serial() { return dbus_message_get_serial(message_.get()); } 93 94 message& set_serial(uint32 serial) { 95 dbus_message_set_serial(message_.get(), serial); 96 return *this; 97 } 98 99 uint32 get_reply_serial() { 100 return dbus_message_get_reply_serial(message_.get()); 101 } 102 103 message& set_reply_serial(uint32 reply_serial) { 104 dbus_message_set_reply_serial(message_.get(), reply_serial); 105 return *this; 106 } 107 108 struct packer { 109 impl::message_iterator iter_; 110 packer(message& m) { impl::message_iterator::init_append(m, iter_); } 111 packer(){}; 112 template <typename Element> 113 packer& pack(const Element& e) { 114 return *this << e; 115 } 116 }; 117 118 template <typename Element> 119 packer pack(const Element& e) { 120 return packer(*this).pack(e); 121 } 122 123 template <typename Element, typename... Args> 124 packer pack(const Element& e, Args&... args) { 125 return packer(*this).pack(e).pack(args...); 126 } 127 128 struct unpacker { 129 impl::message_iterator iter_; 130 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 131 unpacker() {} 132 133 template <typename Element> 134 unpacker& unpack(Element& e) { 135 return *this >> e; 136 } 137 }; 138 139 template <typename Element> 140 unpacker unpack(Element& e) { 141 return unpacker(*this).unpack(e); 142 } 143 144 template <typename Element, typename... Args> 145 unpacker& unpack(Element& e, Args&... args) { 146 return unpack(e).unpack(args...); 147 } 148 149 private: 150 static std::string sanitize(const char* str) { 151 return (str == NULL) ? "(null)" : str; 152 } 153 }; 154 155 template <typename Element> 156 message::packer operator<<(message m, const Element& e) { 157 return message::packer(m).pack(e); 158 } 159 160 template <typename Element> 161 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type 162 operator<<(message::packer& p, const Element& e) { 163 p.iter_.append_basic(element<Element>::code, &e); 164 return p; 165 } 166 167 template <typename Key, typename Value> 168 message::packer& operator<<(message::packer& p, 169 const std::vector<std::pair<Key, Value>>& v) { 170 message::packer sub; 171 char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0}; 172 173 p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_); 174 for (auto& element : v) { 175 sub << element; 176 } 177 178 p.iter_.close_container(sub.iter_); 179 return p; 180 } 181 182 template <typename Element> 183 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) { 184 message::packer sub; 185 char signature[] = {element<Element>::code, 0}; 186 p.iter_.open_container(element<std::vector<Element>>::code, signature, 187 sub.iter_); 188 for (auto& element : v) { 189 sub << element; 190 } 191 192 p.iter_.close_container(sub.iter_); 193 return p; 194 } 195 196 inline message::packer& operator<<(message::packer& p, const char* c) { 197 p.iter_.append_basic(element<string>::code, &c); 198 return p; 199 } 200 201 template <typename Key, typename Value> 202 inline message::packer& operator<<(message::packer& p, 203 const std::pair<Key, Value> element) { 204 message::packer dict_entry; 205 p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_); 206 dict_entry << element.first; 207 dict_entry << element.second; 208 p.iter_.close_container(dict_entry.iter_); 209 return p; 210 } 211 212 inline message::packer& operator<<(message::packer& p, const string& e) { 213 const char* c = e.c_str(); 214 return p << c; 215 } 216 217 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) { 218 message::packer sub; 219 char type = 0; 220 // TODO(ed) there must be a better (more typesafe) way to do this 221 switch (v.which()) { 222 case 0: 223 type = element<std::string>::code; 224 break; 225 case 1: 226 type = element<bool>::code; 227 break; 228 case 2: 229 type = element<byte>::code; 230 break; 231 case 3: 232 type = element<int16>::code; 233 break; 234 case 4: 235 type = element<uint16>::code; 236 break; 237 case 5: 238 type = element<int32>::code; 239 break; 240 case 6: 241 type = element<uint32>::code; 242 break; 243 case 7: 244 type = element<int64>::code; 245 break; 246 case 8: 247 type = element<uint64>::code; 248 break; 249 case 9: 250 type = element<double>::code; 251 break; 252 253 default: 254 // TODO(ed) throw exception 255 break; 256 } 257 char signature[] = {type, 0}; 258 259 p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_); 260 boost::apply_visitor([&](auto val) { sub << val; }, v); 261 // sub << element; 262 p.iter_.close_container(sub.iter_); 263 264 return p; 265 } 266 267 template <typename Element> 268 message::unpacker operator>>(message m, Element& e) { 269 return message::unpacker(m).unpack(e); 270 } 271 272 template <typename Element> 273 typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type 274 operator>>(message::unpacker& u, Element& e) { 275 u.iter_.get_basic(&e); 276 u.iter_.next(); 277 return u; 278 } 279 280 inline message::unpacker& operator>>(message::unpacker& u, string& s) { 281 const char* c; 282 u.iter_.get_basic(&c); 283 s.assign(c); 284 u.iter_.next(); 285 return u; 286 } 287 288 inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) { 289 message::unpacker sub; 290 u.iter_.recurse(sub.iter_); 291 292 auto arg_type = sub.iter_.get_arg_type(); 293 // sub.iter_.get_basic(&c); 294 // Todo(ed) find a better way to do this lookup table 295 switch (arg_type) { 296 case element<std::string>::code: { 297 std::string s; 298 sub >> s; 299 v = s; 300 } break; 301 case element<bool>::code: { 302 bool b; 303 sub >> b; 304 v = b; 305 } break; 306 case element<byte>::code: { 307 byte b; 308 sub >> b; 309 v = b; 310 } break; 311 case element<int16>::code: { 312 int16 b; 313 sub >> b; 314 v = b; 315 } break; 316 case element<uint16>::code: { 317 uint16 b; 318 sub >> b; 319 v = b; 320 } break; 321 case element<int32>::code: { 322 int32 b; 323 sub >> b; 324 v = b; 325 } break; 326 case element<uint32>::code: { 327 uint32 b; 328 sub >> b; 329 v = b; 330 } break; 331 case element<int64>::code: { 332 int64 b; 333 sub >> b; 334 v = b; 335 } break; 336 case element<uint64>::code: { 337 uint64 b; 338 sub >> b; 339 v = b; 340 } break; 341 case element<double>::code: { 342 double b; 343 sub >> b; 344 v = b; 345 } break; 346 347 default: 348 // TODO(ed) throw exception 349 break; 350 } 351 u.iter_.next(); 352 return u; 353 } 354 355 template <typename Key, typename Value> 356 inline message::unpacker& operator>>(message::unpacker& u, 357 std::pair<Key, Value>& v) { 358 message::unpacker sub; 359 u.iter_.recurse(sub.iter_); 360 sub >> v.first; 361 sub >> v.second; 362 363 u.iter_.next(); 364 return u; 365 } 366 367 template <typename Element> 368 inline message::unpacker& operator>>(message::unpacker& u, 369 std::vector<Element>& s) { 370 message::unpacker sub; 371 372 u.iter_.recurse(sub.iter_); 373 auto arg_type = sub.iter_.get_arg_type(); 374 while (arg_type != DBUS_TYPE_INVALID) { 375 s.emplace_back(); 376 sub >> s.back(); 377 arg_type = sub.iter_.get_arg_type(); 378 } 379 u.iter_.next(); 380 return u; 381 } 382 383 inline std::ostream& operator<<(std::ostream& os, const message& m) { 384 os << "type='" << m.get_type() << "'," 385 << "sender='" << m.get_sender() << "'," 386 << "interface='" << m.get_interface() << "'," 387 << "member='" << m.get_member() << "'," 388 << "path='" << m.get_path() << "'," 389 << "destination='" << m.get_destination() << "'"; 390 return os; 391 } 392 393 } // namespace dbus 394 395 #include <dbus/impl/message_iterator.ipp> 396 397 #endif // DBUS_MESSAGE_HPP 398