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