1/***
2  This file is part of avahi.
3
4  avahi is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Lesser General Public License as
6  published by the Free Software Foundation; either version 2.1 of the
7  License, or (at your option) any later version.
8
9  avahi is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12  Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public
15  License along with avahi; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  USA.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <assert.h>
25#include <stdio.h>
26
27#include "malloc.h"
28#include "timeval.h"
29#include "dbus-watch-glue.h"
30
31static AvahiWatchEvent translate_dbus_to_avahi(unsigned int f) {
32    AvahiWatchEvent e = 0;
33
34    if (f & DBUS_WATCH_READABLE)
35        e |= AVAHI_WATCH_IN;
36    if (f & DBUS_WATCH_WRITABLE)
37        e |= AVAHI_WATCH_OUT;
38    if (f & DBUS_WATCH_ERROR)
39        e |= AVAHI_WATCH_ERR;
40    if (f & DBUS_WATCH_HANGUP)
41        e |= AVAHI_WATCH_HUP;
42
43    return e;
44}
45
46static unsigned int translate_avahi_to_dbus(AvahiWatchEvent e) {
47    unsigned int f = 0;
48
49    if (e & AVAHI_WATCH_IN)
50        f |= DBUS_WATCH_READABLE;
51    if (e & AVAHI_WATCH_OUT)
52        f |= DBUS_WATCH_WRITABLE;
53    if (e & AVAHI_WATCH_ERR)
54        f |= DBUS_WATCH_ERROR;
55    if (e & AVAHI_WATCH_HUP)
56        f |= DBUS_WATCH_HANGUP;
57
58    return f;
59}
60
61typedef struct {
62    DBusConnection *connection;
63    const AvahiPoll *poll_api;
64    AvahiTimeout *dispatch_timeout;
65    int ref;
66} ConnectionData;
67
68static ConnectionData *connection_data_ref(ConnectionData *d) {
69    assert(d);
70    assert(d->ref >= 1);
71
72    d->ref++;
73    return d;
74}
75
76static void connection_data_unref(ConnectionData *d) {
77    assert(d);
78    assert(d->ref >= 1);
79
80    if (--d->ref <= 0) {
81        d->poll_api->timeout_free(d->dispatch_timeout);
82        avahi_free(d);
83    }
84}
85
86static void request_dispatch(ConnectionData *d, int enable) {
87    static const struct timeval tv = { 0, 0 };
88    assert(d);
89
90    if (enable) {
91        assert(dbus_connection_get_dispatch_status(d->connection) == DBUS_DISPATCH_DATA_REMAINS);
92        d->poll_api->timeout_update(d->dispatch_timeout, &tv);
93    } else
94        d->poll_api->timeout_update(d->dispatch_timeout, NULL);
95}
96
97static void dispatch_timeout_callback(AvahiTimeout *t, void *userdata) {
98    ConnectionData *d = userdata;
99    assert(t);
100    assert(d);
101
102    connection_data_ref(d);
103    dbus_connection_ref(d->connection);
104
105    if (dbus_connection_dispatch(d->connection) == DBUS_DISPATCH_DATA_REMAINS)
106        /* If there's still data, request that this handler is called again */
107        request_dispatch(d, 1);
108    else
109        request_dispatch(d, 0);
110
111    dbus_connection_unref(d->connection);
112    connection_data_unref(d);
113}
114
115static void watch_callback(AvahiWatch *avahi_watch, AVAHI_GCC_UNUSED int fd, AvahiWatchEvent events, void *userdata) {
116    DBusWatch *dbus_watch = userdata;
117
118    assert(avahi_watch);
119    assert(dbus_watch);
120
121    dbus_watch_handle(dbus_watch, translate_avahi_to_dbus(events));
122    /* Ignore the return value */
123}
124
125static dbus_bool_t update_watch(const AvahiPoll *poll_api, DBusWatch *dbus_watch) {
126    AvahiWatch *avahi_watch;
127    dbus_bool_t b;
128
129    assert(dbus_watch);
130
131    avahi_watch = dbus_watch_get_data(dbus_watch);
132
133    b = dbus_watch_get_enabled(dbus_watch);
134
135    if (b && !avahi_watch) {
136
137        if (!(avahi_watch = poll_api->watch_new(
138                  poll_api,
139#if (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR == 1 && DBUS_VERSION_MICRO >= 1) || (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR > 1) || (DBUS_VERSION_MAJOR > 1)
140                  dbus_watch_get_unix_fd(dbus_watch),
141#else
142                  dbus_watch_get_fd(dbus_watch),
143#endif
144                  translate_dbus_to_avahi(dbus_watch_get_flags(dbus_watch)),
145                  watch_callback,
146                  dbus_watch)))
147            return FALSE;
148
149        dbus_watch_set_data(dbus_watch, avahi_watch, NULL);
150
151    } else if (!b && avahi_watch) {
152
153        poll_api->watch_free(avahi_watch);
154        dbus_watch_set_data(dbus_watch, NULL, NULL);
155
156    } else if (avahi_watch) {
157
158        /* Update flags */
159        poll_api->watch_update(avahi_watch, dbus_watch_get_flags(dbus_watch));
160    }
161
162    return TRUE;
163}
164
165static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *userdata) {
166    ConnectionData *d = userdata;
167
168    assert(dbus_watch);
169    assert(d);
170
171    return update_watch(d->poll_api, dbus_watch);
172}
173
174static void remove_watch(DBusWatch *dbus_watch, void *userdata) {
175    ConnectionData *d = userdata;
176    AvahiWatch *avahi_watch;
177
178    assert(dbus_watch);
179    assert(d);
180
181    if ((avahi_watch = dbus_watch_get_data(dbus_watch))) {
182        d->poll_api->watch_free(avahi_watch);
183        dbus_watch_set_data(dbus_watch, NULL, NULL);
184    }
185}
186
187static void watch_toggled(DBusWatch *dbus_watch, void *userdata) {
188    ConnectionData *d = userdata;
189
190    assert(dbus_watch);
191    assert(d);
192
193    update_watch(d->poll_api, dbus_watch);
194}
195
196typedef struct TimeoutData {
197    const AvahiPoll *poll_api;
198    AvahiTimeout *avahi_timeout;
199    DBusTimeout *dbus_timeout;
200    int ref;
201} TimeoutData;
202
203static TimeoutData* timeout_data_ref(TimeoutData *t) {
204    assert(t);
205    assert(t->ref >= 1);
206
207    t->ref++;
208    return t;
209}
210
211static void timeout_data_unref(TimeoutData *t) {
212    assert(t);
213    assert(t->ref >= 1);
214
215    if (--t->ref <= 0) {
216        if (t->avahi_timeout)
217            t->poll_api->timeout_free(t->avahi_timeout);
218
219        avahi_free(t);
220    }
221}
222
223static void update_timeout(TimeoutData *timeout) {
224    assert(timeout);
225    assert(timeout->ref >= 1);
226
227    if (dbus_timeout_get_enabled(timeout->dbus_timeout)) {
228        struct timeval tv;
229        avahi_elapse_time(&tv, dbus_timeout_get_interval(timeout->dbus_timeout), 0);
230        timeout->poll_api->timeout_update(timeout->
231                                      avahi_timeout, &tv);
232    } else
233        timeout->poll_api->timeout_update(timeout->avahi_timeout, NULL);
234
235}
236
237static void timeout_callback(AvahiTimeout *avahi_timeout, void *userdata) {
238    TimeoutData *timeout = userdata;
239
240    assert(avahi_timeout);
241    assert(timeout);
242
243    timeout_data_ref(timeout);
244
245    dbus_timeout_handle(timeout->dbus_timeout);
246    /* Ignore the return value */
247
248    if (timeout->avahi_timeout)
249        update_timeout(timeout);
250
251    timeout_data_unref(timeout);
252}
253
254static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *userdata) {
255    TimeoutData *timeout;
256    ConnectionData *d = userdata;
257    struct timeval tv;
258    dbus_bool_t b;
259
260    assert(dbus_timeout);
261    assert(d);
262
263    if (!(timeout = avahi_new(TimeoutData, 1)))
264        return FALSE;
265
266    timeout->dbus_timeout = dbus_timeout;
267    timeout->poll_api = d->poll_api;
268    timeout->ref = 1;
269
270    if ((b = dbus_timeout_get_enabled(dbus_timeout)))
271        avahi_elapse_time(&tv, dbus_timeout_get_interval(dbus_timeout), 0);
272
273    if (!(timeout->avahi_timeout = d->poll_api->timeout_new(
274              d->poll_api,
275              b ? &tv : NULL,
276              timeout_callback,
277              timeout))) {
278        avahi_free(timeout);
279        return FALSE;
280    }
281
282    dbus_timeout_set_data(dbus_timeout, timeout, (DBusFreeFunction) timeout_data_unref);
283    return TRUE;
284}
285
286static void remove_timeout(DBusTimeout *dbus_timeout, void *userdata) {
287    ConnectionData *d = userdata;
288    TimeoutData *timeout;
289
290    assert(dbus_timeout);
291    assert(d);
292
293    timeout = dbus_timeout_get_data(dbus_timeout);
294    assert(timeout);
295
296    d->poll_api->timeout_free(timeout->avahi_timeout);
297    timeout->avahi_timeout = NULL;
298}
299
300static void timeout_toggled(DBusTimeout *dbus_timeout, AVAHI_GCC_UNUSED void *userdata) {
301    TimeoutData *timeout;
302
303    assert(dbus_timeout);
304    timeout = dbus_timeout_get_data(dbus_timeout);
305    assert(timeout);
306
307    update_timeout(timeout);
308}
309
310static void dispatch_status(AVAHI_GCC_UNUSED DBusConnection *connection, DBusDispatchStatus new_status, void *userdata) {
311    ConnectionData *d = userdata;
312
313    if (new_status == DBUS_DISPATCH_DATA_REMAINS)
314        request_dispatch(d, 1);
315 }
316
317int avahi_dbus_connection_glue(DBusConnection *c, const AvahiPoll *poll_api) {
318    ConnectionData *d = NULL;
319
320    assert(c);
321    assert(poll_api);
322
323    if (!(d = avahi_new(ConnectionData, 1)))
324        goto fail;;
325
326    d->poll_api = poll_api;
327    d->connection = c;
328    d->ref = 1;
329
330    if (!(d->dispatch_timeout = poll_api->timeout_new(poll_api, NULL, dispatch_timeout_callback, d)))
331        goto fail;
332
333    if (!(dbus_connection_set_watch_functions(c, add_watch, remove_watch, watch_toggled, connection_data_ref(d), (DBusFreeFunction)connection_data_unref)))
334        goto fail;
335
336    if (!(dbus_connection_set_timeout_functions(c, add_timeout, remove_timeout, timeout_toggled, connection_data_ref(d), (DBusFreeFunction)connection_data_unref)))
337        goto fail;
338
339    dbus_connection_set_dispatch_status_function(c, dispatch_status, connection_data_ref(d), (DBusFreeFunction)connection_data_unref);
340
341    if (dbus_connection_get_dispatch_status(c) == DBUS_DISPATCH_DATA_REMAINS)
342        request_dispatch(d, 1);
343
344    connection_data_unref(d);
345
346    return 0;
347
348fail:
349
350    if (d) {
351        d->poll_api->timeout_free(d->dispatch_timeout);
352
353        avahi_free(d);
354    }
355
356    return -1;
357}
358