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 
parse_environment(AvahiDomainBrowser * b)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 
parse_domain_file(AvahiDomainBrowser * b)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 
domain_browser_ref(AvahiDomainBrowser * db)77 static void domain_browser_ref(AvahiDomainBrowser *db) {
78     assert(db);
79     assert(db->ref >= 1);
80     db->ref++;
81 }
82 
defer_timeout_callback(AvahiTimeout * t,void * userdata)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 
avahi_domain_browser_new(AvahiClient * client,AvahiIfIndex interface,AvahiProtocol protocol,const char * domain,AvahiDomainBrowserType btype,AvahiLookupFlags flags,AvahiDomainBrowserCallback callback,void * userdata)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 
avahi_domain_browser_get_client(AvahiDomainBrowser * b)234 AvahiClient* avahi_domain_browser_get_client (AvahiDomainBrowser *b) {
235     assert(b);
236     return b->client;
237 }
238 
avahi_domain_browser_free(AvahiDomainBrowser * b)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 
avahi_domain_browser_event(AvahiClient * client,AvahiBrowserEvent event,DBusMessage * message)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 
avahi_service_type_browser_new(AvahiClient * client,AvahiIfIndex interface,AvahiProtocol protocol,const char * domain,AvahiLookupFlags flags,AvahiServiceTypeBrowserCallback callback,void * userdata)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 
avahi_service_type_browser_get_client(AvahiServiceTypeBrowser * b)465 AvahiClient* avahi_service_type_browser_get_client (AvahiServiceTypeBrowser *b) {
466     assert(b);
467     return b->client;
468 }
469 
avahi_service_type_browser_free(AvahiServiceTypeBrowser * b)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 
avahi_service_type_browser_event(AvahiClient * client,AvahiBrowserEvent event,DBusMessage * message)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 
avahi_service_browser_new(AvahiClient * client,AvahiIfIndex interface,AvahiProtocol protocol,const char * type,const char * domain,AvahiLookupFlags flags,AvahiServiceBrowserCallback callback,void * userdata)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 
avahi_service_browser_get_client(AvahiServiceBrowser * b)686 AvahiClient* avahi_service_browser_get_client (AvahiServiceBrowser *b) {
687     assert(b);
688     return b->client;
689 }
690 
avahi_service_browser_free(AvahiServiceBrowser * b)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 
avahi_service_browser_event(AvahiClient * client,AvahiBrowserEvent event,DBusMessage * message)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 
avahi_record_browser_new(AvahiClient * client,AvahiIfIndex interface,AvahiProtocol protocol,const char * name,uint16_t clazz,uint16_t type,AvahiLookupFlags flags,AvahiRecordBrowserCallback callback,void * userdata)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 
avahi_record_browser_get_client(AvahiRecordBrowser * b)903 AvahiClient* avahi_record_browser_get_client (AvahiRecordBrowser *b) {
904     assert(b);
905     return b->client;
906 }
907 
avahi_record_browser_free(AvahiRecordBrowser * b)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 
avahi_record_browser_event(AvahiClient * client,AvahiBrowserEvent event,DBusMessage * message)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 }
1026