+/***
+ This file is part of avahi.
+
+ avahi is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation; either version 2.1 of the
+ License, or (at your option) any later version.
+
+ avahi is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
+ Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with avahi; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#include <avahi-common/dbus.h>
+#include <avahi-common/llist.h>
+#include <avahi-common/error.h>
+#include <avahi-common/dbus.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/dbus-watch-glue.h>
+
+#include "client.h"
+#include "internal.h"
+
+int avahi_client_set_errno (AvahiClient *client, int error) {
+ assert(client);
+
+ return client->error = error;
+}
+
+int avahi_client_set_dbus_error(AvahiClient *client, DBusError *error) {
+ assert(client);
+ assert(error);
+
+ return avahi_client_set_errno(client, avahi_error_dbus_to_number(error->name));
+}
+
+static void client_set_state (AvahiClient *client, AvahiServerState state) {
+ assert(client);
+
+ if (client->state == state)
+ return;
+
+ client->state = state;
+
+ switch (client->state) {
+ case AVAHI_CLIENT_DISCONNECTED:
+ if (client->bus) {
+ dbus_connection_disconnect(client->bus);
+ dbus_connection_unref(client->bus);
+ client->bus = NULL;
+ }
+
+ /* Fall through */
+
+ case AVAHI_CLIENT_S_COLLISION:
+ case AVAHI_CLIENT_S_REGISTERING:
+
+ /* Clear cached strings */
+ avahi_free(client->host_name);
+ avahi_free(client->host_name_fqdn);
+ avahi_free(client->domain_name);
+
+ client->host_name = NULL;
+ client->host_name_fqdn = NULL;
+ client->domain_name = NULL;
+ break;
+
+ case AVAHI_CLIENT_S_RUNNING:
+ break;
+
+ }
+
+ if (client->callback)
+ client->callback (client, state, client->userdata);
+}
+
+static DBusHandlerResult filter_func(DBusConnection *bus, DBusMessage *message, void *userdata) {
+ AvahiClient *client = userdata;
+ DBusError error;
+
+ assert(bus);
+ assert(message);
+
+ dbus_error_init (&error);
+
+/* fprintf(stderr, "dbus: interface=%s, path=%s, member=%s\n", */
+/* dbus_message_get_interface (message), */
+/* dbus_message_get_path (message), */
+/* dbus_message_get_member (message)); */
+
+ if (client->state == AVAHI_CLIENT_DISCONNECTED)
+ goto fail;
+
+ if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+
+ /* The DBUS server died or kicked us */
+ client_set_state(client, AVAHI_CLIENT_DISCONNECTED);
+
+ } if (dbus_message_is_signal(message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+ char *name, *old, *new;
+
+ if (!dbus_message_get_args(
+ message, &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID) || dbus_error_is_set (&error)) {
+
+ fprintf(stderr, "WARNING: Failed to parse NameOwnerChanged signal: %s\n", error.message);
+ goto fail;
+ }
+
+ if (strcmp(name, AVAHI_DBUS_NAME) == 0)
+
+ /* Regardless if the server lost or acquired its name or
+ * if the name was transfered: our services are no longer
+ * available, so we disconnect ourselves */
+
+ client_set_state(client, AVAHI_CLIENT_DISCONNECTED);
+
+ } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_SERVER, "StateChanged")) {
+ int32_t state;
+ char *e = NULL;
+ int c;
+
+ if (!dbus_message_get_args(
+ message, &error,
+ DBUS_TYPE_INT32, &state,
+ DBUS_TYPE_STRING, &e,
+ DBUS_TYPE_INVALID) || dbus_error_is_set (&error)) {
+ fprintf(stderr, "WARNING: Failed to parse Server.StateChanged signal: %s\n", error.message);
+ goto fail;
+ }
+
+ if ((c = avahi_error_dbus_to_number(e)) != AVAHI_OK)
+ avahi_client_set_errno(client, c);
+
+ client_set_state(client, (AvahiClientState) state);
+
+ } else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_ENTRY_GROUP, "StateChanged")) {
+ const char *path;
+ AvahiEntryGroup *g;
+ path = dbus_message_get_path(message);
+
+ for (g = client->groups; g; g = g->groups_next)
+ if (strcmp(g->path, path) == 0)
+ break;
+
+ if (g) {
+ int32_t state;
+ char *e;
+ int c;
+
+ if (!dbus_message_get_args(
+ message, &error,
+ DBUS_TYPE_INT32, &state,
+ DBUS_TYPE_STRING, &e,
+ DBUS_TYPE_INVALID) ||
+ dbus_error_is_set(&error)) {
+ fprintf(stderr, "WARNING: Failed to parse EntryGroup.StateChanged signal: %s\n", error.message);
+ goto fail;
+ }
+
+ if ((c = avahi_error_dbus_to_number(e)) != AVAHI_OK)
+ avahi_client_set_errno(client, c);
+
+ avahi_entry_group_set_state(g, state);
+ }
+
+ } else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemNew"))
+ return avahi_domain_browser_event(client, AVAHI_BROWSER_NEW, message);
+ else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "ItemRemove"))
+ return avahi_domain_browser_event(client, AVAHI_BROWSER_REMOVE, message);
+ else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "CacheExhausted"))
+ return avahi_domain_browser_event(client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
+ else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "AllForNow"))
+ return avahi_domain_browser_event(client, AVAHI_BROWSER_ALL_FOR_NOW, message);
+ else if (dbus_message_is_signal (message, AVAHI_DBUS_INTERFACE_DOMAIN_BROWSER, "Failure"))
+ return avahi_domain_browser_event(client, AVAHI_BROWSER_FAILURE, message);
+
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemNew"))
+ return avahi_service_type_browser_event (client, AVAHI_BROWSER_NEW, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "ItemRemove"))
+ return avahi_service_type_browser_event (client, AVAHI_BROWSER_REMOVE, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "CacheExhausted"))
+ return avahi_service_type_browser_event (client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "AllForNow"))
+ return avahi_service_type_browser_event (client, AVAHI_BROWSER_ALL_FOR_NOW, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_TYPE_BROWSER, "Failure"))
+ return avahi_service_type_browser_event (client, AVAHI_BROWSER_FAILURE, message);
+
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemNew"))
+ return avahi_service_browser_event (client, AVAHI_BROWSER_NEW, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "ItemRemove"))
+ return avahi_service_browser_event (client, AVAHI_BROWSER_REMOVE, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "CacheExhausted"))
+ return avahi_service_browser_event (client, AVAHI_BROWSER_CACHE_EXHAUSTED, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "AllForNow"))
+ return avahi_service_browser_event (client, AVAHI_BROWSER_ALL_FOR_NOW, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_BROWSER, "Failure"))
+ return avahi_service_browser_event (client, AVAHI_BROWSER_FAILURE, message);
+
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_RESOLVER, "Found"))
+ return avahi_service_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_SERVICE_RESOLVER, "Failure"))
+ return avahi_service_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
+
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_HOST_NAME_RESOLVER, "Found"))
+ return avahi_host_name_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_HOST_NAME_RESOLVER, "Failure"))
+ return avahi_host_name_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
+
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_ADDRESS_RESOLVER, "Found"))
+ return avahi_address_resolver_event (client, AVAHI_RESOLVER_FOUND, message);
+ else if (dbus_message_is_signal(message, AVAHI_DBUS_INTERFACE_ADDRESS_RESOLVER, "Failure"))
+ return avahi_address_resolver_event (client, AVAHI_RESOLVER_FAILURE, message);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+fail:
+
+ dbus_error_free (&error);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int get_server_state(AvahiClient *client, int *ret_error) {
+ DBusMessage *message = NULL, *reply = NULL;
+ DBusError error;
+ int32_t state;
+ int e = AVAHI_ERR_NO_MEMORY;
+
+ assert(client);
+
+ dbus_error_init(&error);
+
+ if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetState")))
+ goto fail;
+
+ reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
+
+ if (!reply || dbus_error_is_set (&error))
+ goto fail;
+
+ if (!dbus_message_get_args(reply, &error, DBUS_TYPE_INT32, &state, DBUS_TYPE_INVALID) ||
+ dbus_error_is_set (&error))
+ goto fail;
+
+ client_set_state(client, (AvahiServerState) state);
+
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+
+ return AVAHI_OK;
+
+fail:
+ if (dbus_error_is_set(&error)) {
+ e = avahi_error_dbus_to_number (error.name);
+ dbus_error_free(&error);
+ }
+
+ if (ret_error)
+ *ret_error = e;
+
+ if (message)
+ dbus_message_unref(message);
+ if (reply)
+ dbus_message_unref(reply);
+
+ return e;
+}
+
+static int check_version(AvahiClient *client, int *ret_error) {
+ DBusMessage *message = NULL, *reply = NULL;
+ DBusError error;
+ char *version;
+ int e = AVAHI_ERR_NO_MEMORY;
+
+ assert(client);
+
+ dbus_error_init(&error);
+
+ if (!(message = dbus_message_new_method_call(AVAHI_DBUS_NAME, AVAHI_DBUS_PATH_SERVER, AVAHI_DBUS_INTERFACE_SERVER, "GetVersionString")))
+ goto fail;
+
+ reply = dbus_connection_send_with_reply_and_block (client->bus, message, -1, &error);
+
+ if (!reply || dbus_error_is_set (&error))
+ goto fail;
+
+ if (!dbus_message_get_args (reply, &error, DBUS_TYPE_STRING, &version, DBUS_TYPE_INVALID) ||
+ dbus_error_is_set (&error))
+ goto fail;
+
+ if (strcmp(version, PACKAGE_STRING) != 0) {
+ e = AVAHI_ERR_VERSION_MISMATCH;
+ goto fail;
+ }
+
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+
+ return AVAHI_OK;
+
+fail:
+ if (dbus_error_is_set(&error)) {
+ e = avahi_error_dbus_to_number (error.name);
+ dbus_error_free(&error);
+ }
+
+ if (ret_error)
+ *ret_error = e;
+
+ if (message)
+ dbus_message_unref(message);
+ if (reply)
+ dbus_message_unref(reply);
+
+ return e;
+}
+
+
+/* This function acts like dbus_bus_get but creates a private
+ * connection instead */
+static DBusConnection*
+avahi_dbus_bus_get (DBusError *error)