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 <stdlib.h>
28#include <sys/stat.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <errno.h>
32#include <string.h>
33
34#include <avahi-common/error.h>
35#include <avahi-common/dbus.h>
36#include <avahi-common/malloc.h>
37#include <avahi-core/log.h>
38#include <avahi-core/core.h>
39
40#ifdef ENABLE_CHROOT
41#include "chroot.h"
42#endif
43
44#include "main.h"
45#include "dbus-util.h"
46
47DBusHandlerResult avahi_dbus_respond_error(DBusConnection *c, DBusMessage *m, int error, const char *text) {
48    DBusMessage *reply;
49
50    assert(-error > -AVAHI_OK);
51    assert(-error < -AVAHI_ERR_MAX);
52
53    if (!text)
54        text = avahi_strerror(error);
55
56    reply = dbus_message_new_error(m, avahi_error_number_to_dbus(error), text);
57    dbus_connection_send(c, reply, NULL);
58    dbus_message_unref(reply);
59
60    avahi_log_debug(__FILE__": Responding error '%s' (%i)", text, error);
61
62    return DBUS_HANDLER_RESULT_HANDLED;
63}
64
65DBusHandlerResult avahi_dbus_respond_string(DBusConnection *c, DBusMessage *m, const char *text) {
66    DBusMessage *reply;
67
68    reply = dbus_message_new_method_return(m);
69    dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
70    dbus_connection_send(c, reply, NULL);
71    dbus_message_unref(reply);
72
73    return DBUS_HANDLER_RESULT_HANDLED;
74}
75
76DBusHandlerResult avahi_dbus_respond_int32(DBusConnection *c, DBusMessage *m, int32_t i) {
77    DBusMessage *reply;
78
79    reply = dbus_message_new_method_return(m);
80    dbus_message_append_args(reply, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID);
81    dbus_connection_send(c, reply, NULL);
82    dbus_message_unref(reply);
83
84    return DBUS_HANDLER_RESULT_HANDLED;
85}
86
87DBusHandlerResult avahi_dbus_respond_uint32(DBusConnection *c, DBusMessage *m, uint32_t u) {
88    DBusMessage *reply;
89
90    reply = dbus_message_new_method_return(m);
91    dbus_message_append_args(reply, DBUS_TYPE_UINT32, &u, DBUS_TYPE_INVALID);
92    dbus_connection_send(c, reply, NULL);
93    dbus_message_unref(reply);
94
95    return DBUS_HANDLER_RESULT_HANDLED;
96}
97
98DBusHandlerResult avahi_dbus_respond_boolean(DBusConnection *c, DBusMessage *m, int b) {
99    DBusMessage *reply;
100
101    reply = dbus_message_new_method_return(m);
102    dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &b, DBUS_TYPE_INVALID);
103    dbus_connection_send(c, reply, NULL);
104    dbus_message_unref(reply);
105
106    return DBUS_HANDLER_RESULT_HANDLED;
107}
108
109DBusHandlerResult avahi_dbus_respond_ok(DBusConnection *c, DBusMessage *m) {
110    DBusMessage *reply;
111
112    reply = dbus_message_new_method_return(m);
113    dbus_connection_send(c, reply, NULL);
114    dbus_message_unref(reply);
115
116    return DBUS_HANDLER_RESULT_HANDLED;
117}
118
119DBusHandlerResult avahi_dbus_respond_path(DBusConnection *c, DBusMessage *m, const char *path) {
120    DBusMessage *reply;
121
122    reply = dbus_message_new_method_return(m);
123    dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
124    dbus_connection_send(c, reply, NULL);
125    dbus_message_unref(reply);
126
127    return DBUS_HANDLER_RESULT_HANDLED;
128}
129
130void avahi_dbus_append_server_error(DBusMessage *reply) {
131    const char *t;
132
133    t = avahi_error_number_to_dbus(avahi_server_errno(avahi_server));
134
135    dbus_message_append_args(
136        reply,
137        DBUS_TYPE_STRING, &t,
138        DBUS_TYPE_INVALID);
139}
140
141const char *avahi_dbus_map_browse_signal_name(AvahiBrowserEvent e) {
142    switch (e) {
143        case AVAHI_BROWSER_NEW : return "ItemNew";
144        case AVAHI_BROWSER_REMOVE : return "ItemRemove";
145        case AVAHI_BROWSER_FAILURE : return "Failure";
146        case AVAHI_BROWSER_CACHE_EXHAUSTED : return "CacheExhausted";
147        case AVAHI_BROWSER_ALL_FOR_NOW : return "AllForNow";
148    }
149
150    abort();
151}
152
153const char *avahi_dbus_map_resolve_signal_name(AvahiResolverEvent e) {
154    switch (e) {
155        case AVAHI_RESOLVER_FOUND : return "Found";
156        case AVAHI_RESOLVER_FAILURE : return "Failure";
157    }
158
159    abort();
160}
161
162static char *file_get_contents(const char *fname) {
163    int fd = -1;
164    struct stat st;
165    ssize_t size;
166    char *buf = NULL;
167
168    assert(fname);
169
170#ifdef ENABLE_CHROOT
171    fd = avahi_chroot_helper_get_fd(fname);
172#else
173    fd = open(fname, O_RDONLY);
174#endif
175
176    if (fd < 0) {
177        avahi_log_error("Failed to open %s: %s", fname, strerror(errno));
178        goto fail;
179    }
180
181    if (fstat(fd, &st) < 0) {
182        avahi_log_error("stat(%s) failed: %s", fname, strerror(errno));
183        goto fail;
184    }
185
186    if (!(S_ISREG(st.st_mode))) {
187        avahi_log_error("Invalid file %s", fname);
188        goto fail;
189    }
190
191    if (st.st_size > 1024*1024) { /** 1MB */
192        avahi_log_error("File too large %s", fname);
193        goto fail;
194    }
195
196    buf = avahi_new(char, st.st_size+1);
197
198    if ((size = read(fd, buf, st.st_size)) < 0) {
199        avahi_log_error("read() failed: %s\n", strerror(errno));
200        goto fail;
201    }
202
203    buf[size] = 0;
204
205    close(fd);
206
207    return buf;
208
209fail:
210    if (fd >= 0)
211        close(fd);
212
213    if (buf)
214        avahi_free(buf);
215
216    return NULL;
217
218}
219
220DBusHandlerResult avahi_dbus_handle_introspect(DBusConnection *c, DBusMessage *m, const char *fname) {
221    char *contents, *path;
222    DBusError error;
223
224    assert(c);
225    assert(m);
226    assert(fname);
227
228    dbus_error_init(&error);
229
230    if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
231        avahi_log_error("Error parsing Introspect message: %s", error.message);
232        goto fail;
233    }
234
235    path = avahi_strdup_printf("%s/%s", AVAHI_DBUS_INTROSPECTION_DIR, fname);
236    contents = file_get_contents(path);
237    avahi_free(path);
238
239    if (!contents) {
240        avahi_log_error("Failed to load introspection data.");
241        goto fail;
242    }
243
244    avahi_dbus_respond_string(c, m, contents);
245    avahi_free(contents);
246
247    return DBUS_HANDLER_RESULT_HANDLED;
248
249fail:
250    if (dbus_error_is_set(&error))
251        dbus_error_free(&error);
252
253    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
254
255}
256
257void avahi_dbus_append_string_list(DBusMessage *reply, AvahiStringList *txt) {
258    AvahiStringList *p;
259    DBusMessageIter iter, sub;
260
261    assert(reply);
262
263    dbus_message_iter_init_append(reply, &iter);
264    dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "ay", &sub);
265
266    for (p = txt; p; p = p->next) {
267        DBusMessageIter sub2;
268        const uint8_t *data = p->text;
269
270        dbus_message_iter_open_container(&sub, DBUS_TYPE_ARRAY, "y", &sub2);
271        dbus_message_iter_append_fixed_array(&sub2, DBUS_TYPE_BYTE, &data, p->size);
272        dbus_message_iter_close_container(&sub, &sub2);
273
274    }
275    dbus_message_iter_close_container(&iter, &sub);
276}
277
278int avahi_dbus_read_rdata(DBusMessage *m, int idx, void **rdata, uint32_t *size) {
279    DBusMessageIter iter, sub;
280    int n, j;
281    uint8_t *k;
282
283    assert(m);
284
285    dbus_message_iter_init(m, &iter);
286
287    for (j = 0; j < idx; j++)
288       dbus_message_iter_next(&iter);
289
290    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
291        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
292        goto fail;
293
294    dbus_message_iter_recurse(&iter, &sub);
295    dbus_message_iter_get_fixed_array(&sub, &k, &n);
296
297    *rdata = k;
298    *size = n;
299
300    return 0;
301
302fail:
303    avahi_log_warn("Error parsing data");
304
305    *rdata = NULL;
306    size = 0;
307    return -1;
308}
309
310int avahi_dbus_read_strlst(DBusMessage *m, int idx, AvahiStringList **l) {
311    DBusMessageIter iter, sub;
312    int j;
313    AvahiStringList *strlst = NULL;
314
315    assert(m);
316    assert(l);
317
318    dbus_message_iter_init(m, &iter);
319
320    for (j = 0; j < idx; j++)
321        dbus_message_iter_next(&iter);
322
323    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
324        dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_ARRAY)
325        goto fail;
326
327    dbus_message_iter_recurse(&iter, &sub);
328
329    for (;;) {
330        int at, n;
331        const uint8_t *k;
332        DBusMessageIter sub2;
333
334        if ((at = dbus_message_iter_get_arg_type(&sub)) == DBUS_TYPE_INVALID)
335            break;
336
337        assert(at == DBUS_TYPE_ARRAY);
338
339        if (dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_BYTE)
340            goto fail;
341
342        dbus_message_iter_recurse(&sub, &sub2);
343
344        k = (const uint8_t*) "";
345        n = 0;
346        dbus_message_iter_get_fixed_array(&sub2, &k, &n);
347
348        if (!k)
349            k = (const uint8_t*) "";
350
351        strlst = avahi_string_list_add_arbitrary(strlst, k, n);
352
353        dbus_message_iter_next(&sub);
354    }
355
356    *l = strlst;
357
358    return 0;
359
360fail:
361    avahi_log_warn("Error parsing TXT data");
362
363    avahi_string_list_free(strlst);
364    *l = NULL;
365    return -1;
366}
367
368int avahi_dbus_is_our_own_service(Client *c, AvahiIfIndex interface, AvahiProtocol protocol, const char *name, const char *type, const char *domain) {
369    AvahiSEntryGroup *g;
370
371    if (avahi_server_get_group_of_service(avahi_server, interface, protocol, name, type, domain, &g) == AVAHI_OK) {
372        EntryGroupInfo *egi;
373
374        for (egi = c->entry_groups; egi; egi = egi->entry_groups_next)
375            if (egi->entry_group == g)
376                return 1;
377    }
378
379    return 0;
380}
381
382int avahi_dbus_append_rdata(DBusMessage *message, const void *rdata, size_t size) {
383    DBusMessageIter iter, sub;
384
385    assert(message);
386
387    dbus_message_iter_init_append(message, &iter);
388
389    if (!(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE_AS_STRING, &sub)) ||
390        !(dbus_message_iter_append_fixed_array(&sub, DBUS_TYPE_BYTE, &rdata, size)) ||
391        !(dbus_message_iter_close_container(&iter, &sub)))
392        return -1;
393
394    return 0;
395}
396