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 <stdlib.h>
27#include <stdio.h>
28#include <string.h>
29
30#include <dbus/dbus.h>
31
32#include <avahi-client/client.h>
33#include <avahi-common/dbus.h>
34#include <avahi-common/llist.h>
35#include <avahi-common/error.h>
36#include <avahi-common/malloc.h>
37#include <avahi-common/domain.h>
38
39#include "client.h"
40#include "internal.h"
41#include "xdg-config.h"
42
43static void parse_environment(AvahiDomainBrowser *b) {
44    char buf[AVAHI_DOMAIN_NAME_MAX*3], *e, *t, *p;
45
46    assert(b);
47
48    if (!(e = getenv("AVAHI_BROWSE_DOMAINS")))
49        return;
50
51    snprintf(buf, sizeof(buf), "%s", e);
52
53    for (t = strtok_r(buf, ":", &p); t; t = strtok_r(NULL, ":", &p)) {
54        char domain[AVAHI_DOMAIN_NAME_MAX];
55        if (avahi_normalize_name(t, domain, sizeof(domain)))
56            b->static_browse_domains = avahi_string_list_add(b->static_browse_domains, domain);
57    }
58}
59
60static void parse_domain_file(AvahiDomainBrowser *b) {
61    FILE *f;
62    char buf[AVAHI_DOMAIN_NAME_MAX];
63
64    assert(b);
65
66    if (!(f = avahi_xdg_config_open("avahi/browse-domains")))
67        return;
68
69
70    while (fgets(buf, sizeof(buf)-1, f)) {
71        char domain[AVAHI_DOMAIN_NAME_MAX];
72        buf[strcspn(buf, "\n\r")] = 0;
73
74        if (avahi_normalize_name(buf, domain, sizeof(domain)))
75            b->static_browse_domains = avahi_string_list_add(b->static_browse_domains, domain);
76    }
77}
78
79static void domain_browser_ref(AvahiDomainBrowser *db) {
80    assert(db);
81    assert(db->ref >= 1);
82    db->ref++;
83}
84
85static void defer_timeout_callback(AvahiTimeout *t, void *userdata) {
86    AvahiDomainBrowser *db = userdata;
87    AvahiStringList *l;
88    assert(t);
89
90    db->client->poll_api->timeout_free(db->defer_timeout);
91    db->defer_timeout = NULL;
92
93    domain_browser_ref(db);
94
95    for (l = db->static_browse_domains; l; l = l->next) {
96
97        if (db->ref <= 1)
98            break;
99
100        db->callback(db, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, AVAHI_BROWSER_NEW, (char*) l->text, AVAHI_LOOKUP_RESULT_STATIC, db->userdata);
101    }
102
103    avahi_domain_browser_free(db);
104}
105
106AvahiDomainBrowser* avahi_domain_browser_new(
107    AvahiClient *client,
108    AvahiIfIndex interface,
109    AvahiProtocol protocol,
110    const char *domain,
111    AvahiDomainBrowserType btype,
112    AvahiLookupFlags flags,
113    AvahiDomainBrowserCallback callback,
114    void *userdata) {
115
116    AvahiDomainBrowser *db = NULL;
117    DBusMessage *message = NULL, *reply = NULL;
118    DBusError error;
119    char *path;
120    int32_t i_interface, i_protocol, bt;
121    uint32_t u_flags;
122
123    assert(client);
124    assert(callback);
125
126    dbus_error_init (&error);
127
128    if (!avahi_client_is_connected(client)) {
129        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
130        goto fail;
131    }
132
133    if (!domain)
134        domain = "";
135
136    if (!(db = avahi_new (AvahiDomainBrowser, 1))) {
137        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
138        goto fail;
139    }
140
141    db->ref = 1;
142    db->client = client;
143    db->callback = callback;
144    db->userdata = userdata;
145    db->path = NULL;
146    db->interface = interface;
147    db->protocol = protocol;
148    db->static_browse_domains = NULL;
149    db->defer_timeout = NULL;
150
151    AVAHI_LLIST_PREPEND(AvahiDomainBrowser, domain_browsers, client->domain_browsers, db);
152
153    if (!(client->flags & AVAHI_CLIENT_IGNORE_USER_CONFIG)) {
154        parse_environment(db);
155        parse_domain_file(db);
156    }
157
158    db->static_browse_domains = avahi_string_list_reverse(db->static_browse_domains);
159
160    if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "DomainBrowserNew"))) {
161        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
162        goto fail;
163    }
164
165    i_interface = (int32_t) interface;
166    i_protocol = (int32_t) protocol;
167    u_flags = (uint32_t) flags;
168    bt = btype;
169
170    if (!(dbus_message_append_args(
171              message,
172              DBUS_TYPE_INT32, &i_interface,
173              DBUS_TYPE_INT32, &i_protocol,
174              DBUS_TYPE_STRING, &domain,
175              DBUS_TYPE_INT32, &bt,
176              DBUS_TYPE_UINT32, &flags,
177              DBUS_TYPE_INVALID))) {
178        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
179        goto fail;
180    }
181
182    if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
183        dbus_error_is_set(&error)) {
184        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
185        goto fail;
186    }
187
188    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
189        dbus_error_is_set(&error) ||
190        !path) {
191        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
192        goto fail;
193    }
194
195    if (!(db->path = avahi_strdup(path))) {
196
197        /* FIXME: We don't remove the object on the server side */
198
199        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
200        goto fail;
201    }
202
203    if (db->static_browse_domains && btype == AVAHI_DOMAIN_BROWSER_BROWSE) {
204        struct timeval tv = { 0, 0 };
205
206        if (!(db->defer_timeout = client->poll_api->timeout_new(client->poll_api, &tv, defer_timeout_callback, db))) {
207            avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
208            goto fail;
209        }
210    }
211
212    dbus_message_unref(message);
213    dbus_message_unref(reply);
214
215    return db;
216
217fail:
218
219    if (dbus_error_is_set(&error)) {
220        avahi_client_set_dbus_error(client, &error);
221        dbus_error_free(&error);
222    }
223
224    if (db)
225        avahi_domain_browser_free(db);
226
227    if (message)
228        dbus_message_unref(message);
229
230    if (reply)
231        dbus_message_unref(reply);
232
233    return NULL;
234}
235
236AvahiClient* avahi_domain_browser_get_client (AvahiDomainBrowser *b) {
237    assert(b);
238    return b->client;
239}
240
241int avahi_domain_browser_free (AvahiDomainBrowser *b) {
242    AvahiClient *client;
243    int r = AVAHI_OK;
244
245    assert(b);
246    assert(b->ref >= 1);
247
248    if (--(b->ref) >= 1)
249        return AVAHI_OK;
250
251    client = b->client;
252
253    if (b->path && avahi_client_is_connected(b->client))
254        r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "Free");
255
256    AVAHI_LLIST_REMOVE(AvahiDomainBrowser, domain_browsers, client->domain_browsers, b);
257
258    if (b->defer_timeout)
259        b->client->poll_api->timeout_free(b->defer_timeout);
260
261    avahi_string_list_free(b->static_browse_domains);
262    avahi_free(b->path);
263    avahi_free(b);
264
265    return r;
266}
267
268DBusHandlerResult avahi_domain_browser_event (AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
269    AvahiDomainBrowser *db = NULL;
270    DBusError error;
271    const char *path;
272    char *domain = NULL;
273    int32_t interface, protocol;
274    uint32_t flags = 0;
275    AvahiStringList *l;
276
277    assert(client);
278    assert(message);
279
280    dbus_error_init (&error);
281
282    if (!(path = dbus_message_get_path(message)))
283        goto fail;
284
285    for (db = client->domain_browsers; db; db = db->domain_browsers_next)
286        if (strcmp (db->path, path) == 0)
287            break;
288
289    if (!db)
290        goto fail;
291
292    interface = db->interface;
293    protocol = db->protocol;
294
295    switch (event) {
296        case AVAHI_BROWSER_NEW:
297        case AVAHI_BROWSER_REMOVE:
298
299            if (!dbus_message_get_args(
300                    message, &error,
301                    DBUS_TYPE_INT32, &interface,
302                    DBUS_TYPE_INT32, &protocol,
303                    DBUS_TYPE_STRING, &domain,
304                    DBUS_TYPE_UINT32, &flags,
305                    DBUS_TYPE_INVALID) ||
306                dbus_error_is_set (&error)) {
307                fprintf(stderr, "Failed to parse browser event.\n");
308                goto fail;
309            }
310
311            break;
312
313        case AVAHI_BROWSER_CACHE_EXHAUSTED:
314        case AVAHI_BROWSER_ALL_FOR_NOW:
315            break;
316
317        case AVAHI_BROWSER_FAILURE: {
318            char *etxt;
319
320            if (!dbus_message_get_args(
321                    message, &error,
322                    DBUS_TYPE_STRING, &etxt,
323                    DBUS_TYPE_INVALID) ||
324                dbus_error_is_set (&error)) {
325                fprintf(stderr, "Failed to parse browser event.\n");
326                goto fail;
327            }
328
329            avahi_client_set_errno(db->client, avahi_error_dbus_to_number(etxt));
330            break;
331        }
332    }
333
334    if (domain)
335        for (l = db->static_browse_domains; l; l = l->next)
336            if (avahi_domain_equal((char*) l->text, domain)) {
337                /* We had this entry already in the static entries */
338                return DBUS_HANDLER_RESULT_HANDLED;
339            }
340
341    db->callback(db, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, domain, (AvahiLookupResultFlags) flags, db->userdata);
342
343    return DBUS_HANDLER_RESULT_HANDLED;
344
345fail:
346    dbus_error_free (&error);
347    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
348}
349
350/* AvahiServiceTypeBrowser */
351
352AvahiServiceTypeBrowser* avahi_service_type_browser_new(
353    AvahiClient *client,
354    AvahiIfIndex interface,
355    AvahiProtocol protocol,
356    const char *domain,
357    AvahiLookupFlags flags,
358    AvahiServiceTypeBrowserCallback callback,
359    void *userdata) {
360
361    AvahiServiceTypeBrowser *b = NULL;
362    DBusMessage *message = NULL, *reply = NULL;
363    DBusError error;
364    char *path;
365    int32_t i_interface, i_protocol;
366    uint32_t u_flags;
367
368    assert(client);
369    assert(callback);
370
371    dbus_error_init(&error);
372
373    if (!avahi_client_is_connected(client)) {
374        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
375        goto fail;
376    }
377
378    if (!domain)
379        domain = "";
380
381    if (!(b = avahi_new(AvahiServiceTypeBrowser, 1))) {
382        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
383        goto fail;
384    }
385
386    b->client = client;
387    b->callback = callback;
388    b->userdata = userdata;
389    b->path = NULL;
390    b->domain = NULL;
391    b->interface = interface;
392    b->protocol = protocol;
393
394    AVAHI_LLIST_PREPEND(AvahiServiceTypeBrowser, service_type_browsers, client->service_type_browsers, b);
395
396    if (domain[0])
397        if (!(b->domain = avahi_strdup(domain))) {
398            avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
399            goto fail;
400        }
401
402    if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "ServiceTypeBrowserNew"))) {
403        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
404        goto fail;
405    }
406
407    i_interface = (int32_t) interface;
408    i_protocol = (int32_t) protocol;
409    u_flags = (uint32_t) flags;
410
411    if (!dbus_message_append_args(
412            message,
413            DBUS_TYPE_INT32, &i_interface,
414            DBUS_TYPE_INT32, &i_protocol,
415            DBUS_TYPE_STRING, &domain,
416            DBUS_TYPE_UINT32, &u_flags,
417            DBUS_TYPE_INVALID)) {
418        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
419        goto fail;
420    }
421
422    if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
423        dbus_error_is_set(&error)) {
424        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
425        goto fail;
426    }
427
428    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
429        dbus_error_is_set(&error) ||
430        !path) {
431        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
432        goto fail;
433    }
434
435    if (!(b->path = avahi_strdup(path))) {
436
437        /* FIXME: We don't remove the object on the server side */
438
439        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
440        goto fail;
441    }
442
443    dbus_message_unref(message);
444    dbus_message_unref(reply);
445
446    return b;
447
448fail:
449
450    if (dbus_error_is_set(&error)) {
451        avahi_client_set_dbus_error(client, &error);
452        dbus_error_free(&error);
453    }
454
455    if (b)
456        avahi_service_type_browser_free(b);
457
458    if (message)
459        dbus_message_unref(message);
460
461    if (reply)
462        dbus_message_unref(reply);
463
464    return NULL;
465}
466
467AvahiClient* avahi_service_type_browser_get_client (AvahiServiceTypeBrowser *b) {
468    assert(b);
469    return b->client;
470}
471
472int avahi_service_type_browser_free (AvahiServiceTypeBrowser *b) {
473    AvahiClient *client;
474    int r = AVAHI_OK;
475
476    assert(b);
477    client = b->client;
478
479    if (b->path && avahi_client_is_connected(b->client))
480        r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "Free");
481
482    AVAHI_LLIST_REMOVE(AvahiServiceTypeBrowser, service_type_browsers, b->client->service_type_browsers, b);
483
484    avahi_free(b->path);
485    avahi_free(b->domain);
486    avahi_free(b);
487    return r;
488}
489
490DBusHandlerResult avahi_service_type_browser_event (AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
491    AvahiServiceTypeBrowser *b = NULL;
492    DBusError error;
493    const char *path;
494    char *domain, *type = NULL;
495    int32_t interface, protocol;
496    uint32_t flags = 0;
497
498    assert(client);
499    assert(message);
500
501    dbus_error_init (&error);
502
503    if (!(path = dbus_message_get_path(message)))
504        goto fail;
505
506    for (b = client->service_type_browsers; b; b = b->service_type_browsers_next)
507        if (strcmp (b->path, path) == 0)
508            break;
509
510    if (!b)
511        goto fail;
512
513    domain = b->domain;
514    interface = b->interface;
515    protocol = b->protocol;
516
517    switch (event) {
518        case AVAHI_BROWSER_NEW:
519        case AVAHI_BROWSER_REMOVE:
520            if (!dbus_message_get_args(
521                    message, &error,
522                    DBUS_TYPE_INT32, &interface,
523                    DBUS_TYPE_INT32, &protocol,
524                    DBUS_TYPE_STRING, &type,
525                    DBUS_TYPE_STRING, &domain,
526                    DBUS_TYPE_UINT32, &flags,
527                    DBUS_TYPE_INVALID) ||
528                dbus_error_is_set(&error)) {
529                fprintf(stderr, "Failed to parse browser event.\n");
530                goto fail;
531            }
532            break;
533
534        case AVAHI_BROWSER_CACHE_EXHAUSTED:
535        case AVAHI_BROWSER_ALL_FOR_NOW:
536            break;
537
538        case AVAHI_BROWSER_FAILURE: {
539            char *etxt;
540
541            if (!dbus_message_get_args(
542                    message, &error,
543                    DBUS_TYPE_STRING, &etxt,
544                    DBUS_TYPE_INVALID) ||
545                dbus_error_is_set (&error)) {
546                fprintf(stderr, "Failed to parse browser event.\n");
547                goto fail;
548            }
549
550            avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
551            break;
552        }
553    }
554
555    b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, type, domain, (AvahiLookupResultFlags) flags, b->userdata);
556
557    return DBUS_HANDLER_RESULT_HANDLED;
558
559fail:
560    dbus_error_free (&error);
561    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
562}
563
564/* AvahiServiceBrowser */
565
566AvahiServiceBrowser* avahi_service_browser_new(
567    AvahiClient *client,
568    AvahiIfIndex interface,
569    AvahiProtocol protocol,
570    const char *type,
571    const char *domain,
572    AvahiLookupFlags flags,
573    AvahiServiceBrowserCallback callback,
574    void *userdata) {
575
576    AvahiServiceBrowser *b = NULL;
577    DBusMessage *message = NULL, *reply = NULL;
578    DBusError error;
579    char *path;
580    int32_t i_protocol, i_interface;
581    uint32_t u_flags;
582
583    assert(client);
584    assert(type);
585    assert(callback);
586
587    dbus_error_init(&error);
588
589    if (!avahi_client_is_connected(client)) {
590        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
591        goto fail;
592    }
593
594    if (!domain)
595        domain = "";
596
597    if (!(b = avahi_new(AvahiServiceBrowser, 1))) {
598        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
599        goto fail;
600    }
601
602    b->client = client;
603    b->callback = callback;
604    b->userdata = userdata;
605    b->path = NULL;
606    b->type = b->domain = NULL;
607    b->interface = interface;
608    b->protocol = protocol;
609
610    AVAHI_LLIST_PREPEND(AvahiServiceBrowser, service_browsers, client->service_browsers, b);
611
612    if (!(b->type = avahi_strdup(type))) {
613        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
614        goto fail;
615    }
616
617    if (domain && domain[0])
618        if (!(b->domain = avahi_strdup(domain))) {
619            avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
620            goto fail;
621        }
622
623    if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "ServiceBrowserNew"))) {
624        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
625        goto fail;
626    }
627
628    i_interface = (int32_t) interface;
629    i_protocol = (int32_t) protocol;
630    u_flags = (uint32_t) flags;
631
632    if (!dbus_message_append_args(
633            message,
634            DBUS_TYPE_INT32, &i_interface,
635            DBUS_TYPE_INT32, &i_protocol,
636            DBUS_TYPE_STRING, &type,
637            DBUS_TYPE_STRING, &domain,
638            DBUS_TYPE_UINT32, &u_flags,
639            DBUS_TYPE_INVALID)) {
640        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
641        goto fail;
642    }
643
644    if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
645        dbus_error_is_set(&error)) {
646        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
647        goto fail;
648    }
649
650    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
651        dbus_error_is_set(&error) ||
652        !path) {
653        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
654        goto fail;
655    }
656
657    if (!(b->path = avahi_strdup(path))) {
658
659        /* FIXME: We don't remove the object on the server side */
660
661        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
662        goto fail;
663    }
664
665    dbus_message_unref(message);
666    dbus_message_unref(reply);
667
668    return b;
669
670fail:
671    if (dbus_error_is_set(&error)) {
672        avahi_client_set_dbus_error(client, &error);
673        dbus_error_free(&error);
674    }
675
676    if (b)
677        avahi_service_browser_free(b);
678
679    if (message)
680        dbus_message_unref(message);
681
682    if (reply)
683        dbus_message_unref(reply);
684
685    return NULL;
686}
687
688AvahiClient* avahi_service_browser_get_client (AvahiServiceBrowser *b) {
689    assert(b);
690    return b->client;
691}
692
693int avahi_service_browser_free (AvahiServiceBrowser *b) {
694    AvahiClient *client;
695    int r = AVAHI_OK;
696
697    assert(b);
698    client = b->client;
699
700    if (b->path && avahi_client_is_connected(b->client))
701        r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "Free");
702
703    AVAHI_LLIST_REMOVE(AvahiServiceBrowser, service_browsers, b->client->service_browsers, b);
704
705    avahi_free(b->path);
706    avahi_free(b->type);
707    avahi_free(b->domain);
708    avahi_free(b);
709    return r;
710}
711
712DBusHandlerResult avahi_service_browser_event(AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
713    AvahiServiceBrowser *b = NULL;
714    DBusError error;
715    const char *path;
716    char *name = NULL, *type, *domain;
717    int32_t interface, protocol;
718    uint32_t flags = 0;
719
720    dbus_error_init (&error);
721
722    if (!(path = dbus_message_get_path(message)))
723        goto fail;
724
725    for (b = client->service_browsers; b; b = b->service_browsers_next)
726        if (strcmp (b->path, path) == 0)
727            break;
728
729    if (!b)
730        goto fail;
731
732    type = b->type;
733    domain = b->domain;
734    interface = b->interface;
735    protocol = b->protocol;
736
737    switch (event) {
738        case AVAHI_BROWSER_NEW:
739        case AVAHI_BROWSER_REMOVE:
740
741            if (!dbus_message_get_args (
742                    message, &error,
743                    DBUS_TYPE_INT32, &interface,
744                    DBUS_TYPE_INT32, &protocol,
745                    DBUS_TYPE_STRING, &name,
746                    DBUS_TYPE_STRING, &type,
747                    DBUS_TYPE_STRING, &domain,
748                    DBUS_TYPE_UINT32, &flags,
749                    DBUS_TYPE_INVALID) ||
750                dbus_error_is_set(&error)) {
751                fprintf(stderr, "Failed to parse browser event.\n");
752                goto fail;
753            }
754            break;
755
756        case AVAHI_BROWSER_CACHE_EXHAUSTED:
757        case AVAHI_BROWSER_ALL_FOR_NOW:
758            break;
759
760        case AVAHI_BROWSER_FAILURE: {
761            char *etxt;
762
763            if (!dbus_message_get_args(
764                    message, &error,
765                    DBUS_TYPE_STRING, &etxt,
766                    DBUS_TYPE_INVALID) ||
767                dbus_error_is_set (&error)) {
768                fprintf(stderr, "Failed to parse browser event.\n");
769                goto fail;
770            }
771
772            avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
773            break;
774        }
775    }
776
777    b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, name, type, domain, (AvahiLookupResultFlags) flags, b->userdata);
778
779    return DBUS_HANDLER_RESULT_HANDLED;
780
781fail:
782    dbus_error_free (&error);
783    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
784}
785
786/* AvahiRecordBrowser */
787
788AvahiRecordBrowser* avahi_record_browser_new(
789    AvahiClient *client,
790    AvahiIfIndex interface,
791    AvahiProtocol protocol,
792    const char *name,
793    uint16_t clazz,
794    uint16_t type,
795    AvahiLookupFlags flags,
796    AvahiRecordBrowserCallback callback,
797    void *userdata) {
798
799    AvahiRecordBrowser *b = NULL;
800    DBusMessage *message = NULL, *reply = NULL;
801    DBusError error;
802    char *path;
803    int32_t i_protocol, i_interface;
804    uint32_t u_flags;
805
806    assert(client);
807    assert(name);
808    assert(callback);
809
810    dbus_error_init(&error);
811
812    if (!avahi_client_is_connected(client)) {
813        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
814        goto fail;
815    }
816
817    if (!(b = avahi_new(AvahiRecordBrowser, 1))) {
818        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
819        goto fail;
820    }
821
822    b->client = client;
823    b->callback = callback;
824    b->userdata = userdata;
825    b->path = NULL;
826    b->name = NULL;
827    b->clazz = clazz;
828    b->type = type;
829    b->interface = interface;
830    b->protocol = protocol;
831
832    AVAHI_LLIST_PREPEND(AvahiRecordBrowser, record_browsers, client->record_browsers, b);
833
834    if (!(b->name = avahi_strdup(name))) {
835        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
836        goto fail;
837    }
838
839    if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "RecordBrowserNew"))) {
840        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
841        goto fail;
842    }
843
844    i_interface = (int32_t) interface;
845    i_protocol = (int32_t) protocol;
846    u_flags = (uint32_t) flags;
847
848    if (!dbus_message_append_args(
849            message,
850            DBUS_TYPE_INT32, &i_interface,
851            DBUS_TYPE_INT32, &i_protocol,
852            DBUS_TYPE_STRING, &name,
853            DBUS_TYPE_UINT16, &clazz,
854            DBUS_TYPE_UINT16, &type,
855            DBUS_TYPE_UINT32, &u_flags,
856            DBUS_TYPE_INVALID)) {
857        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
858        goto fail;
859    }
860
861    if (!(reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error)) ||
862        dbus_error_is_set(&error)) {
863        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
864        goto fail;
865    }
866
867    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) ||
868        dbus_error_is_set(&error) ||
869        !path) {
870        avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
871        goto fail;
872    }
873
874    if (!(b->path = avahi_strdup(path))) {
875
876        /* FIXME: We don't remove the object on the server side */
877
878        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
879        goto fail;
880    }
881
882    dbus_message_unref(message);
883    dbus_message_unref(reply);
884
885    return b;
886
887fail:
888    if (dbus_error_is_set(&error)) {
889        avahi_client_set_dbus_error(client, &error);
890        dbus_error_free(&error);
891    }
892
893    if (b)
894        avahi_record_browser_free(b);
895
896    if (message)
897        dbus_message_unref(message);
898
899    if (reply)
900        dbus_message_unref(reply);
901
902    return NULL;
903}
904
905AvahiClient* avahi_record_browser_get_client (AvahiRecordBrowser *b) {
906    assert(b);
907    return b->client;
908}
909
910int avahi_record_browser_free (AvahiRecordBrowser *b) {
911    AvahiClient *client;
912    int r = AVAHI_OK;
913
914    assert(b);
915    client = b->client;
916
917    if (b->path && avahi_client_is_connected(b->client))
918        r = avahi_client_simple_method_call(client, b->path, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "Free");
919
920    AVAHI_LLIST_REMOVE(AvahiRecordBrowser, record_browsers, b->client->record_browsers, b);
921
922    avahi_free(b->path);
923    avahi_free(b->name);
924    avahi_free(b);
925    return r;
926}
927
928DBusHandlerResult avahi_record_browser_event(AvahiClient *client, AvahiBrowserEvent event, DBusMessage *message) {
929    AvahiRecordBrowser *b = NULL;
930    DBusError error;
931    const char *path;
932    char *name;
933    int32_t interface, protocol;
934    uint32_t flags = 0;
935    uint16_t clazz, type;
936    void *rdata = NULL;
937    int rdata_size = 0;
938
939    dbus_error_init (&error);
940
941    if (!(path = dbus_message_get_path(message)))
942        goto fail;
943
944    for (b = client->record_browsers; b; b = b->record_browsers_next)
945        if (strcmp (b->path, path) == 0)
946            break;
947
948    if (!b)
949        goto fail;
950
951    interface = b->interface;
952    protocol = b->protocol;
953    clazz = b->clazz;
954    type = b->type;
955    name = b->name;
956
957    switch (event) {
958        case AVAHI_BROWSER_NEW:
959        case AVAHI_BROWSER_REMOVE: {
960            DBusMessageIter iter, sub;
961            int j;
962
963            if (!dbus_message_get_args (
964                    message, &error,
965                    DBUS_TYPE_INT32, &interface,
966                    DBUS_TYPE_INT32, &protocol,
967                    DBUS_TYPE_STRING, &name,
968                    DBUS_TYPE_UINT16, &clazz,
969                    DBUS_TYPE_UINT16, &type,
970                    DBUS_TYPE_INVALID) ||
971                dbus_error_is_set(&error)) {
972                fprintf(stderr, "Failed to parse browser event.\n");
973                goto fail;
974            }
975
976
977            dbus_message_iter_init(message, &iter);
978
979            for (j = 0; j < 5; j++)
980                dbus_message_iter_next(&iter);
981
982            if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
983                dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
984                goto fail;
985
986            dbus_message_iter_recurse(&iter, &sub);
987            dbus_message_iter_get_fixed_array(&sub, &rdata, &rdata_size);
988
989            dbus_message_iter_next(&iter);
990
991            if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
992                goto fail;
993
994            dbus_message_iter_get_basic(&iter, &flags);
995
996            break;
997        }
998
999        case AVAHI_BROWSER_CACHE_EXHAUSTED:
1000        case AVAHI_BROWSER_ALL_FOR_NOW:
1001            break;
1002
1003        case AVAHI_BROWSER_FAILURE: {
1004            char *etxt;
1005
1006            if (!dbus_message_get_args(
1007                    message, &error,
1008                    DBUS_TYPE_STRING, &etxt,
1009                    DBUS_TYPE_INVALID) ||
1010                dbus_error_is_set (&error)) {
1011                fprintf(stderr, "Failed to parse browser event.\n");
1012                goto fail;
1013            }
1014
1015            avahi_client_set_errno(b->client, avahi_error_dbus_to_number(etxt));
1016            break;
1017        }
1018    }
1019
1020    b->callback(b, (AvahiIfIndex) interface, (AvahiProtocol) protocol, event, name, clazz, type, rdata, (size_t) rdata_size, (AvahiLookupResultFlags) flags, b->userdata);
1021
1022    return DBUS_HANDLER_RESULT_HANDLED;
1023
1024fail:
1025    dbus_error_free (&error);
1026    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1027}
1028
1029