blob: 695e1fc77e8b0d6436c3ba2c613aee5d50019e5b [file] [log] [blame]
Ed Tanousc9b55212017-06-12 13:25:51 -07001// 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
18inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
19
20inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
21
22namespace dbus {
23
24class 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_); }
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700107 packer(){};
Ed Tanousc9b55212017-06-12 13:25:51 -0700108 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_); }
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700116 unpacker() {}
Ed Tanousc9b55212017-06-12 13:25:51 -0700117
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
140template <typename Element>
141message::packer operator<<(message m, const Element& e) {
142 return message::packer(m).pack(e);
143}
144
145template <typename Element>
146typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
147operator<<(message::packer& p, const Element& e) {
148 p.iter_.append_basic(element<Element>::code, &e);
149 return p;
150}
151
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700152template <typename Key, typename Value>
153message::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
167template <typename Element>
168message::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
Ed Tanousc9b55212017-06-12 13:25:51 -0700181inline message::packer& operator<<(message::packer& p, const char* c) {
182 p.iter_.append_basic(element<string>::code, &c);
183 return p;
184}
185
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700186template <typename Key, typename Value>
187inline 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
Ed Tanousc9b55212017-06-12 13:25:51 -0700197inline message::packer& operator<<(message::packer& p, const string& e) {
198 const char* c = e.c_str();
199 return p << c;
200}
201
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700202inline 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
Ed Tanousc9b55212017-06-12 13:25:51 -0700252template <typename Element>
253message::unpacker operator>>(message m, Element& e) {
254 return message::unpacker(m).unpack(e);
255}
256
257template <typename Element>
258typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
259operator>>(message::unpacker& u, Element& e) {
260 u.iter_.get_basic(&e);
261 u.iter_.next();
262 return u;
263}
264
265inline 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
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700273inline 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
340template <typename Key, typename Value>
341inline 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
Ed Tanousc9b55212017-06-12 13:25:51 -0700352template <typename Element>
353inline message::unpacker& operator>>(message::unpacker& u,
354 std::vector<Element>& s) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700355 message::unpacker sub;
Ed Tanousc9b55212017-06-12 13:25:51 -0700356
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700357 u.iter_.recurse(sub.iter_);
358 auto arg_type = sub.iter_.get_arg_type();
359 while (arg_type != DBUS_TYPE_INVALID) {
Ed Tanousc9b55212017-06-12 13:25:51 -0700360 s.emplace_back();
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700361 sub >> s.back();
362 arg_type = sub.iter_.get_arg_type();
Ed Tanousc9b55212017-06-12 13:25:51 -0700363 }
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700364 u.iter_.next();
Ed Tanousc9b55212017-06-12 13:25:51 -0700365 return u;
366}
367
368inline 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