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