]> git.meshlink.io Git - catta/blob - avahi-client/client.c
* implement avahi_client_free
[catta] / avahi-client / client.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 <avahi-client/client.h>
27 #include <avahi-common/dbus.h>
28 #include <avahi-common/llist.h>
29 #include <avahi-common/error.h>
30 #include <avahi-common/malloc.h>
31 #include <avahi-common/dbus-watch-glue.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include <dbus/dbus.h>
37
38 #include <stdlib.h>
39
40 #include "client.h"
41 #include "internal.h"
42
43 int avahi_client_set_errno (AvahiClient *client, int error) {
44     assert(client);
45
46     return client->error = error;
47 }
48     
49 static void client_set_state (AvahiClient *client, AvahiServerState state) {
50     assert(state);
51
52     if (client->state == state)
53         return;
54
55     client->state = state;
56
57     if (client->callback)
58         client->callback (client, state, client->userdata);
59 }
60
61 static DBusHandlerResult
62 filter_func (DBusConnection *bus, DBusMessage *message, void *data)
63 {
64     AvahiClient *client = data;
65     DBusError error;
66     
67     printf ("dbus: interface=%s, path=%s, member=%s\n",
68             dbus_message_get_interface (message),
69             dbus_message_get_path (message),
70             dbus_message_get_member (message));
71
72     dbus_error_init (&error);
73
74     if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
75         char *name, *old, *new;
76         dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old, DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID);
77         
78         if (dbus_error_is_set (&error)) {
79             dbus_error_free (&error);
80             goto out;
81         }
82
83         if (strcmp (name, AVAHI_DBUS_NAME) == 0) {
84
85             if (old == NULL && new != NULL) {
86                 client_set_state (client, AVAHI_CLIENT_RECONNECTED);
87             } else if (old != NULL && new == NULL) {
88                 client_set_state (client, AVAHI_CLIENT_DISCONNECTED);
89                 /* XXX: we really need to expire all entry groups */
90             }
91         }
92     } else if (dbus_message_is_signal (message, AVAHI_DBUS_NAME, "StateChanged")) {
93         /* XXX: todo */
94         printf ("server statechange\n");
95     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged")) {
96         const char *path;
97         AvahiEntryGroup *n, *group = NULL;
98         path = dbus_message_get_path (message);
99
100         for (n = client->groups; n != NULL; n = n->groups_next)
101         {
102             if (strcmp (n->path, path) == 0)
103             {
104                 group = n;
105                 break;
106             }
107         }
108         
109         if (group != NULL) {
110             int state;
111             dbus_message_get_args (message, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID);
112             if (dbus_error_is_set (&error))
113                 goto out;
114             
115             avahi_entry_group_state_change (group, state);
116         }
117     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemNew")) {
118         return avahi_domain_browser_event (client, AVAHI_BROWSER_NEW, message);
119     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemRemove")) {
120         return avahi_domain_browser_event (client, AVAHI_BROWSER_REMOVE, message);
121     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemNew")) {
122         return avahi_service_type_browser_event (client, AVAHI_BROWSER_NEW, message);
123     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemRemove")) {
124         return avahi_service_type_browser_event (client, AVAHI_BROWSER_REMOVE, message);
125     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemNew")) {
126         return avahi_service_browser_event (client, AVAHI_BROWSER_NEW, message);
127     } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemRemove")) {
128         return avahi_service_browser_event (client, AVAHI_BROWSER_REMOVE, message);
129     }
130
131     return DBUS_HANDLER_RESULT_HANDLED;
132
133 out: 
134     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
135 }
136
137 static int translate_dbus_error(const DBusError *error) {
138     assert(error);
139
140     /*** FIXME! Some more eloquent error translation should happen here */
141     
142     return AVAHI_ERR_DBUS_ERROR;
143 }
144
145 static int get_server_state(AvahiClient *client, int *ret_error) {
146     DBusMessage *message, *reply;
147     DBusError error;
148     int32_t state;
149     int e;
150     
151     assert(client);
152
153     dbus_error_init(&error);
154
155     if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetState")))
156         goto fail;
157
158     reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
159     dbus_message_unref(message);
160
161     if (!reply)
162         goto fail;
163
164     if (!(dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID)))
165         goto fail;
166
167     client_set_state(client, (AvahiServerState) state);
168
169     return AVAHI_OK;
170
171 fail:
172     if (dbus_error_is_set(&error)) {
173         e = translate_dbus_error(&error);
174         dbus_error_free(&error);
175     } else
176         e = AVAHI_ERR_NO_MEMORY;
177
178     if (ret_error)
179         *ret_error = e;
180         
181     return e;
182 }
183
184 AvahiClient *avahi_client_new(const AvahiPoll *poll_api, AvahiClientCallback callback, void *userdata, int *ret_error) {
185     AvahiClient *client = NULL;
186     DBusError error;
187
188     dbus_error_init (&error);
189
190     if (!(client = avahi_new(AvahiClient, 1))) {
191         if (ret_error)
192             *ret_error = AVAHI_ERR_NO_MEMORY;
193         goto fail;
194     }
195
196     client->poll_api = poll_api;
197     client->error = AVAHI_OK;
198     client->callback = callback;
199     client->userdata = userdata;
200     client->state = AVAHI_SERVER_INVALID;
201
202     AVAHI_LLIST_HEAD_INIT(AvahiEntryGroup, client->groups);
203     AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, client->domain_browsers);
204     AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, client->service_browsers);
205     AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, client->service_type_browsers);
206
207     client->bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
208     if (dbus_error_is_set (&error)) {
209         if (ret_error)
210             *ret_error = translate_dbus_error(&error);
211         goto fail;
212     }
213
214     if (avahi_dbus_connection_glue(client->bus, poll_api) < 0) {
215         if (ret_error)
216             *ret_error = AVAHI_ERR_NO_MEMORY; /* Not optimal */
217         goto fail;
218     }
219
220     if (!dbus_connection_add_filter (client->bus, filter_func, client, NULL)) {
221         if (ret_error)
222             *ret_error = AVAHI_ERR_NO_MEMORY; 
223         goto fail;
224     }
225         
226     dbus_bus_add_match(
227         client->bus,
228         "type='signal', "
229         "interface='" AVAHI_DBUS_INTERFACE_SERVER "', "
230         "sender='" AVAHI_DBUS_NAME "', "
231         "path='" AVAHI_DBUS_PATH_SERVER "'",
232         &error);
233
234     if (dbus_error_is_set (&error)) {
235         if (ret_error)
236             *ret_error = translate_dbus_error(&error);
237         goto fail;
238     }   
239
240     dbus_bus_add_match (
241         client->bus,
242         "type='signal', "
243         "interface='" DBUS_INTERFACE_DBUS "', "
244         "sender='" DBUS_SERVICE_DBUS "', "
245         "path='" DBUS_PATH_DBUS "'",
246         &error);
247
248     if (dbus_error_is_set (&error)) {
249         if (ret_error)
250             *ret_error = translate_dbus_error(&error);
251         goto fail;
252     }
253
254     if (!(dbus_bus_name_has_owner(client->bus, AVAHI_DBUS_NAME, &error))) {
255
256         if (dbus_error_is_set (&error)) {
257             if (ret_error)
258                 *ret_error = translate_dbus_error(&error);
259             goto fail;
260         }
261         
262         if (ret_error)
263             *ret_error = AVAHI_ERR_NO_DAEMON;
264         goto fail;
265     }
266
267     if (get_server_state(client, ret_error) < 0)
268         goto fail;
269
270     return client;
271
272 fail:
273
274     if (client)
275         avahi_client_free(client);
276
277     if (dbus_error_is_set(&error))
278         dbus_error_free(&error);
279         
280     return NULL;
281 }
282
283 void avahi_client_free(AvahiClient *client) {
284     assert(client);
285
286     if (client->bus) {
287         dbus_connection_disconnect(client->bus);
288         dbus_connection_unref(client->bus);
289     }
290
291     while (client->groups)
292         avahi_entry_group_free(client->groups);
293
294     while (client->domain_browsers)
295         avahi_domain_browser_free(client->domain_browsers);
296
297     while (client->service_browsers)
298         avahi_service_browser_free(client->service_browsers);
299
300     while (client->service_type_browsers)
301         avahi_service_type_browser_free(client->service_type_browsers);
302     
303     avahi_free(client);
304 }
305
306 static char*
307 avahi_client_get_string_reply_and_block (AvahiClient *client, const char *method, const char *param)
308 {
309     DBusMessage *message;
310     DBusMessage *reply;
311     DBusError error;
312     char *ret, *new;
313
314     if (client == NULL || method == NULL) return NULL;
315
316     dbus_error_init (&error);
317
318     message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, method);
319
320     if (param != NULL)
321     {
322         if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &param, DBUS_TYPE_INVALID))
323         {
324             avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
325             return NULL;
326         }
327     }
328     
329     reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
330
331     if (dbus_error_is_set (&error))
332     {
333         dbus_error_free (&error);
334         dbus_message_unref (message);
335
336         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
337         return NULL;
338     }
339
340     if (reply == NULL)
341     {
342         dbus_message_unref (message);
343
344         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
345         return NULL;
346     }
347
348     dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID);
349
350     if (dbus_error_is_set (&error))
351     {
352         dbus_error_free (&error);
353
354         avahi_client_set_errno (client, AVAHI_ERR_DBUS_ERROR);
355         return NULL;
356     }
357
358     new = avahi_strdup (ret);
359
360     avahi_client_set_errno (client, AVAHI_OK);
361     return new;
362 }
363
364 char*
365 avahi_client_get_version_string (AvahiClient *client)
366 {
367     return avahi_client_get_string_reply_and_block (client, "GetVersionString", NULL);
368 }
369
370 char*
371 avahi_client_get_domain_name (AvahiClient *client)
372 {
373     return avahi_client_get_string_reply_and_block (client, "GetDomainName", NULL);
374 }
375
376 char*
377 avahi_client_get_host_name (AvahiClient *client)
378 {
379     return avahi_client_get_string_reply_and_block (client, "GetHostName", NULL);
380 }
381
382 char*
383 avahi_client_get_host_name_fqdn (AvahiClient *client)
384 {
385     return avahi_client_get_string_reply_and_block (client, "GetHostNameFqdn", NULL);
386 }