]> git.meshlink.io Git - catta/blob - avahi-daemon/dbus-protocol.c
DBUS: implement ServiceBrowser
[catta] / avahi-daemon / dbus-protocol.c
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 <glib.h>
27 #include <string.h>
28
29 #define DBUS_API_SUBJECT_TO_CHANGE
30 #include <dbus/dbus.h>
31 #include <dbus/dbus-glib-lowlevel.h>
32
33 #include <avahi-core/llist.h>
34 #include <avahi-core/log.h>
35 #include <avahi-core/core.h>
36
37 #include "dbus-protocol.h"
38 #include "main.h"
39
40 #define AVAHI_DBUS_NAME "org.freedesktop.Avahi"
41 #define AVAHI_DBUS_INTERFACE_SERVER AVAHI_DBUS_NAME".Server"
42 #define AVAHI_DBUS_PATH_SERVER "/org/freedesktop/Avahi/Server"
43 #define AVAHI_DBUS_INTERFACE_ENTRY_GROUP AVAHI_DBUS_NAME".EntryGroup"
44 #define AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER AVAHI_DBUS_NAME".DomainBrowser"
45 #define AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER AVAHI_DBUS_NAME".ServiceTypeBrowser"
46 #define AVAHI_DBUS_INTERFACE_SERVICE_BROWSER AVAHI_DBUS_NAME".ServiceBrowser"
47
48 /* Needs wrapping:
49    - AvahiServiceResolver */
50
51 typedef struct Server Server;
52 typedef struct Client Client;
53 typedef struct EntryGroupInfo EntryGroupInfo;
54 typedef struct HostNameResolverInfo HostNameResolverInfo;
55 typedef struct AddressResolverInfo AddressResolverInfo;
56 typedef struct DomainBrowserInfo DomainBrowserInfo;
57 typedef struct ServiceTypeBrowserInfo ServiceTypeBrowserInfo;
58 typedef struct ServiceBrowserInfo ServiceBrowserInfo;
59 typedef struct ServiceResolverInfo ServiceResolverInfo;
60
61 struct EntryGroupInfo {
62     guint id;
63     Client *client;
64     AvahiEntryGroup *entry_group;
65     gchar *path;
66     
67     AVAHI_LLIST_FIELDS(EntryGroupInfo, entry_groups);
68 };
69
70 struct HostNameResolverInfo {
71     Client *client;
72     AvahiHostNameResolver *host_name_resolver;
73     DBusMessage *message;
74
75     AVAHI_LLIST_FIELDS(HostNameResolverInfo, host_name_resolvers);
76 };
77
78 struct AddressResolverInfo {
79     Client *client;
80     AvahiAddressResolver *address_resolver;
81     DBusMessage *message;
82
83     AVAHI_LLIST_FIELDS(AddressResolverInfo, address_resolvers);
84 };
85
86 struct DomainBrowserInfo {
87     guint id;
88     Client *client;
89     AvahiDomainBrowser *domain_browser;
90     gchar *path;
91
92     AVAHI_LLIST_FIELDS(DomainBrowserInfo, domain_browsers);
93 };
94
95 struct ServiceTypeBrowserInfo {
96     guint id;
97     Client *client;
98     AvahiServiceTypeBrowser *service_type_browser;
99     gchar *path;
100
101     AVAHI_LLIST_FIELDS(ServiceTypeBrowserInfo, service_type_browsers);
102 };
103
104 struct ServiceBrowserInfo {
105     guint id;
106     Client *client;
107     AvahiServiceBrowser *service_browser;
108     gchar *path;
109
110     AVAHI_LLIST_FIELDS(ServiceBrowserInfo, service_browsers);
111 };
112
113 struct ServiceResolverInfo {
114     Client *client;
115     AvahiServiceResolver *service_resolver;
116     DBusMessage *message;
117
118     AVAHI_LLIST_FIELDS(ServiceResolverInfo, service_resolvers);
119 };
120
121 struct Client {
122     guint id;
123     gchar *name;
124     guint current_id;
125     
126     AVAHI_LLIST_FIELDS(Client, clients);
127     AVAHI_LLIST_HEAD(EntryGroupInfo, entry_groups);
128     AVAHI_LLIST_HEAD(HostNameResolverInfo, host_name_resolvers);
129     AVAHI_LLIST_HEAD(AddressResolverInfo, address_resolvers);
130     AVAHI_LLIST_HEAD(DomainBrowserInfo, domain_browsers);
131     AVAHI_LLIST_HEAD(ServiceTypeBrowserInfo, service_type_browsers);
132     AVAHI_LLIST_HEAD(ServiceBrowserInfo, service_browsers);
133     AVAHI_LLIST_HEAD(ServiceResolverInfo, service_resolvers);
134 };
135
136 struct Server {
137     DBusConnection *bus;
138     AVAHI_LLIST_HEAD(Client, clients);
139     guint current_id;
140 };
141
142 static Server *server = NULL;
143
144 static void entry_group_free(EntryGroupInfo *i) {
145     g_assert(i);
146
147     if (i->entry_group)
148         avahi_entry_group_free(i->entry_group);
149     dbus_connection_unregister_object_path(server->bus, i->path);
150     g_free(i->path);
151     AVAHI_LLIST_REMOVE(EntryGroupInfo, entry_groups, i->client->entry_groups, i);
152     g_free(i);
153  }
154
155 static void host_name_resolver_free(HostNameResolverInfo *i) {
156     g_assert(i);
157
158     if (i->host_name_resolver)
159         avahi_host_name_resolver_free(i->host_name_resolver);
160     dbus_message_unref(i->message);
161     AVAHI_LLIST_REMOVE(HostNameResolverInfo, host_name_resolvers, i->client->host_name_resolvers, i);
162     g_free(i);
163 }
164
165 static void address_resolver_free(AddressResolverInfo *i) {
166     g_assert(i);
167
168     if (i->address_resolver)
169         avahi_address_resolver_free(i->address_resolver);
170     dbus_message_unref(i->message);
171     AVAHI_LLIST_REMOVE(AddressResolverInfo, address_resolvers, i->client->address_resolvers, i);
172     g_free(i);
173 }
174
175 static void domain_browser_free(DomainBrowserInfo *i) {
176     g_assert(i);
177
178     if (i->domain_browser)
179         avahi_domain_browser_free(i->domain_browser);
180     dbus_connection_unregister_object_path(server->bus, i->path);
181     g_free(i->path);
182     AVAHI_LLIST_REMOVE(DomainBrowserInfo, domain_browsers, i->client->domain_browsers, i);
183     g_free(i);
184 }
185
186 static void service_type_browser_free(ServiceTypeBrowserInfo *i) {
187     g_assert(i);
188
189     if (i->service_type_browser)
190         avahi_service_type_browser_free(i->service_type_browser);
191     dbus_connection_unregister_object_path(server->bus, i->path);
192     g_free(i->path);
193     AVAHI_LLIST_REMOVE(ServiceTypeBrowserInfo, service_type_browsers, i->client->service_type_browsers, i);
194     g_free(i);
195 }
196
197 static void service_browser_free(ServiceBrowserInfo *i) {
198     g_assert(i);
199
200     if (i->service_browser)
201         avahi_service_browser_free(i->service_browser);
202     dbus_connection_unregister_object_path(server->bus, i->path);
203     g_free(i->path);
204     AVAHI_LLIST_REMOVE(ServiceBrowserInfo, service_browsers, i->client->service_browsers, i);
205     g_free(i);
206 }
207
208 static void service_resolver_free(ServiceResolverInfo *i) {
209     g_assert(i);
210
211     if (i->service_resolver)
212         avahi_service_resolver_free(i->service_resolver);
213     dbus_message_unref(i->message);
214     AVAHI_LLIST_REMOVE(ServiceResolverInfo, service_resolvers, i->client->service_resolvers, i);
215     g_free(i);
216 }
217
218 static void client_free(Client *c) {
219     
220     g_assert(server);
221     g_assert(c);
222
223     while (c->entry_groups)
224         entry_group_free(c->entry_groups);
225
226     while (c->host_name_resolvers)
227         host_name_resolver_free(c->host_name_resolvers);
228
229     while (c->address_resolvers)
230         address_resolver_free(c->address_resolvers);
231
232     while (c->domain_browsers)
233         domain_browser_free(c->domain_browsers);
234
235     while (c->service_type_browsers)
236         service_type_browser_free(c->service_type_browsers);
237
238     while (c->service_browsers)
239         service_browser_free(c->service_browsers);
240
241     while (c->service_resolvers)
242         service_resolver_free(c->service_resolvers);
243
244     g_free(c->name);
245     AVAHI_LLIST_REMOVE(Client, clients, server->clients, c);
246     g_free(c);
247 }
248
249 static Client *client_get(const gchar *name, gboolean create) {
250     Client *client;
251
252     g_assert(server);
253     g_assert(name);
254
255     for (client = server->clients; client; client = client->clients_next)
256         if (!strcmp(name, client->name))
257             return client;
258
259     if (!create)
260         return NULL;
261
262     /* If not existant yet, create a new entry */
263     client = g_new(Client, 1);
264     client->id = server->current_id++;
265     client->name = g_strdup(name);
266     client->current_id = 0;
267     AVAHI_LLIST_HEAD_INIT(EntryGroupInfo, client->entry_groups);
268     AVAHI_LLIST_HEAD_INIT(HostNameResolverInfo, client->host_name_resolvers);
269     AVAHI_LLIST_HEAD_INIT(AddressResolverInfo, client->address_resolvers);
270     AVAHI_LLIST_HEAD_INIT(DomainBrowserInfo, client->domain_browsers);
271     AVAHI_LLIST_HEAD_INIT(ServiceTypeBrowserInfo, client->service_type_browsers);
272     AVAHI_LLIST_HEAD_INIT(ServiceBrowserInfo, client->service_browsers);
273     AVAHI_LLIST_HEAD_INIT(ServiceResolverInfo, client->service_resolvers);
274
275     AVAHI_LLIST_PREPEND(Client, clients, server->clients, client);
276     return client;
277 }
278
279 static DBusHandlerResult respond_error(DBusConnection *c, DBusMessage *m, const gchar *error, const gchar *text) {
280     DBusMessage *reply;
281
282     reply = dbus_message_new_error(m, error, text);
283     dbus_connection_send(c, reply, NULL);
284     dbus_message_unref(reply);
285     
286     return DBUS_HANDLER_RESULT_HANDLED;
287 }
288
289 static DBusHandlerResult respond_string(DBusConnection *c, DBusMessage *m, const gchar *text) {
290     DBusMessage *reply;
291
292     reply = dbus_message_new_method_return(m);
293     dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
294     dbus_connection_send(c, reply, NULL);
295     dbus_message_unref(reply);
296     
297     return DBUS_HANDLER_RESULT_HANDLED;
298 }
299
300 static DBusHandlerResult respond_ok(DBusConnection *c, DBusMessage *m) {
301     DBusMessage *reply;
302
303     reply = dbus_message_new_method_return(m);
304     dbus_connection_send(c, reply, NULL);
305     dbus_message_unref(reply);
306     
307     return DBUS_HANDLER_RESULT_HANDLED;
308 }
309
310 static DBusHandlerResult respond_path(DBusConnection *c, DBusMessage *m, const gchar *path) {
311     DBusMessage *reply;
312
313     reply = dbus_message_new_method_return(m);
314     dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
315     dbus_connection_send(c, reply, NULL);
316     dbus_message_unref(reply);
317     
318     return DBUS_HANDLER_RESULT_HANDLED;
319 }
320
321 static DBusHandlerResult msg_signal_filter_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
322     GMainLoop *loop = userdata;
323     DBusError error;
324
325     dbus_error_init(&error);
326
327 /*     avahi_log_debug("dbus: interface=%s, path=%s, member=%s", */
328 /*                     dbus_message_get_interface(m), */
329 /*                     dbus_message_get_path(m), */
330 /*                     dbus_message_get_member(m)); */
331
332     if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
333         /* No, we shouldn't quit, but until we get somewhere
334          * usefull such that we can restore our state, we will */
335         avahi_log_warn("Disconnnected from d-bus, terminating...");
336         g_main_loop_quit (loop);
337         return DBUS_HANDLER_RESULT_HANDLED;
338         
339     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameAcquired")) {
340         gchar *name;
341
342         if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
343             avahi_log_warn("Error parsing NameAcquired message");
344             goto fail;
345         }
346
347 /*         avahi_log_info("dbus: name acquired (%s)", name); */
348         return DBUS_HANDLER_RESULT_HANDLED;
349         
350     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
351         gchar *name, *old, *new;
352
353         if (!dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
354             avahi_log_warn("Error parsing NameOwnerChanged message");
355             goto fail;
356         }
357
358         if (!*new) {
359             Client *client;
360
361             if ((client = client_get(name, FALSE))) {
362 /*                 avahi_log_info("dbus: client %s vanished", name); */
363                 client_free(client);
364             }
365         }
366     }
367
368 fail:
369     if (dbus_error_is_set(&error))
370         dbus_error_free(&error);
371     
372     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
373 }
374
375
376 static void entry_group_callback(AvahiServer *s, AvahiEntryGroup *g, AvahiEntryGroupState state, gpointer userdata) {
377     EntryGroupInfo *i = userdata;
378     DBusMessage *m;
379     gint32 t;
380     
381     g_assert(s);
382     g_assert(g);
383     g_assert(i);
384
385     m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged");
386     t = (gint32) state;
387     dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
388     dbus_message_set_destination(m, i->client->name);  
389     dbus_connection_send(server->bus, m, NULL);
390     dbus_message_unref(m);
391 }
392
393 static DBusHandlerResult msg_entry_group_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
394     DBusError error;
395     EntryGroupInfo *i = userdata;
396
397     g_assert(c);
398     g_assert(m);
399     g_assert(i);
400     
401     dbus_error_init(&error);
402
403     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
404                     dbus_message_get_interface(m),
405                     dbus_message_get_path(m),
406                     dbus_message_get_member(m));
407
408     /* Access control */
409     if (strcmp(dbus_message_get_sender(m), i->client->name)) 
410         return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
411     
412     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Free")) {
413
414         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
415             avahi_log_warn("Error parsing EntryGroup::Free message");
416             goto fail;
417         }
418
419         entry_group_free(i);
420         return respond_ok(c, m);
421         
422     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "Commit")) {
423
424         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
425             avahi_log_warn("Error parsing EntryGroup::Commit message");
426             goto fail;
427         }
428
429         avahi_entry_group_commit(i->entry_group);
430         return respond_ok(c, m);
431         
432     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "GetState")) {
433         DBusMessage *reply;
434         gint32 t;
435
436         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
437             avahi_log_warn("Error parsing EntryGroup::GetState message");
438             goto fail;
439         }
440
441         t = (gint32) avahi_entry_group_get_state(i->entry_group);
442         reply = dbus_message_new_method_return(m);
443         dbus_message_append_args(reply, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
444         dbus_connection_send(c, reply, NULL);
445         dbus_message_unref(reply);
446         
447         return DBUS_HANDLER_RESULT_HANDLED;
448         
449     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddService")) {
450         gint32 interface, protocol;
451         gchar *type, *name, *domain, *host;
452         guint16 port;
453         gchar **txt = NULL;
454         gint txt_len;
455         AvahiStringList *strlst;
456         
457         if (!dbus_message_get_args(
458                 m, &error,
459                 DBUS_TYPE_INT32, &interface,
460                 DBUS_TYPE_INT32, &protocol,
461                 DBUS_TYPE_STRING, &name,
462                 DBUS_TYPE_STRING, &type,
463                 DBUS_TYPE_STRING, &domain,
464                 DBUS_TYPE_STRING, &host,
465                 DBUS_TYPE_UINT16, &port, 
466                 DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &txt, &txt_len,
467                 DBUS_TYPE_INVALID) || !type || !*type || !name || !*name || !port) {
468             avahi_log_warn("Error parsing EntryGroup::AddService message");
469             goto fail;
470         }
471
472         strlst = avahi_string_list_new_from_array((const gchar**) txt, txt_len);
473         dbus_free_string_array(txt);
474
475         if (domain && !*domain)
476             domain = NULL;
477
478         if (host && !*host)
479             host = NULL;
480
481         if (avahi_server_add_service_strlst(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, name, type, domain, host, port, strlst) < 0) {
482             avahi_log_warn("Failed to add service: %s", name);
483             return respond_error(c, m, "org.freedesktop.Avahi.InvalidServiceError", NULL);
484         } /* else */
485 /*             avahi_log_info("Successfully added service: %s", name); */
486         
487         return respond_ok(c, m);
488         
489     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "AddAddress")) {
490         gint32 interface, protocol;
491         gchar *name, *address;
492         AvahiAddress a;
493         
494         if (!dbus_message_get_args(
495                 m, &error,
496                 DBUS_TYPE_INT32, &interface,
497                 DBUS_TYPE_INT32, &protocol,
498                 DBUS_TYPE_STRING, &name,
499                 DBUS_TYPE_STRING, &address,
500                 DBUS_TYPE_INVALID) || !name || !*name || !address || !*address) {
501             avahi_log_warn("Error parsing EntryGroup::AddAddress message");
502             goto fail;
503         }
504
505         if (!(avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a))) {
506             avahi_log_warn("Error parsing address data");
507             return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
508         }
509
510         if (avahi_server_add_address(avahi_server, i->entry_group, (AvahiIfIndex) interface, (AvahiProtocol) protocol, 0, name, &a) < 0) {
511             avahi_log_warn("Failed to add service: %s", name);
512             return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
513         }/*  else */
514 /*             avahi_log_info("Successfully added address: %s -> %s", name, address); */
515         
516         return respond_ok(c, m);
517     }
518     
519     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
520
521 fail:
522     if (dbus_error_is_set(&error))
523         dbus_error_free(&error);
524     
525     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
526 }
527
528 static void host_name_resolver_callback(AvahiHostNameResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const gchar *host_name, const AvahiAddress *a, gpointer userdata) {
529     HostNameResolverInfo *i = userdata;
530     DBusMessage *reply;
531     
532     g_assert(r);
533     g_assert(host_name);
534     g_assert(i);
535
536     if (event == AVAHI_RESOLVER_FOUND) {
537         char t[256], *pt = t;
538         gint32 i_interface, i_protocol, i_aprotocol;
539
540         g_assert(a);
541         avahi_address_snprint(t, sizeof(t), a);
542
543         i_interface = (gint32) interface;
544         i_protocol = (gint32) protocol;
545         i_aprotocol = (gint32) a->family;
546         
547         reply = dbus_message_new_method_return(i->message);
548         dbus_message_append_args(
549             reply,
550             DBUS_TYPE_INT32, &i_interface,
551             DBUS_TYPE_INT32, &i_protocol,
552             DBUS_TYPE_STRING, &host_name,
553             DBUS_TYPE_INT32, &i_aprotocol,
554             DBUS_TYPE_STRING, &pt,
555             DBUS_TYPE_INVALID);
556
557     } else {
558         g_assert(event == AVAHI_RESOLVER_TIMEOUT);
559         reply = dbus_message_new_error(i->message, "org.freedesktop.Avahi.TimeoutError", NULL);
560     }
561
562     dbus_connection_send(server->bus, reply, NULL);
563     dbus_message_unref(reply);
564
565     host_name_resolver_free(i);
566 }
567
568 static void address_resolver_callback(AvahiAddressResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const AvahiAddress *address, const gchar *host_name, gpointer userdata) {
569     AddressResolverInfo *i = userdata;
570     DBusMessage *reply;
571     
572     g_assert(r);
573     g_assert(address);
574     g_assert(i);
575
576     if (event == AVAHI_RESOLVER_FOUND) {
577         char t[256], *pt = t;
578         gint32 i_interface, i_protocol, i_aprotocol;
579
580         g_assert(host_name);
581         avahi_address_snprint(t, sizeof(t), address);
582
583         i_interface = (gint32) interface;
584         i_protocol = (gint32) protocol;
585         i_aprotocol = (gint32) address->family;
586         
587         reply = dbus_message_new_method_return(i->message);
588         dbus_message_append_args(
589             reply,
590             DBUS_TYPE_INT32, &i_interface,
591             DBUS_TYPE_INT32, &i_protocol,
592             DBUS_TYPE_INT32, &i_aprotocol,
593             DBUS_TYPE_STRING, &pt,
594             DBUS_TYPE_STRING, &host_name,
595             DBUS_TYPE_INVALID);
596
597     } else {
598         g_assert(event == AVAHI_RESOLVER_TIMEOUT);
599         reply = dbus_message_new_error(i->message, "org.freedesktop.Avahi.TimeoutError", NULL);
600     }
601
602     dbus_connection_send(server->bus, reply, NULL);
603     dbus_message_unref(reply);
604
605     address_resolver_free(i);
606 }
607
608 static DBusHandlerResult msg_domain_browser_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
609     DBusError error;
610     DomainBrowserInfo *i = userdata;
611
612     g_assert(c);
613     g_assert(m);
614     g_assert(i);
615     
616     dbus_error_init(&error);
617
618     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
619                     dbus_message_get_interface(m),
620                     dbus_message_get_path(m),
621                     dbus_message_get_member(m));
622
623     /* Access control */
624     if (strcmp(dbus_message_get_sender(m), i->client->name)) 
625         return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
626     
627     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "Free")) {
628
629         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
630             avahi_log_warn("Error parsing DomainBrowser::Free message");
631             goto fail;
632         }
633
634         domain_browser_free(i);
635         return respond_ok(c, m);
636         
637     }
638     
639     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
640
641 fail:
642     if (dbus_error_is_set(&error))
643         dbus_error_free(&error);
644     
645     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
646 }
647
648 static void domain_browser_callback(AvahiDomainBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const gchar *domain, gpointer userdata) {
649     DomainBrowserInfo *i = userdata;
650     DBusMessage *m;
651     gint32 i_interface, i_protocol;
652     
653     g_assert(b);
654     g_assert(domain);
655     g_assert(i);
656
657     i_interface = (gint32) interface;
658     i_protocol = (gint32) protocol;
659
660     m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, event == AVAHI_BROWSER_NEW ? "ItemNew" : "ItemRemove");
661     dbus_message_append_args(
662         m,
663         DBUS_TYPE_INT32, &i_interface,
664         DBUS_TYPE_INT32, &i_protocol,
665         DBUS_TYPE_STRING, &domain,
666         DBUS_TYPE_INVALID);
667     dbus_message_set_destination(m, i->client->name);   
668     dbus_connection_send(server->bus, m, NULL);
669     dbus_message_unref(m);
670 }
671
672 static DBusHandlerResult msg_service_type_browser_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
673     DBusError error;
674     ServiceTypeBrowserInfo *i = userdata;
675
676     g_assert(c);
677     g_assert(m);
678     g_assert(i);
679     
680     dbus_error_init(&error);
681
682     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
683                     dbus_message_get_interface(m),
684                     dbus_message_get_path(m),
685                     dbus_message_get_member(m));
686
687     /* Access control */
688     if (strcmp(dbus_message_get_sender(m), i->client->name)) 
689         return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
690     
691     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "Free")) {
692
693         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
694             avahi_log_warn("Error parsing ServiceTypeBrowser::Free message");
695             goto fail;
696         }
697
698         service_type_browser_free(i);
699         return respond_ok(c, m);
700         
701     }
702     
703     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
704
705 fail:
706     if (dbus_error_is_set(&error))
707         dbus_error_free(&error);
708     
709     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
710 }
711
712 static void service_type_browser_callback(AvahiServiceTypeBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const gchar *type, const gchar *domain, gpointer userdata) {
713     ServiceTypeBrowserInfo *i = userdata;
714     DBusMessage *m;
715     gint32 i_interface, i_protocol;
716     
717     g_assert(b);
718     g_assert(type);
719     g_assert(domain);
720     g_assert(i);
721
722     i_interface = (gint32) interface;
723     i_protocol = (gint32) protocol;
724
725     m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, event == AVAHI_BROWSER_NEW ? "ItemNew" : "ItemRemove");
726     dbus_message_append_args(
727         m,
728         DBUS_TYPE_INT32, &i_interface,
729         DBUS_TYPE_INT32, &i_protocol,
730         DBUS_TYPE_STRING, &type,
731         DBUS_TYPE_STRING, &domain,
732         DBUS_TYPE_INVALID);
733     dbus_message_set_destination(m, i->client->name);   
734     dbus_connection_send(server->bus, m, NULL);
735     dbus_message_unref(m);
736 }
737
738 static DBusHandlerResult msg_service_browser_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
739     DBusError error;
740     ServiceBrowserInfo *i = userdata;
741
742     g_assert(c);
743     g_assert(m);
744     g_assert(i);
745     
746     dbus_error_init(&error);
747
748     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
749                     dbus_message_get_interface(m),
750                     dbus_message_get_path(m),
751                     dbus_message_get_member(m));
752
753     /* Access control */
754     if (strcmp(dbus_message_get_sender(m), i->client->name)) 
755         return respond_error(c, m, DBUS_ERROR_ACCESS_DENIED, NULL);
756     
757     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "Free")) {
758
759         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
760             avahi_log_warn("Error parsing ServiceBrowser::Free message");
761             goto fail;
762         }
763
764         service_browser_free(i);
765         return respond_ok(c, m);
766         
767     }
768     
769     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
770
771 fail:
772     if (dbus_error_is_set(&error))
773         dbus_error_free(&error);
774     
775     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
776 }
777
778 static void service_browser_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const gchar *name, const gchar *type, const gchar *domain, gpointer userdata) {
779     ServiceBrowserInfo *i = userdata;
780     DBusMessage *m;
781     gint32 i_interface, i_protocol;
782     
783     g_assert(b);
784     g_assert(name);
785     g_assert(type);
786     g_assert(domain);
787     g_assert(i);
788
789     i_interface = (gint32) interface;
790     i_protocol = (gint32) protocol;
791
792     m = dbus_message_new_signal(i->path, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, event == AVAHI_BROWSER_NEW ? "ItemNew" : "ItemRemove");
793     dbus_message_append_args(
794         m,
795         DBUS_TYPE_INT32, &i_interface,
796         DBUS_TYPE_INT32, &i_protocol,
797         DBUS_TYPE_STRING, &name,
798         DBUS_TYPE_STRING, &type,
799         DBUS_TYPE_STRING, &domain,
800         DBUS_TYPE_INVALID);
801     dbus_message_set_destination(m, i->client->name);   
802     dbus_connection_send(server->bus, m, NULL);
803     dbus_message_unref(m);
804 }
805
806 static DBusHandlerResult msg_server_impl(DBusConnection *c, DBusMessage *m, void *userdata) {
807     DBusError error;
808
809     dbus_error_init(&error);
810
811     avahi_log_debug("dbus: interface=%s, path=%s, member=%s",
812                     dbus_message_get_interface(m),
813                     dbus_message_get_path(m),
814                     dbus_message_get_member(m));
815
816     if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostName")) {
817
818         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
819             avahi_log_warn("Error parsing Server::GetHostName message");
820             goto fail;
821         }
822
823         return respond_string(c, m, avahi_server_get_host_name(avahi_server));
824         
825     } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetDomainName")) {
826
827         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
828             avahi_log_warn("Error parsing Server::GetDomainName message");
829             goto fail;
830         }
831
832         return respond_string(c, m, avahi_server_get_domain_name(avahi_server));
833
834     } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetHostNameFqdn")) {
835
836         if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INVALID))) {
837             avahi_log_warn("Error parsing Server::GetHostNameFqdn message");
838             goto fail;
839         }
840     
841         return respond_string(c, m, avahi_server_get_host_name_fqdn(avahi_server));
842         
843     } if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetVersionString")) {
844
845         if (!(dbus_message_get_args(m, &error, DBUS_TYPE_INVALID))) {
846             avahi_log_warn("Error parsing Server::GetVersionString message");
847             goto fail;
848         }
849     
850         return respond_string(c, m, PACKAGE_STRING);
851         
852     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "EntryGroupNew")) {
853         Client *client;
854         EntryGroupInfo *i;
855         static const DBusObjectPathVTable vtable = {
856             NULL,
857             msg_entry_group_impl,
858             NULL,
859             NULL,
860             NULL,
861             NULL
862         };
863
864         if (!dbus_message_get_args(m, &error, DBUS_TYPE_INVALID)) {
865             avahi_log_warn("Error parsing Server::EntryGroupNew message");
866             goto fail;
867         }
868
869         client = client_get(dbus_message_get_sender(m), TRUE);
870
871         i = g_new(EntryGroupInfo, 1);
872         i->id = ++client->current_id;
873         i->client = client;
874         i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/EntryGroup%u", client->id, i->id);
875         AVAHI_LLIST_PREPEND(EntryGroupInfo, entry_groups, client->entry_groups, i);
876
877         if (!(i->entry_group = avahi_entry_group_new(avahi_server, entry_group_callback, i))) {
878             avahi_log_warn("Failed to create entry group");
879             entry_group_free(i);
880             goto fail;
881         }
882
883         dbus_connection_register_object_path(c, i->path, &vtable, i);
884         return respond_path(c, m, i->path);
885         
886     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ResolveHostName")) {
887         Client *client;
888         gint32 interface, protocol, aprotocol;
889         gchar *name;
890         HostNameResolverInfo *i;
891             
892         if (!dbus_message_get_args(
893                 m, &error,
894                 DBUS_TYPE_INT32, &interface,
895                 DBUS_TYPE_INT32, &protocol,
896                 DBUS_TYPE_STRING, &name,
897                 DBUS_TYPE_INT32, &aprotocol,
898                 DBUS_TYPE_INVALID) || !name || !*name) {
899             avahi_log_warn("Error parsing Server::ResolveHostName message");
900             goto fail;
901         }
902
903         client = client_get(dbus_message_get_sender(m), TRUE);
904
905         i = g_new(HostNameResolverInfo, 1);
906         i->client = client;
907         i->message = dbus_message_ref(m);
908         AVAHI_LLIST_PREPEND(HostNameResolverInfo, host_name_resolvers, client->host_name_resolvers, i);
909
910         if (!(i->host_name_resolver = avahi_host_name_resolver_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, name, (AvahiProtocol) aprotocol, host_name_resolver_callback, i))) {
911             host_name_resolver_free(i);
912             avahi_log_warn("Failed to create host name resolver");
913             goto fail;
914         }
915         
916         return DBUS_HANDLER_RESULT_HANDLED;
917         
918     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ResolveAddress")) {
919         Client *client;
920         gint32 interface, protocol;
921         gchar *address;
922         AddressResolverInfo *i;
923         AvahiAddress a;
924             
925         if (!dbus_message_get_args(
926                 m, &error,
927                 DBUS_TYPE_INT32, &interface,
928                 DBUS_TYPE_INT32, &protocol,
929                 DBUS_TYPE_STRING, &address,
930                 DBUS_TYPE_INVALID) || !address || !*address) {
931             avahi_log_warn("Error parsing Server::ResolveAddress message");
932             goto fail;
933         }
934
935         if (!avahi_address_parse(address, AVAHI_PROTO_UNSPEC, &a)) {
936             avahi_log_warn("Error parsing address data");
937             return respond_error(c, m, "org.freedesktop.Avahi.InvalidAddressError", NULL);
938         }
939         
940         client = client_get(dbus_message_get_sender(m), TRUE);
941
942         i = g_new(AddressResolverInfo, 1);
943         i->client = client;
944         i->message = dbus_message_ref(m);
945
946         AVAHI_LLIST_PREPEND(AddressResolverInfo, address_resolvers, client->address_resolvers, i);
947
948         if (!(i->address_resolver = avahi_address_resolver_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, &a, address_resolver_callback, i))) {
949             address_resolver_free(i);
950             avahi_log_warn("Failed to create address resolver");
951             goto fail;
952         }
953         
954         return DBUS_HANDLER_RESULT_HANDLED;
955         
956     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "DomainBrowserNew")) {
957         Client *client;
958         DomainBrowserInfo *i;
959         static const DBusObjectPathVTable vtable = {
960             NULL,
961             msg_domain_browser_impl,
962             NULL,
963             NULL,
964             NULL,
965             NULL
966         };
967         gint32 interface, protocol, type;
968         gchar *domain;
969         
970
971         if (!dbus_message_get_args(
972                 m, &error,
973                 DBUS_TYPE_INT32, &interface,
974                 DBUS_TYPE_INT32, &protocol,
975                 DBUS_TYPE_STRING, &domain,
976                 DBUS_TYPE_INT32, &type,
977                 DBUS_TYPE_INVALID) || type < 0 || type >= AVAHI_DOMAIN_BROWSER_MAX) {
978             avahi_log_warn("Error parsing Server::DomainBrowserNew message");
979             goto fail;
980         }
981
982         client = client_get(dbus_message_get_sender(m), TRUE);
983
984         if (!*domain)
985             domain = NULL;
986
987         i = g_new(DomainBrowserInfo, 1);
988         i->id = ++client->current_id;
989         i->client = client;
990         i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/DomainBrowser%u", client->id, i->id);
991
992         AVAHI_LLIST_PREPEND(DomainBrowserInfo, domain_browsers, client->domain_browsers, i);
993
994         if (!(i->domain_browser = avahi_domain_browser_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, domain, (AvahiDomainBrowserType) type, domain_browser_callback, i))) {
995             avahi_log_warn("Failed to create domain browser");
996             domain_browser_free(i);
997             goto fail;
998         }
999         
1000         dbus_connection_register_object_path(c, i->path, &vtable, i);
1001         return respond_path(c, m, i->path);
1002
1003     } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ServiceTypeBrowserNew")) {
1004         Client *client;
1005         ServiceTypeBrowserInfo *i;
1006         static const DBusObjectPathVTable vtable = {
1007             NULL,
1008             msg_service_type_browser_impl,
1009             NULL,
1010             NULL,
1011             NULL,
1012             NULL
1013         };
1014         gint32 interface, protocol;
1015         gchar *domain;
1016         
1017         if (!dbus_message_get_args(
1018                 m, &error,
1019                 DBUS_TYPE_INT32, &interface,
1020                 DBUS_TYPE_INT32, &protocol,
1021                 DBUS_TYPE_STRING, &domain,
1022                 DBUS_TYPE_INVALID)) {
1023             avahi_log_warn("Error parsing Server::ServiceTypeBrowserNew message");
1024             goto fail;
1025         }
1026
1027         client = client_get(dbus_message_get_sender(m), TRUE);
1028
1029         if (!*domain)
1030             domain = NULL;
1031
1032         i = g_new(ServiceTypeBrowserInfo, 1);
1033         i->id = ++client->current_id;
1034         i->client = client;
1035         i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/ServiceTypeBrowser%u", client->id, i->id);
1036
1037         AVAHI_LLIST_PREPEND(ServiceTypeBrowserInfo, service_type_browsers, client->service_type_browsers, i);
1038
1039         if (!(i->service_type_browser = avahi_service_type_browser_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, domain, service_type_browser_callback, i))) {
1040             avahi_log_warn("Failed to create service type browser");
1041             service_type_browser_free(i);
1042             goto fail;
1043         }
1044         
1045         dbus_connection_register_object_path(c, i->path, &vtable, i);
1046         return respond_path(c, m, i->path);
1047         
1048      } else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "ServiceBrowserNew")) {
1049         Client *client;
1050         ServiceBrowserInfo *i;
1051         static const DBusObjectPathVTable vtable = {
1052             NULL,
1053             msg_service_browser_impl,
1054             NULL,
1055             NULL,
1056             NULL,
1057             NULL
1058         };
1059         gint32 interface, protocol;
1060         gchar *domain, *type;
1061         
1062         if (!dbus_message_get_args(
1063                 m, &error,
1064                 DBUS_TYPE_INT32, &interface,
1065                 DBUS_TYPE_INT32, &protocol,
1066                 DBUS_TYPE_STRING, &type,
1067                 DBUS_TYPE_STRING, &domain,
1068                 DBUS_TYPE_INVALID) || !type || !*type) {
1069             avahi_log_warn("Error parsing Server::ServiceBrowserNew message");
1070             goto fail;
1071         }
1072
1073         client = client_get(dbus_message_get_sender(m), TRUE);
1074
1075         if (!*domain)
1076             domain = NULL;
1077
1078         i = g_new(ServiceBrowserInfo, 1);
1079         i->id = ++client->current_id;
1080         i->client = client;
1081         i->path = g_strdup_printf("/org/freedesktop/Avahi/Client%u/ServiceBrowser%u", client->id, i->id);
1082
1083         AVAHI_LLIST_PREPEND(ServiceBrowserInfo, service_browsers, client->service_browsers, i);
1084
1085         if (!(i->service_browser = avahi_service_browser_new(avahi_server, (AvahiIfIndex) interface, (AvahiProtocol) protocol, type, domain, service_browser_callback, i))) {
1086             avahi_log_warn("Failed to create service browser");
1087             service_browser_free(i);
1088             goto fail;
1089         }
1090         
1091         dbus_connection_register_object_path(c, i->path, &vtable, i);
1092         return respond_path(c, m, i->path);
1093      }
1094
1095     avahi_log_warn("Missed message %s::%s()", dbus_message_get_interface(m), dbus_message_get_member(m));
1096
1097
1098 fail:
1099     if (dbus_error_is_set(&error))
1100         dbus_error_free(&error);
1101     
1102     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1103 }
1104
1105 void dbus_protocol_server_state_changed(AvahiServerState state) {
1106     DBusMessage *m;
1107     gint32 t;
1108     
1109     if (!server)
1110         return;
1111
1112     m = dbus_message_new_signal(AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged");
1113     t = (gint32) state;
1114     dbus_message_append_args(m, DBUS_TYPE_INT32, &t, DBUS_TYPE_INVALID);
1115     dbus_connection_send(server->bus, m, NULL);
1116     dbus_message_unref(m);
1117 }
1118
1119 int dbus_protocol_setup(GMainLoop *loop) {
1120     DBusError error;
1121
1122     static const DBusObjectPathVTable server_vtable = {
1123         NULL,
1124         msg_server_impl,
1125         NULL,
1126         NULL,
1127         NULL,
1128         NULL
1129     };
1130
1131     dbus_error_init(&error);
1132
1133     server = g_malloc(sizeof(Server));
1134     server->clients = NULL;
1135     server->current_id = 0;
1136
1137     server->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
1138     if (dbus_error_is_set(&error)) {
1139         avahi_log_warn("dbus_bus_get(): %s", error.message);
1140         goto fail;
1141     }
1142
1143     dbus_connection_setup_with_g_main(server->bus, NULL);
1144     dbus_connection_set_exit_on_disconnect(server->bus, FALSE);
1145
1146     dbus_bus_request_name(server->bus, AVAHI_DBUS_NAME, 0, &error);
1147     if (dbus_error_is_set(&error)) {
1148         avahi_log_warn("dbus_bus_request_name(): %s", error.message);
1149         goto fail;
1150     }
1151
1152     dbus_bus_add_match(server->bus, "type='signal',""interface='" DBUS_INTERFACE_DBUS  "'", &error);
1153
1154     dbus_connection_add_filter(server->bus, msg_signal_filter_impl, loop, NULL);
1155     dbus_connection_register_object_path(server->bus, AVAHI_DBUS_PATH_SERVER, &server_vtable, NULL);
1156
1157     return 0;
1158
1159 fail:
1160     if (server->bus) {
1161         dbus_connection_disconnect(server->bus);
1162         dbus_connection_unref(server->bus);
1163     }
1164     
1165     dbus_error_free (&error);
1166     g_free(server);
1167     server = NULL;
1168     return -1;
1169 }
1170
1171 void dbus_protocol_shutdown(void) {
1172
1173     if (server) {
1174     
1175         while (server->clients)
1176             client_free(server->clients);
1177
1178         if (server->bus) {
1179             dbus_connection_disconnect(server->bus);
1180             dbus_connection_unref(server->bus);
1181         }
1182
1183         g_free(server);
1184         server = NULL;
1185     }
1186 }