blob: 695e1fc77e8b0d6436c3ba2c613aee5d50019e5b [file] [log] [blame]
// Copyright (c) Benjamin Kietzman (github.com/bkietz)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#ifndef DBUS_MESSAGE_HPP
#define DBUS_MESSAGE_HPP
#include <dbus/dbus.h>
#include <dbus/element.hpp>
#include <dbus/endpoint.hpp>
#include <dbus/impl/message_iterator.hpp>
#include <iostream>
#include <vector>
#include <boost/intrusive_ptr.hpp>
#include <boost/utility/enable_if.hpp>
inline void intrusive_ptr_add_ref(DBusMessage* m) { dbus_message_ref(m); }
inline void intrusive_ptr_release(DBusMessage* m) { dbus_message_unref(m); }
namespace dbus {
class message {
boost::intrusive_ptr<DBusMessage> message_;
public:
/// Create a method call message
static message new_call(const endpoint& destination,
const string& method_name) {
return dbus_message_new_method_call(
destination.get_process_name().c_str(), destination.get_path().c_str(),
destination.get_interface().c_str(), method_name.c_str());
}
/// Create a method return message
static message new_return(message& call) {
return dbus_message_new_method_return(call);
}
/// Create an error message
static message new_error(message& call, const string& error_name,
const string& error_message) {
return dbus_message_new_error(call, error_name.c_str(),
error_message.c_str());
}
/// Create a signal message
static message new_signal(const endpoint& origin, const string& signal_name) {
return dbus_message_new_signal(origin.get_path().c_str(),
origin.get_interface().c_str(),
signal_name.c_str());
}
message() {}
message(DBusMessage* m) : message_(dbus_message_ref(m)) {}
operator DBusMessage*() { return message_.get(); }
operator const DBusMessage*() const { return message_.get(); }
string get_path() const {
return sanitize(dbus_message_get_path(message_.get()));
}
string get_interface() const {
return sanitize(dbus_message_get_interface(message_.get()));
}
string get_member() const {
return sanitize(dbus_message_get_member(message_.get()));
}
string get_type() const {
return sanitize(
dbus_message_type_to_string(dbus_message_get_type(message_.get())));
}
string get_sender() const {
return sanitize(dbus_message_get_sender(message_.get()));
}
string get_destination() const {
return sanitize(dbus_message_get_destination(message_.get()));
}
uint32 get_serial() { return dbus_message_get_serial(message_.get()); }
message& set_serial(uint32 serial) {
dbus_message_set_serial(message_.get(), serial);
return *this;
}
uint32 get_reply_serial() {
return dbus_message_get_reply_serial(message_.get());
}
message& set_reply_serial(uint32 reply_serial) {
dbus_message_set_reply_serial(message_.get(), reply_serial);
return *this;
}
struct packer {
impl::message_iterator iter_;
packer(message& m) { impl::message_iterator::init_append(m, iter_); }
packer(){};
template <typename Element>
packer& pack(const Element& e) {
return *this << e;
}
};
struct unpacker {
impl::message_iterator iter_;
unpacker(message& m) { impl::message_iterator::init(m, iter_); }
unpacker() {}
template <typename Element>
unpacker& unpack(Element& e) {
return *this >> e;
}
};
template <typename Element>
packer pack(const Element& e) {
return packer(*this).pack(e);
}
template <typename Element>
unpacker unpack(Element& e) {
return unpacker(*this).unpack(e);
}
private:
static std::string sanitize(const char* str) {
return (str == NULL) ? "(null)" : str;
}
};
template <typename Element>
message::packer operator<<(message m, const Element& e) {
return message::packer(m).pack(e);
}
template <typename Element>
typename boost::enable_if<is_fixed_type<Element>, message::packer&>::type
operator<<(message::packer& p, const Element& e) {
p.iter_.append_basic(element<Element>::code, &e);
return p;
}
template <typename Key, typename Value>
message::packer& operator<<(message::packer& p,
const std::vector<std::pair<Key, Value>>& v) {
message::packer sub;
char signature[] = {'{', element<Key>::code, element<Value>::code, '}', 0};
p.iter_.open_container(DBUS_TYPE_ARRAY, signature, sub.iter_);
for (auto& element : v) {
sub << element;
}
p.iter_.close_container(sub.iter_);
return p;
}
template <typename Element>
message::packer& operator<<(message::packer& p, const std::vector<Element>& v) {
message::packer sub;
char signature[] = {element<Element>::code, 0};
p.iter_.open_container(element<std::vector<Element>>::code, signature,
sub.iter_);
for (auto& element : v) {
sub << element;
}
p.iter_.close_container(sub.iter_);
return p;
}
inline message::packer& operator<<(message::packer& p, const char* c) {
p.iter_.append_basic(element<string>::code, &c);
return p;
}
template <typename Key, typename Value>
inline message::packer& operator<<(message::packer& p,
const std::pair<Key, Value> element) {
message::packer dict_entry;
p.iter_.open_container(DBUS_TYPE_DICT_ENTRY, NULL, dict_entry.iter_);
dict_entry << element.first;
dict_entry << element.second;
p.iter_.close_container(dict_entry.iter_);
return p;
}
inline message::packer& operator<<(message::packer& p, const string& e) {
const char* c = e.c_str();
return p << c;
}
inline message::packer& operator<<(message::packer& p, const dbus_variant& v) {
message::packer sub;
char type = 0;
// TODO(ed) there must be a better (more typesafe) way to do this
switch (v.which()) {
case 0:
type = element<std::string>::code;
break;
case 1:
type = element<bool>::code;
break;
case 2:
type = element<byte>::code;
break;
case 3:
type = element<int16>::code;
break;
case 4:
type = element<uint16>::code;
break;
case 5:
type = element<int32>::code;
break;
case 6:
type = element<uint32>::code;
break;
case 7:
type = element<int64>::code;
break;
case 8:
type = element<uint64>::code;
break;
case 9:
type = element<double>::code;
break;
default:
// TODO(ed) throw exception
break;
}
char signature[] = {type, 0};
p.iter_.open_container(element<dbus_variant>::code, signature, sub.iter_);
boost::apply_visitor([&](auto val) { sub << val; }, v);
// sub << element;
p.iter_.close_container(sub.iter_);
return p;
}
template <typename Element>
message::unpacker operator>>(message m, Element& e) {
return message::unpacker(m).unpack(e);
}
template <typename Element>
typename boost::enable_if<is_fixed_type<Element>, message::unpacker&>::type
operator>>(message::unpacker& u, Element& e) {
u.iter_.get_basic(&e);
u.iter_.next();
return u;
}
inline message::unpacker& operator>>(message::unpacker& u, string& s) {
const char* c;
u.iter_.get_basic(&c);
s.assign(c);
u.iter_.next();
return u;
}
inline message::unpacker& operator>>(message::unpacker& u, dbus_variant& v) {
message::unpacker sub;
u.iter_.recurse(sub.iter_);
auto arg_type = sub.iter_.get_arg_type();
// sub.iter_.get_basic(&c);
// Todo(ed) find a better way to do this lookup table
switch (arg_type) {
case element<std::string>::code: {
std::string s;
sub >> s;
v = s;
} break;
case element<bool>::code: {
bool b;
sub >> b;
v = b;
} break;
case element<byte>::code: {
byte b;
sub >> b;
v = b;
} break;
case element<int16>::code: {
int16 b;
sub >> b;
v = b;
} break;
case element<uint16>::code: {
uint16 b;
sub >> b;
v = b;
} break;
case element<int32>::code: {
int32 b;
sub >> b;
v = b;
} break;
case element<uint32>::code: {
uint32 b;
sub >> b;
v = b;
} break;
case element<int64>::code: {
int64 b;
sub >> b;
v = b;
} break;
case element<uint64>::code: {
uint64 b;
sub >> b;
v = b;
} break;
case element<double>::code: {
double b;
sub >> b;
v = b;
} break;
default:
// TODO(ed) throw exception
break;
}
u.iter_.next();
return u;
}
template <typename Key, typename Value>
inline message::unpacker& operator>>(message::unpacker& u,
std::pair<Key, Value>& v) {
message::unpacker sub;
u.iter_.recurse(sub.iter_);
sub >> v.first;
sub >> v.second;
u.iter_.next();
return u;
}
template <typename Element>
inline message::unpacker& operator>>(message::unpacker& u,
std::vector<Element>& s) {
message::unpacker sub;
u.iter_.recurse(sub.iter_);
auto arg_type = sub.iter_.get_arg_type();
while (arg_type != DBUS_TYPE_INVALID) {
s.emplace_back();
sub >> s.back();
arg_type = sub.iter_.get_arg_type();
}
u.iter_.next();
return u;
}
inline std::ostream& operator<<(std::ostream& os, const message& m) {
os << "type='" << m.get_type() << "',"
<< "sender='" << m.get_sender() << "',"
<< "interface='" << m.get_interface() << "',"
<< "member='" << m.get_member() << "',"
<< "path='" << m.get_path() << "',"
<< "destination='" << m.get_destination() << "'";
return os;
}
} // namespace dbus
#include <dbus/impl/message_iterator.ipp>
#endif // DBUS_MESSAGE_HPP