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 template <typename Element> 123 packer& pack(const Element& e) { 124 return *this << e; 125 } 126 127 template <typename Element, typename... Args> 128 packer pack(const Element& e, const Args&... args) { 129 return this->pack(e).pack(args...); 130 } 131 }; 132 133 template <typename... Args> 134 packer pack(const Args&... args) { 135 return packer(*this).pack(args...); 136 } 137 138 struct unpacker { 139 impl::message_iterator iter_; 140 unpacker(message& m) { impl::message_iterator::init(m, iter_); } 141 unpacker() {} 142 143 template <typename Element> 144 unpacker& unpack(Element& e) { 145 return *this >> e; 146 } 147 148 template <typename Element, typename... Args> 149 unpacker& unpack(Element& e, Args&... args) { 150 return unpack(e).unpack(args...); 151 } 152 }; 153 154 template <typename... Args> 155 unpacker& unpack(Args&... args) { 156 return unpacker(*this).unpack(args...); 157 } 158 159 private: 160 static std::string sanitize(const char* str) { 161 return (str == NULL) ? "(null)" : str; 162 } 163 }; 164 165 template <typename Element> 166 message::packer operator<<(message m, const Element& e) { 167 return message::packer(m).pack(e); 168 } 169 170 template <typename Element> 171 typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type 172 operator<<(message::packer& p, const Element& e) { 173 p.iter_.append_basic(element<Element>::code, &e); 174 return p; 175 } 176 177 // Specialization used to represent "dict" in dbus. 178 // TODO(ed) generalize for all "map like" types instead of using vector 179 template <typename Key, typename Value> 180 message::packer& operator<<(message::packer& p, 181 const std::vector<std::pair<Key, Value>>& v) { 182 message::packer sub; 183 static const constexpr auto sig = 184 element_signature<std::vector<std::pair<Key, Value>>>::code; 185 static_assert(std::tuple_size<decltype(sig)>::value > 2, 186 "Signature size must be greater than 2 characters long"); 187 // Skip over the array part "a" of the signature to get the element signature. 188 // Open container expects JUST the portion after the "a" 189 p.iter_.open_container(sig[0], &sig[1], sub.iter_); 190 for (auto& element : v) { 191 sub << element; 192 } 193 194 p.iter_.close_container(sub.iter_); 195 return p; 196 } 197 198 template <typename Element> 199 message::packer& operator<<(message::packer& p, const std::vector<Element>& v) { 200 message::packer sub; 201 static const constexpr auto signature = 202 element_signature<std::vector<Element>>::code; 203 static_assert(std::tuple_size<decltype(signature)>::value > 2, 204 "Signature size must be greater than 2 characters long"); 205 p.iter_.open_container(signature[0], &signature[1], sub.iter_); 206 for (auto& element : v) { 207 sub << element; 208 } 209 210 p.iter_.close_container(sub.iter_); 211 return p; 212 } 213 214 inline message::packer& operator<<(message::packer& p, const char* c) { 215 p.iter_.append_basic(element<string>::code, &c); 216 return p; 217 } 218 219 template <typename Key, typename Value> 220 inline message::packer& operator<<(message::packer& p, 221 const std::pair<Key, Value> element) { 222 message::packer dict_entry; 223 p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_); 224 dict_entry << element.first; 225 dict_entry << element.second; 226 p.iter_.close_container(dict_entry.iter_); 227 return p; 228 } 229 230 inline message::packer& operator<<(message::packer& p, const string& e) { 231 const char* c = e.c_str(); 232 return p << c; 233 } 234 235 inline message::packer& operator<<(message::packer& p, const dbus_variant& v) { 236 // Get the dbus typecode of the variant being packed 237 const char* type = boost::apply_visitor( 238 [&](auto val) { 239 static const constexpr auto sig = 240 element_signature<decltype(val)>::code; 241 static_assert(std::tuple_size<decltype(sig)>::value == 2, 242 "Element signature for dbus_variant too long. Expected " 243 "length of 1"); 244 return &sig[0]; 245 }, 246 v); 247 248 message::packer sub; 249 p.iter_.open_container(element<dbus_variant>::code, type, sub.iter_); 250 boost::apply_visitor([&](auto val) { sub << val; }, v); 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 char arg_type = sub.iter_.get_arg_type(); 282 283 boost::mpl::for_each<dbus_variant::types>([&](auto t) { 284 if (arg_type == element<decltype(t)>::code) { 285 decltype(t) val_to_fill; 286 sub >> val_to_fill; 287 v = val_to_fill; 288 } 289 }); 290 291 u.iter_.next(); 292 return u; 293 } 294 295 template <typename Key, typename Value> 296 inline message::unpacker& operator>>(message::unpacker& u, 297 std::pair<Key, Value>& v) { 298 message::unpacker sub; 299 u.iter_.recurse(sub.iter_); 300 sub >> v.first; 301 sub >> v.second; 302 303 u.iter_.next(); 304 return u; 305 } 306 307 template <typename Element> 308 inline message::unpacker& operator>>(message::unpacker& u, 309 std::vector<Element>& s) { 310 message::unpacker sub; 311 312 u.iter_.recurse(sub.iter_); 313 auto arg_type = sub.iter_.get_arg_type(); 314 while (arg_type != DBUS_TYPE_INVALID) { 315 s.emplace_back(); 316 sub >> s.back(); 317 arg_type = sub.iter_.get_arg_type(); 318 } 319 u.iter_.next(); 320 return u; 321 } 322 323 inline std::ostream& operator<<(std::ostream& os, const message& m) { 324 os << "type='" << m.get_type() << "'," 325 << "sender='" << m.get_sender() << "'," 326 << "interface='" << m.get_interface() << "'," 327 << "member='" << m.get_member() << "'," 328 << "path='" << m.get_path() << "'," 329 << "destination='" << m.get_destination() << "'"; 330 return os; 331 } 332 333 } // namespace dbus 334 335 #include <dbus/impl/message_iterator.ipp> 336 337 #endif // DBUS_MESSAGE_HPP 338