+#define MAX_CLIENTS 20
+#define MAX_OBJECTS_PER_CLIENT 50
+#define MAX_ENTRIES_PER_ENTRY_GROUP 20
+
+/* #define VALGRIND_WORKAROUND */
+
+struct EntryGroupInfo {
+ guint id;
+ Client *client;
+ AvahiEntryGroup *entry_group;
+ gchar *path;
+
+ gint n_entries;
+
+ AVAHI_LLIST_FIELDS(EntryGroupInfo, entry_groups);
+};
+
+struct HostNameResolverInfo {
+ Client *client;
+ AvahiHostNameResolver *host_name_resolver;
+ DBusMessage *message;
+
+ AVAHI_LLIST_FIELDS(HostNameResolverInfo, host_name_resolvers);
+};
+
+struct AddressResolverInfo {
+ Client *client;
+ AvahiAddressResolver *address_resolver;
+ DBusMessage *message;
+
+ AVAHI_LLIST_FIELDS(AddressResolverInfo, address_resolvers);
+};
+
+struct DomainBrowserInfo {
+ guint id;
+ Client *client;
+ AvahiDomainBrowser *domain_browser;
+ gchar *path;
+
+ AVAHI_LLIST_FIELDS(DomainBrowserInfo, domain_browsers);
+};
+
+struct ServiceTypeBrowserInfo {
+ guint id;
+ Client *client;
+ AvahiServiceTypeBrowser *service_type_browser;
+ gchar *path;
+
+ AVAHI_LLIST_FIELDS(ServiceTypeBrowserInfo, service_type_browsers);
+};
+
+struct ServiceBrowserInfo {
+ guint id;
+ Client *client;
+ AvahiServiceBrowser *service_browser;
+ gchar *path;
+
+ AVAHI_LLIST_FIELDS(ServiceBrowserInfo, service_browsers);
+};
+
+struct ServiceResolverInfo {
+ Client *client;
+ AvahiServiceResolver *service_resolver;
+ DBusMessage *message;
+
+ AVAHI_LLIST_FIELDS(ServiceResolverInfo, service_resolvers);
+};
+
+struct Client {
+ guint id;
+ gchar *name;
+ guint current_id;
+ gint n_objects;
+
+ AVAHI_LLIST_FIELDS(Client, clients);
+ AVAHI_LLIST_HEAD(EntryGroupInfo, entry_groups);
+ AVAHI_LLIST_HEAD(HostNameResolverInfo, host_name_resolvers);
+ AVAHI_LLIST_HEAD(AddressResolverInfo, address_resolvers);
+ AVAHI_LLIST_HEAD(DomainBrowserInfo, domain_browsers);
+ AVAHI_LLIST_HEAD(ServiceTypeBrowserInfo, service_type_browsers);
+ AVAHI_LLIST_HEAD(ServiceBrowserInfo, service_browsers);
+ AVAHI_LLIST_HEAD(ServiceResolverInfo, service_resolvers);
+};
+
+struct Server {
+ DBusConnection *bus;
+ AVAHI_LLIST_HEAD(Client, clients);
+ gint n_clients;
+ guint current_id;
+};
+
+static Server *server = NULL;
+
+static void entry_group_free(EntryGroupInfo *i) {
+ g_assert(i);
+
+ if (i->entry_group)
+ avahi_entry_group_free(i->entry_group);
+ dbus_connection_unregister_object_path(server->bus, i->path);
+ g_free(i->path);
+ AVAHI_LLIST_REMOVE(EntryGroupInfo, entry_groups, i->client->entry_groups, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+ }
+
+static void host_name_resolver_free(HostNameResolverInfo *i) {
+ g_assert(i);
+
+ if (i->host_name_resolver)
+ avahi_host_name_resolver_free(i->host_name_resolver);
+ dbus_message_unref(i->message);
+ AVAHI_LLIST_REMOVE(HostNameResolverInfo, host_name_resolvers, i->client->host_name_resolvers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void address_resolver_free(AddressResolverInfo *i) {
+ g_assert(i);
+
+ if (i->address_resolver)
+ avahi_address_resolver_free(i->address_resolver);
+ dbus_message_unref(i->message);
+ AVAHI_LLIST_REMOVE(AddressResolverInfo, address_resolvers, i->client->address_resolvers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void domain_browser_free(DomainBrowserInfo *i) {
+ g_assert(i);
+
+ if (i->domain_browser)
+ avahi_domain_browser_free(i->domain_browser);
+ dbus_connection_unregister_object_path(server->bus, i->path);
+ g_free(i->path);
+ AVAHI_LLIST_REMOVE(DomainBrowserInfo, domain_browsers, i->client->domain_browsers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void service_type_browser_free(ServiceTypeBrowserInfo *i) {
+ g_assert(i);
+
+ if (i->service_type_browser)
+ avahi_service_type_browser_free(i->service_type_browser);
+ dbus_connection_unregister_object_path(server->bus, i->path);
+ g_free(i->path);
+ AVAHI_LLIST_REMOVE(ServiceTypeBrowserInfo, service_type_browsers, i->client->service_type_browsers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void service_browser_free(ServiceBrowserInfo *i) {
+ g_assert(i);
+
+ if (i->service_browser)
+ avahi_service_browser_free(i->service_browser);
+ dbus_connection_unregister_object_path(server->bus, i->path);
+ g_free(i->path);
+ AVAHI_LLIST_REMOVE(ServiceBrowserInfo, service_browsers, i->client->service_browsers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void service_resolver_free(ServiceResolverInfo *i) {
+ g_assert(i);
+
+ if (i->service_resolver)
+ avahi_service_resolver_free(i->service_resolver);
+ dbus_message_unref(i->message);
+ AVAHI_LLIST_REMOVE(ServiceResolverInfo, service_resolvers, i->client->service_resolvers, i);
+
+ i->client->n_objects--;
+ g_assert(i->client->n_objects >= 0);
+
+ g_free(i);
+}
+
+static void client_free(Client *c) {
+
+ g_assert(server);
+ g_assert(c);
+
+ while (c->entry_groups)
+ entry_group_free(c->entry_groups);
+
+ while (c->host_name_resolvers)
+ host_name_resolver_free(c->host_name_resolvers);
+
+ while (c->address_resolvers)
+ address_resolver_free(c->address_resolvers);
+
+ while (c->domain_browsers)
+ domain_browser_free(c->domain_browsers);
+
+ while (c->service_type_browsers)
+ service_type_browser_free(c->service_type_browsers);
+
+ while (c->service_browsers)
+ service_browser_free(c->service_browsers);
+
+ while (c->service_resolvers)
+ service_resolver_free(c->service_resolvers);
+
+ g_assert(c->n_objects == 0);
+
+ g_free(c->name);
+ AVAHI_LLIST_REMOVE(Client, clients, server->clients, c);
+ g_free(c);
+
+ server->n_clients --;
+ g_assert(server->n_clients >= 0);
+}
+
+static Client *client_get(const gchar *name, gboolean create) {
+ Client *client;
+
+ g_assert(server);
+ g_assert(name);
+
+ for (client = server->clients; client; client = client->clients_next)
+ if (!strcmp(name, client->name))
+ return client;
+
+ if (!create)
+ return NULL;
+
+ if (server->n_clients >= MAX_CLIENTS)
+ return NULL;
+
+ /* If not existant yet, create a new entry */
+ client = g_new(Client, 1);
+ client->id = server->current_id++;
+ client->name = g_strdup(name);
+ client->current_id = 0;
+ client->n_objects = 0;
+ AVAHI_LLIST_HEAD_INIT(EntryGroupInfo, client->entry_groups);
+ AVAHI_LLIST_HEAD_INIT(HostNameResolverInfo, client->host_name_resolvers);
+ AVAHI_LLIST_HEAD_INIT(AddressResolverInfo, client->address_resolvers);
+ AVAHI_LLIST_HEAD_INIT(DomainBrowserInfo, client->domain_browsers);
+ AVAHI_LLIST_HEAD_INIT(ServiceTypeBrowserInfo, client->service_type_browsers);
+ AVAHI_LLIST_HEAD_INIT(ServiceBrowserInfo, client->service_browsers);
+ AVAHI_LLIST_HEAD_INIT(ServiceResolverInfo, client->service_resolvers);
+
+ AVAHI_LLIST_PREPEND(Client, clients, server->clients, client);
+
+ server->n_clients++;
+ g_assert(server->n_clients > 0);
+
+ return client;
+}
+
+static DBusHandlerResult respond_error(DBusConnection *c, DBusMessage *m, gint error, const gchar *text) {
+ DBusMessage *reply;
+
+ const gchar * const table[- AVAHI_ERR_MAX] = {
+ NULL, /* OK */
+ AVAHI_DBUS_ERR_FAILURE,
+ AVAHI_DBUS_ERR_BAD_STATE,
+ AVAHI_DBUS_ERR_INVALID_HOST_NAME,
+ AVAHI_DBUS_ERR_INVALID_DOMAIN_NAME,
+ AVAHI_DBUS_ERR_NO_NETWORK,
+ AVAHI_DBUS_ERR_INVALID_TTL,
+ AVAHI_DBUS_ERR_IS_PATTERN,
+ AVAHI_DBUS_ERR_LOCAL_COLLISION,
+ AVAHI_DBUS_ERR_INVALID_RECORD,
+ AVAHI_DBUS_ERR_INVALID_SERVICE_NAME,
+ AVAHI_DBUS_ERR_INVALID_SERVICE_TYPE,
+ AVAHI_DBUS_ERR_INVALID_PORT,
+ AVAHI_DBUS_ERR_INVALID_KEY,
+ AVAHI_DBUS_ERR_INVALID_ADDRESS,
+ AVAHI_DBUS_ERR_TIMEOUT,
+ AVAHI_DBUS_ERR_TOO_MANY_CLIENTS,
+ AVAHI_DBUS_ERR_TOO_MANY_OBJECTS,
+ AVAHI_DBUS_ERR_TOO_MANY_ENTRIES,
+ AVAHI_DBUS_ERR_OS,
+ AVAHI_DBUS_ERR_ACCESS_DENIED,
+ AVAHI_DBUS_ERR_INVALID_OPERATION
+ };
+
+ g_assert(-error > -AVAHI_OK);
+ g_assert(-error < -AVAHI_ERR_MAX);
+
+ reply = dbus_message_new_error(m, table[-error], text ? text : avahi_strerror(error));
+ dbus_connection_send(c, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult respond_string(DBusConnection *c, DBusMessage *m, const gchar *text) {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(m);
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &text, DBUS_TYPE_INVALID);
+ dbus_connection_send(c, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult respond_int32(DBusConnection *c, DBusMessage *m, gint32 i) {
+ DBusMessage *reply;
+
+ reply = dbus_message_new_method_return(m);
+ dbus_message_append_args(reply, DBUS_TYPE_INT32, &i, DBUS_TYPE_INVALID);
+ dbus_connection_send(c, reply, NULL);
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult respond_ok(DBusConnection *c, DBusMessage *m) {
+ DBusMessage *reply;