2 * ga-entry-group.c - Source for GaEntryGroup
3 * Copyright (C) 2005 Collabora Ltd.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <avahi-common/malloc.h>
31 #include "ga-entry-group.h"
32 #include "ga-entry-group-enumtypes.h"
34 G_DEFINE_TYPE(GaEntryGroup, ga_entry_group, G_TYPE_OBJECT)
36 static void _free_service(gpointer data);
44 static guint signals[LAST_SIGNAL] = { 0 };
51 /* private structures */
52 typedef struct _GaEntryGroupPrivate GaEntryGroupPrivate;
54 struct _GaEntryGroupPrivate {
55 GaEntryGroupState state;
57 AvahiEntryGroup *group;
59 gboolean dispose_has_run;
62 typedef struct _GaEntryGroupServicePrivate GaEntryGroupServicePrivate;
64 struct _GaEntryGroupServicePrivate {
65 GaEntryGroupService public;
71 typedef struct _GaEntryGroupServiceEntry GaEntryGroupServiceEntry;
73 struct _GaEntryGroupServiceEntry {
79 #define GA_ENTRY_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GA_TYPE_ENTRY_GROUP, GaEntryGroupPrivate))
81 static void ga_entry_group_init(GaEntryGroup * obj) {
82 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(obj);
83 /* allocate any data required by the object here */
84 priv->state = GA_ENTRY_GROUP_STATE_UNCOMMITED;
87 priv->services = g_hash_table_new_full(g_direct_hash,
92 static void ga_entry_group_dispose(GObject * object);
93 static void ga_entry_group_finalize(GObject * object);
95 static void ga_entry_group_get_property(GObject * object,
97 GValue * value, GParamSpec * pspec) {
98 GaEntryGroup *group = GA_ENTRY_GROUP(object);
99 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
101 switch (property_id) {
103 g_value_set_enum(value, priv->state);
106 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
111 static void ga_entry_group_class_init(GaEntryGroupClass * ga_entry_group_class) {
112 GObjectClass *object_class = G_OBJECT_CLASS(ga_entry_group_class);
113 GParamSpec *param_spec;
115 g_type_class_add_private(ga_entry_group_class,
116 sizeof (GaEntryGroupPrivate));
118 object_class->dispose = ga_entry_group_dispose;
119 object_class->finalize = ga_entry_group_finalize;
120 object_class->get_property = ga_entry_group_get_property;
122 param_spec = g_param_spec_enum("state", "Entry Group state",
123 "The state of the avahi entry group",
124 GA_TYPE_ENTRY_GROUP_STATE,
125 GA_ENTRY_GROUP_STATE_UNCOMMITED,
127 G_PARAM_STATIC_NAME |
128 G_PARAM_STATIC_BLURB);
129 g_object_class_install_property(object_class, PROP_STATE, param_spec);
131 signals[STATE_CHANGED] =
132 g_signal_new("state-changed",
133 G_OBJECT_CLASS_TYPE(ga_entry_group_class),
134 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
137 g_cclosure_marshal_VOID__ENUM,
138 G_TYPE_NONE, 1, GA_TYPE_ENTRY_GROUP_STATE);
141 void ga_entry_group_dispose(GObject * object) {
142 GaEntryGroup *self = GA_ENTRY_GROUP(object);
143 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self);
145 if (priv->dispose_has_run)
147 priv->dispose_has_run = TRUE;
149 /* release any references held by the object here */
151 avahi_entry_group_free(priv->group);
156 g_object_unref(priv->client);
160 if (G_OBJECT_CLASS(ga_entry_group_parent_class)->dispose)
161 G_OBJECT_CLASS(ga_entry_group_parent_class)->dispose(object);
164 void ga_entry_group_finalize(GObject * object) {
165 GaEntryGroup *self = GA_ENTRY_GROUP(object);
166 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self);
168 /* free any data held directly by the object here */
169 g_hash_table_destroy(priv->services);
170 priv->services = NULL;
172 G_OBJECT_CLASS(ga_entry_group_parent_class)->finalize(object);
175 static void _free_service(gpointer data) {
176 GaEntryGroupService *s = (GaEntryGroupService *) data;
177 GaEntryGroupServicePrivate *p = (GaEntryGroupServicePrivate *) s;
182 g_hash_table_destroy(p->entries);
186 static GQuark detail_for_state(AvahiEntryGroupState state) {
188 AvahiClientState state;
192 { AVAHI_ENTRY_GROUP_UNCOMMITED, "uncommited", 0},
193 { AVAHI_ENTRY_GROUP_REGISTERING, "registering", 0},
194 { AVAHI_ENTRY_GROUP_ESTABLISHED, "established", 0},
195 { AVAHI_ENTRY_GROUP_COLLISION, "collistion", 0},
196 { AVAHI_ENTRY_GROUP_FAILURE, "failure", 0},
201 for (i = 0; states[i].name != NULL; i++) {
202 if (state != states[i].state)
205 if (!states[i].quark)
206 states[i].quark = g_quark_from_static_string(states[i].name);
207 return states[i].quark;
209 g_assert_not_reached();
212 static void _avahi_entry_group_cb(AvahiEntryGroup * g,
213 AvahiEntryGroupState state, void *data) {
214 GaEntryGroup *self = GA_ENTRY_GROUP(data);
215 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(self);
217 /* Avahi can call the callback before return from _client_new */
218 if (priv->group == NULL)
221 g_assert(g == priv->group);
223 g_signal_emit(self, signals[STATE_CHANGED],
224 detail_for_state(state), state);
227 GaEntryGroup *ga_entry_group_new(void) {
228 return g_object_new(GA_TYPE_ENTRY_GROUP, NULL);
231 static guint _entry_hash(gconstpointer v) {
232 const GaEntryGroupServiceEntry *entry =
233 (const GaEntryGroupServiceEntry *) v;
237 for (i = 0; i < entry->size; i++) {
238 h = (h << 5) - h + entry->value[i];
244 static gboolean _entry_equal(gconstpointer a, gconstpointer b) {
245 const GaEntryGroupServiceEntry *aentry =
246 (const GaEntryGroupServiceEntry *) a;
247 const GaEntryGroupServiceEntry *bentry =
248 (const GaEntryGroupServiceEntry *) b;
250 if (aentry->size != bentry->size) {
254 return memcmp(aentry->value, bentry->value, aentry->size) == 0;
257 static GaEntryGroupServiceEntry *_new_entry(const guint8 * value, gsize size) {
258 GaEntryGroupServiceEntry *entry;
264 entry = g_slice_new(GaEntryGroupServiceEntry);
265 entry->value = g_malloc(size + 1);
266 memcpy(entry->value, value, size);
267 /* for string keys, make sure it's NUL-terminated too */
268 entry->value[size] = 0;
274 static void _set_entry(GHashTable * table, const guint8 * key, gsize ksize,
275 const guint8 * value, gsize vsize) {
277 g_hash_table_insert(table, _new_entry(key, ksize),
278 _new_entry(value, vsize));
281 static void _free_entry(gpointer data) {
282 GaEntryGroupServiceEntry *entry = (GaEntryGroupServiceEntry *) data;
288 g_free(entry->value);
289 g_slice_free(GaEntryGroupServiceEntry, entry);
292 static GHashTable *_string_list_to_hash(AvahiStringList * list) {
296 ret = g_hash_table_new_full(_entry_hash,
297 _entry_equal, _free_entry, _free_entry);
299 for (t = list; t != NULL; t = avahi_string_list_get_next(t)) {
305 /* list_get_pair only fails if if memory allocation fails. Normal glib
306 * behaviour is to assert/abort when that happens */
307 r = avahi_string_list_get_pair(t, &key, &value, &size);
311 _set_entry(ret, t->text, t->size, NULL, 0);
313 _set_entry(ret, (const guint8 *) key, strlen(key),
314 (const guint8 *) value, size);
322 static void _hash_to_string_list_foreach(gpointer key, gpointer value, gpointer data) {
323 AvahiStringList **list = (AvahiStringList **) data;
324 GaEntryGroupServiceEntry *kentry = (GaEntryGroupServiceEntry *) key;
325 GaEntryGroupServiceEntry *ventry = (GaEntryGroupServiceEntry *) value;
328 *list = avahi_string_list_add_pair_arbitrary(*list,
329 (gchar *) kentry->value,
333 *list = avahi_string_list_add_arbitrary(*list,
334 kentry->value, kentry->size);
338 static AvahiStringList *_hash_to_string_list(GHashTable * table) {
339 AvahiStringList *list = NULL;
340 g_hash_table_foreach(table, _hash_to_string_list_foreach,
345 GaEntryGroupService *ga_entry_group_add_service_strlist(GaEntryGroup * group,
352 return ga_entry_group_add_service_full_strlist(group, AVAHI_IF_UNSPEC,
353 AVAHI_PROTO_UNSPEC, 0,
354 name, type, NULL, NULL,
358 GaEntryGroupService *ga_entry_group_add_service_full_strlist(GaEntryGroup *
378 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
379 GaEntryGroupServicePrivate *service = NULL;
382 ret = avahi_entry_group_add_service_strlst(priv->group,
386 domain, host, port, txt);
389 *error = g_error_new(GA_ERROR, ret,
390 "Adding service to group failed: %s",
391 avahi_strerror(ret));
396 service = g_new0(GaEntryGroupServicePrivate, 1);
397 service->public.interface = interface;
398 service->public.protocol = protocol;
399 service->public.flags = flags;
400 service->public.name = g_strdup(name);
401 service->public.type = g_strdup(type);
402 service->public.domain = g_strdup(domain);
403 service->public.host = g_strdup(host);
404 service->public.port = port;
405 service->group = group;
406 service->frozen = FALSE;
407 service->entries = _string_list_to_hash(txt);
408 g_hash_table_insert(priv->services, group, service);
410 return (GaEntryGroupService *) service;
413 GaEntryGroupService *ga_entry_group_add_service(GaEntryGroup * group,
417 GError ** error, ...) {
418 GaEntryGroupService *ret;
419 AvahiStringList *txt = NULL;
422 txt = avahi_string_list_new_va(va);
424 ret = ga_entry_group_add_service_full_strlist(group,
431 avahi_string_list_free(txt);
436 GaEntryGroupService *ga_entry_group_add_service_full(GaEntryGroup * group,
437 AvahiIfIndex interface,
438 AvahiProtocol protocol,
439 AvahiPublishFlags flags,
442 const gchar * domain,
445 GError ** error, ...) {
446 GaEntryGroupService *ret;
447 AvahiStringList *txt = NULL;
451 txt = avahi_string_list_new_va(va);
453 ret = ga_entry_group_add_service_full_strlist(group,
459 avahi_string_list_free(txt);
464 gboolean ga_entry_group_add_record(GaEntryGroup * group,
465 AvahiPublishFlags flags,
469 const void *rdata, gsize size, GError ** error) {
470 return ga_entry_group_add_record_full(group,
471 AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
472 flags, name, AVAHI_DNS_CLASS_IN,
473 type, ttl, rdata, size, error);
476 gboolean ga_entry_group_add_record_full(GaEntryGroup * group,
477 AvahiIfIndex interface,
478 AvahiProtocol protocol,
479 AvahiPublishFlags flags,
485 gsize size, GError ** error) {
487 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
488 g_assert(group != NULL && priv->group != NULL);
490 ret = avahi_entry_group_add_record(priv->group, interface, protocol,
491 flags, name, clazz, type, ttl, rdata,
495 *error = g_error_new(GA_ERROR, ret,
496 "Setting raw record failed: %s",
497 avahi_strerror(ret));
505 void ga_entry_group_service_freeze(GaEntryGroupService * service) {
506 GaEntryGroupServicePrivate *p = (GaEntryGroupServicePrivate *) service;
510 gboolean ga_entry_group_service_thaw(GaEntryGroupService * service, GError ** error) {
511 GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service;
513 gboolean result = TRUE;
515 AvahiStringList *txt = _hash_to_string_list(priv->entries);
516 ret = avahi_entry_group_update_service_txt_strlst
517 (GA_ENTRY_GROUP_GET_PRIVATE(priv->group)->group,
518 service->interface, service->protocol, service->flags,
519 service->name, service->type, service->domain, txt);
522 *error = g_error_new(GA_ERROR, ret,
523 "Updating txt record failed: %s",
524 avahi_strerror(ret));
529 avahi_string_list_free(txt);
530 priv->frozen = FALSE;
534 gboolean ga_entry_group_service_set(GaEntryGroupService * service,
535 const gchar * key, const gchar * value,
537 return ga_entry_group_service_set_arbitrary(service, key,
538 (const guint8 *) value,
539 strlen(value), error);
543 gboolean ga_entry_group_service_set_arbitrary(GaEntryGroupService * service,
544 const gchar * key, const guint8 * value,
545 gsize size, GError ** error) {
546 GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service;
548 _set_entry(priv->entries, (const guint8 *) key, strlen(key), value, size);
551 return ga_entry_group_service_thaw(service, error);
556 gboolean ga_entry_group_service_remove_key(GaEntryGroupService * service,
557 const gchar * key, GError ** error) {
558 GaEntryGroupServicePrivate *priv = (GaEntryGroupServicePrivate *) service;
560 g_hash_table_remove(priv->entries, key);
563 return ga_entry_group_service_thaw(service, error);
569 gboolean ga_entry_group_attach(GaEntryGroup * group,
570 GaClient * client, GError ** error) {
571 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
573 g_assert(priv->client == NULL || priv->client == client);
574 g_assert(priv->group == NULL);
576 priv->client = client;
577 g_object_ref(client);
579 priv->group = avahi_entry_group_new(client->avahi_client,
580 _avahi_entry_group_cb, group);
581 if (priv->group == NULL) {
583 int aerrno = avahi_client_errno(client->avahi_client);
584 *error = g_error_new(GA_ERROR, aerrno,
585 "Attaching group failed: %s",
586 avahi_strerror(aerrno));
593 gboolean ga_entry_group_commit(GaEntryGroup * group, GError ** error) {
594 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
596 ret = avahi_entry_group_commit(priv->group);
599 *error = g_error_new(GA_ERROR, ret,
600 "Committing group failed: %s",
601 avahi_strerror(ret));
608 gboolean ga_entry_group_reset(GaEntryGroup * group, GError ** error) {
609 GaEntryGroupPrivate *priv = GA_ENTRY_GROUP_GET_PRIVATE(group);
611 ret = avahi_entry_group_reset(priv->group);
614 *error = g_error_new(GA_ERROR, ret,
615 "Resetting group failed: %s",
616 avahi_strerror(ret));