--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_resolve.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,389 @@ + +/* + 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 <glib/ghash.h> + +#include "mw_channel.h" +#include "mw_common.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_resolve.h" + + +#define PROTOCOL_TYPE 0x00000015 +#define PROTOCOL_VER 0x00000000 + + +/** oddly, there is only one message type in this service */ +#define RESOLVE_ACTION 0x02 + + +struct mwServiceResolve { + struct mwService service; + + struct mwChannel *channel; /**< channel for this service */ + GHashTable *searches; /**< guint32:struct mw_search */ + guint32 counter; /**< incremented to provide searche IDs */ +}; + + +/** structure representing an active search. keeps track of the ID, + the handler, and the optional user data and cleanup */ +struct mw_search { + struct mwServiceResolve *service; + guint32 id; + mwResolveHandler handler; + gpointer data; + GDestroyNotify cleanup; +}; + + +static struct mw_search *search_new(struct mwServiceResolve *srvc, + mwResolveHandler handler, + gpointer data, GDestroyNotify cleanup) { + + struct mw_search *search = g_new0(struct mw_search, 1); + + search->service = srvc; + search->handler = handler; + + /* we want search IDs that aren't SEARCH_ERROR */ + do { + search->id = srvc->counter++; + } while(search->id == SEARCH_ERROR); + + search->data = data; + search->cleanup = cleanup; + + return search; +} + + +/** called whenever a mw_search is removed from the searches table of + the service */ +static void search_free(struct mw_search *search) { + g_return_if_fail(search != NULL); + + if(search->cleanup) + search->cleanup(search->data); + + g_free(search); +} + + +static const char *get_name(struct mwService *srvc) { + return "Identity Resolution"; +} + + +static const char *get_desc(struct mwService *srvc) { + return "Resolves short IDs to full IDs"; +} + + +static struct mwChannel *make_channel(struct mwServiceResolve *srvc) { + struct mwSession *session; + struct mwChannelSet *cs; + struct mwChannel *chan; + + session = mwService_getSession(MW_SERVICE(srvc)); + cs = mwSession_getChannels(session); + chan = mwChannel_newOutgoing(cs); + + mwChannel_setService(chan, MW_SERVICE(srvc)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + return mwChannel_create(chan)? NULL: chan; +} + + +static void start(struct mwServiceResolve *srvc) { + struct mwChannel *chan; + + g_return_if_fail(srvc != NULL); + + chan = make_channel(srvc); + if(chan) { + srvc->channel = chan; + } else { + mwService_stopped(MW_SERVICE(srvc)); + return; + } + + /* semi-lazily create the searches table */ + srvc->searches = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) search_free); +} + + +static void stop(struct mwServiceResolve *srvc) { + g_return_if_fail(srvc != NULL); + + if(srvc->channel) { + mwChannel_destroy(srvc->channel, ERR_SUCCESS, NULL); + srvc->channel = NULL; + } + + /* destroy all the pending requests. */ + g_hash_table_destroy(srvc->searches); + srvc->searches = NULL; + + mwService_stopped(MW_SERVICE(srvc)); +} + + +static void clear(struct mwServiceResolve *srvc) { + if(srvc->searches) { + g_hash_table_destroy(srvc->searches); + srvc->searches = NULL; + } +} + + +static void recv_create(struct mwServiceResolve *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* you serve me, not the other way around */ + mwChannel_destroy(chan, ERR_FAILURE, NULL); +} + + +static void recv_accept(struct mwServiceResolve *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc->channel); + + mwService_started(MW_SERVICE(srvc)); +} + + +static void recv_destroy(struct mwServiceResolve *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwSession *session; + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc->channel); + + srvc->channel = NULL; + mwService_stop(MW_SERVICE(srvc)); + + session = mwService_getSession(MW_SERVICE(srvc)); + g_return_if_fail(session != NULL); + + mwSession_senseService(session, mwService_getType(MW_SERVICE(srvc))); +} + + +static GList *load_matches(struct mwGetBuffer *b, guint32 count) { + GList *matches = NULL; + + while(count--) { + struct mwResolveMatch *m = g_new0(struct mwResolveMatch, 1); + + mwString_get(b, &m->id); + mwString_get(b, &m->name); + mwString_get(b, &m->desc); + guint32_get(b, &m->type); + + matches = g_list_append(matches, m); + } + + return matches; +} + + +static GList *load_results(struct mwGetBuffer *b, guint32 count) { + GList *results = NULL; + + while(count--) { + struct mwResolveResult *r = g_new0(struct mwResolveResult, 1); + guint32 junk, matches; + + guint32_get(b, &junk); + guint32_get(b, &r->code); + mwString_get(b, &r->name); + + guint32_get(b, &matches); + r->matches = load_matches(b, matches); + + results = g_list_append(results, r); + } + + return results; +} + + +static void free_matches(GList *matches) { + for(; matches; matches = g_list_delete_link(matches, matches)) { + struct mwResolveMatch *m = matches->data; + g_free(m->id); + g_free(m->name); + g_free(m->desc); + g_free(m); + } +} + + +static void free_results(GList *results) { + for(; results; results = g_list_delete_link(results, results)) { + struct mwResolveResult *r = results->data; + g_free(r->name); + free_matches(r->matches); + g_free(r); + } +} + + +static void recv(struct mwServiceResolve *srvc, + struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + struct mwGetBuffer *b; + guint32 junk, id, code, count; + struct mw_search *search; + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc->channel); + g_return_if_fail(data != NULL); + + if(type != RESOLVE_ACTION) { + mw_mailme_opaque(data, "unknown message in resolve service: 0x%04x", type); + return; + } + + b = mwGetBuffer_wrap(data); + guint32_get(b, &junk); + guint32_get(b, &id); + guint32_get(b, &code); + guint32_get(b, &count); + + if(mwGetBuffer_error(b)) { + g_warning("error parsing search result"); + mwGetBuffer_free(b); + return; + } + + search = g_hash_table_lookup(srvc->searches, GUINT_TO_POINTER(id)); + + if(search) { + GList *results = load_results(b, count); + if(mwGetBuffer_error(b)) { + g_warning("error parsing search results"); + } else { + g_debug("triggering handler"); + search->handler(srvc, id, code, results, search->data); + } + free_results(results); + g_hash_table_remove(srvc->searches, GUINT_TO_POINTER(id)); + + } else { + g_debug("no search found: 0x%x", id); + } + + mwGetBuffer_free(b); +} + + +struct mwServiceResolve *mwServiceResolve_new(struct mwSession *session) { + struct mwServiceResolve *srvc_resolve; + struct mwService *srvc; + + g_return_val_if_fail(session != NULL, NULL); + + srvc_resolve = g_new0(struct mwServiceResolve, 1); + + srvc = MW_SERVICE(srvc_resolve); + + mwService_init(srvc, session, mwService_RESOLVE); + srvc->get_name = get_name; + srvc->get_desc = get_desc; + srvc->recv_create = (mwService_funcRecvCreate) recv_create; + srvc->recv_accept = (mwService_funcRecvAccept) recv_accept; + srvc->recv_destroy = (mwService_funcRecvDestroy) recv_destroy; + srvc->recv = (mwService_funcRecv) recv; + srvc->start = (mwService_funcStart) start; + srvc->stop = (mwService_funcStop) stop; + srvc->clear = (mwService_funcClear) clear; + + return srvc_resolve; +} + + +guint32 mwServiceResolve_resolve(struct mwServiceResolve *srvc, + GList *queries, enum mwResolveFlag flags, + mwResolveHandler handler, + gpointer data, GDestroyNotify cleanup) { + + struct mw_search *search; + struct mwPutBuffer *b; + struct mwOpaque o = { 0, 0 }; + int ret, count = 0; + + g_return_val_if_fail(srvc != NULL, SEARCH_ERROR); + g_return_val_if_fail(handler != NULL, SEARCH_ERROR); + + count = g_list_length(queries); + g_return_val_if_fail(count > 0, SEARCH_ERROR); + + search = search_new(srvc, handler, data, cleanup); + + b = mwPutBuffer_new(); + guint32_put(b, 0x00); /* to be overwritten */ + guint32_put(b, search->id); + guint32_put(b, count); + for(; queries; queries = queries->next) + mwString_put(b, queries->data); + guint32_put(b, flags); + + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_send(srvc->channel, RESOLVE_ACTION, &o); + if(ret) { + search_free(search); + return SEARCH_ERROR; + + } else { + g_hash_table_insert(srvc->searches, + GUINT_TO_POINTER(search->id), search); + return search->id; + } +} + + +void mwServiceResolve_cancelResolve(struct mwServiceResolve *srvc, + guint32 id) { + + g_return_if_fail(srvc != NULL); + g_return_if_fail(srvc->searches != NULL); + + g_hash_table_remove(srvc->searches, GUINT_TO_POINTER(id)); +} +