1 #ifndef DBUS_CONNECTION_SERVICE_HPP
2 #define DBUS_CONNECTION_SERVICE_HPP
3 
4 #include <string>
5 #include <boost/asio.hpp>
6 
7 #include <dbus/message.hpp>
8 #include <dbus/detail/watch_timeout.hpp>
9 #include <dbus/detail/queue.hpp>
10 #include <dbus/match.hpp>
11 
12 namespace dbus {
13 
14 namespace bus {
15   static const int session = DBUS_BUS_SESSION;
16   static const int system  = DBUS_BUS_SYSTEM;
17   static const int starter = DBUS_BUS_STARTER;
18 } // namespace bus
19 
20 using std::string;
21 using namespace boost::asio;
22 
23 class connection_service
24   : public io_service::service
25 {
26 public:
27   static io_service::id id;
28 
29   typedef DBusConnection *implementation_type;
30 
31   explicit connection_service(io_service& io)
32     :  service(io)
33   {
34   }
35 
36   void construct(implementation_type& impl)
37   {
38   }
39 
40   void destroy(implementation_type& impl)
41   {
42     dbus_connection_unref(impl);
43   }
44 
45   void shutdown_service()
46   {
47     //TODO is there anything that needs shutting down?
48   }
49 
50   void open(implementation_type& impl,
51       const string& address, bool shared=true)
52   {
53     io_service& io = this->get_io_service();
54 
55     DBusError error;
56     dbus_error_init(&error);
57     impl = dbus_connection_open(address.c_str(), &error);
58     //TODO actually deal with that error
59 
60     detail::set_watch_timeout_dispatch_functions(impl, io);
61   }
62 
63   void open(implementation_type& impl,
64       const int bus = (int) DBUS_BUS_SYSTEM,
65       bool shared=true)
66   {
67     io_service& io = this->get_io_service();
68 
69     DBusError error;
70     dbus_error_init(&error);
71     impl = dbus_bus_get((DBusBusType)bus, &error);
72     //TODO actually deal with that error
73 
74     detail::set_watch_timeout_dispatch_functions(impl, io);
75   }
76 
77   message send(implementation_type& impl,
78       message& m)
79   {
80     DBusError error;
81     dbus_error_init(&error);
82     return message(dbus_connection_send_with_reply_and_block(impl,
83         m, -1, &error));
84         //TODO deal with that error
85   }
86 
87   template <typename Duration>
88   message send(implementation_type& impl,
89       message& m,
90       const Duration& timeout)
91   {
92     //TODO generically convert timeout to milliseconds
93     if(timeout == Duration::zero()) {
94       //TODO this can return false if it failed
95       dbus_connection_send(impl, m, &m.serial);
96       return message();
97     } else {
98       DBusError error;
99       dbus_error_init(&error);
100       return message(dbus_connection_send_with_reply_and_block(impl,
101           m, chrono::milliseconds(timeout).count(), &error));
102           //TODO deal with that error
103     }
104   }
105 
106   template<typename MessageHandler>
107   inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,
108       void(boost::system::error_code, message))
109   async_send(implementation_type& impl,
110       message& m,
111       BOOST_ASIO_MOVE_ARG(MessageHandler) handler)
112   {
113     DBusPendingCall *p;
114     dbus_connection_send_with_reply(impl,
115         m, &p, -1);
116     /*
117     dbus_pending_call_set_notify(p,
118         &pending_call_notify, &get_io_service(), NULL);
119 
120     //FIXME Race condition: another thread might have
121     // processed the pending call's reply before a notify
122     // function could be set. If so, the notify function
123     // will never trigger, so it must be called manually:
124     if(dbus_pending_call_get_completed(p))
125     {
126       //TODO: does this work, or might it call the notify
127       // function too many times? Might have to use steal_reply
128       pending_call_notify(p, &get_io_service());
129     }
130     */
131   }
132 
133   void new_match(implementation_type& impl,
134       match& m)
135   {
136     DBusError err;
137     dbus_error_init(&err);
138     dbus_bus_add_match(impl, m.get_expression().c_str(), &err);
139     //TODO deal with that error
140     // eventually, for complete asynchronicity, this should connect to
141     // org.freedesktop.DBus and call org.freedesktop.DBus.AddMatch
142   }
143 
144   void delete_match(implementation_type& impl,
145       match& m)
146   {
147     DBusError err;
148     dbus_error_init(&err);
149     dbus_bus_remove_match(impl, m.get_expression().c_str(), &err);
150   }
151 
152   static DBusHandlerResult filter_wrap(
153       DBusConnection *c,
154       DBusMessage *m,
155       void *userdata)
156   {
157     try
158     {
159       filter& f = *static_cast<filter *>(userdata);
160       if(f.offer(message(m)))
161       {
162         return DBUS_HANDLER_RESULT_HANDLED;
163       }
164     } catch(...) {
165       // do not throw in C callbacks. Just don't.
166     }
167 
168     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
169   }
170 
171   void new_filter(implementation_type& impl,
172       filter& f)
173   {
174     dbus_connection_add_filter(impl,
175         &filter_wrap, &f, NULL);
176   }
177 
178   void delete_filter(implementation_type& impl,
179       filter& f)
180   {
181     dbus_connection_remove_filter(impl,
182         &filter_wrap, &f);
183   }
184 };
185 
186 io_service::id connection_service::id;
187 
188 
189 
190 
191 } // namespace dbus
192 
193 #endif // DBUS_CONNECTION_SERVICE_HPP
194