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 struct unpacker { 118 impl::message_iterator iter_; 119 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 120 unpacker() {} 121 122 template <typename Element> 123 unpacker& unpack(Element& e) { 124 return *this >> e; 125 } 126 }; 127 128 template <typename Element> 129 packer pack(const Element& e) { 130 return packer(*this).pack(e); 131 } 132 133 template <typename Element> 134 unpacker unpack(Element& e) { 135 return unpacker(*this).unpack(e); 136 } 137 138 private: 139 static std::string sanitize(const char* str) { 140 return (str == NULL) ? "(null)" : str; 141 } 142 }; 143 144 template <typename Element> 145 message::packer operator<<(message m, const Element& e) { 146 return message::packer(m).pack(e); 147 } 148 149 template <typename Element> 150 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type 151 operator<<(message::packer& p, const Element& e) { 152 p.iter_.append_basic(element<Element>::code, &e); 153 return p; 154 } 155 156 template <typename Key, typename Value> 157 message::packer& operator<<(message::packer& p, 158 const std::vector<std::pair<Key, Value>>& v) { 159 message::packer sub; 160 char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0}; 161 162 p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_); 163 for (auto& element : v) { 164 sub << element; 165 } 166 167 p.iter_.close_container(sub.iter_); 168 return p; 169 } 170 171 template <typename Element> 172 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) { 173 message::packer sub; 174 char signature[] = {element<Element>::code, 0}; 175 p.iter_.open_container(element<std::vector<Element>>::code, signature, 176 sub.iter_); 177 for (auto& element : v) { 178 sub << element; 179 } 180 181 p.iter_.close_container(sub.iter_); 182 return p; 183 } 184 185 inline message::packer& operator<<(message::packer& p, const char* c) { 186 p.iter_.append_basic(element<string>::code, &c); 187 return p; 188 } 189 190 template <typename Key, typename Value> 191 inline message::packer& operator<<(message::packer& p, 192 const std::pair<Key, Value> element) { 193 message::packer dict_entry; 194 p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_); 195 dict_entry << element.first; 196 dict_entry << element.second; 197 p.iter_.close_container(dict_entry.iter_); 198 return p; 199 } 200 201 inline message::packer& operator<<(message::packer& p, const string& e) { 202 const char* c = e.c_str(); 203 return p << c; 204 } 205 206 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) { 207 message::packer sub; 208 char type = 0; 209 // TODO(ed) there must be a better (more typesafe) way to do this 210 switch (v.which()) { 211 case 0: 212 type = element<std::string>::code; 213 break; 214 case 1: 215 type = element<bool>::code; 216 break; 217 case 2: 218 type = element<byte>::code; 219 break; 220 case 3: 221 type = element<int16>::code; 222 break; 223 case 4: 224 type = element<uint16>::code; 225 break; 226 case 5: 227 type = element<int32>::code; 228 break; 229 case 6: 230 type = element<uint32>::code; 231 break; 232 case 7: 233 type = element<int64>::code; 234 break; 235 case 8: 236 type = element<uint64>::code; 237 break; 238 case 9: 239 type = element<double>::code; 240 break; 241 242 default: 243 // TODO(ed) throw exception 244 break; 245 } 246 char signature[] = {type, 0}; 247 248 p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_); 249 boost::apply_visitor([&](auto val) { sub << val; }, v); 250 // sub << element; 251 p.iter_.close_container(sub.iter_); 252 253 return p; 254 } 255 256 template <typename Element> 257 message::unpacker operator>>(message m, Element& e) { 258 return message::unpacker(m).unpack(e); 259 } 260 261 template <typename Element> 262 typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type 263 operator>>(message::unpacker& u, Element& e) { 264 u.iter_.get_basic(&e); 265 u.iter_.next(); 266 return u; 267 } 268 269 inline message::unpacker& operator>>(message::unpacker& u, string& s) { 270 const char* c; 271 u.iter_.get_basic(&c); 272 s.assign(c); 273 u.iter_.next(); 274 return u; 275 } 276 277 inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) { 278 message::unpacker sub; 279 u.iter_.recurse(sub.iter_); 280 281 auto arg_type = sub.iter_.get_arg_type(); 282 // sub.iter_.get_basic(&c); 283 // Todo(ed) find a better way to do this lookup table 284 switch (arg_type) { 285 case element<std::string>::code: { 286 std::string s; 287 sub >> s; 288 v = s; 289 } break; 290 case element<bool>::code: { 291 bool b; 292 sub >> b; 293 v = b; 294 } break; 295 case element<byte>::code: { 296 byte b; 297 sub >> b; 298 v = b; 299 } break; 300 case element<int16>::code: { 301 int16 b; 302 sub >> b; 303 v = b; 304 } break; 305 case element<uint16>::code: { 306 uint16 b; 307 sub >> b; 308 v = b; 309 } break; 310 case element<int32>::code: { 311 int32 b; 312 sub >> b; 313 v = b; 314 } break; 315 case element<uint32>::code: { 316 uint32 b; 317 sub >> b; 318 v = b; 319 } break; 320 case element<int64>::code: { 321 int64 b; 322 sub >> b; 323 v = b; 324 } break; 325 case element<uint64>::code: { 326 uint64 b; 327 sub >> b; 328 v = b; 329 } break; 330 case element<double>::code: { 331 double b; 332 sub >> b; 333 v = b; 334 } break; 335 336 default: 337 // TODO(ed) throw exception 338 break; 339 } 340 u.iter_.next(); 341 return u; 342 } 343 344 template <typename Key, typename Value> 345 inline message::unpacker& operator>>(message::unpacker& u, 346 std::pair<Key, Value>& v) { 347 message::unpacker sub; 348 u.iter_.recurse(sub.iter_); 349 sub >> v.first; 350 sub >> v.second; 351 352 u.iter_.next(); 353 return u; 354 } 355 356 template <typename Element> 357 inline message::unpacker& operator>>(message::unpacker& u, 358 std::vector<Element>& s) { 359 message::unpacker sub; 360 361 u.iter_.recurse(sub.iter_); 362 auto arg_type = sub.iter_.get_arg_type(); 363 while (arg_type != DBUS_TYPE_INVALID) { 364 s.emplace_back(); 365 sub >> s.back(); 366 arg_type = sub.iter_.get_arg_type(); 367 } 368 u.iter_.next(); 369 return u; 370 } 371 372 inline std::ostream& operator<<(std::ostream& os, const message& m) { 373 os << "type='" << m.get_type() << "'," 374 << "sender='" << m.get_sender() << "'," 375 << "interface='" << m.get_interface() << "'," 376 << "member='" << m.get_member() << "'," 377 << "path='" << m.get_path() << "'," 378 << "destination='" << m.get_destination() << "'"; 379 return os; 380 } 381 382 } // namespace dbus 383 384 #include <dbus/impl/message_iterator.ipp> 385 386 #endif // DBUS_MESSAGE_HPP 387