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