view src/protocols/sametime/meanwhile/srvc_resolve.c @ 11943:0110fc7c6a8a

[gaim-migrate @ 14234] Bringing things up to date with the last Meanwhile release, 0.5.0 and the last gaim-meanwhile plugin release, 1.2.5 (which should be the last plugin release against oldstatus, if all goes well with HEAD and no major bugs crop up) It builds, so that's a start. The status bits that have been empty since the first import of the sametime stuff are still empty, but I'm going to try and fill those in tomorrow. I've decided to try and start using HEAD actively, to encourage me to get this freaking prpl fully functional. committer: Tailor Script <tailor@pidgin.im>
author Christopher O'Brien <siege@pidgin.im>
date Wed, 02 Nov 2005 03:39:03 +0000
parents 3ef77720e577
children
line wrap: on
line source


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