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