From de6fc99ef5a1767fae07fbea9bc384f512639e57 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Sun, 28 Oct 2007 00:17:16 +0000 Subject: split off libavahi-gobject into its own directory git-svn-id: file:///home/lennart/svn/public/avahi/trunk@1564 941a03a8-eaeb-0310-b9a0-b1bbd8fe43fe --- avahi-gobject/ga-entry-group.c | 621 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 621 insertions(+) create mode 100644 avahi-gobject/ga-entry-group.c (limited to 'avahi-gobject/ga-entry-group.c') diff --git a/avahi-gobject/ga-entry-group.c b/avahi-gobject/ga-entry-group.c new file mode 100644 index 0000000..9b79e83 --- /dev/null +++ b/avahi-gobject/ga-entry-group.c @@ -0,0 +1,621 @@ +/* + * ga-entry-group.c - Source for GaEntryGroup + * Copyright (C) 2005 Collabora Ltd. + * + * This library 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. + * + * This library 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "ga-errors.h" +#include "ga-entry-group.h" +#include "ga-entry-group-enumtypes.h" + +G_DEFINE_TYPE(GaEntryGroup, ga_entry_group, G_TYPE_OBJECT) + +static void _free_service(gpointer data); + +/* signal enum */ +enum { + STATE_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* properties */ +enum { + PROP_STATE = 1 +}; + +/* private structures */ +typedef struct _GaEntryGroupPrivate GaEntryGroupPrivate; + +struct _GaEntryGroupPrivate { + GaEntryGroupState state; + GaClient *client; + AvahiEntryGroup *group; + GHashTable *services; + gboolean dispose_has_run; +}; + +typedef struct _GaEntryGroupServicePrivate GaEntryGroupServicePrivate; + +struct _GaEntryGroupServicePrivate { + GaEntryGroupService public; + GaEntryGroup *group; + gboolean frozen; + GHashTable *entries; +}; + +typedef struct _GaEntryGroupServiceEntry GaEntryGroupServiceEntry; + +struct _GaEntryGroupServiceEntry { + guint8 *value; + gsize size; +}; + + +#define GA_ENTRY_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GA_TYPE_ENTRY_GROUP, GaEntryGroupPrivate)) + +static void ga_entry_group_init(GaEntryGroup * obj) { + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(obj); + /* allocate any data required by the object here */ + priv->state = GA_ENTRY_GROUP_STATE_UNCOMMITED; + priv->client = NULL; + priv->group = NULL; + priv->services = g_hash_table_new_full(g_direct_hash, + g_direct_equal, + NULL, _free_service); +} + +static void ga_entry_group_dispose(GObject * object); +static void ga_entry_group_finalize(GObject * object); + +static void ga_entry_group_get_property(GObject * object, + guint property_id, + GValue * value, GParamSpec * pspec) { + GaEntryGroup *group = GA_ENTRY_GROUP(object); + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + + switch (property_id) { + case PROP_STATE: + g_value_set_enum(value, priv->state); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + break; + } +} + +static void ga_entry_group_class_init(GaEntryGroupClass * ga_entry_group_class) { + GObjectClass *object_class = G_OBJECT_CLASS(ga_entry_group_class); + GParamSpec *param_spec; + + g_type_class_add_private(ga_entry_group_class, + sizeof (GaEntryGroupPrivate)); + + object_class->dispose = ga_entry_group_dispose; + object_class->finalize = ga_entry_group_finalize; + object_class->get_property = ga_entry_group_get_property; + + param_spec = g_param_spec_enum("state", "Entry Group state", + "The state of the avahi entry group", + GA_TYPE_ENTRY_GROUP_STATE, + GA_ENTRY_GROUP_STATE_UNCOMMITED, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB); + g_object_class_install_property(object_class, PROP_STATE, param_spec); + + signals[STATE_CHANGED] = + g_signal_new("state-changed", + G_OBJECT_CLASS_TYPE(ga_entry_group_class), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__ENUM, + G_TYPE_NONE, 1, GA_TYPE_ENTRY_GROUP_STATE); +} + +void ga_entry_group_dispose(GObject * object) { + GaEntryGroup *self = GA_ENTRY_GROUP(object); + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self); + + if (priv->dispose_has_run) + return; + priv->dispose_has_run = TRUE; + + /* release any references held by the object here */ + if (priv->group) { + avahi_entry_group_free(priv->group); + priv->group = NULL; + } + + if (priv->client) { + g_object_unref(priv->client); + priv->client = NULL; + } + + if (G_OBJECT_CLASS(ga_entry_group_parent_class)->dispose) + G_OBJECT_CLASS(ga_entry_group_parent_class)->dispose(object); +} + +void ga_entry_group_finalize(GObject * object) { + GaEntryGroup *self = GA_ENTRY_GROUP(object); + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self); + + /* free any data held directly by the object here */ + g_hash_table_destroy(priv->services); + priv->services = NULL; + + G_OBJECT_CLASS(ga_entry_group_parent_class)->finalize(object); +} + +static void _free_service(gpointer data) { + GaEntryGroupService *s = (GaEntryGroupService *) data; + GaEntryGroupServicePrivate *p = (GaEntryGroupServicePrivate *) s; + g_free(s->name); + g_free(s->type); + g_free(s->domain); + g_free(s->host); + g_hash_table_destroy(p->entries); + g_free(s); +} + +static GQuark detail_for_state(AvahiEntryGroupState state) { + static struct { + AvahiClientState state; + const gchar *name; + GQuark quark; + } states[] = { + { AVAHI_ENTRY_GROUP_UNCOMMITED, "uncommited", 0}, + { AVAHI_ENTRY_GROUP_REGISTERING, "registering", 0}, + { AVAHI_ENTRY_GROUP_ESTABLISHED, "established", 0}, + { AVAHI_ENTRY_GROUP_COLLISION, "collistion", 0}, + { AVAHI_ENTRY_GROUP_FAILURE, "failure", 0}, + { 0, NULL, 0} + }; + int i; + + for (i = 0; states[i].name != NULL; i++) { + if (state != states[i].state) + continue; + + if (!states[i].quark) + states[i].quark = g_quark_from_static_string(states[i].name); + return states[i].quark; + } + g_assert_not_reached(); +} + +static void _avahi_entry_group_cb(AvahiEntryGroup * g, + AvahiEntryGroupState state, void *data) { + GaEntryGroup *self = GA_ENTRY_GROUP(data); + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self); + + /* Avahi can call the callback before return from _client_new */ + if (priv->group == NULL) + priv->group = g; + + g_assert(g == priv->group); + priv->state = state; + g_signal_emit(self, signals[STATE_CHANGED], + detail_for_state(state), state); +} + +GaEntryGroup *ga_entry_group_new(void) { + return g_object_new(GA_TYPE_ENTRY_GROUP, NULL); +} + +static guint _entry_hash(gconstpointer v) { + const GaEntryGroupServiceEntry *entry = + (const GaEntryGroupServiceEntry *) v; + guint32 h = 0; + guint i; + + for (i = 0; i < entry->size; i++) { + h = (h << 5) - h + entry->value[i]; + } + + return h; +} + +static gboolean _entry_equal(gconstpointer a, gconstpointer b) { + const GaEntryGroupServiceEntry *aentry = + (const GaEntryGroupServiceEntry *) a; + const GaEntryGroupServiceEntry *bentry = + (const GaEntryGroupServiceEntry *) b; + + if (aentry->size != bentry->size) { + return FALSE; + } + + return memcmp(aentry->value, bentry->value, aentry->size) == 0; +} + +static GaEntryGroupServiceEntry *_new_entry(const guint8 * value, gsize size) { + GaEntryGroupServiceEntry *entry; + + if (value == NULL) { + return NULL; + } + + entry = g_slice_new(GaEntryGroupServiceEntry); + entry->value = g_malloc(size + 1); + memcpy(entry->value, value, size); + /* for string keys, make sure it's NUL-terminated too */ + entry->value[size] = 0; + entry->size = size; + + return entry; +} + +static void _set_entry(GHashTable * table, const guint8 * key, gsize ksize, + const guint8 * value, gsize vsize) { + + g_hash_table_insert(table, _new_entry(key, ksize), + _new_entry(value, vsize)); +} + +static void _free_entry(gpointer data) { + GaEntryGroupServiceEntry *entry = (GaEntryGroupServiceEntry *) data; + + if (entry == NULL) { + return; + } + + g_free(entry->value); + g_slice_free(GaEntryGroupServiceEntry, entry); +} + +static GHashTable *_string_list_to_hash(AvahiStringList * list) { + GHashTable *ret; + AvahiStringList *t; + + ret = g_hash_table_new_full(_entry_hash, + _entry_equal, _free_entry, _free_entry); + + for (t = list; t != NULL; t = avahi_string_list_get_next(t)) { + gchar *key; + gchar *value; + gsize size; + int r; + + /* list_get_pair only fails if if memory allocation fails. Normal glib + * behaviour is to assert/abort when that happens */ + r = avahi_string_list_get_pair(t, &key, &value, &size); + g_assert(r == 0); + + if (value == NULL) { + _set_entry(ret, t->text, t->size, NULL, 0); + } else { + _set_entry(ret, (const guint8 *) key, strlen(key), + (const guint8 *) value, size); + } + avahi_free(key); + avahi_free(value); + } + return ret; +} + +static void _hash_to_string_list_foreach(gpointer key, gpointer value, gpointer data) { + AvahiStringList **list = (AvahiStringList **) data; + GaEntryGroupServiceEntry *kentry = (GaEntryGroupServiceEntry *) key; + GaEntryGroupServiceEntry *ventry = (GaEntryGroupServiceEntry *) value; + + if (value != NULL) { + *list = avahi_string_list_add_pair_arbitrary(*list, + (gchar *) kentry->value, + ventry->value, + ventry->size); + } else { + *list = avahi_string_list_add_arbitrary(*list, + kentry->value, kentry->size); + } +} + +static AvahiStringList *_hash_to_string_list(GHashTable * table) { + AvahiStringList *list = NULL; + g_hash_table_foreach(table, _hash_to_string_list_foreach, + (gpointer) & list); + return list; +} + +GaEntryGroupService *ga_entry_group_add_service_strlist(GaEntryGroup * group, + const gchar * name, + const gchar * type, + guint16 port, + GError ** error, + AvahiStringList * + txt) { + return ga_entry_group_add_service_full_strlist(group, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, 0, + name, type, NULL, NULL, + port, error, txt); +} + +GaEntryGroupService *ga_entry_group_add_service_full_strlist(GaEntryGroup * + group, + AvahiIfIndex + interface, + AvahiProtocol + protocol, + AvahiPublishFlags + flags, + const gchar * + name, + const gchar * + type, + const gchar * + domain, + const gchar * + host, + guint16 port, + GError ** error, + AvahiStringList * + txt) { + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + GaEntryGroupServicePrivate *service = NULL; + int ret; + + ret = avahi_entry_group_add_service_strlst(priv->group, + interface, protocol, + flags, + name, type, + domain, host, port, txt); + if (ret) { + if (error != NULL) { + *error = g_error_new(GA_ERRORS, ret, + "Adding service to group failed: %s", + avahi_strerror(ret)); + } + goto out; + } + + service = g_new0(GaEntryGroupServicePrivate, 1); + service->public.interface = interface; + service->public.protocol = protocol; + service->public.flags = flags; + service->public.name = g_strdup(name); + service->public.type = g_strdup(type); + service->public.domain = g_strdup(domain); + service->public.host = g_strdup(host); + service->public.port = port; + service->group = group; + service->frozen = FALSE; + service->entries = _string_list_to_hash(txt); + g_hash_table_insert(priv->services, group, service); + out: + return (GaEntryGroupService *) service; +} + +GaEntryGroupService *ga_entry_group_add_service(GaEntryGroup * group, + const gchar * name, + const gchar * type, + guint16 port, + GError ** error, ...) { + GaEntryGroupService *ret; + AvahiStringList *txt = NULL; + va_list va; + va_start(va, error); + txt = avahi_string_list_new_va(va); + + ret = ga_entry_group_add_service_full_strlist(group, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + 0, + name, type, + NULL, NULL, + port, error, txt); + avahi_string_list_free(txt); + va_end(va); + return ret; +} + +GaEntryGroupService *ga_entry_group_add_service_full(GaEntryGroup * group, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const gchar * name, + const gchar * type, + const gchar * domain, + const gchar * host, + guint16 port, + GError ** error, ...) { + GaEntryGroupService *ret; + AvahiStringList *txt = NULL; + va_list va; + + va_start(va, error); + txt = avahi_string_list_new_va(va); + + ret = ga_entry_group_add_service_full_strlist(group, + interface, protocol, + flags, + name, type, + domain, host, + port, error, txt); + avahi_string_list_free(txt); + va_end(va); + return ret; +} + +gboolean ga_entry_group_add_record(GaEntryGroup * group, + AvahiPublishFlags flags, + const gchar * name, + guint16 type, + guint32 ttl, + const void *rdata, gsize size, GError ** error) { + return ga_entry_group_add_record_full(group, + AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + flags, name, AVAHI_DNS_CLASS_IN, + type, ttl, rdata, size, error); +} + +gboolean ga_entry_group_add_record_full(GaEntryGroup * group, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiPublishFlags flags, + const gchar * name, + guint16 clazz, + guint16 type, + guint32 ttl, + const void *rdata, + gsize size, GError ** error) { + int ret; + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + g_assert(group != NULL && priv->group != NULL); + + ret = avahi_entry_group_add_record(priv->group, interface, protocol, + flags, name, clazz, type, ttl, rdata, + size); + if (ret) { + if (error != NULL) { + *error = g_error_new(GA_ERRORS, ret, + "Setting raw record failed: %s", + avahi_strerror(ret)); + } + return FALSE; + } + return TRUE; +} + + +void ga_entry_group_service_freeze(GaEntryGroupService * service) { + GaEntryGroupServicePrivate *p = (GaEntryGroupServicePrivate *) service; + p->frozen = TRUE; +} + +gboolean ga_entry_group_service_thaw(GaEntryGroupService * service, GError ** error) { + GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service; + int ret; + gboolean result = TRUE; + + AvahiStringList *txt = _hash_to_string_list(priv->entries); + ret = avahi_entry_group_update_service_txt_strlst + (GA_ENTRY_GROUP_GET_PRIVATE(priv->group)->group, + service->interface, service->protocol, service->flags, + service->name, service->type, service->domain, txt); + if (ret) { + if (error != NULL) { + *error = g_error_new(GA_ERRORS, ret, + "Updating txt record failed: %s", + avahi_strerror(ret)); + } + result = FALSE; + } + + avahi_string_list_free(txt); + priv->frozen = FALSE; + return result; +} + +gboolean ga_entry_group_service_set(GaEntryGroupService * service, + const gchar * key, const gchar * value, + GError ** error) { + return ga_entry_group_service_set_arbitrary(service, key, + (const guint8 *) value, + strlen(value), error); + +} + +gboolean ga_entry_group_service_set_arbitrary(GaEntryGroupService * service, + const gchar * key, const guint8 * value, + gsize size, GError ** error) { + GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service; + + _set_entry(priv->entries, (const guint8 *) key, strlen(key), value, size); + + if (!priv->frozen) + return ga_entry_group_service_thaw(service, error); + else + return TRUE; +} + +gboolean ga_entry_group_service_remove_key(GaEntryGroupService * service, + const gchar * key, GError ** error) { + GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service; + + g_hash_table_remove(priv->entries, key); + + if (!priv->frozen) + return ga_entry_group_service_thaw(service, error); + else + return TRUE; +} + + +gboolean ga_entry_group_attach(GaEntryGroup * group, + GaClient * client, GError ** error) { + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + + g_assert(priv->client == NULL || priv->client == client); + g_assert(priv->group == NULL); + + priv->client = client; + g_object_ref(client); + + priv->group = avahi_entry_group_new(client->avahi_client, + _avahi_entry_group_cb, group); + if (priv->group == NULL) { + if (error != NULL) { + int aerrno = avahi_client_errno(client->avahi_client); + *error = g_error_new(GA_ERRORS, aerrno, + "Attaching group failed: %s", + avahi_strerror(aerrno)); + } + return FALSE; + } + return TRUE; +} + +gboolean ga_entry_group_commit(GaEntryGroup * group, GError ** error) { + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + int ret; + ret = avahi_entry_group_commit(priv->group); + if (ret) { + if (error != NULL) { + *error = g_error_new(GA_ERRORS, ret, + "Committing group failed: %s", + avahi_strerror(ret)); + } + return FALSE; + } + return TRUE; +} + +gboolean ga_entry_group_reset(GaEntryGroup * group, GError ** error) { + GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group); + int ret; + ret = avahi_entry_group_reset(priv->group); + if (ret) { + if (error != NULL) { + *error = g_error_new(GA_ERRORS, ret, + "Resetting group failed: %s", + avahi_strerror(ret)); + } + return FALSE; + } + return TRUE; +} -- cgit