/* Meanwhile - Unofficial Lotus Sametime Community Client Library Copyright (C) 2004 Christopher (siege) O'Brien This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include "mw_channel.h" #include "mw_debug.h" #include "mw_error.h" #include "mw_message.h" #include "mw_service.h" #include "mw_session.h" #include "mw_srvc_aware.h" #include "mw_util.h" struct mwServiceAware { struct mwService service; struct mwAwareHandler *handler; /** map of ENTRY_KEY(aware_entry):aware_entry */ GHashTable *entries; /** set of guint32:attrib_watch_entry attribute keys */ GHashTable *attribs; /** collection of lists of awareness for this service. Each item is a mwAwareList */ GList *lists; /** the buddy list channel */ struct mwChannel *channel; }; struct mwAwareList { /** the owning service */ struct mwServiceAware *service; /** map of ENTRY_KEY(aware_entry):aware_entry */ GHashTable *entries; /** set of guint32:attrib_watch_entry attribute keys */ GHashTable *attribs; struct mwAwareListHandler *handler; struct mw_datum client_data; }; struct mwAwareAttribute { guint32 key; struct mwOpaque data; }; struct attrib_entry { guint32 key; GList *membership; }; /** an actual awareness entry, belonging to any number of aware lists */ struct aware_entry { struct mwAwareSnapshot aware; /** list of mwAwareList containing this entry */ GList *membership; /** collection of attribute values for this entry. map of ATTRIB_KEY(mwAwareAttribute):mwAwareAttribute */ GHashTable *attribs; }; #define ENTRY_KEY(entry) &entry->aware.id /** the channel send types used by this service */ enum msg_types { msg_AWARE_ADD = 0x0068, /**< remove an aware */ msg_AWARE_REMOVE = 0x0069, /**< add an aware */ msg_OPT_DO_SET = 0x00c9, /**< set an attribute */ msg_OPT_DO_UNSET = 0x00ca, /**< unset an attribute */ msg_OPT_WATCH = 0x00cb, /**< set the attribute watch list */ msg_AWARE_SNAPSHOT = 0x01f4, /**< recv aware snapshot */ msg_AWARE_UPDATE = 0x01f5, /**< recv aware update */ msg_AWARE_GROUP = 0x01f6, /**< recv group aware */ msg_OPT_GOT_SET = 0x0259, /**< recv attribute set update */ msg_OPT_GOT_UNSET = 0x025a, /**< recv attribute unset update */ msg_OPT_GOT_UNKNOWN = 0x025b, /**< UNKNOWN */ msg_OPT_DID_SET = 0x025d, /**< attribute set response */ msg_OPT_DID_UNSET = 0x025e, /**< attribute unset response */ msg_OPT_DID_ERROR = 0x025f, /**< attribute set/unset error */ }; static void aware_entry_free(struct aware_entry *ae) { mwAwareSnapshot_clear(&ae->aware); g_list_free(ae->membership); g_hash_table_destroy(ae->attribs); g_free(ae); } static void attrib_entry_free(struct attrib_entry *ae) { g_list_free(ae->membership); g_free(ae); } static void attrib_free(struct mwAwareAttribute *attrib) { mwOpaque_clear(&attrib->data); g_free(attrib); } static struct aware_entry *aware_find(struct mwServiceAware *srvc, struct mwAwareIdBlock *srch) { g_return_val_if_fail(srvc != NULL, NULL); g_return_val_if_fail(srvc->entries != NULL, NULL); g_return_val_if_fail(srch != NULL, NULL); return g_hash_table_lookup(srvc->entries, srch); } static struct aware_entry *list_aware_find(struct mwAwareList *list, struct mwAwareIdBlock *srch) { g_return_val_if_fail(list != NULL, NULL); g_return_val_if_fail(list->entries != NULL, NULL); g_return_val_if_fail(srch != NULL, NULL); return g_hash_table_lookup(list->entries, srch); } static void compose_list(struct mwPutBuffer *b, GList *id_list) { guint32_put(b, g_list_length(id_list)); for(; id_list; id_list = id_list->next) mwAwareIdBlock_put(b, id_list->data); } static int send_add(struct mwChannel *chan, GList *id_list) { struct mwPutBuffer *b = mwPutBuffer_new(); struct mwOpaque o; int ret; g_return_val_if_fail(chan != NULL, 0); compose_list(b, id_list); mwPutBuffer_finalize(&o, b); ret = mwChannel_send(chan, msg_AWARE_ADD, &o); mwOpaque_clear(&o); return ret; } static int send_rem(struct mwChannel *chan, GList *id_list) { struct mwPutBuffer *b = mwPutBuffer_new(); struct mwOpaque o; int ret; g_return_val_if_fail(chan != NULL, 0); compose_list(b, id_list); mwPutBuffer_finalize(&o, b); ret = mwChannel_send(chan, msg_AWARE_REMOVE, &o); mwOpaque_clear(&o); return ret; } static gboolean collect_dead(gpointer key, gpointer val, gpointer data) { struct aware_entry *aware = val; GList **dead = data; if(aware->membership == NULL) { g_info(" removing %s, %s", NSTR(aware->aware.id.user), NSTR(aware->aware.id.community)); *dead = g_list_append(*dead, aware); return TRUE; } else { return FALSE; } } static int remove_unused(struct mwServiceAware *srvc) { /* - create a GList of all the unused aware entries - remove each unused aware from the service - if the service is alive, send a removal message for the collected unused. */ int ret = 0; GList *dead = NULL, *l; if(srvc->entries) { g_info("bring out your dead *clang*"); g_hash_table_foreach_steal(srvc->entries, collect_dead, &dead); } if(dead) { if(MW_SERVICE_IS_LIVE(srvc)) ret = send_rem(srvc->channel, dead) || ret; for(l = dead; l; l = l->next) aware_entry_free(l->data); g_list_free(dead); } return ret; } static int send_attrib_list(struct mwServiceAware *srvc) { struct mwPutBuffer *b; struct mwOpaque o; int tmp; GList *l; g_return_val_if_fail(srvc != NULL, -1); g_return_val_if_fail(srvc->channel != NULL, 0); l = map_collect_keys(srvc->attribs); tmp = g_list_length(l); b = mwPutBuffer_new(); guint32_put(b, 0x00); guint32_put(b, tmp); for(; l; l = g_list_delete_link(l, l)) { guint32_put(b, GPOINTER_TO_UINT(l->data)); } mwPutBuffer_finalize(&o, b); tmp = mwChannel_send(srvc->channel, msg_OPT_WATCH, &o); mwOpaque_clear(&o); return tmp; } static gboolean collect_attrib_dead(gpointer key, gpointer val, gpointer data) { struct attrib_entry *attrib = val; GList **dead = data; if(attrib->membership == NULL) { g_info(" removing 0x%08x", GPOINTER_TO_UINT(key)); *dead = g_list_append(*dead, attrib); return TRUE; } else { return FALSE; } } static int remove_unused_attrib(struct mwServiceAware *srvc) { GList *dead = NULL; if(srvc->attribs) { g_info("collecting dead attributes"); g_hash_table_foreach_steal(srvc->attribs, collect_attrib_dead, &dead); } /* since we stole them, we'll have to clean 'em up manually */ for(; dead; dead = g_list_delete_link(dead, dead)) { attrib_entry_free(dead->data); } return MW_SERVICE_IS_LIVE(srvc)? send_attrib_list(srvc): 0; } static void recv_accept(struct mwServiceAware *srvc, struct mwChannel *chan, struct mwMsgChannelAccept *msg) { g_return_if_fail(srvc->channel != NULL); g_return_if_fail(srvc->channel == chan); if(MW_SERVICE_IS_STARTING(MW_SERVICE(srvc))) { GList *list = NULL; list = map_collect_values(srvc->entries); send_add(chan, list); g_list_free(list); send_attrib_list(srvc); mwService_started(MW_SERVICE(srvc)); } else { mwChannel_destroy(chan, ERR_FAILURE, NULL); } } static void recv_destroy(struct mwServiceAware *srvc, struct mwChannel *chan, struct mwMsgChannelDestroy *msg) { srvc->channel = NULL; mwService_stop(MW_SERVICE(srvc)); /** @todo session sense service and mwService_start */ } /** called from SNAPSHOT_recv, UPDATE_recv, and mwServiceAware_setStatus */ static void status_recv(struct mwServiceAware *srvc, struct mwAwareSnapshot *idb) { struct aware_entry *aware; GList *l; aware = aware_find(srvc, &idb->id); if(! aware) { /* we don't deal with receiving status for something we're not monitoring, but it will happen sometimes, eg from manually set status */ return; } /* clear the existing status, then clone in the new status */ mwAwareSnapshot_clear(&aware->aware); mwAwareSnapshot_clone(&aware->aware, idb); /* trigger each of the entry's lists */ for(l = aware->membership; l; l = l->next) { struct mwAwareList *alist = l->data; struct mwAwareListHandler *handler = alist->handler; if(handler && handler->on_aware) handler->on_aware(alist, idb); } } static void attrib_recv(struct mwServiceAware *srvc, struct mwAwareIdBlock *idb, struct mwAwareAttribute *attrib) { struct aware_entry *aware; struct mwAwareAttribute *old_attrib = NULL; GList *l; guint32 key; gpointer k; aware = aware_find(srvc, idb); g_return_if_fail(aware != NULL); key = attrib->key; k = GUINT_TO_POINTER(key); if(aware->attribs) old_attrib = g_hash_table_lookup(aware->attribs, k); if(! old_attrib) { old_attrib = g_new0(struct mwAwareAttribute, 1); old_attrib->key = key; g_hash_table_insert(aware->attribs, k, old_attrib); } mwOpaque_clear(&old_attrib->data); mwOpaque_clone(&old_attrib->data, &attrib->data); for(l = aware->membership; l; l = l->next) { struct mwAwareList *list = l->data; struct mwAwareListHandler *h = list->handler; if(h && h->on_attrib && list->attribs && g_hash_table_lookup(list->attribs, k)) h->on_attrib(list, idb, old_attrib); } } static gboolean list_add(struct mwAwareList *list, struct mwAwareIdBlock *id) { struct mwServiceAware *srvc = list->service; struct aware_entry *aware; g_return_val_if_fail(id->user != NULL, FALSE); g_return_val_if_fail(strlen(id->user) > 0, FALSE); if(! list->entries) list->entries = g_hash_table_new((GHashFunc) mwAwareIdBlock_hash, (GEqualFunc) mwAwareIdBlock_equal); aware = list_aware_find(list, id); if(aware) return FALSE; aware = aware_find(srvc, id); if(! aware) { aware = g_new0(struct aware_entry, 1); aware->attribs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) attrib_free); mwAwareIdBlock_clone(ENTRY_KEY(aware), id); g_hash_table_insert(srvc->entries, ENTRY_KEY(aware), aware); } aware->membership = g_list_append(aware->membership, list); g_hash_table_insert(list->entries, ENTRY_KEY(aware), aware); return TRUE; } static void group_member_recv(struct mwServiceAware *srvc, struct mwAwareSnapshot *idb) { /* @todo - look up group by id - find each list group belongs to - add user to lists */ struct mwAwareIdBlock gsrch = { mwAware_GROUP, idb->group, NULL }; struct aware_entry *grp; GList *l, *m; grp = aware_find(srvc, &gsrch); g_return_if_fail(grp != NULL); /* this could happen, with timing. */ l = g_list_prepend(NULL, &idb->id); for(m = grp->membership; m; m = m->next) { /* if we just list_add, we won't receive updates for attributes, so annoyingly we have to turn around and send out an add aware message for each incoming group member */ /* list_add(m->data, &idb->id); */ mwAwareList_addAware(m->data, l); } g_list_free(l); } static void recv_SNAPSHOT(struct mwServiceAware *srvc, struct mwGetBuffer *b) { guint32 count; struct mwAwareSnapshot *snap; snap = g_new0(struct mwAwareSnapshot, 1); guint32_get(b, &count); while(count--) { mwAwareSnapshot_get(b, snap); if(mwGetBuffer_error(b)) { mwAwareSnapshot_clear(snap); break; } if(snap->group) group_member_recv(srvc, snap); status_recv(srvc, snap); mwAwareSnapshot_clear(snap); } g_free(snap); } static void recv_UPDATE(struct mwServiceAware *srvc, struct mwGetBuffer *b) { struct mwAwareSnapshot *snap; snap = g_new0(struct mwAwareSnapshot, 1); mwAwareSnapshot_get(b, snap); if(snap->group) group_member_recv(srvc, snap); if(! mwGetBuffer_error(b)) status_recv(srvc, snap); mwAwareSnapshot_clear(snap); g_free(snap); } static void recv_GROUP(struct mwServiceAware *srvc, struct mwGetBuffer *b) { struct mwAwareIdBlock idb = { 0, 0, 0 }; /* really nothing to be done with this. The group should have already been added to the list and service, and is now simply awaiting a snapshot/update with users listed as belonging in said group. */ mwAwareIdBlock_get(b, &idb); mwAwareIdBlock_clear(&idb); } static void recv_OPT_GOT_SET(struct mwServiceAware *srvc, struct mwGetBuffer *b) { struct mwAwareAttribute attrib; struct mwAwareIdBlock idb; guint32 junk, check; guint32_get(b, &junk); mwAwareIdBlock_get(b, &idb); guint32_get(b, &junk); guint32_get(b, &check); guint32_get(b, &junk); guint32_get(b, &attrib.key); if(check) { mwOpaque_get(b, &attrib.data); } else { attrib.data.len = 0; attrib.data.data = NULL; } attrib_recv(srvc, &idb, &attrib); mwAwareIdBlock_clear(&idb); mwOpaque_clear(&attrib.data); } static void recv_OPT_GOT_UNSET(struct mwServiceAware *srvc, struct mwGetBuffer *b) { struct mwAwareAttribute attrib; struct mwAwareIdBlock idb; guint32 junk; attrib.key = 0; attrib.data.len = 0; attrib.data.data = NULL; guint32_get(b, &junk); mwAwareIdBlock_get(b, &idb); guint32_get(b, &attrib.key); attrib_recv(srvc, &idb, &attrib); mwAwareIdBlock_clear(&idb); } static void recv(struct mwService *srvc, struct mwChannel *chan, guint16 type, struct mwOpaque *data) { struct mwServiceAware *srvc_aware = (struct mwServiceAware *) srvc; struct mwGetBuffer *b; g_return_if_fail(srvc_aware->channel == chan); g_return_if_fail(srvc->session == mwChannel_getSession(chan)); g_return_if_fail(data != NULL); b = mwGetBuffer_wrap(data); switch(type) { case msg_AWARE_SNAPSHOT: recv_SNAPSHOT(srvc_aware, b); break; case msg_AWARE_UPDATE: recv_UPDATE(srvc_aware, b); break; case msg_AWARE_GROUP: recv_GROUP(srvc_aware, b); break; case msg_OPT_GOT_SET: recv_OPT_GOT_SET(srvc_aware, b); break; case msg_OPT_GOT_UNSET: recv_OPT_GOT_UNSET(srvc_aware, b); break; case msg_OPT_GOT_UNKNOWN: case msg_OPT_DID_SET: case msg_OPT_DID_UNSET: case msg_OPT_DID_ERROR: break; default: mw_mailme_opaque(data, "unknown message in aware service: 0x%04x", type); } mwGetBuffer_free(b); } static void clear(struct mwService *srvc) { struct mwServiceAware *srvc_aware = (struct mwServiceAware *) srvc; g_return_if_fail(srvc != NULL); while(srvc_aware->lists) mwAwareList_free( (struct mwAwareList *) srvc_aware->lists->data ); g_hash_table_destroy(srvc_aware->entries); srvc_aware->entries = NULL; g_hash_table_destroy(srvc_aware->attribs); srvc_aware->attribs = NULL; } static const char *name(struct mwService *srvc) { return "Presence Awareness"; } static const char *desc(struct mwService *srvc) { return "Buddy list service with support for server-side groups"; } static struct mwChannel *make_blist(struct mwServiceAware *srvc, struct mwChannelSet *cs) { struct mwChannel *chan = mwChannel_newOutgoing(cs); mwChannel_setService(chan, MW_SERVICE(srvc)); mwChannel_setProtoType(chan, 0x00000011); mwChannel_setProtoVer(chan, 0x00030005); return mwChannel_create(chan)? NULL: chan; } static void start(struct mwService *srvc) { struct mwServiceAware *srvc_aware; struct mwChannel *chan = NULL; srvc_aware = (struct mwServiceAware *) srvc; chan = make_blist(srvc_aware, mwSession_getChannels(srvc->session)); if(chan != NULL) { srvc_aware->channel = chan; } else { mwService_stopped(srvc); } } static void stop(struct mwService *srvc) { struct mwServiceAware *srvc_aware; srvc_aware = (struct mwServiceAware *) srvc; if(srvc_aware->channel) { mwChannel_destroy(srvc_aware->channel, ERR_SUCCESS, NULL); srvc_aware->channel = NULL; } mwService_stopped(srvc); } struct mwServiceAware * mwServiceAware_new(struct mwSession *session, struct mwAwareHandler *handler) { struct mwService *service; struct mwServiceAware *srvc; g_return_val_if_fail(session != NULL, NULL); g_return_val_if_fail(handler != NULL, NULL); srvc = g_new0(struct mwServiceAware, 1); srvc->handler = handler; srvc->entries = g_hash_table_new_full((GHashFunc) mwAwareIdBlock_hash, (GEqualFunc) mwAwareIdBlock_equal, NULL, (GDestroyNotify) aware_entry_free); srvc->attribs = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) attrib_entry_free); service = MW_SERVICE(srvc); mwService_init(service, session, mwService_AWARE); service->recv_accept = (mwService_funcRecvAccept) recv_accept; service->recv_destroy = (mwService_funcRecvDestroy) recv_destroy; service->recv = recv; service->start = start; service->stop = stop; service->clear = clear; service->get_name = name; service->get_desc = desc; return srvc; } int mwServiceAware_setAttribute(struct mwServiceAware *srvc, guint32 key, struct mwOpaque *data) { struct mwPutBuffer *b; struct mwOpaque o; int ret; b = mwPutBuffer_new(); guint32_put(b, 0x00); guint32_put(b, data->len); guint32_put(b, 0x00); guint32_put(b, key); mwOpaque_put(b, data); mwPutBuffer_finalize(&o, b); ret = mwChannel_send(srvc->channel, msg_OPT_DO_SET, &o); mwOpaque_clear(&o); return ret; } int mwServiceAware_setAttributeBoolean(struct mwServiceAware *srvc, guint32 key, gboolean val) { int ret; struct mwPutBuffer *b; struct mwOpaque o; b = mwPutBuffer_new(); gboolean_put(b, FALSE); gboolean_put(b, val); mwPutBuffer_finalize(&o, b); ret = mwServiceAware_setAttribute(srvc, key, &o); mwOpaque_clear(&o); return ret; } int mwServiceAware_setAttributeInteger(struct mwServiceAware *srvc, guint32 key, guint32 val) { int ret; struct mwPutBuffer *b; struct mwOpaque o; b = mwPutBuffer_new(); guint32_put(b, val); mwPutBuffer_finalize(&o, b); ret = mwServiceAware_setAttribute(srvc, key, &o); mwOpaque_clear(&o); return ret; } int mwServiceAware_setAttributeString(struct mwServiceAware *srvc, guint32 key, const char *str) { int ret; struct mwPutBuffer *b; struct mwOpaque o; b = mwPutBuffer_new(); mwString_put(b, str); mwPutBuffer_finalize(&o, b); ret = mwServiceAware_setAttribute(srvc, key, &o); mwOpaque_clear(&o); return ret; } int mwServiceAware_unsetAttribute(struct mwServiceAware *srvc, guint32 key) { struct mwPutBuffer *b; struct mwOpaque o; int ret; b = mwPutBuffer_new(); guint32_put(b, 0x00); guint32_put(b, key); mwPutBuffer_finalize(&o, b); ret = mwChannel_send(srvc->channel, msg_OPT_DO_UNSET, &o); mwOpaque_clear(&o); return ret; } guint32 mwAwareAttribute_getKey(const struct mwAwareAttribute *attrib) { g_return_val_if_fail(attrib != NULL, 0x00); return attrib->key; } gboolean mwAwareAttribute_asBoolean(const struct mwAwareAttribute *attrib) { struct mwGetBuffer *b; gboolean ret; if(! attrib) return FALSE; b = mwGetBuffer_wrap(&attrib->data); if(attrib->data.len >= 4) { guint32 r32 = 0x00; guint32_get(b, &r32); ret = !! r32; } else if(attrib->data.len >= 2) { guint16 r16 = 0x00; guint16_get(b, &r16); ret = !! r16; } else if(attrib->data.len) { gboolean_get(b, &ret); } mwGetBuffer_free(b); return ret; } guint32 mwAwareAttribute_asInteger(const struct mwAwareAttribute *attrib) { struct mwGetBuffer *b; guint32 r32 = 0x00; if(! attrib) return 0x00; b = mwGetBuffer_wrap(&attrib->data); if(attrib->data.len >= 4) { guint32_get(b, &r32); } else if(attrib->data.len == 3) { gboolean rb = FALSE; guint16 r16 = 0x00; gboolean_get(b, &rb); guint16_get(b, &r16); r32 = (guint32) r16; } else if(attrib->data.len == 2) { guint16 r16 = 0x00; guint16_get(b, &r16); r32 = (guint32) r16; } else if(attrib->data.len) { gboolean rb = FALSE; gboolean_get(b, &rb); r32 = (guint32) rb; } mwGetBuffer_free(b); return r32; } char *mwAwareAttribute_asString(const struct mwAwareAttribute *attrib) { struct mwGetBuffer *b; char *ret = NULL; if(! attrib) return NULL; b = mwGetBuffer_wrap(&attrib->data); mwString_get(b, &ret); mwGetBuffer_free(b); return ret; } const struct mwOpaque * mwAwareAttribute_asOpaque(const struct mwAwareAttribute *attrib) { g_return_val_if_fail(attrib != NULL, NULL); return &attrib->data; } struct mwAwareList * mwAwareList_new(struct mwServiceAware *srvc, struct mwAwareListHandler *handler) { struct mwAwareList *al; g_return_val_if_fail(srvc != NULL, NULL); g_return_val_if_fail(handler != NULL, NULL); al = g_new0(struct mwAwareList, 1); al->service = srvc; al->handler = handler; srvc->lists = g_list_prepend(srvc->lists, al); return al; } void mwAwareList_free(struct mwAwareList *list) { struct mwServiceAware *srvc; struct mwAwareListHandler *handler; g_return_if_fail(list != NULL); g_return_if_fail(list->service != NULL); srvc = list->service; srvc->lists = g_list_remove_all(srvc->lists, list); handler = list->handler; if(handler && handler->clear) { handler->clear(list); list->handler = NULL; } mw_datum_clear(&list->client_data); mwAwareList_unwatchAllAttributes(list); mwAwareList_removeAllAware(list); list->service = NULL; g_free(list); } struct mwAwareListHandler *mwAwareList_getHandler(struct mwAwareList *list) { g_return_val_if_fail(list != NULL, NULL); return list->handler; } static void watch_add(struct mwAwareList *list, guint32 key) { struct mwServiceAware *srvc; struct attrib_entry *watch; gpointer k = GUINT_TO_POINTER(key); if(! list->attribs) list->attribs = g_hash_table_new(g_direct_hash, g_direct_equal); if(g_hash_table_lookup(list->attribs, k)) return; srvc = list->service; watch = g_hash_table_lookup(srvc->attribs, k); if(! watch) { watch = g_new0(struct attrib_entry, 1); watch->key = key; g_hash_table_insert(srvc->attribs, k, watch); } g_hash_table_insert(list->attribs, k, watch); watch->membership = g_list_prepend(watch->membership, list); } static void watch_remove(struct mwAwareList *list, guint32 key) { struct attrib_entry *watch = NULL; gpointer k = GUINT_TO_POINTER(key); if(list->attribs) watch = g_hash_table_lookup(list->attribs, k); g_return_if_fail(watch != NULL); g_hash_table_remove(list->attribs, k); watch->membership = g_list_remove(watch->membership, list); } int mwAwareList_watchAttributeArray(struct mwAwareList *list, guint32 *keys) { guint32 k; g_return_val_if_fail(list != NULL, -1); g_return_val_if_fail(list->service != NULL, -1); if(! keys) return 0; for(k = *keys; k; keys++) watch_add(list, k); return send_attrib_list(list->service); } int mwAwareList_watchAttributes(struct mwAwareList *list, guint32 key, ...) { guint32 k; va_list args; g_return_val_if_fail(list != NULL, -1); g_return_val_if_fail(list->service != NULL, -1); va_start(args, key); for(k = key; k; k = va_arg(args, guint32)) watch_add(list, k); va_end(args); return send_attrib_list(list->service); } int mwAwareList_unwatchAttributeArray(struct mwAwareList *list, guint32 *keys) { guint32 k; g_return_val_if_fail(list != NULL, -1); g_return_val_if_fail(list->service != NULL, -1); if(! keys) return 0; for(k = *keys; k; keys++) watch_add(list, k); return remove_unused_attrib(list->service); } int mwAwareList_unwatchAttributes(struct mwAwareList *list, guint32 key, ...) { guint32 k; va_list args; g_return_val_if_fail(list != NULL, -1); g_return_val_if_fail(list->service != NULL, -1); va_start(args, key); for(k = key; k; k = va_arg(args, guint32)) watch_remove(list, k); va_end(args); return remove_unused_attrib(list->service); } static void dismember_attrib(gpointer k, struct attrib_entry *watch, struct mwAwareList *list) { watch->membership = g_list_remove(watch->membership, list); } int mwAwareList_unwatchAllAttributes(struct mwAwareList *list) { struct mwServiceAware *srvc; g_return_val_if_fail(list != NULL, -1); srvc = list->service; if(list->attribs) { g_hash_table_foreach(list->attribs, (GHFunc) dismember_attrib, list); g_hash_table_destroy(list->attribs); } return remove_unused_attrib(srvc); } static void collect_attrib_keys(gpointer key, struct attrib_entry *attrib, guint32 **ck) { guint32 *keys = (*ck)++; *keys = GPOINTER_TO_UINT(key); } guint32 *mwAwareList_getWatchedAttributes(struct mwAwareList *list) { guint32 *keys, **ck; guint count; g_return_val_if_fail(list != NULL, NULL); g_return_val_if_fail(list->attribs != NULL, NULL); count = g_hash_table_size(list->attribs); keys = g_new0(guint32, count + 1); ck = &keys; g_hash_table_foreach(list->attribs, (GHFunc) collect_attrib_keys, ck); return keys; } int mwAwareList_addAware(struct mwAwareList *list, GList *id_list) { /* for each awareness id: - if it's already in the list, continue - if it's not in the service list: - create an awareness - add it to the service list - add this list to the membership - add to the list */ struct mwServiceAware *srvc; GList *additions = NULL; int ret = 0; g_return_val_if_fail(list != NULL, -1); srvc = list->service; g_return_val_if_fail(srvc != NULL, -1); for(; id_list; id_list = id_list->next) { if(list_add(list, id_list->data)) additions = g_list_prepend(additions, id_list->data); } /* if the service is alive-- or getting there-- we'll need to send these additions upstream */ if(MW_SERVICE_IS_LIVE(srvc) && additions) ret = send_add(srvc->channel, additions); g_list_free(additions); return ret; } int mwAwareList_removeAware(struct mwAwareList *list, GList *id_list) { /* for each awareness id: - if it's not in the list, forget it - remove from the list - remove list from the membership - call remove round */ struct mwServiceAware *srvc; struct mwAwareIdBlock *id; struct aware_entry *aware; g_return_val_if_fail(list != NULL, -1); srvc = list->service; g_return_val_if_fail(srvc != NULL, -1); for(; id_list; id_list = id_list->next) { id = id_list->data; aware = list_aware_find(list, id); if(! aware) { g_warning("buddy %s, %s not in list", NSTR(id->user), NSTR(id->community)); continue; } aware->membership = g_list_remove(aware->membership, list); g_hash_table_remove(list->entries, id); } return remove_unused(srvc); } static void dismember_aware(gpointer k, struct aware_entry *aware, struct mwAwareList *list) { aware->membership = g_list_remove(aware->membership, list); } int mwAwareList_removeAllAware(struct mwAwareList *list) { struct mwServiceAware *srvc; g_return_val_if_fail(list != NULL, -1); srvc = list->service; g_return_val_if_fail(srvc != NULL, -1); /* for each entry, remove the aware list from the service entry's membership collection */ if(list->entries) { g_hash_table_foreach(list->entries, (GHFunc) dismember_aware, list); g_hash_table_destroy(list->entries); } return remove_unused(srvc); } void mwAwareList_setClientData(struct mwAwareList *list, gpointer data, GDestroyNotify clear) { g_return_if_fail(list != NULL); mw_datum_set(&list->client_data, data, clear); } gpointer mwAwareList_getClientData(struct mwAwareList *list) { g_return_val_if_fail(list != NULL, NULL); return mw_datum_get(&list->client_data); } void mwAwareList_removeClientData(struct mwAwareList *list) { g_return_if_fail(list != NULL); mw_datum_clear(&list->client_data); } void mwServiceAware_setStatus(struct mwServiceAware *srvc, struct mwAwareIdBlock *user, struct mwUserStatus *stat) { struct mwAwareSnapshot idb; g_return_if_fail(srvc != NULL); g_return_if_fail(user != NULL); g_return_if_fail(stat != NULL); /* just reference the strings. then we don't need to free them */ idb.id.type = user->type; idb.id.user = user->user; idb.id.community = user->community; idb.group = NULL; idb.online = TRUE; idb.alt_id = NULL; idb.status.status = stat->status; idb.status.time = stat->time; idb.status.desc = stat->desc; idb.name = NULL; status_recv(srvc, &idb); } const struct mwAwareAttribute * mwServiceAware_getAttribute(struct mwServiceAware *srvc, struct mwAwareIdBlock *user, guint32 key) { struct aware_entry *aware; g_return_val_if_fail(srvc != NULL, NULL); g_return_val_if_fail(user != NULL, NULL); g_return_val_if_fail(key != 0x00, NULL); aware = aware_find(srvc, user); g_return_val_if_fail(aware != NULL, NULL); return g_hash_table_lookup(aware->attribs, GUINT_TO_POINTER(key)); } const char *mwServiceAware_getText(struct mwServiceAware *srvc, struct mwAwareIdBlock *user) { struct aware_entry *aware; g_return_val_if_fail(srvc != NULL, NULL); g_return_val_if_fail(user != NULL, NULL); aware = aware_find(srvc, user); if(! aware) return NULL; return aware->aware.status.desc; }