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 <stdlib.h>
27#include <stdio.h>
28#include <string.h>
29
30#include <dbus/dbus.h>
31
32#include <avahi-common/dbus.h>
33#include <avahi-common/llist.h>
34#include <avahi-common/error.h>
35#include <avahi-common/dbus.h>
36#include <avahi-common/malloc.h>
37#include <avahi-common/dbus-watch-glue.h>
38
39#include "client.h"
40#include "internal.h"
41
42#define AVAHI_CLIENT_DBUS_API_SUPPORTED ((uint32_t) 0x0201)
43
44static int init_server(AvahiClient *client, int *ret_error);
45
46int avahi_client_set_errno (AvahiClient *client, int error) {
47    assert(client);
48
49    return client->error = error;
50}
51
52int avahi_client_set_dbus_error(AvahiClient *client, DBusError *error) {
53    assert(client);
54    assert(error);
55
56    return avahi_client_set_errno(client, avahi_error_dbus_to_number(error->name));
57}
58
59static void client_set_state (AvahiClient *client, AvahiServerState state) {
60    assert(client);
61
62    if (client->state == state)
63        return;
64
65    client->state = state;
66
67    switch (client->state) {
68        case AVAHI_CLIENT_FAILURE:
69            if (client->bus) {
70#ifdef HAVE_DBUS_CONNECTION_CLOSE
71                dbus_connection_close(client->bus);
72#else
73                dbus_connection_disconnect(client->bus);
74#endif
75                dbus_connection_unref(client->bus);
76                client->bus = NULL;
77            }
78
79            /* Fall through */
80
81        case AVAHI_CLIENT_S_COLLISION:
82        case AVAHI_CLIENT_S_REGISTERING:
83
84            /* Clear cached strings */
85            avahi_free(client->host_name);
86            avahi_free(client->host_name_fqdn);
87            avahi_free(client->domain_name);
88
89            client->host_name =  NULL;
90            client->host_name_fqdn = NULL;
91            client->domain_name = NULL;
92            break;
93
94        case AVAHI_CLIENT_S_RUNNING:
95        case AVAHI_CLIENT_CONNECTING:
96            break;
97
98    }
99
100    if (client->callback)
101        client->callback (client, state, client->userdata);
102}
103
104static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message, void *userdata) {
105    AvahiClient *client = userdata;
106    DBusError error;
107
108    assert(bus);
109    assert(message);
110
111    dbus_error_init(&error);
112
113/*     fprintf(stderr, "dbus: interface=%s, path=%s, member=%s\n", */
114/*             dbus_message_get_interface (message), */
115/*             dbus_message_get_path (message), */
116/*             dbus_message_get_member (message)); */
117
118    if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
119
120        /* The DBUS server died or kicked us */
121        avahi_client_set_errno(client, AVAHI_ERR_DISCONNECTED);
122        goto fail;
123
124    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameAcquired")) {
125
126        /* Ignore this message */
127
128    } else if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
129        char *name, *old, *new;
130
131        if (!dbus_message_get_args(
132                  message, &error,
133                  DBUS_TYPE_STRING, &name,
134                  DBUS_TYPE_STRING, &old,
135                  DBUS_TYPE_STRING, &new,
136                  DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
137
138            fprintf(stderr, "WARNING: Failed to parse NameOwnerChanged signal: %s\n", error.message);
139            avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
140            goto fail;
141        }
142
143        if (strcmp(name, AVAHI_DBUS_NAME) == 0) {
144
145            if (avahi_client_is_connected(client)) {
146
147                /* Regardless if the server lost or acquired its name or
148                 * if the name was transfered: our services are no longer
149                 * available, so we disconnect ourselves */
150                avahi_client_set_errno(client, AVAHI_ERR_DISCONNECTED);
151                goto fail;
152
153            } else if (client->state == AVAHI_CLIENT_CONNECTING && (!old || *old == 0)) {
154                int ret;
155
156                /* Server appeared */
157
158                if ((ret = init_server(client, NULL)) < 0) {
159                    avahi_client_set_errno(client, ret);
160                    goto fail;
161                }
162            }
163        }
164
165    } else if (!avahi_client_is_connected(client)) {
166
167        /* Ignore messages we get in unconnected state */
168
169    } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged")) {
170        int32_t state;
171        char *e = NULL;
172        int c;
173
174        if (!dbus_message_get_args(
175                  message, &error,
176                  DBUS_TYPE_INT32, &state,
177                  DBUS_TYPE_STRING, &e,
178                  DBUS_TYPE_INVALID) || dbus_error_is_set (&error)) {
179
180            fprintf(stderr, "WARNING: Failed to parse Server.StateChanged signal: %s\n", error.message);
181            avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
182            goto fail;
183        }
184
185        if ((c = avahi_error_dbus_to_number(e)) != AVAHI_OK)
186            avahi_client_set_errno(client, c);
187
188        client_set_state(client, (AvahiClientState) state);
189
190    } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged")) {
191        const char *path;
192        AvahiEntryGroup *g;
193        path = dbus_message_get_path(message);
194
195        for (g = client->groups; g; g = g->groups_next)
196            if (strcmp(g->path, path) == 0)
197                break;
198
199        if (g) {
200            int32_t state;
201            char *e;
202            int c;
203
204            if (!dbus_message_get_args(
205                      message, &error,
206                      DBUS_TYPE_INT32, &state,
207                      DBUS_TYPE_STRING, &e,
208                      DBUS_TYPE_INVALID) ||
209                dbus_error_is_set(&error)) {
210
211                fprintf(stderr, "WARNING: Failed to parse EntryGroup.StateChanged signal: %s\n", error.message);
212                avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
213                goto fail;
214            }
215
216            if ((c = avahi_error_dbus_to_number(e)) != AVAHI_OK)
217                avahi_client_set_errno(client, c);
218
219            avahi_entry_group_set_state(g, state);
220        }
221
222    } else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemNew"))
223        return avahi_domain_browser_event(client, AVAHI_BROWSER_NEW, message);
224    else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemRemove"))
225        return avahi_domain_browser_event(client, AVAHI_BROWSER_REMOVE, message);
226    else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "CacheExhausted"))
227        return avahi_domain_browser_event(client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
228    else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "AllForNow"))
229        return avahi_domain_browser_event(client, AVAHI_BROWSER_ALL_FOR_NOW, message);
230    else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "Failure"))
231        return avahi_domain_browser_event(client, AVAHI_BROWSER_FAILURE, message);
232
233    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemNew"))
234        return avahi_service_type_browser_event (client, AVAHI_BROWSER_NEW, message);
235    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemRemove"))
236        return avahi_service_type_browser_event (client, AVAHI_BROWSER_REMOVE, message);
237    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "CacheExhausted"))
238        return avahi_service_type_browser_event (client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
239    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "AllForNow"))
240        return avahi_service_type_browser_event (client, AVAHI_BROWSER_ALL_FOR_NOW, message);
241    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "Failure"))
242        return avahi_service_type_browser_event (client, AVAHI_BROWSER_FAILURE, message);
243
244    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemNew"))
245        return avahi_service_browser_event (client, AVAHI_BROWSER_NEW, message);
246    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemRemove"))
247        return avahi_service_browser_event (client, AVAHI_BROWSER_REMOVE, message);
248    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "CacheExhausted"))
249        return avahi_service_browser_event (client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
250    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "AllForNow"))
251        return avahi_service_browser_event (client, AVAHI_BROWSER_ALL_FOR_NOW, message);
252    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "Failure"))
253        return avahi_service_browser_event (client, AVAHI_BROWSER_FAILURE, message);
254
255    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_RESOLVER, "Found"))
256        return avahi_service_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
257    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_RESOLVER, "Failure"))
258        return avahi_service_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
259
260    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_HOST_NAME_RESOLVER, "Found"))
261        return avahi_host_name_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
262    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_HOST_NAME_RESOLVER, "Failure"))
263        return avahi_host_name_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
264
265    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_ADDRESS_RESOLVER, "Found"))
266        return avahi_address_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
267    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_ADDRESS_RESOLVER, "Failure"))
268        return avahi_address_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
269
270    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "ItemNew"))
271        return avahi_record_browser_event (client, AVAHI_BROWSER_NEW, message);
272    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "ItemRemove"))
273        return avahi_record_browser_event (client, AVAHI_BROWSER_REMOVE, message);
274    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "CacheExhausted"))
275        return avahi_record_browser_event (client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
276    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "AllForNow"))
277        return avahi_record_browser_event (client, AVAHI_BROWSER_ALL_FOR_NOW, message);
278    else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_RECORD_BROWSER, "Failure"))
279        return avahi_record_browser_event (client, AVAHI_BROWSER_FAILURE, message);
280
281    else {
282
283        fprintf(stderr, "WARNING: Unhandled message: interface=%s, path=%s, member=%s\n",
284               dbus_message_get_interface(message),
285               dbus_message_get_path(message),
286               dbus_message_get_member(message));
287
288        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
289    }
290
291    return DBUS_HANDLER_RESULT_HANDLED;
292
293fail:
294
295    if (dbus_error_is_set(&error)) {
296        avahi_client_set_errno(client, avahi_error_dbus_to_number(error.name));
297        dbus_error_free(&error);
298    }
299
300    client_set_state(client, AVAHI_CLIENT_FAILURE);
301
302    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
303}
304
305static int get_server_state(AvahiClient *client, int *ret_error) {
306    DBusMessage *message = NULL, *reply = NULL;
307    DBusError error;
308    int32_t state;
309    int e = AVAHI_ERR_NO_MEMORY;
310
311    assert(client);
312
313    dbus_error_init(&error);
314
315    if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetState")))
316        goto fail;
317
318    reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
319
320    if (!reply || dbus_error_is_set (&error))
321        goto fail;
322
323    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID) ||
324        dbus_error_is_set (&error))
325        goto fail;
326
327    client_set_state(client, (AvahiServerState) state);
328
329    dbus_message_unref(message);
330    dbus_message_unref(reply);
331
332    return AVAHI_OK;
333
334fail:
335    if (dbus_error_is_set(&error)) {
336        e = avahi_error_dbus_to_number (error.name);
337        dbus_error_free(&error);
338    }
339
340    if (ret_error)
341        *ret_error = e;
342
343    if (message)
344        dbus_message_unref(message);
345    if (reply)
346        dbus_message_unref(reply);
347
348    return e;
349}
350
351static int check_version(AvahiClient *client, int *ret_error) {
352    DBusMessage *message = NULL, *reply  = NULL;
353    DBusError error;
354    uint32_t version;
355    int e = AVAHI_ERR_NO_MEMORY;
356
357    assert(client);
358
359    dbus_error_init(&error);
360
361    if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetAPIVersion")))
362        goto fail;
363
364    reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
365
366    if (!reply || dbus_error_is_set (&error)) {
367        char *version_str;
368
369        if (!dbus_error_is_set(&error) || strcmp(error.name, DBUS_ERROR_UNKNOWN_METHOD))
370            goto fail;
371
372        /* If the method GetAPIVersion is not known, we look if
373         * GetVersionString matches "avahi 0.6" which is the only
374         * version we support which doesn't have GetAPIVersion() .*/
375
376        dbus_message_unref(message);
377        if (reply) dbus_message_unref(reply);
378        dbus_error_free(&error);
379
380        if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetVersionString")))
381            goto fail;
382
383        reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
384
385        if (!reply || dbus_error_is_set (&error))
386            goto fail;
387
388        if (!dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &version_str, DBUS_TYPE_INVALID) ||
389            dbus_error_is_set (&error))
390            goto fail;
391
392        version = strcmp(version_str, "avahi 0.6") == 0 ? 0x0201 : 0x0000;
393
394    } else {
395
396        if (!dbus_message_get_args (reply, &error, DBUS_TYPE_UINT32, &version, DBUS_TYPE_INVALID) ||
397            dbus_error_is_set(&error))
398            goto fail;
399    }
400
401    /*fprintf(stderr, "API Version 0x%04x\n", version);*/
402
403    if ((version & 0xFF00) != (AVAHI_CLIENT_DBUS_API_SUPPORTED & 0xFF00) ||
404        (version & 0x00FF) < (AVAHI_CLIENT_DBUS_API_SUPPORTED & 0x00FF)) {
405        e = AVAHI_ERR_VERSION_MISMATCH;
406        goto fail;
407    }
408
409    dbus_message_unref(message);
410    dbus_message_unref(reply);
411
412    return AVAHI_OK;
413
414fail:
415    if (dbus_error_is_set(&error)) {
416        e = avahi_error_dbus_to_number (error.name);
417        dbus_error_free(&error);
418    }
419
420    if (ret_error)
421        *ret_error = e;
422
423    if (message)
424        dbus_message_unref(message);
425    if (reply)
426        dbus_message_unref(reply);
427
428    return e;
429}
430
431static int init_server(AvahiClient *client, int *ret_error) {
432    int r;
433
434    if ((r = check_version(client, ret_error)) < 0)
435        return r;
436
437    if ((r = get_server_state(client, ret_error)) < 0)
438        return r;
439
440    return AVAHI_OK;
441}
442
443/* This function acts like dbus_bus_get but creates a private
444 * connection instead.  */
445static DBusConnection* avahi_dbus_bus_get(DBusError *error) {
446    DBusConnection *c;
447
448#ifdef HAVE_DBUS_BUS_GET_PRIVATE
449    if (!(c = dbus_bus_get_private(DBUS_BUS_SYSTEM, error)))
450        return NULL;
451
452    dbus_connection_set_exit_on_disconnect(c, FALSE);
453#else
454    const char *a;
455
456    if (!(a = getenv("DBUS_SYSTEM_BUS_ADDRESS")) || !*a)
457        a = DBUS_SYSTEM_BUS_DEFAULT_ADDRESS;
458
459    if (!(c = dbus_connection_open_private(a, error)))
460        return NULL;
461
462    dbus_connection_set_exit_on_disconnect(c, FALSE);
463
464    if (!dbus_bus_register(c, error)) {
465#ifdef HAVE_DBUS_CONNECTION_CLOSE
466        dbus_connection_close(c);
467#else
468        dbus_connection_disconnect(c);
469#endif
470        dbus_connection_unref(c);
471        return NULL;
472    }
473#endif
474
475    return c;
476}
477
478AvahiClient *avahi_client_new(const AvahiPoll *poll_api, AvahiClientFlags flags, AvahiClientCallback callback, void *userdata, int *ret_error) {
479    AvahiClient *client = NULL;
480    DBusError error;
481
482    avahi_init_i18n();
483
484    dbus_error_init(&error);
485
486    if (!(client = avahi_new(AvahiClient, 1))) {
487        if (ret_error)
488            *ret_error = AVAHI_ERR_NO_MEMORY;
489        goto fail;
490    }
491
492    client->poll_api = poll_api;
493    client->error = AVAHI_OK;
494    client->callback = callback;
495    client->userdata = userdata;
496    client->state = (AvahiClientState) -1;
497    client->flags = flags;
498
499    client->host_name = NULL;
500    client->host_name_fqdn = NULL;
501    client->domain_name = NULL;
502    client->version_string = NULL;
503    client->local_service_cookie_valid = 0;
504
505    AVAHI_LLIST_HEAD_INIT(AvahiEntryGroup, client->groups);
506    AVAHI_LLIST_HEAD_INIT(AvahiDomainBrowser, client->domain_browsers);
507    AVAHI_LLIST_HEAD_INIT(AvahiServiceBrowser, client->service_browsers);
508    AVAHI_LLIST_HEAD_INIT(AvahiServiceTypeBrowser, client->service_type_browsers);
509    AVAHI_LLIST_HEAD_INIT(AvahiServiceResolver, client->service_resolvers);
510    AVAHI_LLIST_HEAD_INIT(AvahiHostNameResolver, client->host_name_resolvers);
511    AVAHI_LLIST_HEAD_INIT(AvahiAddressResolver, client->address_resolvers);
512    AVAHI_LLIST_HEAD_INIT(AvahiRecordBrowser, client->record_browsers);
513
514    if (!(client->bus = avahi_dbus_bus_get(&error)) || dbus_error_is_set(&error)) {
515        if (ret_error)
516            *ret_error = AVAHI_ERR_DBUS_ERROR;
517        goto fail;
518    }
519
520    if (avahi_dbus_connection_glue(client->bus, poll_api) < 0) {
521        if (ret_error)
522            *ret_error = AVAHI_ERR_NO_MEMORY; /* Not optimal */
523        goto fail;
524    }
525
526    if (!dbus_connection_add_filter (client->bus, filter_func, client, NULL)) {
527        if (ret_error)
528            *ret_error = AVAHI_ERR_NO_MEMORY;
529        goto fail;
530    }
531
532    dbus_bus_add_match(
533        client->bus,
534        "type='signal', "
535        "interface='" AVAHI_DBUS_INTERFACE_SERVER "', "
536        "sender='" AVAHI_DBUS_NAME "', "
537        "path='" AVAHI_DBUS_PATH_SERVER "'",
538        &error);
539
540    if (dbus_error_is_set(&error))
541        goto fail;
542
543    dbus_bus_add_match (
544        client->bus,
545        "type='signal', "
546        "interface='" DBUS_INTERFACE_DBUS "', "
547        "sender='" DBUS_SERVICE_DBUS "', "
548        "path='" DBUS_PATH_DBUS "'",
549        &error);
550
551    if (dbus_error_is_set(&error))
552        goto fail;
553
554    dbus_bus_add_match (
555        client->bus,
556        "type='signal', "
557        "interface='" DBUS_INTERFACE_LOCAL "'",
558        &error);
559
560    if (dbus_error_is_set(&error))
561        goto fail;
562
563
564    if (!(dbus_bus_name_has_owner(client->bus, AVAHI_DBUS_NAME, &error)) ||
565        dbus_error_is_set(&error)) {
566
567        /* We free the error so its not set, that way the fail target
568         * will return the NO_DAEMON error rather than a DBUS error */
569        dbus_error_free(&error);
570
571        if (!(flags & AVAHI_CLIENT_NO_FAIL)) {
572
573            if (ret_error)
574                *ret_error = AVAHI_ERR_NO_DAEMON;
575
576            goto fail;
577        }
578
579        /* The user doesn't want this call to fail if the daemon is not
580         * available, so let's return succesfully */
581        client_set_state(client, AVAHI_CLIENT_CONNECTING);
582
583    } else {
584
585        if (init_server(client, ret_error) < 0)
586            goto fail;
587    }
588
589    return client;
590
591fail:
592
593    if (client)
594        avahi_client_free(client);
595
596    if (dbus_error_is_set(&error)) {
597
598        if (ret_error) {
599            if (strcmp(error.name, DBUS_ERROR_FILE_NOT_FOUND) == 0)
600                /* DBUS returns this error when the DBUS daemon is not running */
601                *ret_error = AVAHI_ERR_NO_DAEMON;
602            else
603                *ret_error = avahi_error_dbus_to_number(error.name);
604        }
605
606        dbus_error_free(&error);
607    }
608
609    return NULL;
610}
611
612void avahi_client_free(AvahiClient *client) {
613    assert(client);
614
615    if (client->bus)
616        /* Disconnect in advance, so that the free() functions won't
617         * issue needless server calls */
618#ifdef HAVE_DBUS_CONNECTION_CLOSE
619        dbus_connection_close(client->bus);
620#else
621        dbus_connection_disconnect(client->bus);
622#endif
623
624    while (client->groups)
625        avahi_entry_group_free(client->groups);
626
627    while (client->domain_browsers)
628        avahi_domain_browser_free(client->domain_browsers);
629
630    while (client->service_browsers)
631        avahi_service_browser_free(client->service_browsers);
632
633    while (client->service_type_browsers)
634        avahi_service_type_browser_free(client->service_type_browsers);
635
636    while (client->service_resolvers)
637        avahi_service_resolver_free(client->service_resolvers);
638
639    while (client->host_name_resolvers)
640        avahi_host_name_resolver_free(client->host_name_resolvers);
641
642    while (client->address_resolvers)
643        avahi_address_resolver_free(client->address_resolvers);
644
645    while (client->record_browsers)
646        avahi_record_browser_free(client->record_browsers);
647
648    if (client->bus)
649        dbus_connection_unref(client->bus);
650
651    avahi_free(client->version_string);
652    avahi_free(client->host_name);
653    avahi_free(client->host_name_fqdn);
654    avahi_free(client->domain_name);
655
656    avahi_free(client);
657}
658
659static char* avahi_client_get_string_reply_and_block (AvahiClient *client, const char *method, const char *param) {
660    DBusMessage *message = NULL, *reply = NULL;
661    DBusError error;
662    char *ret, *n;
663
664    assert(client);
665    assert(method);
666
667    dbus_error_init (&error);
668
669    if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, method))) {
670        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
671        goto fail;
672    }
673
674    if (param) {
675        if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &param, DBUS_TYPE_INVALID)) {
676            avahi_client_set_errno (client, AVAHI_ERR_NO_MEMORY);
677            goto fail;
678        }
679    }
680
681    reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
682
683    if (!reply || dbus_error_is_set (&error))
684        goto fail;
685
686    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &ret, DBUS_TYPE_INVALID) ||
687        dbus_error_is_set (&error))
688        goto fail;
689
690    if (!(n = avahi_strdup(ret))) {
691        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
692        goto fail;
693    }
694
695    dbus_message_unref(message);
696    dbus_message_unref(reply);
697
698    return n;
699
700fail:
701
702    if (message)
703        dbus_message_unref(message);
704    if (reply)
705        dbus_message_unref(reply);
706
707    if (dbus_error_is_set(&error)) {
708        avahi_client_set_dbus_error(client, &error);
709        dbus_error_free(&error);
710    }
711
712    return NULL;
713}
714
715const char* avahi_client_get_version_string(AvahiClient *client) {
716    assert(client);
717
718    if (!avahi_client_is_connected(client)) {
719        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
720        return NULL;
721    }
722
723    if (!client->version_string)
724        client->version_string = avahi_client_get_string_reply_and_block(client, "GetVersionString", NULL);
725
726    return client->version_string;
727}
728
729const char* avahi_client_get_domain_name(AvahiClient *client) {
730    assert(client);
731
732    if (!avahi_client_is_connected(client)) {
733        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
734        return NULL;
735    }
736
737    if (!client->domain_name)
738        client->domain_name = avahi_client_get_string_reply_and_block(client, "GetDomainName", NULL);
739
740    return client->domain_name;
741}
742
743const char* avahi_client_get_host_name(AvahiClient *client) {
744    assert(client);
745
746    if (!avahi_client_is_connected(client)) {
747        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
748        return NULL;
749    }
750
751    if (!client->host_name)
752        client->host_name = avahi_client_get_string_reply_and_block(client, "GetHostName", NULL);
753
754    return client->host_name;
755}
756
757const char* avahi_client_get_host_name_fqdn (AvahiClient *client) {
758    assert(client);
759
760    if (!avahi_client_is_connected(client)) {
761        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
762        return NULL;
763    }
764
765    if (!client->host_name_fqdn)
766        client->host_name_fqdn = avahi_client_get_string_reply_and_block(client, "GetHostNameFqdn", NULL);
767
768    return client->host_name_fqdn;
769}
770
771AvahiClientState avahi_client_get_state(AvahiClient *client) {
772    assert(client);
773
774    return client->state;
775}
776
777int avahi_client_errno(AvahiClient *client) {
778    assert(client);
779
780    return client->error;
781}
782
783/* Just for internal use */
784int avahi_client_simple_method_call(AvahiClient *client, const char *path, const char *interface, const char *method) {
785    DBusMessage *message = NULL, *reply = NULL;
786    DBusError error;
787    int r = AVAHI_OK;
788
789    dbus_error_init(&error);
790
791    assert(client);
792    assert(path);
793    assert(interface);
794    assert(method);
795
796    if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, path, interface, method))) {
797        r = avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
798        goto fail;
799    }
800
801    if (!(reply = dbus_connection_send_with_reply_and_block(client->bus, message, -1, &error)) ||
802        dbus_error_is_set (&error)) {
803        r = avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
804        goto fail;
805    }
806
807    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) ||
808        dbus_error_is_set (&error)) {
809        r = avahi_client_set_errno(client, AVAHI_ERR_DBUS_ERROR);
810        goto fail;
811    }
812
813    dbus_message_unref(message);
814    dbus_message_unref(reply);
815
816    return AVAHI_OK;
817
818fail:
819    if (dbus_error_is_set(&error)) {
820        r = avahi_client_set_dbus_error(client, &error);
821        dbus_error_free(&error);
822    }
823
824    if (message)
825        dbus_message_unref(message);
826
827    if (reply)
828        dbus_message_unref(reply);
829
830    return r;
831}
832
833uint32_t avahi_client_get_local_service_cookie(AvahiClient *client) {
834    DBusMessage *message = NULL, *reply = NULL;
835    DBusError error;
836    assert(client);
837
838    if (!avahi_client_is_connected(client)) {
839        avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
840        return AVAHI_SERVICE_COOKIE_INVALID;
841    }
842
843    if (client->local_service_cookie_valid)
844        return client->local_service_cookie;
845
846    dbus_error_init (&error);
847
848    if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetLocalServiceCookie"))) {
849        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
850        goto fail;
851    }
852
853    reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
854
855    if (!reply || dbus_error_is_set (&error))
856        goto fail;
857
858    if (!dbus_message_get_args (reply, &error, DBUS_TYPE_UINT32, &client->local_service_cookie, DBUS_TYPE_INVALID) ||
859        dbus_error_is_set (&error))
860        goto fail;
861
862    dbus_message_unref(message);
863    dbus_message_unref(reply);
864
865    client->local_service_cookie_valid = 1;
866    return client->local_service_cookie;
867
868fail:
869
870    if (message)
871        dbus_message_unref(message);
872    if (reply)
873        dbus_message_unref(reply);
874
875    if (dbus_error_is_set(&error)) {
876        avahi_client_set_dbus_error(client, &error);
877        dbus_error_free(&error);
878    }
879
880    return AVAHI_SERVICE_COOKIE_INVALID;
881}
882
883int avahi_client_is_connected(AvahiClient *client) {
884    assert(client);
885
886    return
887        client->bus &&
888        dbus_connection_get_is_connected(client->bus) &&
889        (client->state == AVAHI_CLIENT_S_RUNNING || client->state == AVAHI_CLIENT_S_REGISTERING || client->state == AVAHI_CLIENT_S_COLLISION);
890}
891
892int avahi_client_set_host_name(AvahiClient* client, const char *name) {
893    DBusMessage *message = NULL, *reply = NULL;
894    DBusError error;
895
896    assert(client);
897
898    if (!avahi_client_is_connected(client))
899        return avahi_client_set_errno(client, AVAHI_ERR_BAD_STATE);
900
901    dbus_error_init (&error);
902
903    if (!(message = dbus_message_new_method_call (AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "SetHostName"))) {
904        avahi_client_set_errno(client, AVAHI_ERR_NO_MEMORY);
905        goto fail;
906    }
907
908    if (!dbus_message_append_args (message, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
909        avahi_client_set_errno (client, AVAHI_ERR_NO_MEMORY);
910        goto fail;
911    }
912
913    reply = dbus_connection_send_with_reply_and_block(client->bus, message, -1, &error);
914
915    if (!reply || dbus_error_is_set (&error))
916        goto fail;
917
918    if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) ||
919        dbus_error_is_set (&error))
920        goto fail;
921
922    dbus_message_unref(message);
923    dbus_message_unref(reply);
924
925    avahi_free(client->host_name);
926    client->host_name = NULL;
927    avahi_free(client->host_name_fqdn);
928    client->host_name_fqdn = NULL;
929
930    return 0;
931
932fail:
933
934    if (message)
935        dbus_message_unref(message);
936    if (reply)
937        dbus_message_unref(reply);
938
939    if (dbus_error_is_set(&error)) {
940        avahi_client_set_dbus_error(client, &error);
941        dbus_error_free(&error);
942    }
943
944    return client->error;
945}
946