/* 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 "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)); }