Mercurial > pidgin
changeset 12956:921e17c06a1d
[gaim-migrate @ 15309]
Add .cvsignore actions to meanwhile so cvs shuts up about it.
committer: Tailor Script <tailor@pidgin.im>
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/.cvsignore Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,6 @@ +*.lo +*.la +Makefile +Makefile.in +.deps +.libs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/Makefile.am Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,54 @@ +SUBDIRS = mpi + +noinst_HEADERS = \ + mw_channel.h \ + mw_cipher.h \ + mw_common.h \ + mw_debug.h \ + mw_error.h \ + mw_message.h \ + mw_service.h \ + mw_session.h \ + mw_srvc_aware.h \ + mw_srvc_conf.h \ + mw_srvc_ft.h \ + mw_srvc_im.h \ + mw_srvc_place.h \ + mw_srvc_resolve.h \ + mw_srvc_store.h \ + mw_st_list.h \ + mw_util.h + + +MEANWHILESOURCES = \ + channel.c \ + cipher.c \ + common.c \ + error.c \ + message.c \ + mw_debug.c \ + mw_util.c \ + service.c \ + session.c \ + srvc_aware.c \ + srvc_conf.c \ + srvc_ft.c \ + srvc_im.c \ + srvc_place.c \ + srvc_store.c \ + srvc_resolve.c \ + st_list.c + +AM_CFLAGS = \ + $(GLIB_CFLAGS) \ + $(DEBUG_CFLAGS) \ + -DG_LOG_DOMAIN=\"meanwhile\" + -I$(top_srcdir)/src + +libmeanwhile_la_SOURCES = $(MEANWHILESOURCES) + +libmeanwhile_la_LIBADD = $(GLIB_LIBS) mpi/libmpi.la + +st = +noinst_LTLIBRARIES = libmeanwhile.la +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/channel.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,961 @@ + +/* + 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.h> +#include <glib/ghash.h> +#include <glib/glist.h> +#include <string.h> + +#include "mw_channel.h" +#include "mw_cipher.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_util.h" + + +/** @todo reorganize this file, stuff is just strewn about */ + + +struct mwChannel { + + /** session this channel belongs to */ + struct mwSession *session; + + enum mwChannelState state; + + /** creator for incoming channel, target for outgoing channel */ + struct mwLoginInfo user; + + /* similar to data from the CreateCnl message in 8.4.1.7 */ + guint32 reserved; /**< special, unknown meaning */ + guint32 id; /**< channel ID */ + guint32 service; /**< service ID */ + guint32 proto_type; /**< service protocol type */ + guint32 proto_ver; /**< service protocol version */ + guint32 options; /**< channel options */ + + struct mwOpaque addtl_create; + struct mwOpaque addtl_accept; + + /** all those supported ciphers */ + GHashTable *supported; + guint16 offered_policy; /**< @see enum mwEncryptPolicy */ + guint16 policy; /**< @see enum mwEncryptPolicy */ + + /** cipher information determined at channel acceptance */ + struct mwCipherInstance *cipher; + + /** statistics table */ + GHashTable *stats; + + GSList *outgoing_queue; /**< queued outgoing messages */ + GSList *incoming_queue; /**< queued incoming messages */ + + struct mw_datum srvc_data; /**< service-specific data */ +}; + + +struct mwChannelSet { + struct mwSession *session; /**< owning session */ + GHashTable *map; /**< map of all channels, by ID */ + guint32 counter; /**< counter for outgoing ID */ +}; + + +static void flush_channel(struct mwChannel *); + + +static const char *state_str(enum mwChannelState state) { + switch(state) { + case mwChannel_NEW: return "new"; + case mwChannel_INIT: return "initializing"; + case mwChannel_WAIT: return "waiting"; + case mwChannel_OPEN: return "open"; + case mwChannel_DESTROY: return "closing"; + case mwChannel_ERROR: return "error"; + + case mwChannel_UNKNOWN: /* fall through */ + default: return "UNKNOWN"; + } +} + + +static void state(struct mwChannel *chan, enum mwChannelState state, + guint32 err_code) { + + g_return_if_fail(chan != NULL); + + if(chan->state == state) return; + + chan->state = state; + + if(err_code) { + g_message("channel 0x%08x state: %s (0x%08x)", + chan->id, state_str(state), err_code); + } else { + g_message("channel 0x%08x state: %s", chan->id, state_str(state)); + } +} + + +static gpointer get_stat(struct mwChannel *chan, + enum mwChannelStatField field) { + + return g_hash_table_lookup(chan->stats, (gpointer) field); +} + + +static void set_stat(struct mwChannel *chan, enum mwChannelStatField field, + gpointer val) { + + g_hash_table_insert(chan->stats, (gpointer) field, val); +} + + +#define incr_stat(chan, field, incr) \ + set_stat(chan, field, get_stat(chan, field) + incr) + + +#define timestamp_stat(chan, field) \ + set_stat(chan, field, (gpointer) time(NULL)) + + +static void sup_free(gpointer a) { + mwCipherInstance_free(a); +} + + +struct mwCipherInstance *get_supported(struct mwChannel *chan, guint16 id) { + guint32 cid = (guint32) id; + return g_hash_table_lookup(chan->supported, GUINT_TO_POINTER(cid)); +} + + +void put_supported(struct mwChannel *chan, struct mwCipherInstance *ci) { + struct mwCipher *cipher = mwCipherInstance_getCipher(ci); + guint32 cid = (guint32) mwCipher_getType(cipher); + g_hash_table_insert(chan->supported, GUINT_TO_POINTER(cid), ci); +} + + +struct mwChannel *mwChannel_newIncoming(struct mwChannelSet *cs, guint32 id) { + struct mwChannel *chan; + + g_return_val_if_fail(cs != NULL, NULL); + g_return_val_if_fail(cs->session != NULL, NULL); + + chan = g_new0(struct mwChannel, 1); + chan->state = mwChannel_NEW; + chan->session = cs->session; + chan->id = id; + + chan->stats = g_hash_table_new(g_direct_hash, g_direct_equal); + + chan->supported = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, sup_free); + + g_hash_table_insert(cs->map, GUINT_TO_POINTER(id), chan); + + state(chan, mwChannel_WAIT, 0); + + return chan; +} + + +struct mwChannel *mwChannel_newOutgoing(struct mwChannelSet *cs) { + guint32 id; + struct mwChannel *chan; + + g_return_val_if_fail(cs != NULL, NULL); + g_return_val_if_fail(cs->map != NULL, NULL); + + /* grab the next id, and try to make sure there isn't already a + channel using it */ + do { + id = ++cs->counter; + } while(g_hash_table_lookup(cs->map, GUINT_TO_POINTER(id))); + + chan = mwChannel_newIncoming(cs, id); + state(chan, mwChannel_INIT, 0); + + return chan; +} + + +guint32 mwChannel_getId(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0); + return chan->id; +} + + +struct mwSession *mwChannel_getSession(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return chan->session; +} + + +guint32 mwChannel_getServiceId(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0); + return chan->service; +} + + +struct mwService *mwChannel_getService(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return mwSession_getService(chan->session, chan->service); +} + + +void mwChannel_setService(struct mwChannel *chan, struct mwService *srvc) { + g_return_if_fail(chan != NULL); + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan->state == mwChannel_INIT); + chan->service = mwService_getType(srvc); +} + + +gpointer mwChannel_getServiceData(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return mw_datum_get(&chan->srvc_data); +} + + +void mwChannel_setServiceData(struct mwChannel *chan, + gpointer data, GDestroyNotify clean) { + + g_return_if_fail(chan != NULL); + mw_datum_set(&chan->srvc_data, data, clean); +} + + +void mwChannel_removeServiceData(struct mwChannel *chan) { + g_return_if_fail(chan != NULL); + mw_datum_clear(&chan->srvc_data); +} + + +guint32 mwChannel_getProtoType(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0x00); + return chan->proto_type; +} + + +void mwChannel_setProtoType(struct mwChannel *chan, guint32 proto_type) { + g_return_if_fail(chan != NULL); + g_return_if_fail(chan->state == mwChannel_INIT); + chan->proto_type = proto_type; +} + + +guint32 mwChannel_getProtoVer(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0x00); + return chan->proto_ver; +} + + +void mwChannel_setProtoVer(struct mwChannel *chan, guint32 proto_ver) { + g_return_if_fail(chan != NULL); + g_return_if_fail(chan->state == mwChannel_INIT); + chan->proto_ver = proto_ver; +} + + +guint16 mwChannel_getEncryptPolicy(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0x00); + return chan->policy; +} + + +guint32 mwChannel_getOptions(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, 0x00); + return chan->options; +} + + +void mwChannel_setOptions(struct mwChannel *chan, guint32 options) { + g_return_if_fail(chan != NULL); + g_return_if_fail(chan->state == mwChannel_INIT); + chan->options = options; +} + + +struct mwLoginInfo *mwChannel_getUser(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return &chan->user; +} + + +struct mwOpaque *mwChannel_getAddtlCreate(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return &chan->addtl_create; +} + + +struct mwOpaque *mwChannel_getAddtlAccept(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return &chan->addtl_accept; +} + + +struct mwCipherInstance *mwChannel_getCipherInstance(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, NULL); + return chan->cipher; +} + + +enum mwChannelState mwChannel_getState(struct mwChannel *chan) { + g_return_val_if_fail(chan != NULL, mwChannel_UNKNOWN); + return chan->state; +} + + +gpointer mwChannel_getStatistic(struct mwChannel *chan, + enum mwChannelStatField stat) { + + g_return_val_if_fail(chan != NULL, 0); + g_return_val_if_fail(chan->stats != NULL, 0); + + return get_stat(chan, stat); +} + + +/* send a channel create message */ +int mwChannel_create(struct mwChannel *chan) { + struct mwMsgChannelCreate *msg; + GList *list, *l; + int ret; + + g_return_val_if_fail(chan != NULL, -1); + g_return_val_if_fail(chan->state == mwChannel_INIT, -1); + g_return_val_if_fail(mwChannel_isOutgoing(chan), -1); + + msg = (struct mwMsgChannelCreate *) + mwMessage_new(mwMessage_CHANNEL_CREATE); + + msg->channel = chan->id; + msg->target.user = g_strdup(chan->user.user_id); + msg->target.community = g_strdup(chan->user.community); + msg->service = chan->service; + msg->proto_type = chan->proto_type; + msg->proto_ver = chan->proto_ver; + msg->options = chan->options; + mwOpaque_clone(&msg->addtl, &chan->addtl_create); + + list = mwChannel_getSupportedCipherInstances(chan); + if(list) { + /* offer what we have */ + for(l = list; l; l = l->next) { + struct mwEncryptItem *ei = mwCipherInstance_offer(l->data); + msg->encrypt.items = g_list_append(msg->encrypt.items, ei); + } + + /* we're easy to get along with */ + chan->offered_policy = mwEncrypt_WHATEVER; + g_list_free(list); + + } else { + /* we apparently don't support anything */ + chan->offered_policy = mwEncrypt_NONE; + } + + msg->encrypt.mode = chan->offered_policy; + msg->encrypt.extra = chan->offered_policy; + + ret = mwSession_send(chan->session, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + state(chan, (ret)? mwChannel_ERROR: mwChannel_WAIT, ret); + + return ret; +} + + +static void channel_open(struct mwChannel *chan) { + state(chan, mwChannel_OPEN, 0); + timestamp_stat(chan, mwChannelStat_OPENED_AT); + flush_channel(chan); +} + + +int mwChannel_accept(struct mwChannel *chan) { + struct mwSession *session; + struct mwMsgChannelAccept *msg; + struct mwCipherInstance *ci; + + int ret; + + g_return_val_if_fail(chan != NULL, -1); + g_return_val_if_fail(mwChannel_isIncoming(chan), -1); + g_return_val_if_fail(chan->state == mwChannel_WAIT, -1); + + session = chan->session; + g_return_val_if_fail(session != NULL, -1); + + msg = (struct mwMsgChannelAccept *) + mwMessage_new(mwMessage_CHANNEL_ACCEPT); + + msg->head.channel = chan->id; + msg->service = chan->service; + msg->proto_type = chan->proto_type; + msg->proto_ver = chan->proto_ver; + mwOpaque_clone(&msg->addtl, &chan->addtl_accept); + + ci = chan->cipher; + + if(! ci) { + /* automatically select a cipher if one hasn't been already */ + + switch(chan->offered_policy) { + case mwEncrypt_NONE: + mwChannel_selectCipherInstance(chan, NULL); + break; + + case mwEncrypt_RC2_40: + ci = get_supported(chan, mwCipher_RC2_40); + mwChannel_selectCipherInstance(chan, ci); + break; + + case mwEncrypt_RC2_128: + ci = get_supported(chan, mwCipher_RC2_128); + mwChannel_selectCipherInstance(chan, ci); + break; + + case mwEncrypt_WHATEVER: + case mwEncrypt_ALL: + default: + { + GList *l, *ll; + + l = mwChannel_getSupportedCipherInstances(chan); + if(l) { + /* nobody selected a cipher, so we'll just pick the last in + the list of available ones */ + for(ll = l; ll->next; ll = ll->next); + ci = ll->data; + g_list_free(l); + + mwChannel_selectCipherInstance(chan, ci); + + } else { + /* this may cause breakage, but there's really nothing else + we can do. They want something we can't provide. If they + don't like it, then they'll error the channel out */ + mwChannel_selectCipherInstance(chan, NULL); + } + } + } + } + + msg->encrypt.mode = chan->policy; /* set in selectCipherInstance */ + msg->encrypt.extra = chan->offered_policy; + + if(chan->cipher) { + msg->encrypt.item = mwCipherInstance_accept(chan->cipher); + } + + ret = mwSession_send(session, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + if(ret) { + state(chan, mwChannel_ERROR, ret); + } else { + channel_open(chan); + } + + return ret; +} + + +static void channel_free(struct mwChannel *chan) { + struct mwSession *s; + struct mwMessage *msg; + GSList *l; + + /* maybe no warning in the future */ + g_return_if_fail(chan != NULL); + + s = chan->session; + + mwLoginInfo_clear(&chan->user); + mwOpaque_clear(&chan->addtl_create); + mwOpaque_clear(&chan->addtl_accept); + + if(chan->supported) { + g_hash_table_destroy(chan->supported); + chan->supported = NULL; + } + + if(chan->stats) { + g_hash_table_destroy(chan->stats); + chan->stats = NULL; + } + + mwCipherInstance_free(chan->cipher); + + /* clean up the outgoing queue */ + for(l = chan->outgoing_queue; l; l = l->next) { + msg = (struct mwMessage *) l->data; + l->data = NULL; + mwMessage_free(msg); + } + g_slist_free(chan->outgoing_queue); + + /* clean up the incoming queue */ + for(l = chan->incoming_queue; l; l = l->next) { + msg = (struct mwMessage *) l->data; + l->data = NULL; + mwMessage_free(msg); + } + g_slist_free(chan->incoming_queue); + + g_free(chan); +} + + +int mwChannel_destroy(struct mwChannel *chan, + guint32 reason, struct mwOpaque *info) { + + struct mwMsgChannelDestroy *msg; + struct mwSession *session; + struct mwChannelSet *cs; + int ret; + + /* may make this not a warning in the future */ + g_return_val_if_fail(chan != NULL, 0); + + state(chan, reason? mwChannel_ERROR: mwChannel_DESTROY, reason); + + session = chan->session; + g_return_val_if_fail(session != NULL, -1); + + cs = mwSession_getChannels(session); + g_return_val_if_fail(cs != NULL, -1); + + /* compose the message */ + msg = (struct mwMsgChannelDestroy *) + mwMessage_new(mwMessage_CHANNEL_DESTROY); + msg->head.channel = chan->id; + msg->reason = reason; + if(info) mwOpaque_clone(&msg->data, info); + + /* remove the channel from the channel set */ + g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id)); + + /* send the message */ + ret = mwSession_send(session, (struct mwMessage *) msg); + mwMessage_free(MW_MESSAGE(msg)); + + return ret; +} + + +static void queue_outgoing(struct mwChannel *chan, + struct mwMsgChannelSend *msg) { + + g_info("queue_outgoing, channel 0x%08x", chan->id); + chan->outgoing_queue = g_slist_append(chan->outgoing_queue, msg); +} + + +static int channel_send(struct mwChannel *chan, + struct mwMsgChannelSend *msg) { + + int ret = 0; + + /* if the channel is open, send and free the message. Otherwise, + queue the message to be sent once the channel is finally + opened */ + + if(chan->state == mwChannel_OPEN) { + ret = mwSession_send(chan->session, (struct mwMessage *) msg); + mwMessage_free(MW_MESSAGE(msg)); + + } else { + queue_outgoing(chan, msg); + } + + return ret; +} + + +int mwChannel_sendEncrypted(struct mwChannel *chan, + guint32 type, struct mwOpaque *data, + gboolean encrypt) { + + struct mwMsgChannelSend *msg; + + g_return_val_if_fail(chan != NULL, -1); + + msg = (struct mwMsgChannelSend *) mwMessage_new(mwMessage_CHANNEL_SEND); + msg->head.channel = chan->id; + msg->type = type; + + mwOpaque_clone(&msg->data, data); + + if(encrypt && chan->cipher) { + msg->head.options = mwMessageOption_ENCRYPT; + mwCipherInstance_encrypt(chan->cipher, &msg->data); + } + + return channel_send(chan, msg); +} + + +int mwChannel_send(struct mwChannel *chan, guint32 type, + struct mwOpaque *data) { + + return mwChannel_sendEncrypted(chan, type, data, TRUE); +} + + +static void queue_incoming(struct mwChannel *chan, + struct mwMsgChannelSend *msg) { + + /* we clone the message, because session_process will clear it once + we return */ + + struct mwMsgChannelSend *m = g_new0(struct mwMsgChannelSend, 1); + m->head.type = msg->head.type; + m->head.options = msg->head.options; + m->head.channel = msg->head.channel; + mwOpaque_clone(&m->head.attribs, &msg->head.attribs); + + m->type = msg->type; + mwOpaque_clone(&m->data, &msg->data); + + g_info("queue_incoming, channel 0x%08x", chan->id); + chan->incoming_queue = g_slist_append(chan->incoming_queue, m); +} + + +static void channel_recv(struct mwChannel *chan, + struct mwMsgChannelSend *msg) { + + struct mwService *srvc; + srvc = mwChannel_getService(chan); + + incr_stat(chan, mwChannelStat_MSG_RECV, 1); + + if(msg->head.options & mwMessageOption_ENCRYPT) { + struct mwOpaque data = { 0, 0 }; + mwOpaque_clone(&data, &msg->data); + + mwCipherInstance_decrypt(chan->cipher, &data); + mwService_recv(srvc, chan, msg->type, &data); + mwOpaque_clear(&data); + + } else { + mwService_recv(srvc, chan, msg->type, &msg->data); + } +} + + +static void flush_channel(struct mwChannel *chan) { + GSList *l; + + for(l = chan->incoming_queue; l; l = l->next) { + struct mwMsgChannelSend *msg = (struct mwMsgChannelSend *) l->data; + l->data = NULL; + + channel_recv(chan, msg); + mwMessage_free(MW_MESSAGE(msg)); + } + g_slist_free(chan->incoming_queue); + chan->incoming_queue = NULL; + + for(l = chan->outgoing_queue; l; l = l->next) { + struct mwMessage *msg = (struct mwMessage *) l->data; + l->data = NULL; + + mwSession_send(chan->session, msg); + mwMessage_free(msg); + } + g_slist_free(chan->outgoing_queue); + chan->outgoing_queue = NULL; +} + + +void mwChannel_recv(struct mwChannel *chan, struct mwMsgChannelSend *msg) { + if(chan->state == mwChannel_OPEN) { + channel_recv(chan, msg); + + } else { + queue_incoming(chan, msg); + } +} + + +struct mwChannel *mwChannel_find(struct mwChannelSet *cs, guint32 chan) { + g_return_val_if_fail(cs != NULL, NULL); + g_return_val_if_fail(cs->map != NULL, NULL); + return g_hash_table_lookup(cs->map, GUINT_TO_POINTER(chan)); +} + + +void mwChannelSet_free(struct mwChannelSet *cs) { + if(! cs) return; + if(cs->map) g_hash_table_destroy(cs->map); + g_free(cs); +} + + +struct mwChannelSet *mwChannelSet_new(struct mwSession *s) { + struct mwChannelSet *cs = g_new0(struct mwChannelSet, 1); + cs->session = s; + + /* for some reason, g_int_hash/g_int_equal cause a SIGSEGV */ + cs->map = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) channel_free); + return cs; +} + + +void mwChannel_recvCreate(struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + struct mwSession *session; + GList *list; + struct mwService *srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(chan->id == msg->channel); + + session = chan->session; + g_return_if_fail(session != NULL); + + if(mwChannel_isOutgoing(chan)) { + g_warning("channel 0x%08x not an incoming channel", chan->id); + mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); + return; + } + + chan->offered_policy = msg->encrypt.mode; + g_message("channel offered with encrypt policy 0x%04x", chan->policy); + + for(list = msg->encrypt.items; list; list = list->next) { + struct mwEncryptItem *ei = list->data; + struct mwCipher *cipher; + struct mwCipherInstance *ci; + + g_message("channel offered cipher id 0x%04x", ei->id); + cipher = mwSession_getCipher(session, ei->id); + if(! cipher) { + g_message("no such cipher found in session"); + continue; + } + + ci = mwCipher_newInstance(cipher, chan); + mwCipherInstance_offered(ci, ei); + mwChannel_addSupportedCipherInstance(chan, ci); + } + + mwLoginInfo_clone(&chan->user, &msg->creator); + chan->service = msg->service; + chan->proto_type = msg->proto_type; + chan->proto_ver = msg->proto_ver; + + srvc = mwSession_getService(session, msg->service); + if(srvc) { + mwService_recvCreate(srvc, chan, msg); + + } else { + mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); + } +} + + +void mwChannel_recvAccept(struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + struct mwService *srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(chan->id == msg->head.channel); + + if(mwChannel_isIncoming(chan)) { + g_warning("channel 0x%08x not an outgoing channel", chan->id); + mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); + return; + } + + if(chan->state != mwChannel_WAIT) { + g_warning("channel 0x%08x state not WAIT: %s", + chan->id, state_str(chan->state)); + mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); + return; + } + + mwLoginInfo_clone(&chan->user, &msg->acceptor); + + srvc = mwSession_getService(chan->session, chan->service); + if(! srvc) { + g_warning("no service: 0x%08x", chan->service); + mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); + return; + } + + chan->policy = msg->encrypt.mode; + g_message("channel accepted with encrypt policy 0x%04x", chan->policy); + + if(! msg->encrypt.mode || ! msg->encrypt.item) { + /* no mode or no item means no encryption */ + mwChannel_selectCipherInstance(chan, NULL); + + } else { + guint16 cid = msg->encrypt.item->id; + struct mwCipherInstance *ci = get_supported(chan, cid); + + if(! ci) { + g_warning("not an offered cipher: 0x%04x", cid); + mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL); + return; + } + + mwCipherInstance_accepted(ci, msg->encrypt.item); + mwChannel_selectCipherInstance(chan, ci); + } + + /* mark it as open for the service */ + state(chan, mwChannel_OPEN, 0); + + /* let the service know */ + mwService_recvAccept(srvc, chan, msg); + + /* flush it if the service didn't just immediately close it */ + if(mwChannel_isState(chan, mwChannel_OPEN)) { + channel_open(chan); + } +} + + +void mwChannel_recvDestroy(struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwChannelSet *cs; + struct mwService *srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(chan->id == msg->head.channel); + + state(chan, msg->reason? mwChannel_ERROR: mwChannel_DESTROY, msg->reason); + + srvc = mwChannel_getService(chan); + if(srvc) mwService_recvDestroy(srvc, chan, msg); + + cs = mwSession_getChannels(chan->session); + g_return_if_fail(cs != NULL); + g_return_if_fail(cs->map != NULL); + + g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id)); +} + + +void mwChannel_populateSupportedCipherInstances(struct mwChannel *chan) { + struct mwSession *session; + GList *list; + + g_return_if_fail(chan != NULL); + + session = chan->session; + g_return_if_fail(session != NULL); + + for(list = mwSession_getCiphers(session); list; list = list->next) { + struct mwCipherInstance *ci = mwCipher_newInstance(list->data, chan); + if(! ci) continue; + put_supported(chan, ci); + } +} + + +void mwChannel_addSupportedCipherInstance(struct mwChannel *chan, + struct mwCipherInstance *ci) { + g_return_if_fail(chan != NULL); + g_message("channel 0x%08x added cipher %s", chan->id, + NSTR(mwCipher_getName(mwCipherInstance_getCipher(ci)))); + put_supported(chan, ci); +} + + +static void collect(gpointer a, gpointer b, gpointer c) { + GList **list = c; + *list = g_list_append(*list, b); +} + + +GList *mwChannel_getSupportedCipherInstances(struct mwChannel *chan) { + GList *list = NULL; + + g_return_val_if_fail(chan != NULL, NULL); + g_hash_table_foreach(chan->supported, collect, &list); + + return list; +} + + +void mwChannel_selectCipherInstance(struct mwChannel *chan, + struct mwCipherInstance *ci) { + struct mwCipher *c; + + g_return_if_fail(chan != NULL); + g_return_if_fail(chan->supported != NULL); + + chan->cipher = ci; + if(ci) { + guint cid; + + c = mwCipherInstance_getCipher(ci); + cid = mwCipher_getType(c); + + g_hash_table_steal(chan->supported, GUINT_TO_POINTER(cid)); + + switch(mwCipher_getType(c)) { + case mwCipher_RC2_40: + chan->policy = mwEncrypt_RC2_40; + break; + + case mwCipher_RC2_128: + chan->policy = mwEncrypt_RC2_128; + break; + + default: + /* unsure if this is bad */ + chan->policy = mwEncrypt_WHATEVER; + } + + g_message("channel 0x%08x selected cipher %s", + chan->id, NSTR(mwCipher_getName(c))); + + } else { + + chan->policy = mwEncrypt_NONE; + g_message("channel 0x%08x selected no cipher", chan->id); + } + + g_hash_table_destroy(chan->supported); + chan->supported = NULL; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/cipher.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,968 @@ + +/* + 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 <stdlib.h> +#include <time.h> + +#include "mpi/mpi.h" + +#include "mw_channel.h" +#include "mw_cipher.h" +#include "mw_debug.h" +#include "mw_session.h" + + +struct mwMpi { + mp_int i; +}; + + +/** From RFC2268 */ +static guchar PT[] = { + 0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED, + 0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D, + 0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E, + 0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2, + 0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13, + 0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32, + 0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B, + 0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82, + 0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C, + 0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC, + 0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1, + 0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26, + 0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57, + 0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03, + 0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7, + 0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7, + 0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7, + 0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A, + 0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74, + 0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC, + 0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC, + 0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39, + 0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A, + 0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31, + 0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE, + 0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9, + 0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C, + 0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9, + 0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0, + 0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E, + 0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77, + 0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD +}; + + +/** prime number used in DH exchange */ +static guchar dh_prime[] = { + 0xCF, 0x84, 0xAF, 0xCE, 0x86, 0xDD, 0xFA, 0x52, + 0x7F, 0x13, 0x6D, 0x10, 0x35, 0x75, 0x28, 0xEE, + 0xFB, 0xA0, 0xAF, 0xEF, 0x80, 0x8F, 0x29, 0x17, + 0x4E, 0x3B, 0x6A, 0x9E, 0x97, 0x00, 0x01, 0x71, + 0x7C, 0x8F, 0x10, 0x6C, 0x41, 0xC1, 0x61, 0xA6, + 0xCE, 0x91, 0x05, 0x7B, 0x34, 0xDA, 0x62, 0xCB, + 0xB8, 0x7B, 0xFD, 0xC1, 0xB3, 0x5C, 0x1B, 0x91, + 0x0F, 0xEA, 0x72, 0x24, 0x9D, 0x56, 0x6B, 0x9F +}; + + +/** base used in DH exchange */ +#define DH_BASE 3 + + +struct mwMpi *mwMpi_new() { + struct mwMpi *i; + i = g_new0(struct mwMpi, 1); + mp_init(&i->i); + return i; +} + + +void mwMpi_free(struct mwMpi *i) { + if(! i) return; + mp_clear(&i->i); + g_free(i); +} + + +static void mwInitDHPrime(mp_int *i) { + mp_init(i); + mp_read_unsigned_bin(i, dh_prime, 64); +} + + +void mwMpi_setDHPrime(struct mwMpi *i) { + g_return_if_fail(i != NULL); + mp_read_unsigned_bin(&i->i, dh_prime, 64); +} + + +static void mwInitDHBase(mp_int *i) { + mp_init(i); + mp_set_int(i, DH_BASE); +} + + +void mwMpi_setDHBase(struct mwMpi *i) { + g_return_if_fail(i != NULL); + mp_set_int(&i->i, DH_BASE); +} + + +static void mp_set_rand(mp_int *i, guint bits) { + size_t len, l; + guchar *buf; + + l = len = (bits / 8) + 1; + buf = g_malloc(len); + + srand(clock()); + while(l--) buf[l] = rand() & 0xff; + + buf[0] &= (0xff >> (8 - (bits % 8))); + + mp_read_unsigned_bin(i, buf, len); + g_free(buf); +} + + +void mwMpi_rand(struct mwMpi *i, guint bits) { + g_return_if_fail(i != NULL); + mp_set_rand(&i->i, bits); +} + + +static void mwDHRandKeypair(mp_int *private, mp_int *public) { + mp_int prime, base; + + mwInitDHPrime(&prime); + mwInitDHBase(&base); + + mp_set_rand(private, 512); + mp_exptmod(&base, private, &prime, public); + + mp_clear(&prime); + mp_clear(&base); +} + + +void mwMpi_randDHKeypair(struct mwMpi *private, struct mwMpi *public) { + g_return_if_fail(private != NULL); + g_return_if_fail(public != NULL); + + mwDHRandKeypair(&private->i, &public->i); +} + + +static void mwDHCalculateShared(mp_int *shared, mp_int *remote, + mp_int *private) { + mp_int prime; + + mwInitDHPrime(&prime); + mp_exptmod(remote, private, &prime, shared); + mp_clear(&prime); +} + + +void mwMpi_calculateDHShared(struct mwMpi *shared, struct mwMpi *remote, + struct mwMpi *private) { + + g_return_if_fail(shared != NULL); + g_return_if_fail(remote != NULL); + g_return_if_fail(private != NULL); + + mwDHCalculateShared(&shared->i, &remote->i, &private->i); +} + + +static void mwDHImportKey(mp_int *key, struct mwOpaque *o) { + mp_read_unsigned_bin(key, o->data, o->len); +} + + +void mwMpi_import(struct mwMpi *i, struct mwOpaque *o) { + g_return_if_fail(i != NULL); + g_return_if_fail(o != NULL); + + mwDHImportKey(&i->i, o); +} + + +static void mwDHExportKey(mp_int *key, struct mwOpaque *o) { + o->len = mp_unsigned_bin_size(key); + o->data = g_malloc0(o->len); + mp_to_unsigned_bin(key, o->data); +} + + +void mwMpi_export(struct mwMpi *i, struct mwOpaque *o) { + g_return_if_fail(i != NULL); + g_return_if_fail(o != NULL); + + mwDHExportKey(&i->i, o); +} + + +void mwKeyRandom(guchar *key, gsize keylen) { + g_return_if_fail(key != NULL); + + srand(clock()); + while(keylen--) key[keylen] = rand() & 0xff; +} + + +void mwIV_init(guchar *iv) { + int i; + static guchar normal_iv[] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef + }; + for(i = 8; i--; iv[i] = normal_iv[i]); + /* memcpy(iv, normal_iv, 8); */ +} + + +/* This does not seem to produce the same results as normal RC2 key + expansion would, but it works, so eh. It might be smart to farm + this out to mozilla or openssl */ +void mwKeyExpand(int *ekey, const guchar *key, gsize keylen) { + guchar tmp[128]; + int i, j; + + g_return_if_fail(keylen > 0); + g_return_if_fail(key != NULL); + + if(keylen > 128) keylen = 128; + + /* fill the first chunk with what key bytes we have */ + for(i = keylen; i--; tmp[i] = key[i]); + /* memcpy(tmp, key, keylen); */ + + /* build the remaining key from the given data */ + for(i = 0; keylen < 128; i++) { + tmp[keylen] = PT[ (tmp[keylen - 1] + tmp[i]) & 0xff ]; + keylen++; + } + + tmp[0] = PT[ tmp[0] & 0xff ]; + + for(i = 0, j = 0; i < 64; i++) { + ekey[i] = (tmp[j] & 0xff) | (tmp[j+1] << 8); + j += 2; + } +} + + +/* normal RC2 encryption given a full 128-byte (as 64 ints) key */ +static void mwEncryptBlock(const int *ekey, guchar *out) { + + int a, b, c, d; + int i, j; + + a = (out[7] << 8) | (out[6] & 0xff); + b = (out[5] << 8) | (out[4] & 0xff); + c = (out[3] << 8) | (out[2] & 0xff); + d = (out[1] << 8) | (out[0] & 0xff); + + for(i = 0; i < 16; i++) { + j = i * 4; + + d += ((c & (a ^ 0xffff)) + (b & a) + ekey[j++]); + d = (d << 1) | (d >> 15 & 0x0001); + + c += ((b & (d ^ 0xffff)) + (a & d) + ekey[j++]); + c = (c << 2) | (c >> 14 & 0x0003); + + b += ((a & (c ^ 0xffff)) + (d & c) + ekey[j++]); + b = (b << 3) | (b >> 13 & 0x0007); + + a += ((d & (b ^ 0xffff)) + (c & b) + ekey[j++]); + a = (a << 5) | (a >> 11 & 0x001f); + + if(i == 4 || i == 10) { + d += ekey[a & 0x003f]; + c += ekey[d & 0x003f]; + b += ekey[c & 0x003f]; + a += ekey[b & 0x003f]; + } + } + + *out++ = d & 0xff; + *out++ = (d >> 8) & 0xff; + *out++ = c & 0xff; + *out++ = (c >> 8) & 0xff; + *out++ = b & 0xff; + *out++ = (b >> 8) & 0xff; + *out++ = a & 0xff; + *out++ = (a >> 8) & 0xff; +} + + +void mwEncryptExpanded(const int *ekey, guchar *iv, + struct mwOpaque *in_data, + struct mwOpaque *out_data) { + + guchar *i = in_data->data; + gsize i_len = in_data->len; + + guchar *o; + gsize o_len; + + int x, y; + + /* pad upwards to a multiple of 8 */ + o_len = (i_len & -8) + 8; + o = g_malloc(o_len); + + out_data->data = o; + out_data->len = o_len; + + /* figure out the amount of padding */ + y = o_len - i_len; + + /* copy in to out, and write padding bytes */ + for(x = i_len; x--; o[x] = i[x]); + for(x = i_len; x < o_len; o[x++] = y); + /* memcpy(o, i, i_len); + memset(o + i_len, y, y); */ + + /* encrypt in blocks */ + for(x = o_len; x > 0; x -= 8) { + for(y = 8; y--; o[y] ^= iv[y]); + mwEncryptBlock(ekey, o); + for(y = 8; y--; iv[y] = o[y]); + /* memcpy(iv, o, 8); */ + o += 8; + } +} + + +void mwEncrypt(const guchar *key, gsize keylen, + guchar *iv, + struct mwOpaque *in, struct mwOpaque *out) { + + int ekey[64]; + mwKeyExpand(ekey, key, keylen); + mwEncryptExpanded(ekey, iv, in, out); +} + + +static void mwDecryptBlock(const int *ekey, guchar *out) { + + int a, b, c, d; + int i, j; + + a = (out[7] << 8) | (out[6] & 0xff); + b = (out[5] << 8) | (out[4] & 0xff); + c = (out[3] << 8) | (out[2] & 0xff); + d = (out[1] << 8) | (out[0] & 0xff); + + for(i = 16; i--; ) { + j = i * 4 + 3; + + a = (a << 11) | (a >> 5 & 0x07ff); + a -= ((d & (b ^ 0xffff)) + (c & b) + ekey[j--]); + + b = (b << 13) | (b >> 3 & 0x1fff); + b -= ((a & (c ^ 0xffff)) + (d & c) + ekey[j--]); + + c = (c << 14) | (c >> 2 & 0x3fff); + c -= ((b & (d ^ 0xffff)) + (a & d) + ekey[j--]); + + d = (d << 15) | (d >> 1 & 0x7fff); + d -= ((c & (a ^ 0xffff)) + (b & a) + ekey[j--]); + + if(i == 5 || i == 11) { + a -= ekey[b & 0x003f]; + b -= ekey[c & 0x003f]; + c -= ekey[d & 0x003f]; + d -= ekey[a & 0x003f]; + } + } + + *out++ = d & 0xff; + *out++ = (d >> 8) & 0xff; + *out++ = c & 0xff; + *out++ = (c >> 8) & 0xff; + *out++ = b & 0xff; + *out++ = (b >> 8) & 0xff; + *out++ = a & 0xff; + *out++ = (a >> 8) & 0xff; +} + + +void mwDecryptExpanded(const int *ekey, guchar *iv, + struct mwOpaque *in_data, + struct mwOpaque *out_data) { + + guchar *i = in_data->data; + gsize i_len = in_data->len; + + guchar *o; + gsize o_len; + + int x, y; + + /* this doesn't check to ensure that in_data->len is a multiple of + 8, which is damn well ought to be. */ + + o = g_malloc(i_len); + o_len = i_len; + for(x = i_len; x--; o[x] = i[x]); + /* memcpy(o, i, i_len); */ + + out_data->data = o; + out_data->len = o_len; + + for(x = o_len; x > 0; x -= 8) { + /* decrypt a block */ + mwDecryptBlock(ekey, o); + + /* modify the initialization vector */ + for(y = 8; y--; o[y] ^= iv[y]); + for(y = 8; y--; iv[y] = i[y]); + /* memcpy(iv, i, 8); */ + i += 8; + o += 8; + } + + /* shorten the length by the value of the filler in the padding + bytes */ + out_data->len -= *(o - 1); +} + + +void mwDecrypt(const guchar *key, gsize keylen, guchar *iv, + struct mwOpaque *in, struct mwOpaque *out) { + + int ekey[64]; + mwKeyExpand(ekey, key, keylen); + mwDecryptExpanded(ekey, iv, in, out); +} + + + +struct mwCipher_RC2_40 { + struct mwCipher cipher; + int session_key[64]; + gboolean ready; +}; + + +struct mwCipherInstance_RC2_40 { + struct mwCipherInstance instance; + int incoming_key[64]; + guchar outgoing_iv[8]; + guchar incoming_iv[8]; +}; + + +static const char *get_name_RC2_40() { + return "RC2/40 Cipher"; +} + + +static const char *get_desc_RC2_40() { + return "RC2, 40-bit effective key"; +} + + +static int encrypt_RC2_40(struct mwCipherInstance *ci, + struct mwOpaque *data) { + + struct mwCipherInstance_RC2_40 *cir; + struct mwCipher_RC2_40 *cr; + struct mwOpaque o = { 0, 0 }; + + cir = (struct mwCipherInstance_RC2_40 *) ci; + cr = (struct mwCipher_RC2_40 *) ci->cipher; + + mwEncryptExpanded(cr->session_key, cir->outgoing_iv, data, &o); + + mwOpaque_clear(data); + data->data = o.data; + data->len = o.len; + + return 0; +} + + +static int decrypt_RC2_40(struct mwCipherInstance *ci, + struct mwOpaque *data) { + + struct mwCipherInstance_RC2_40 *cir; + struct mwCipher_RC2_40 *cr; + struct mwOpaque o = { 0, 0 }; + + cir = (struct mwCipherInstance_RC2_40 *) ci; + cr = (struct mwCipher_RC2_40 *) ci->cipher; + + mwDecryptExpanded(cir->incoming_key, cir->incoming_iv, data, &o); + + mwOpaque_clear(data); + data->data = o.data; + data->len = o.len; + + return 0; +} + + +static struct mwCipherInstance * +new_instance_RC2_40(struct mwCipher *cipher, + struct mwChannel *chan) { + + struct mwCipher_RC2_40 *cr; + struct mwCipherInstance_RC2_40 *cir; + struct mwCipherInstance *ci; + + cr = (struct mwCipher_RC2_40 *) cipher; + + /* a bit of lazy initialization here */ + if(! cr->ready) { + struct mwLoginInfo *info = mwSession_getLoginInfo(cipher->session); + mwKeyExpand(cr->session_key, (guchar *) info->login_id, 5); + cr->ready = TRUE; + } + + cir = g_new0(struct mwCipherInstance_RC2_40, 1); + ci = &cir->instance; + + ci->cipher = cipher; + ci->channel = chan; + + mwIV_init(cir->incoming_iv); + mwIV_init(cir->outgoing_iv); + + return ci; +} + + +static struct mwEncryptItem *new_item_RC2_40(struct mwCipherInstance *ci) { + struct mwEncryptItem *e; + + e = g_new0(struct mwEncryptItem, 1); + e->id = mwCipher_RC2_40; + return e; +} + + +static struct mwEncryptItem * +offer_RC2_40(struct mwCipherInstance *ci) { + return new_item_RC2_40(ci); +} + + +static void accepted_RC2_40(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + + struct mwCipherInstance_RC2_40 *cir; + struct mwLoginInfo *info; + + cir = (struct mwCipherInstance_RC2_40 *) ci; + info = mwChannel_getUser(ci->channel); + + if(info->login_id) { + mwKeyExpand(cir->incoming_key, (guchar *) info->login_id, 5); + } +} + + +static struct mwEncryptItem * +accept_RC2_40(struct mwCipherInstance *ci) { + + accepted_RC2_40(ci, NULL); + return new_item_RC2_40(ci); +} + + +struct mwCipher *mwCipher_new_RC2_40(struct mwSession *s) { + struct mwCipher_RC2_40 *cr = g_new0(struct mwCipher_RC2_40, 1); + struct mwCipher *c = &cr->cipher; + + c->session = s; + c->type = mwCipher_RC2_40; + c->get_name = get_name_RC2_40; + c->get_desc = get_desc_RC2_40; + c->new_instance = new_instance_RC2_40; + + c->offer = offer_RC2_40; + + c->accepted = accepted_RC2_40; + c->accept = accept_RC2_40; + + c->encrypt = encrypt_RC2_40; + c->decrypt = decrypt_RC2_40; + + return c; +} + + +struct mwCipher_RC2_128 { + struct mwCipher cipher; + mp_int private_key; + struct mwOpaque public_key; +}; + + +struct mwCipherInstance_RC2_128 { + struct mwCipherInstance instance; + int shared[64]; /* shared secret determined via DH exchange */ + guchar outgoing_iv[8]; + guchar incoming_iv[8]; +}; + + +static const char *get_name_RC2_128() { + return "RC2/128 Cipher"; +} + + +static const char *get_desc_RC2_128() { + return "RC2, DH shared secret key"; +} + + +static struct mwCipherInstance * +new_instance_RC2_128(struct mwCipher *cipher, + struct mwChannel *chan) { + + struct mwCipher_RC2_128 *cr; + struct mwCipherInstance_RC2_128 *cir; + struct mwCipherInstance *ci; + + cr = (struct mwCipher_RC2_128 *) cipher; + + cir = g_new0(struct mwCipherInstance_RC2_128, 1); + ci = &cir->instance; + + ci->cipher = cipher; + ci->channel = chan; + + mwIV_init(cir->incoming_iv); + mwIV_init(cir->outgoing_iv); + + return ci; +} + + +static void offered_RC2_128(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + + mp_int remote_key; + mp_int shared; + struct mwOpaque sho = { 0, 0 }; + + struct mwCipher *c; + struct mwCipher_RC2_128 *cr; + struct mwCipherInstance_RC2_128 *cir; + + c = ci->cipher; + cr = (struct mwCipher_RC2_128 *) c; + cir = (struct mwCipherInstance_RC2_128 *) ci; + + mp_init(&remote_key); + mp_init(&shared); + + mwDHImportKey(&remote_key, &item->info); + mwDHCalculateShared(&shared, &remote_key, &cr->private_key); + mwDHExportKey(&shared, &sho); + + /* key expanded from the last 16 bytes of the DH shared secret. This + took me forever to figure out. 16 bytes is 128 bit. */ + /* the sh_len-16 is important, because the key len could + hypothetically start with 8bits or more unset, meaning the + exported key might be less than 64 bytes in length */ + mwKeyExpand(cir->shared, sho.data+(sho.len-16), 16); + + mp_clear(&remote_key); + mp_clear(&shared); + mwOpaque_clear(&sho); +} + + +static struct mwEncryptItem * +offer_RC2_128(struct mwCipherInstance *ci) { + + struct mwCipher *c; + struct mwCipher_RC2_128 *cr; + struct mwEncryptItem *ei; + + c = ci->cipher; + cr = (struct mwCipher_RC2_128 *) c; + + ei = g_new0(struct mwEncryptItem, 1); + ei->id = mwCipher_RC2_128; + mwOpaque_clone(&ei->info, &cr->public_key); + + return ei; +} + + +static void accepted_RC2_128(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + + return offered_RC2_128(ci, item); +} + + +static struct mwEncryptItem * +accept_RC2_128(struct mwCipherInstance *ci) { + + return offer_RC2_128(ci); +} + + +static int encrypt_RC2_128(struct mwCipherInstance *ci, + struct mwOpaque *data) { + + struct mwCipherInstance_RC2_128 *cir; + struct mwOpaque o = { 0, 0 }; + + cir = (struct mwCipherInstance_RC2_128 *) ci; + + mwEncryptExpanded(cir->shared, cir->outgoing_iv, data, &o); + + mwOpaque_clear(data); + data->data = o.data; + data->len = o.len; + + return 0; +} + + +static int decrypt_RC2_128(struct mwCipherInstance *ci, + struct mwOpaque *data) { + + struct mwCipherInstance_RC2_128 *cir; + struct mwOpaque o = { 0, 0 }; + + cir = (struct mwCipherInstance_RC2_128 *) ci; + + mwDecryptExpanded(cir->shared, cir->incoming_iv, data, &o); + + mwOpaque_clear(data); + data->data = o.data; + data->len = o.len; + + return 0; +} + + +static void clear_RC2_128(struct mwCipher *c) { + struct mwCipher_RC2_128 *cr; + cr = (struct mwCipher_RC2_128 *) c; + + mp_clear(&cr->private_key); + mwOpaque_clear(&cr->public_key); +} + + +struct mwCipher *mwCipher_new_RC2_128(struct mwSession *s) { + struct mwCipher_RC2_128 *cr; + struct mwCipher *c; + + mp_int pubkey; + + cr = g_new0(struct mwCipher_RC2_128, 1); + c = &cr->cipher; + + c->session = s; + c->type = mwCipher_RC2_128; + c->get_name = get_name_RC2_128; + c->get_desc = get_desc_RC2_128; + c->new_instance = new_instance_RC2_128; + + c->offered = offered_RC2_128; + c->offer = offer_RC2_128; + + c->accepted = accepted_RC2_128; + c->accept = accept_RC2_128; + + c->encrypt = encrypt_RC2_128; + c->decrypt = decrypt_RC2_128; + + c->clear = clear_RC2_128; + + mp_init(&cr->private_key); + mp_init(&pubkey); + mwDHRandKeypair(&cr->private_key, &pubkey); + mwDHExportKey(&pubkey, &cr->public_key); + mp_clear(&pubkey); + + return c; +} + + +struct mwSession *mwCipher_getSession(struct mwCipher *cipher) { + g_return_val_if_fail(cipher != NULL, NULL); + return cipher->session; +} + + +guint16 mwCipher_getType(struct mwCipher *cipher) { + /* oh man, this is a bad failover... who the hell decided to make + zero a real cipher id? */ + g_return_val_if_fail(cipher != NULL, 0xffff); + return cipher->type; +} + + +const char *mwCipher_getName(struct mwCipher *cipher) { + g_return_val_if_fail(cipher != NULL, NULL); + g_return_val_if_fail(cipher->get_name != NULL, NULL); + return cipher->get_name(); +} + + +const char *mwCipher_getDesc(struct mwCipher *cipher) { + g_return_val_if_fail(cipher != NULL, NULL); + g_return_val_if_fail(cipher->get_desc != NULL, NULL); + return cipher->get_desc(); +} + + +void mwCipher_free(struct mwCipher *cipher) { + if(! cipher) return; + + if(cipher->clear) + cipher->clear(cipher); + + g_free(cipher); +} + + +struct mwCipherInstance *mwCipher_newInstance(struct mwCipher *cipher, + struct mwChannel *chan) { + g_return_val_if_fail(cipher != NULL, NULL); + g_return_val_if_fail(chan != NULL, NULL); + g_return_val_if_fail(cipher->new_instance != NULL, NULL); + return cipher->new_instance(cipher, chan); +} + + +struct mwCipher *mwCipherInstance_getCipher(struct mwCipherInstance *ci) { + g_return_val_if_fail(ci != NULL, NULL); + return ci->cipher; +} + + +struct mwChannel *mwCipherInstance_getChannel(struct mwCipherInstance *ci) { + g_return_val_if_fail(ci != NULL, NULL); + return ci->channel; +} + + +void mwCipherInstance_offered(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + struct mwCipher *cipher; + + g_return_if_fail(ci != NULL); + + cipher = ci->cipher; + g_return_if_fail(cipher != NULL); + + if(cipher->offered) cipher->offered(ci, item); +} + + +struct mwEncryptItem * +mwCipherInstance_offer(struct mwCipherInstance *ci) { + struct mwCipher *cipher; + + g_return_val_if_fail(ci != NULL, NULL); + + cipher = ci->cipher; + g_return_val_if_fail(cipher != NULL, NULL); + + return cipher->offer(ci); +} + + +void mwCipherInstance_accepted(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + struct mwCipher *cipher; + + g_return_if_fail(ci != NULL); + + cipher = ci->cipher; + g_return_if_fail(cipher != NULL); + + if(cipher->accepted) cipher->accepted(ci, item); +} + + +struct mwEncryptItem * +mwCipherInstance_accept(struct mwCipherInstance *ci) { + struct mwCipher *cipher; + + g_return_val_if_fail(ci != NULL, NULL); + + cipher = ci->cipher; + g_return_val_if_fail(cipher != NULL, NULL); + + return cipher->accept(ci); +} + + +int mwCipherInstance_encrypt(struct mwCipherInstance *ci, + struct mwOpaque *data) { + struct mwCipher *cipher; + + g_return_val_if_fail(data != NULL, 0); + + if(! ci) return 0; + cipher = ci->cipher; + + g_return_val_if_fail(cipher != NULL, -1); + + return (cipher->encrypt)? + cipher->encrypt(ci, data): 0; +} + + +int mwCipherInstance_decrypt(struct mwCipherInstance *ci, + struct mwOpaque *data) { + struct mwCipher *cipher; + + g_return_val_if_fail(data != NULL, 0); + + if(! ci) return 0; + cipher = ci->cipher; + + g_return_val_if_fail(cipher != NULL, -1); + + return (cipher->decrypt)? + cipher->decrypt(ci, data): 0; +} + + +void mwCipherInstance_free(struct mwCipherInstance *ci) { + struct mwCipher *cipher; + + if(! ci) return; + + cipher = ci->cipher; + + if(cipher && cipher->clear_instance) + cipher->clear_instance(ci); + + g_free(ci); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/common.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,927 @@ + +/* + 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.h> +#include <string.h> + +#include "mw_common.h" + + +/** @todo the *_get functions should make sure to clear their + structures in the event of failure, to prevent memory leaks */ + + +#define MW16_PUT(b, val) \ + *(b)++ = ((val) >> 0x08) & 0xff; \ + *(b)++ = (val) & 0xff; + + +#define MW16_GET(b, val) \ + val = (*(b)++ & 0xff) << 8; \ + val = val | (*(b)++ & 0xff); + + +#define MW32_PUT(b, val) \ + *(b)++ = ((val) >> 0x18) & 0xff; \ + *(b)++ = ((val) >> 0x10) & 0xff; \ + *(b)++ = ((val) >> 0x08) & 0xff; \ + *(b)++ = (val) & 0xff; + + +#define MW32_GET(b, val) \ + val = (*(b)++ & 0xff) << 0x18; \ + val = val | (*(b)++ & 0xff) << 0x10; \ + val = val | (*(b)++ & 0xff) << 0x08; \ + val = val | (*(b)++ & 0xff); + + +struct mwPutBuffer { + guchar *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + guchar *ptr; /**< offset to first unused byte */ + gsize rem; /**< count of unused bytes remaining */ +}; + + +struct mwGetBuffer { + guchar *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + guchar *ptr; /**< offset to first unused byte */ + gsize rem; /**< count of unused bytes remaining */ + + gboolean wrap; /**< TRUE to indicate buf shouldn't be freed */ + gboolean error; /**< TRUE to indicate an error */ +}; + + +#define BUFFER_USED(buffer) \ + ((buffer)->len - (buffer)->rem) + + +/** ensure that there's at least enough space remaining in the put + buffer to fit needed. */ +static void ensure_buffer(struct mwPutBuffer *b, gsize needed) { + if(b->rem < needed) { + gsize len = b->len, use = BUFFER_USED(b); + guchar *buf; + + /* newly created buffers are empty until written to, and then they + have 1024 available */ + if(! len) len = 1024; + + /* double len until it's large enough to fit needed */ + while( (len - use) < needed ) len = len << 1; + + /* create the new buffer. if there was anything in the old buffer, + copy it into the new buffer and free the old copy */ + buf = g_malloc(len); + if(b->buf) { + memcpy(buf, b->buf, use); + g_free(b->buf); + } + + /* put the new buffer into b */ + b->buf = buf; + b->len = len; + b->ptr = buf + use; + b->rem = len - use; + } +} + + +/** determine if there are at least needed bytes available in the + buffer. sets the error flag if there's not at least needed bytes + left in the buffer + + @returns true if there's enough data, false if not */ +static gboolean check_buffer(struct mwGetBuffer *b, gsize needed) { + if(! b->error) b->error = (b->rem < needed); + return ! b->error; +} + + +struct mwPutBuffer *mwPutBuffer_new() { + return g_new0(struct mwPutBuffer, 1); +} + + +void mwPutBuffer_write(struct mwPutBuffer *b, gpointer data, gsize len) { + g_return_if_fail(b != NULL); + g_return_if_fail(data != NULL); + + if(! len) return; + + ensure_buffer(b, len); + memcpy(b->ptr, data, len); + b->ptr += len; + b->rem -= len; +} + + +void mwPutBuffer_free(struct mwPutBuffer *b) { + if(! b) return; + g_free(b->buf); + g_free(b); +} + + +void mwPutBuffer_finalize(struct mwOpaque *to, struct mwPutBuffer *from) { + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->len = BUFFER_USED(from); + to->data = from->buf; + + g_free(from); +} + + +struct mwGetBuffer *mwGetBuffer_new(struct mwOpaque *o) { + struct mwGetBuffer *b = g_new0(struct mwGetBuffer, 1); + + if(o && o->len) { + b->buf = b->ptr = g_memdup(o->data, o->len); + b->len = b->rem = o->len; + } + + return b; +} + + +struct mwGetBuffer *mwGetBuffer_wrap(const struct mwOpaque *o) { + struct mwGetBuffer *b = g_new0(struct mwGetBuffer, 1); + + if(o && o->len) { + b->buf = b->ptr = o->data; + b->len = b->rem = o->len; + } + b->wrap = TRUE; + + return b; +} + + +gsize mwGetBuffer_read(struct mwGetBuffer *b, gpointer data, gsize len) { + g_return_val_if_fail(b != NULL, 0); + g_return_val_if_fail(data != NULL, 0); + + if(b->error) return 0; + if(! len) return 0; + + if(b->rem < len) + len = b->rem; + + memcpy(data, b->ptr, len); + b->ptr += len; + b->rem -= len; + + return len; +} + + +gsize mwGetBuffer_advance(struct mwGetBuffer *b, gsize len) { + g_return_val_if_fail(b != NULL, 0); + + if(b->error) return 0; + if(! len) return 0; + + if(b->rem < len) + len = b->rem; + + b->ptr += len; + b->rem -= len; + + return len; +} + + +void mwGetBuffer_reset(struct mwGetBuffer *b) { + g_return_if_fail(b != NULL); + + b->rem = b->len; + b->ptr = b->buf; + b->error = FALSE; +} + + +gsize mwGetBuffer_remaining(struct mwGetBuffer *b) { + g_return_val_if_fail(b != NULL, 0); + return b->rem; +} + + +gboolean mwGetBuffer_error(struct mwGetBuffer *b) { + g_return_val_if_fail(b != NULL, TRUE); + return b->error; +} + + +void mwGetBuffer_free(struct mwGetBuffer *b) { + if(! b) return; + if(! b->wrap) g_free(b->buf); + g_free(b); +} + + +#define guint16_buflen() 2 + + +void guint16_put(struct mwPutBuffer *b, guint16 val) { + g_return_if_fail(b != NULL); + + ensure_buffer(b, guint16_buflen()); + MW16_PUT(b->ptr, val); + b->rem -= guint16_buflen(); +} + + +void guint16_get(struct mwGetBuffer *b, guint16 *val) { + g_return_if_fail(b != NULL); + + if(b->error) return; + g_return_if_fail(check_buffer(b, guint16_buflen())); + + MW16_GET(b->ptr, *val); + b->rem -= guint16_buflen(); +} + + +guint16 guint16_peek(struct mwGetBuffer *b) { + guchar *buf = b->buf; + guint16 r = 0; + + if(b->rem >= guint16_buflen()) + MW16_GET(buf, r); + + return r; +} + + +#define guint32_buflen() 4 + + +void guint32_put(struct mwPutBuffer *b, guint32 val) { + g_return_if_fail(b != NULL); + + ensure_buffer(b, guint32_buflen()); + MW32_PUT(b->ptr, val); + b->rem -= guint32_buflen(); +} + + +void guint32_get(struct mwGetBuffer *b, guint32 *val) { + g_return_if_fail(b != NULL); + + if(b->error) return; + g_return_if_fail(check_buffer(b, guint32_buflen())); + + MW32_GET(b->ptr, *val); + b->rem -= guint32_buflen(); +} + + +guint32 guint32_peek(struct mwGetBuffer *b) { + guchar *buf = b->buf; + guint32 r = 0; + + if(b->rem >= guint32_buflen()) + MW32_GET(buf, r); + + return r; +} + + +#define gboolean_buflen() 1 + + +void gboolean_put(struct mwPutBuffer *b, gboolean val) { + g_return_if_fail(b != NULL); + + ensure_buffer(b, gboolean_buflen()); + *(b->ptr) = !! val; + b->ptr++; + b->rem--; +} + + +void gboolean_get(struct mwGetBuffer *b, gboolean *val) { + g_return_if_fail(b != NULL); + + if(b->error) return; + g_return_if_fail(check_buffer(b, gboolean_buflen())); + + *val = !! *(b->ptr); + b->ptr++; + b->rem--; +} + + +gboolean gboolean_peek(struct mwGetBuffer *b) { + gboolean v = FALSE; + + if(b->rem >= gboolean_buflen()) + v = !! *(b->ptr); + + return v; +} + + +gboolean mw_streq(const char *a, const char *b) { + return (a == b) || (a && b && !strcmp(a, b)); +} + + +void mwString_put(struct mwPutBuffer *b, const char *val) { + gsize len = 0; + + g_return_if_fail(b != NULL); + + if(val) len = strlen(val); + + guint16_put(b, (guint16) len); + + if(len) { + ensure_buffer(b, len); + memcpy(b->ptr, val, len); + b->ptr += len; + b->rem -= len; + } +} + + +void mwString_get(struct mwGetBuffer *b, char **val) { + guint16 len = 0; + + g_return_if_fail(b != NULL); + g_return_if_fail(val != NULL); + + *val = NULL; + + if(b->error) return; + guint16_get(b, &len); + + g_return_if_fail(check_buffer(b, (gsize) len)); + + if(len) { + *val = g_malloc0(len + 1); + memcpy(*val, b->ptr, len); + b->ptr += len; + b->rem -= len; + } +} + + +void mwOpaque_put(struct mwPutBuffer *b, const struct mwOpaque *o) { + gsize len; + + g_return_if_fail(b != NULL); + + if(! o) { + guint32_put(b, 0x00); + return; + } + + len = o->len; + if(len) + g_return_if_fail(o->data != NULL); + + guint32_put(b, (guint32) len); + + if(len) { + ensure_buffer(b, len); + memcpy(b->ptr, o->data, len); + b->ptr += len; + b->rem -= len; + } +} + + +void mwOpaque_get(struct mwGetBuffer *b, struct mwOpaque *o) { + guint32 tmp = 0; + + g_return_if_fail(b != NULL); + g_return_if_fail(o != NULL); + + o->len = 0; + o->data = NULL; + + if(b->error) return; + guint32_get(b, &tmp); + + g_return_if_fail(check_buffer(b, (gsize) tmp)); + + o->len = (gsize) tmp; + if(tmp > 0) { + o->data = g_memdup(b->ptr, tmp); + b->ptr += tmp; + b->rem -= tmp; + } +} + + +void mwOpaque_clear(struct mwOpaque *o) { + if(! o) return; + g_free(o->data); + o->data = NULL; + o->len = 0; +} + + +void mwOpaque_free(struct mwOpaque *o) { + if(! o) return; + g_free(o->data); + g_free(o); +} + + +void mwOpaque_clone(struct mwOpaque *to, const struct mwOpaque *from) { + g_return_if_fail(to != NULL); + + to->len = 0; + to->data = NULL; + + if(from) { + to->len = from->len; + if(to->len) + to->data = g_memdup(from->data, to->len); + } +} + + +/* 8.2 Common Structures */ +/* 8.2.1 Login Info block */ + + +void mwLoginInfo_put(struct mwPutBuffer *b, const struct mwLoginInfo *login) { + g_return_if_fail(b != NULL); + g_return_if_fail(login != NULL); + + mwString_put(b, login->login_id); + guint16_put(b, login->type); + mwString_put(b, login->user_id); + mwString_put(b, login->user_name); + mwString_put(b, login->community); + gboolean_put(b, login->full); + + if(login->full) { + mwString_put(b, login->desc); + guint32_put(b, login->ip_addr); + mwString_put(b, login->server_id); + } +} + + +void mwLoginInfo_get(struct mwGetBuffer *b, struct mwLoginInfo *login) { + g_return_if_fail(b != NULL); + g_return_if_fail(login != NULL); + + if(b->error) return; + + mwString_get(b, &login->login_id); + guint16_get(b, &login->type); + mwString_get(b, &login->user_id); + mwString_get(b, &login->user_name); + mwString_get(b, &login->community); + gboolean_get(b, &login->full); + + if(login->full) { + mwString_get(b, &login->desc); + guint32_get(b, &login->ip_addr); + mwString_get(b, &login->server_id); + } +} + + +void mwLoginInfo_clear(struct mwLoginInfo *login) { + if(! login) return; + + g_free(login->login_id); + g_free(login->user_id); + g_free(login->user_name); + g_free(login->community); + g_free(login->desc); + g_free(login->server_id); + + memset(login, 0x00, sizeof(struct mwLoginInfo)); +} + + +void mwLoginInfo_clone(struct mwLoginInfo *to, + const struct mwLoginInfo *from) { + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->login_id= g_strdup(from->login_id); + to->type = from->type; + to->user_id = g_strdup(from->user_id); + to->user_name = g_strdup(from->user_name); + to->community = g_strdup(from->community); + + if( (to->full = from->full) ) { + to->desc = g_strdup(from->desc); + to->ip_addr = from->ip_addr; + to->server_id = g_strdup(from->server_id); + } +} + + +/* 8.2.2 Private Info Block */ + + +void mwUserItem_put(struct mwPutBuffer *b, const struct mwUserItem *user) { + g_return_if_fail(b != NULL); + g_return_if_fail(user != NULL); + + gboolean_put(b, user->full); + mwString_put(b, user->id); + mwString_put(b, user->community); + + if(user->full) + mwString_put(b, user->name); +} + + +void mwUserItem_get(struct mwGetBuffer *b, struct mwUserItem *user) { + g_return_if_fail(b != NULL); + g_return_if_fail(user != NULL); + + if(b->error) return; + + gboolean_get(b, &user->full); + mwString_get(b, &user->id); + mwString_get(b, &user->community); + + if(user->full) + mwString_get(b, &user->name); +} + + +void mwUserItem_clear(struct mwUserItem *user) { + if(! user) return; + + g_free(user->id); + g_free(user->community); + g_free(user->name); + + memset(user, 0x00, sizeof(struct mwUserItem)); +} + + +void mwUserItem_clone(struct mwUserItem *to, + const struct mwUserItem *from) { + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->full = from->full; + to->id = g_strdup(from->id); + to->community = g_strdup(from->community); + to->name = (to->full)? g_strdup(from->name): NULL; +} + + +void mwPrivacyInfo_put(struct mwPutBuffer *b, + const struct mwPrivacyInfo *info) { + guint32 c; + + g_return_if_fail(b != NULL); + g_return_if_fail(info != NULL); + + gboolean_put(b, info->deny); + guint32_put(b, info->count); + + for(c = info->count; c--; ) mwUserItem_put(b, info->users + c); +} + + +void mwPrivacyInfo_get(struct mwGetBuffer *b, struct mwPrivacyInfo *info) { + g_return_if_fail(b != NULL); + g_return_if_fail(info != NULL); + + if(b->error) return; + + gboolean_get(b, &info->deny); + guint32_get(b, &info->count); + + if(info->count) { + guint32 c = info->count; + info->users = g_new0(struct mwUserItem, c); + while(c--) mwUserItem_get(b, info->users + c); + } +} + + +void mwPrivacyInfo_clone(struct mwPrivacyInfo *to, + const struct mwPrivacyInfo *from) { + + guint32 c; + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->deny = from->deny; + c = to->count = from->count; + + to->users = g_new0(struct mwUserItem, c); + while(c--) mwUserItem_clone(to->users+c, from->users+c); +} + + +void mwPrivacyInfo_clear(struct mwPrivacyInfo *info) { + struct mwUserItem *u; + guint32 c; + + g_return_if_fail(info != NULL); + + u = info->users; + c = info->count; + + while(c--) mwUserItem_clear(u + c); + g_free(u); + + info->count = 0; + info->users = NULL; +} + + +/* 8.2.3 User Status Block */ + + +void mwUserStatus_put(struct mwPutBuffer *b, + const struct mwUserStatus *stat) { + + g_return_if_fail(b != NULL); + g_return_if_fail(stat != NULL); + + guint16_put(b, stat->status); + guint32_put(b, stat->time); + mwString_put(b, stat->desc); +} + + +void mwUserStatus_get(struct mwGetBuffer *b, struct mwUserStatus *stat) { + g_return_if_fail(b != NULL); + g_return_if_fail(stat != NULL); + + if(b->error) return; + + guint16_get(b, &stat->status); + guint32_get(b, &stat->time); + mwString_get(b, &stat->desc); +} + + +void mwUserStatus_clear(struct mwUserStatus *stat) { + if(! stat) return; + g_free(stat->desc); + memset(stat, 0x00, sizeof(struct mwUserStatus)); +} + + +void mwUserStatus_clone(struct mwUserStatus *to, + const struct mwUserStatus *from) { + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->status = from->status; + to->time = from->time; + to->desc = g_strdup(from->desc); +} + + +/* 8.2.4 ID Block */ + + +void mwIdBlock_put(struct mwPutBuffer *b, const struct mwIdBlock *id) { + g_return_if_fail(b != NULL); + g_return_if_fail(id != NULL); + + mwString_put(b, id->user); + mwString_put(b, id->community); +} + + +void mwIdBlock_get(struct mwGetBuffer *b, struct mwIdBlock *id) { + g_return_if_fail(b != NULL); + g_return_if_fail(id != NULL); + + if(b->error) return; + + mwString_get(b, &id->user); + mwString_get(b, &id->community); +} + + +void mwIdBlock_clear(struct mwIdBlock *id) { + if(! id) return; + + g_free(id->user); + id->user = NULL; + + g_free(id->community); + id->community = NULL; +} + + +void mwIdBlock_clone(struct mwIdBlock *to, const struct mwIdBlock *from) { + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->user = g_strdup(from->user); + to->community = g_strdup(from->community); +} + + +guint mwIdBlock_hash(const struct mwIdBlock *idb) { + return (idb)? g_str_hash(idb->user): 0; +} + + +gboolean mwIdBlock_equal(const struct mwIdBlock *a, + const struct mwIdBlock *b) { + + g_return_val_if_fail(a != NULL, FALSE); + g_return_val_if_fail(b != NULL, FALSE); + + return ( mw_streq(a->user, b->user) && + mw_streq(a->community, b->community) ); +} + + +/* 8.2.5 Encryption Block */ + +/** @todo I think this can be put into cipher */ + +void mwEncryptItem_put(struct mwPutBuffer *b, + const struct mwEncryptItem *ei) { + + g_return_if_fail(b != NULL); + g_return_if_fail(ei != NULL); + + guint16_put(b, ei->id); + mwOpaque_put(b, &ei->info); + +} + + +void mwEncryptItem_get(struct mwGetBuffer *b, struct mwEncryptItem *ei) { + g_return_if_fail(b != NULL); + g_return_if_fail(ei != NULL); + + if(b->error) return; + + guint16_get(b, &ei->id); + mwOpaque_get(b, &ei->info); +} + + +void mwEncryptItem_clear(struct mwEncryptItem *ei) { + if(! ei) return; + ei->id = 0x0000; + mwOpaque_clear(&ei->info); +} + + +void mwEncryptItem_free(struct mwEncryptItem *ei) { + mwEncryptItem_clear(ei); + g_free(ei); +} + + +/* 8.4.2.1 Awareness ID Block */ + + +/** @todo move this into srvc_aware */ + +void mwAwareIdBlock_put(struct mwPutBuffer *b, + const struct mwAwareIdBlock *idb) { + + g_return_if_fail(b != NULL); + g_return_if_fail(idb != NULL); + + guint16_put(b, idb->type); + mwString_put(b, idb->user); + mwString_put(b, idb->community); +} + + +void mwAwareIdBlock_get(struct mwGetBuffer *b, struct mwAwareIdBlock *idb) { + g_return_if_fail(b != NULL); + g_return_if_fail(idb != NULL); + + if(b->error) return; + + guint16_get(b, &idb->type); + mwString_get(b, &idb->user); + mwString_get(b, &idb->community); +} + + +void mwAwareIdBlock_clone(struct mwAwareIdBlock *to, + const struct mwAwareIdBlock *from) { + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + to->type = from->type; + to->user = g_strdup(from->user); + to->community = g_strdup(from->community); +} + + +void mwAwareIdBlock_clear(struct mwAwareIdBlock *idb) { + if(! idb) return; + g_free(idb->user); + g_free(idb->community); + memset(idb, 0x00, sizeof(struct mwAwareIdBlock)); +} + + +guint mwAwareIdBlock_hash(const struct mwAwareIdBlock *a) { + return (a)? g_str_hash(a->user): 0; +} + + +gboolean mwAwareIdBlock_equal(const struct mwAwareIdBlock *a, + const struct mwAwareIdBlock *b) { + + g_return_val_if_fail(a != NULL, FALSE); + g_return_val_if_fail(b != NULL, FALSE); + + return ( (a->type == b->type) && + mw_streq(a->user, b->user) && + mw_streq(a->community, b->community) ); +} + + +/* 8.4.2.4 Snapshot */ + +void mwAwareSnapshot_get(struct mwGetBuffer *b, struct mwAwareSnapshot *idb) { + guint32 junk; + char *empty = NULL; + + g_return_if_fail(b != NULL); + g_return_if_fail(idb != NULL); + + guint32_get(b, &junk); + mwAwareIdBlock_get(b, &idb->id); + mwString_get(b, &idb->group); + gboolean_get(b, &idb->online); + + g_free(empty); + + if(idb->online) { + mwString_get(b, &idb->alt_id); + mwUserStatus_get(b, &idb->status); + mwString_get(b, &idb->name); + } +} + + +void mwAwareSnapshot_clone(struct mwAwareSnapshot *to, + const struct mwAwareSnapshot *from) { + + g_return_if_fail(to != NULL); + g_return_if_fail(from != NULL); + + mwAwareIdBlock_clone(&to->id, &from->id); + if( (to->online = from->online) ) { + to->alt_id = g_strdup(from->alt_id); + mwUserStatus_clone(&to->status, &from->status); + to->name = g_strdup(from->name); + to->group = g_strdup(from->group); + } +} + + +void mwAwareSnapshot_clear(struct mwAwareSnapshot *idb) { + if(! idb) return; + mwAwareIdBlock_clear(&idb->id); + mwUserStatus_clear(&idb->status); + g_free(idb->alt_id); + g_free(idb->name); + g_free(idb->group); + memset(idb, 0x00, sizeof(struct mwAwareSnapshot)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/error.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,97 @@ + +/* + 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 <stdio.h> +#include <string.h> + +#include "mw_error.h" + + +static char *err_to_str(guint32 code) { + static char b[11]; /* 0x12345678 + NULL terminator */ + sprintf((char *) b, "0x%08x", code); + b[10] = '\0'; + return b; +} + + +#define CASE(val, str) \ +case val: \ + m = str; \ + break; + + +char* mwError(guint32 code) { + const char *m; + + switch(code) { + + /* 8.3.1.1 General error/success codes */ + CASE(ERR_SUCCESS, "Success"); + CASE(ERR_FAILURE, "General failure"); + CASE(ERR_REQUEST_DELAY, "Request delayed"); + CASE(ERR_REQUEST_INVALID, "Request is invalid"); + CASE(ERR_NOT_AUTHORIZED, "Not authorized"); + CASE(ERR_NO_USER, "User is not online"); + CASE(ERR_CHANNEL_NO_SUPPORT, "Requested channel is not supported"); + CASE(ERR_CHANNEL_EXISTS, "Requested channel already exists"); + CASE(ERR_SERVICE_NO_SUPPORT, "Requested service is not supported"); + CASE(ERR_PROTOCOL_NO_SUPPORT, "Requested protocol is not supported"); + CASE(ERR_VERSION_NO_SUPPORT, "Version is not supported"); + CASE(ERR_USER_SKETCHY, "User is invalid or not trusted"); + CASE(ERR_ALREADY_INITIALIZED, "Already initialized"); + CASE(ERR_ENCRYPT_NO_SUPPORT, "Encryption method not supported"); + CASE(ERR_NO_COMMON_ENCRYPT, "No common encryption method"); + + /* 8.3.1.2 Connection/disconnection errors */ + CASE(VERSION_MISMATCH, "Version mismatch"); + CASE(FAT_MESSAGE, "Message is too large"); + CASE(CONNECTION_BROKEN, "Connection broken"); + CASE(CONNECTION_ABORTED, "Connection aborted"); + CASE(CONNECTION_REFUSED, "Connection refused"); + CASE(CONNECTION_RESET, "Connection reset"); + CASE(CONNECTION_TIMED, "Connection timed out"); + CASE(CONNECTION_CLOSED, "Connection closed"); + CASE(INCORRECT_LOGIN, "Incorrect Username/Password"); + CASE(VERIFICATION_DOWN, "Login verification down or unavailable"); + CASE(GUEST_IN_USE, "The guest name is currently being used"); + CASE(MULTI_SERVER_LOGIN, "Login to two different servers concurrently"); + CASE(MULTI_SERVER_LOGIN2, "Login to two different servers concurrently"); + CASE(SERVER_BROKEN, "Server misconfiguration"); + + /* 8.3.1.3 Client error codes */ + CASE(ERR_CLIENT_USER_GONE, "User is not online"); + CASE(ERR_CLIENT_USER_DND, "User is in Do Not Disturb mode"); + CASE(ERR_CLIENT_USER_ELSEWHERE, "Already logged in elsewhere"); + + /* 8.3.1.4 IM error codes */ + CASE(ERR_IM_COULDNT_REGISTER, "Cannot register a reserved type"); + CASE(ERR_IM_ALREADY_REGISTERED, "Requested type is already registered"); + CASE(ERR_IM_NOT_REGISTERED, "Requested type is not registered"); + + default: + m = err_to_str(code); + } + + return g_strdup(m); +} + + +#undef CASE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/message.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,853 @@ + +/* + 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.h> + +#include "mw_debug.h" +#include "mw_message.h" + + +/* 7.1 Layering and message encapsulation */ +/* 7.1.1 The Sametime Message Header */ + + +static void mwMessageHead_put(struct mwPutBuffer *b, struct mwMessage *msg) { + guint16_put(b, msg->type); + guint16_put(b, msg->options); + guint32_put(b, msg->channel); + + if(msg->options & mwMessageOption_HAS_ATTRIBS) + mwOpaque_put(b, &msg->attribs); +} + + +static void mwMessageHead_get(struct mwGetBuffer *b, struct mwMessage *msg) { + + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &msg->type); + guint16_get(b, &msg->options); + guint32_get(b, &msg->channel); + + if(msg->options & mwMessageOption_HAS_ATTRIBS) + mwOpaque_get(b, &msg->attribs); +} + + +static void mwMessageHead_clone(struct mwMessage *to, + struct mwMessage *from) { + + to->type = from->type; + to->options = from->options; + to->channel = from->channel; + mwOpaque_clone(&to->attribs, &from->attribs); +} + + +static void mwMessageHead_clear(struct mwMessage *msg) { + mwOpaque_clear(&msg->attribs); +} + + +/* 8.4 Messages */ +/* 8.4.1 Basic Community Messages */ +/* 8.4.1.1 Handshake */ + + +static void HANDSHAKE_put(struct mwPutBuffer *b, struct mwMsgHandshake *msg) { + guint16_put(b, msg->major); + guint16_put(b, msg->minor); + guint32_put(b, msg->head.channel); + guint32_put(b, msg->srvrcalc_addr); + guint16_put(b, msg->login_type); + guint32_put(b, msg->loclcalc_addr); + + if(msg->major >= 0x001e && msg->minor >= 0x001d) { + guint16_put(b, msg->unknown_a); + guint32_put(b, msg->unknown_b); + mwString_put(b, msg->local_host); + } +} + + +static void HANDSHAKE_get(struct mwGetBuffer *b, struct mwMsgHandshake *msg) { + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &msg->major); + guint16_get(b, &msg->minor); + guint32_get(b, &msg->head.channel); + guint32_get(b, &msg->srvrcalc_addr); + guint16_get(b, &msg->login_type); + guint32_get(b, &msg->loclcalc_addr); + + if(msg->major >= 0x001e && msg->minor >= 0x001d) { + guint16_get(b, &msg->unknown_a); + guint32_get(b, &msg->unknown_b); + mwString_get(b, &msg->local_host); + } +} + + +static void HANDSHAKE_clear(struct mwMsgHandshake *msg) { + ; /* nothing to clean up */ +} + + +/* 8.4.1.2 HandshakeAck */ + + +static void HANDSHAKE_ACK_get(struct mwGetBuffer *b, + struct mwMsgHandshakeAck *msg) { + + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &msg->major); + guint16_get(b, &msg->minor); + guint32_get(b, &msg->srvrcalc_addr); + + /** @todo: get a better handle on what versions support what parts + of this message. eg: minor version 0x0018 doesn't send the + following */ + if(msg->major >= 0x1e && msg->minor > 0x18) { + guint32_get(b, &msg->magic); + mwOpaque_get(b, &msg->data); + } +} + + +static void HANDSHAKE_ACK_put(struct mwPutBuffer *b, + struct mwMsgHandshakeAck *msg) { + + guint16_put(b, msg->major); + guint16_put(b, msg->minor); + guint32_put(b, msg->srvrcalc_addr); + + if(msg->major >= 0x1e && msg->minor > 0x18) { + guint32_put(b, msg->magic); + mwOpaque_put(b, &msg->data); + } +} + + +static void HANDSHAKE_ACK_clear(struct mwMsgHandshakeAck *msg) { + mwOpaque_clear(&msg->data); +} + + +/* 8.4.1.3 Login */ + + +static void LOGIN_put(struct mwPutBuffer *b, struct mwMsgLogin *msg) { + guint16_put(b, msg->login_type); + mwString_put(b, msg->name); + + /* ordering reversed from houri draft?? */ + mwOpaque_put(b, &msg->auth_data); + guint16_put(b, msg->auth_type); + + guint16_put(b, 0x0000); /* unknown */ +} + + +static void LOGIN_get(struct mwGetBuffer *b, struct mwMsgLogin *msg) { + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &msg->login_type); + mwString_get(b, &msg->name); + mwOpaque_get(b, &msg->auth_data); + guint16_get(b, &msg->auth_type); +} + + +static void LOGIN_clear(struct mwMsgLogin *msg) { + g_free(msg->name); msg->name = NULL; + mwOpaque_clear(&msg->auth_data); +} + + +/* 8.4.1.4 LoginAck */ + + +static void LOGIN_ACK_get(struct mwGetBuffer *b, struct mwMsgLoginAck *msg) { + guint16 junk; + + if(mwGetBuffer_error(b)) return; + + mwLoginInfo_get(b, &msg->login); + guint16_get(b, &junk); + mwPrivacyInfo_get(b, &msg->privacy); + mwUserStatus_get(b, &msg->status); +} + + +static void LOGIN_ACK_clear(struct mwMsgLoginAck *msg) { + mwLoginInfo_clear(&msg->login); + mwPrivacyInfo_clear(&msg->privacy); + mwUserStatus_clear(&msg->status); +} + + +/* 8.4.1.5 LoginCont */ + + +static void LOGIN_CONTINUE_put(struct mwPutBuffer *b, + struct mwMsgLoginContinue *msg) { + + ; /* nothing but a message header */ +} + + +static void LOGIN_CONTINUE_get(struct mwGetBuffer *b, + struct mwMsgLoginContinue *msg) { + + ; /* nothing but a message header */ +} + + +static void LOGIN_CONTINUE_clear(struct mwMsgLoginContinue *msg) { + ; /* this is a very simple message */ +} + + +/* 8.4.1.6 AuthPassed */ + + +static void LOGIN_REDIRECT_get(struct mwGetBuffer *b, + struct mwMsgLoginRedirect *msg) { + + if(mwGetBuffer_error(b)) return; + mwString_get(b, &msg->host); + mwString_get(b, &msg->server_id); +} + + +static void LOGIN_REDIRECT_put(struct mwPutBuffer *b, + struct mwMsgLoginRedirect *msg) { + mwString_put(b, msg->host); + mwString_put(b, msg->server_id); +} + + +static void LOGIN_REDIRECT_clear(struct mwMsgLoginRedirect *msg) { + g_free(msg->host); + msg->host = NULL; + + g_free(msg->server_id); + msg->server_id = NULL; +} + + +/* 8.4.1.7 CreateCnl */ + + +static void enc_offer_put(struct mwPutBuffer *b, struct mwEncryptOffer *enc) { + guint16_put(b, enc->mode); + + if(enc->items) { + guint32 count; + struct mwPutBuffer *p; + struct mwOpaque o; + GList *list; + + /* write the count, items, extra, and flag into a tmp buffer, + render that buffer into an opaque, and write it into b */ + + count = g_list_length(enc->items); + p = mwPutBuffer_new(); + + guint32_put(p, count); + for(list = enc->items; list; list = list->next) { + mwEncryptItem_put(p, list->data); + } + + guint16_put(p, enc->extra); + gboolean_put(p, enc->flag); + + mwPutBuffer_finalize(&o, p); + mwOpaque_put(b, &o); + mwOpaque_clear(&o); + } +} + + +static void CHANNEL_CREATE_put(struct mwPutBuffer *b, + struct mwMsgChannelCreate *msg) { + + guint32_put(b, msg->reserved); + guint32_put(b, msg->channel); + mwIdBlock_put(b, &msg->target); + guint32_put(b, msg->service); + guint32_put(b, msg->proto_type); + guint32_put(b, msg->proto_ver); + guint32_put(b, msg->options); + mwOpaque_put(b, &msg->addtl); + gboolean_put(b, msg->creator_flag); + + if(msg->creator_flag) + mwLoginInfo_put(b, &msg->creator); + + enc_offer_put(b, &msg->encrypt); + + guint32_put(b, 0x00); + guint32_put(b, 0x00); + guint16_put(b, 0x07); +} + + +static void enc_offer_get(struct mwGetBuffer *b, + struct mwEncryptOffer *enc) { + guint32 skip; + + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &enc->mode); + guint32_get(b, &skip); + + if(skip >= 7) { + guint32 count; + + guint32_get(b, &count); + + while(count-- && (! mwGetBuffer_error(b))) { + struct mwEncryptItem *ei = g_new0(struct mwEncryptItem, 1); + mwEncryptItem_get(b, ei); + enc->items = g_list_append(enc->items, ei); + } + + guint16_get(b, &enc->extra); + gboolean_get(b, &enc->flag); + } +} + + +static void CHANNEL_CREATE_get(struct mwGetBuffer *b, + struct mwMsgChannelCreate *msg) { + + if(mwGetBuffer_error(b)) return; + + guint32_get(b, &msg->reserved); + guint32_get(b, &msg->channel); + mwIdBlock_get(b, &msg->target); + guint32_get(b, &msg->service); + guint32_get(b, &msg->proto_type); + guint32_get(b, &msg->proto_ver); + guint32_get(b, &msg->options); + mwOpaque_get(b, &msg->addtl); + gboolean_get(b, &msg->creator_flag); + + if(msg->creator_flag) + mwLoginInfo_get(b, &msg->creator); + + enc_offer_get(b, &msg->encrypt); +} + + +static void CHANNEL_CREATE_clear(struct mwMsgChannelCreate *msg) { + GList *list; + + mwIdBlock_clear(&msg->target); + mwOpaque_clear(&msg->addtl); + mwLoginInfo_clear(&msg->creator); + + for(list = msg->encrypt.items; list; list = list->next) { + mwEncryptItem_clear(list->data); + g_free(list->data); + } + g_list_free(msg->encrypt.items); +} + + +/* 8.4.1.8 AcceptCnl */ + + +static void enc_accept_put(struct mwPutBuffer *b, + struct mwEncryptAccept *enc) { + + guint16_put(b, enc->mode); + + if(enc->item) { + struct mwPutBuffer *p; + struct mwOpaque o; + + p = mwPutBuffer_new(); + + mwEncryptItem_put(p, enc->item); + guint16_put(p, enc->extra); + gboolean_put(p, enc->flag); + + mwPutBuffer_finalize(&o, p); + mwOpaque_put(b, &o); + mwOpaque_clear(&o); + } +} + + +static void CHANNEL_ACCEPT_put(struct mwPutBuffer *b, + struct mwMsgChannelAccept *msg) { + + guint32_put(b, msg->service); + guint32_put(b, msg->proto_type); + guint32_put(b, msg->proto_ver); + mwOpaque_put(b, &msg->addtl); + gboolean_put(b, msg->acceptor_flag); + + if(msg->acceptor_flag) + mwLoginInfo_put(b, &msg->acceptor); + + enc_accept_put(b, &msg->encrypt); + + guint32_put(b, 0x00); + guint32_put(b, 0x00); + guint16_put(b, 0x07); +} + + +static void enc_accept_get(struct mwGetBuffer *b, + struct mwEncryptAccept *enc) { + guint32 skip; + + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &enc->mode); + guint32_get(b, &skip); + + if(skip >= 6) { + enc->item = g_new0(struct mwEncryptItem, 1); + mwEncryptItem_get(b, enc->item); + } + + if(skip >= 9) { + guint16_get(b, &enc->extra); + gboolean_get(b, &enc->flag); + } +} + + +static void CHANNEL_ACCEPT_get(struct mwGetBuffer *b, + struct mwMsgChannelAccept *msg) { + + if(mwGetBuffer_error(b)) return; + + guint32_get(b, &msg->service); + guint32_get(b, &msg->proto_type); + guint32_get(b, &msg->proto_ver); + mwOpaque_get(b, &msg->addtl); + gboolean_get(b, &msg->acceptor_flag); + + if(msg->acceptor_flag) + mwLoginInfo_get(b, &msg->acceptor); + + enc_accept_get(b, &msg->encrypt); +} + + +static void CHANNEL_ACCEPT_clear(struct mwMsgChannelAccept *msg) { + mwOpaque_clear(&msg->addtl); + mwLoginInfo_clear(&msg->acceptor); + + if(msg->encrypt.item) { + mwEncryptItem_clear(msg->encrypt.item); + g_free(msg->encrypt.item); + } +} + + +/* 8.4.1.9 SendOnCnl */ + + +static void CHANNEL_SEND_put(struct mwPutBuffer *b, + struct mwMsgChannelSend *msg) { + + guint16_put(b, msg->type); + mwOpaque_put(b, &msg->data); +} + + +static void CHANNEL_SEND_get(struct mwGetBuffer *b, + struct mwMsgChannelSend *msg) { + + if(mwGetBuffer_error(b)) return; + + guint16_get(b, &msg->type); + mwOpaque_get(b, &msg->data); +} + + +static void CHANNEL_SEND_clear(struct mwMsgChannelSend *msg) { + mwOpaque_clear(&msg->data); +} + + +/* 8.4.1.10 DestroyCnl */ + + +static void CHANNEL_DESTROY_put(struct mwPutBuffer *b, + struct mwMsgChannelDestroy *msg) { + guint32_put(b, msg->reason); + mwOpaque_put(b, &msg->data); +} + + +static void CHANNEL_DESTROY_get(struct mwGetBuffer *b, + struct mwMsgChannelDestroy *msg) { + + if(mwGetBuffer_error(b)) return; + + guint32_get(b, &msg->reason); + mwOpaque_get(b, &msg->data); +} + + +static void CHANNEL_DESTROY_clear(struct mwMsgChannelDestroy *msg) { + mwOpaque_clear(&msg->data); +} + + +/* 8.4.1.11 SetUserStatus */ + + +static void SET_USER_STATUS_put(struct mwPutBuffer *b, + struct mwMsgSetUserStatus *msg) { + mwUserStatus_put(b, &msg->status); +} + + +static void SET_USER_STATUS_get(struct mwGetBuffer *b, + struct mwMsgSetUserStatus *msg) { + + if(mwGetBuffer_error(b)) return; + mwUserStatus_get(b, &msg->status); +} + + +static void SET_USER_STATUS_clear(struct mwMsgSetUserStatus *msg) { + mwUserStatus_clear(&msg->status); +} + + +/* 8.4.1.12 SetPrivacyList */ + + +static void SET_PRIVACY_LIST_put(struct mwPutBuffer *b, + struct mwMsgSetPrivacyList *msg) { + mwPrivacyInfo_put(b, &msg->privacy); +} + + +static void SET_PRIVACY_LIST_get(struct mwGetBuffer *b, + struct mwMsgSetPrivacyList *msg) { + + if(mwGetBuffer_error(b)) return; + mwPrivacyInfo_get(b, &msg->privacy); +} + + +static void SET_PRIVACY_LIST_clear(struct mwMsgSetPrivacyList *msg) { + mwPrivacyInfo_clear(&msg->privacy); +} + + +/* Sense Service messages */ + + +static void SENSE_SERVICE_put(struct mwPutBuffer *b, + struct mwMsgSenseService *msg) { + guint32_put(b, msg->service); +} + + +static void SENSE_SERVICE_get(struct mwGetBuffer *b, + struct mwMsgSenseService *msg) { + + if(mwGetBuffer_error(b)) return; + guint32_get(b, &msg->service); +} + + +static void SENSE_SERVICE_clear(struct mwMsgSenseService *msg) { + ; +} + + +/* Admin messages */ + + +static void ADMIN_get(struct mwGetBuffer *b, struct mwMsgAdmin *msg) { + mwString_get(b, &msg->text); +} + + +static void ADMIN_clear(struct mwMsgAdmin *msg) { + g_free(msg->text); + msg->text = NULL; +} + + +/* Announcement messages */ + + +static void ANNOUNCE_get(struct mwGetBuffer *b, struct mwMsgAnnounce *msg) { + struct mwOpaque o = { 0, 0 }; + struct mwGetBuffer *gb; + guint32 count; + + gboolean_get(b, &msg->sender_present); + if(msg->sender_present) + mwLoginInfo_get(b, &msg->sender); + guint16_get(b, &msg->unknown_a); + + mwOpaque_get(b, &o); + gb = mwGetBuffer_wrap(&o); + + gboolean_get(gb, &msg->may_reply); + mwString_get(gb, &msg->text); + + mwGetBuffer_free(gb); + mwOpaque_clear(&o); + + guint32_get(b, &count); + while(count--) { + char *r = NULL; + mwString_get(b, &r); + msg->recipients = g_list_prepend(msg->recipients, r); + } +} + + +static void ANNOUNCE_put(struct mwPutBuffer *b, struct mwMsgAnnounce *msg) { + struct mwOpaque o = { 0, 0 }; + struct mwPutBuffer *pb; + GList *l; + + gboolean_put(b, msg->sender_present); + if(msg->sender_present) + mwLoginInfo_put(b, &msg->sender); + guint16_put(b, msg->unknown_a); + + pb = mwPutBuffer_new(); + + gboolean_put(pb, msg->may_reply); + mwString_put(pb, msg->text); + + mwPutBuffer_finalize(&o, pb); + mwOpaque_put(b, &o); + mwOpaque_clear(&o); + + guint32_put(b, g_list_length(msg->recipients)); + for(l = msg->recipients; l; l = l->next) { + mwString_put(b, l->data); + } +} + + +static void ANNOUNCE_clear(struct mwMsgAnnounce *msg) { + mwLoginInfo_clear(&msg->sender); + + g_free(msg->text); + msg->text = NULL; + + while(msg->recipients) { + g_free(msg->recipients->data); + msg->recipients = g_list_delete_link(msg->recipients, msg->recipients); + } +} + + +/* general functions */ + + +#define CASE(v, t) \ +case mwMessage_ ## v: \ + msg = (struct mwMessage *) g_new0(struct t, 1); \ + msg->type = type; \ + break; + + +struct mwMessage *mwMessage_new(enum mwMessageType type) { + struct mwMessage *msg = NULL; + + switch(type) { + CASE(HANDSHAKE, mwMsgHandshake); + CASE(HANDSHAKE_ACK, mwMsgHandshakeAck); + CASE(LOGIN, mwMsgLogin); + CASE(LOGIN_REDIRECT, mwMsgLoginRedirect); + CASE(LOGIN_CONTINUE, mwMsgLoginContinue); + CASE(LOGIN_ACK, mwMsgLoginAck); + CASE(CHANNEL_CREATE, mwMsgChannelCreate); + CASE(CHANNEL_DESTROY, mwMsgChannelDestroy); + CASE(CHANNEL_SEND, mwMsgChannelSend); + CASE(CHANNEL_ACCEPT, mwMsgChannelAccept); + CASE(SET_USER_STATUS, mwMsgSetUserStatus); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ADMIN, mwMsgAdmin); + CASE(ANNOUNCE, mwMsgAnnounce); + + default: + g_warning("unknown message type 0x%02x\n", type); + } + + return msg; +} + + +#undef CASE + + +/* each type needs to be passed to a specially named _get functions, + and cast to a specific subclass of mwMessage. */ +#define CASE(v, t) \ +case mwMessage_ ## v: \ + msg = (struct mwMessage *) g_new0(struct t, 1); \ + mwMessageHead_clone(msg, &head); \ + v ## _get(b, (struct t *) msg); \ + break; + + +struct mwMessage *mwMessage_get(struct mwGetBuffer *b) { + struct mwMessage *msg = NULL; + struct mwMessage head; + + g_return_val_if_fail(b != NULL, NULL); + + head.attribs.len = 0; + head.attribs.data = NULL; + + /* attempt to read the header first */ + mwMessageHead_get(b, &head); + + if(mwGetBuffer_error(b)) { + mwMessageHead_clear(&head); + g_warning("problem parsing message head from buffer"); + return NULL; + } + + /* load the rest of the message depending on the header type */ + switch(head.type) { + CASE(HANDSHAKE, mwMsgHandshake); + CASE(HANDSHAKE_ACK, mwMsgHandshakeAck); + CASE(LOGIN, mwMsgLogin); + CASE(LOGIN_REDIRECT, mwMsgLoginRedirect); + CASE(LOGIN_CONTINUE, mwMsgLoginContinue); + CASE(LOGIN_ACK, mwMsgLoginAck); + CASE(CHANNEL_CREATE, mwMsgChannelCreate); + CASE(CHANNEL_DESTROY, mwMsgChannelDestroy); + CASE(CHANNEL_SEND, mwMsgChannelSend); + CASE(CHANNEL_ACCEPT, mwMsgChannelAccept); + CASE(SET_USER_STATUS, mwMsgSetUserStatus); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ADMIN, mwMsgAdmin); + CASE(ANNOUNCE, mwMsgAnnounce); + + default: + g_warning("unknown message type 0x%02x, no parse handler", head.type); + } + + if(mwGetBuffer_error(b)) { + g_warning("problem parsing message type 0x%02x, not enough data", + head.type); + } + + mwMessageHead_clear(&head); + + return msg; +} + + +#undef CASE + + +#define CASE(v, t) \ +case mwMessage_ ## v: \ + v ## _put(b, (struct t *) msg); \ + break; + + +void mwMessage_put(struct mwPutBuffer *b, struct mwMessage *msg) { + + g_return_if_fail(b != NULL); + g_return_if_fail(msg != NULL); + + mwMessageHead_put(b, msg); + + switch(msg->type) { + CASE(HANDSHAKE, mwMsgHandshake); + CASE(HANDSHAKE_ACK, mwMsgHandshakeAck); + CASE(LOGIN, mwMsgLogin); + CASE(LOGIN_REDIRECT, mwMsgLoginRedirect); + CASE(LOGIN_CONTINUE, mwMsgLoginContinue); + CASE(CHANNEL_CREATE, mwMsgChannelCreate); + CASE(CHANNEL_DESTROY, mwMsgChannelDestroy); + CASE(CHANNEL_SEND, mwMsgChannelSend); + CASE(CHANNEL_ACCEPT, mwMsgChannelAccept); + CASE(SET_USER_STATUS, mwMsgSetUserStatus); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ANNOUNCE, mwMsgAnnounce); + + default: + ; /* hrm. */ + } +} + + +#undef CASE + + +#define CASE(v, t) \ +case mwMessage_ ## v: \ + v ## _clear((struct t *) msg); \ + break; + + +void mwMessage_free(struct mwMessage *msg) { + if(! msg) return; + + mwMessageHead_clear(msg); + + switch(msg->type) { + CASE(HANDSHAKE, mwMsgHandshake); + CASE(HANDSHAKE_ACK, mwMsgHandshakeAck); + CASE(LOGIN, mwMsgLogin); + CASE(LOGIN_REDIRECT, mwMsgLoginRedirect); + CASE(LOGIN_CONTINUE, mwMsgLoginContinue); + CASE(LOGIN_ACK, mwMsgLoginAck); + CASE(CHANNEL_CREATE, mwMsgChannelCreate); + CASE(CHANNEL_DESTROY, mwMsgChannelDestroy); + CASE(CHANNEL_SEND, mwMsgChannelSend); + CASE(CHANNEL_ACCEPT, mwMsgChannelAccept); + CASE(SET_USER_STATUS, mwMsgSetUserStatus); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ADMIN, mwMsgAdmin); + CASE(ANNOUNCE, mwMsgAnnounce); + + default: + ; /* hrm. */ + } + + g_free(msg); +} + + +#undef CASE + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/.cvsignore Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.deps +.libs +*.la +*.lo
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/Makefile.am Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,7 @@ + +noinst_LTLIBRARIES = libmpi.la + +libmpi_la_SOURCES = mpi.c + +noinst_HEADERS = logtab.h mpi-config.h mpi.h mpi-types.h +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/logtab.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,20 @@ +const double s_logv_2[] = { + 0.000000000, 0.000000000, 1.000000000, 0.630929754, /* 0 1 2 3 */ + 0.500000000, 0.430676558, 0.386852807, 0.356207187, /* 4 5 6 7 */ + 0.333333333, 0.315464877, 0.301029996, 0.289064826, /* 8 9 10 11 */ + 0.278942946, 0.270238154, 0.262649535, 0.255958025, /* 12 13 14 15 */ + 0.250000000, 0.244650542, 0.239812467, 0.235408913, /* 16 17 18 19 */ + 0.231378213, 0.227670249, 0.224243824, 0.221064729, /* 20 21 22 23 */ + 0.218104292, 0.215338279, 0.212746054, 0.210309918, /* 24 25 26 27 */ + 0.208014598, 0.205846832, 0.203795047, 0.201849087, /* 28 29 30 31 */ + 0.200000000, 0.198239863, 0.196561632, 0.194959022, /* 32 33 34 35 */ + 0.193426404, 0.191958720, 0.190551412, 0.189200360, /* 36 37 38 39 */ + 0.187901825, 0.186652411, 0.185449023, 0.184288833, /* 40 41 42 43 */ + 0.183169251, 0.182087900, 0.181042597, 0.180031327, /* 44 45 46 47 */ + 0.179052232, 0.178103594, 0.177183820, 0.176291434, /* 48 49 50 51 */ + 0.175425064, 0.174583430, 0.173765343, 0.172969690, /* 52 53 54 55 */ + 0.172195434, 0.171441601, 0.170707280, 0.169991616, /* 56 57 58 59 */ + 0.169293808, 0.168613099, 0.167948779, 0.167300179, /* 60 61 62 63 */ + 0.166666667 +}; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/mpi-config.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,85 @@ +/* Default configuration for MPI library */ +/* $Id: mpi-config.h 14563 2005-11-29 23:31:40Z taliesein $ */ + +#ifndef MPI_CONFIG_H_ +#define MPI_CONFIG_H_ + +/* + For boolean options, + 0 = no + 1 = yes + + Other options are documented individually. + + */ + +#ifndef MP_IOFUNC +#define MP_IOFUNC 0 /* include mp_print() ? */ +#endif + +#ifndef MP_MODARITH +#define MP_MODARITH 1 /* include modular arithmetic ? */ +#endif + +#ifndef MP_NUMTH +#define MP_NUMTH 1 /* include number theoretic functions? */ +#endif + +#ifndef MP_LOGTAB +#define MP_LOGTAB 1 /* use table of logs instead of log()? */ +#endif + +#ifndef MP_MEMSET +#define MP_MEMSET 1 /* use memset() to zero buffers? */ +#endif + +#ifndef MP_MEMCPY +#define MP_MEMCPY 1 /* use memcpy() to copy buffers? */ +#endif + +#ifndef MP_CRYPTO +#define MP_CRYPTO 0 /* erase memory on free? */ +#endif + +#ifndef MP_ARGCHK +/* + 0 = no parameter checks + 1 = runtime checks, continue execution and return an error to caller + 2 = assertions; dump core on parameter errors + */ +#define MP_ARGCHK 2 /* how to check input arguments */ +#endif + +#ifndef MP_DEBUG +#define MP_DEBUG 0 /* print diagnostic output? */ +#endif + +#ifndef MP_DEFPREC +#define MP_DEFPREC 16 /* default precision, in digits */ +#endif + +#ifndef MP_MACRO +#define MP_MACRO 1 /* use macros for frequent calls? */ +#endif + +#ifndef MP_SQUARE +#define MP_SQUARE 1 /* use separate squaring code? */ +#endif + +#ifndef MP_PTAB_SIZE +/* + When building mpprime.c, we build in a table of small prime + values to use for primality testing. The more you include, + the more space they take up. See primes.c for the possible + values (currently 16, 32, 64, 128, 256, and 6542) + */ +#define MP_PTAB_SIZE 128 /* how many built-in primes? */ +#endif + +#ifndef MP_COMPAT_MACROS +#define MP_COMPAT_MACROS 0 /* define compatibility macros? */ +#endif + +#endif /* ifndef MPI_CONFIG_H_ */ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/mpi-types.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,17 @@ +/* Type definitions generated by 'types.pl' */ + +typedef char mp_sign; +typedef unsigned short mp_digit; /* 2 byte type */ +typedef unsigned int mp_word; /* 4 byte type */ +typedef unsigned int mp_size; +typedef int mp_err; + +#define MP_DIGIT_BIT (CHAR_BIT*sizeof(mp_digit)) +#define MP_DIGIT_MAX USHRT_MAX +#define MP_WORD_BIT (CHAR_BIT*sizeof(mp_word)) +#define MP_WORD_MAX UINT_MAX + +#define RADIX (MP_DIGIT_MAX+1) + +#define MP_DIGIT_SIZE 2 +#define DIGIT_FMT "%04X"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/mpi.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,4001 @@ +/* + mpi.c + + by Michael J. Fromberger <http://www.dartmouth.edu/~sting/> + Copyright (C) 1998 Michael J. Fromberger, All Rights Reserved + + Arbitrary precision integer arithmetic library + + $Id: mpi.c 14563 2005-11-29 23:31:40Z taliesein $ + */ + +#include "mpi.h" +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#if MP_DEBUG +#include <stdio.h> + +#define DIAG(T,V) {fprintf(stderr,T);mp_print(V,stderr);fputc('\n',stderr);} +#else +#define DIAG(T,V) +#endif + +/* + If MP_LOGTAB is not defined, use the math library to compute the + logarithms on the fly. Otherwise, use the static table below. + Pick which works best for your system. + */ +#if MP_LOGTAB + +/* {{{ s_logv_2[] - log table for 2 in various bases */ + +/* + A table of the logs of 2 for various bases (the 0 and 1 entries of + this table are meaningless and should not be referenced). + + This table is used to compute output lengths for the mp_toradix() + function. Since a number n in radix r takes up about log_r(n) + digits, we estimate the output size by taking the least integer + greater than log_r(n), where: + + log_r(n) = log_2(n) * log_r(2) + + This table, therefore, is a table of log_r(2) for 2 <= r <= 36, + which are the output bases supported. + */ + +#include "logtab.h" + +/* }}} */ +#define LOG_V_2(R) s_logv_2[(R)] + +#else + +#include <math.h> +#define LOG_V_2(R) (log(2.0)/log(R)) + +#endif + +/* Default precision for newly created mp_int's */ +static unsigned int s_mp_defprec = MP_DEFPREC; + +/* {{{ Digit arithmetic macros */ + +/* + When adding and multiplying digits, the results can be larger than + can be contained in an mp_digit. Thus, an mp_word is used. These + macros mask off the upper and lower digits of the mp_word (the + mp_word may be more than 2 mp_digits wide, but we only concern + ourselves with the low-order 2 mp_digits) + + If your mp_word DOES have more than 2 mp_digits, you need to + uncomment the first line, and comment out the second. + */ + +/* #define CARRYOUT(W) (((W)>>DIGIT_BIT)&MP_DIGIT_MAX) */ +#define CARRYOUT(W) ((W)>>DIGIT_BIT) +#define ACCUM(W) ((W)&MP_DIGIT_MAX) + +/* }}} */ + +/* {{{ Comparison constants */ + +#define MP_LT -1 +#define MP_EQ 0 +#define MP_GT 1 + +/* }}} */ + +/* {{{ Constant strings */ + +/* Constant strings returned by mp_strerror() */ +static const char *mp_err_string[] = { + "unknown result code", /* say what? */ + "boolean true", /* MP_OKAY, MP_YES */ + "boolean false", /* MP_NO */ + "out of memory", /* MP_MEM */ + "argument out of range", /* MP_RANGE */ + "invalid input parameter", /* MP_BADARG */ + "result is undefined" /* MP_UNDEF */ +}; + +/* Value to digit maps for radix conversion */ + +/* s_dmap_1 - standard digits and letters */ +static const char *s_dmap_1 = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; + +#if 0 +/* s_dmap_2 - base64 ordering for digits */ +static const char *s_dmap_2 = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +#endif + +/* }}} */ + +/* {{{ Static function declarations */ + +/* + If MP_MACRO is false, these will be defined as actual functions; + otherwise, suitable macro definitions will be used. This works + around the fact that ANSI C89 doesn't support an 'inline' keyword + (although I hear C9x will ... about bloody time). At present, the + macro definitions are identical to the function bodies, but they'll + expand in place, instead of generating a function call. + + I chose these particular functions to be made into macros because + some profiling showed they are called a lot on a typical workload, + and yet they are primarily housekeeping. + */ +#if MP_MACRO == 0 + void s_mp_setz(mp_digit *dp, mp_size count); /* zero digits */ + void s_mp_copy(mp_digit *sp, mp_digit *dp, mp_size count); /* copy */ + void *s_mp_alloc(size_t nb, size_t ni); /* general allocator */ + void s_mp_free(void *ptr); /* general free function */ +#else + + /* Even if these are defined as macros, we need to respect the settings + of the MP_MEMSET and MP_MEMCPY configuration options... + */ + #if MP_MEMSET == 0 + #define s_mp_setz(dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=0;} + #else + #define s_mp_setz(dp, count) memset(dp, 0, (count) * sizeof(mp_digit)) + #endif /* MP_MEMSET */ + + #if MP_MEMCPY == 0 + #define s_mp_copy(sp, dp, count) \ + {int ix;for(ix=0;ix<(count);ix++)(dp)[ix]=(sp)[ix];} + #else + #define s_mp_copy(sp, dp, count) memcpy(dp, sp, (count) * sizeof(mp_digit)) + #endif /* MP_MEMCPY */ + + #define s_mp_alloc(nb, ni) calloc(nb, ni) + #define s_mp_free(ptr) {if(ptr) free(ptr);} +#endif /* MP_MACRO */ + +mp_err s_mp_grow(mp_int *mp, mp_size min); /* increase allocated size */ +mp_err s_mp_pad(mp_int *mp, mp_size min); /* left pad with zeroes */ + +void s_mp_clamp(mp_int *mp); /* clip leading zeroes */ + +void s_mp_exch(mp_int *a, mp_int *b); /* swap a and b in place */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p); /* left-shift by p digits */ +void s_mp_rshd(mp_int *mp, mp_size p); /* right-shift by p digits */ +void s_mp_div_2d(mp_int *mp, mp_digit d); /* divide by 2^d in place */ +void s_mp_mod_2d(mp_int *mp, mp_digit d); /* modulo 2^d in place */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d); /* multiply by 2^d in place*/ +void s_mp_div_2(mp_int *mp); /* divide by 2 in place */ +mp_err s_mp_mul_2(mp_int *mp); /* multiply by 2 in place */ +mp_digit s_mp_norm(mp_int *a, mp_int *b); /* normalize for division */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d); /* unsigned digit addition */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d); /* unsigned digit subtract */ +mp_err s_mp_mul_d(mp_int *mp, mp_digit d); /* unsigned digit multiply */ +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r); + /* unsigned digit divide */ +mp_err s_mp_reduce(mp_int *x, mp_int *m, mp_int *mu); + /* Barrett reduction */ +mp_err s_mp_add(mp_int *a, mp_int *b); /* magnitude addition */ +mp_err s_mp_sub(mp_int *a, mp_int *b); /* magnitude subtract */ +mp_err s_mp_mul(mp_int *a, mp_int *b); /* magnitude multiply */ +#if 0 +void s_mp_kmul(mp_digit *a, mp_digit *b, mp_digit *out, mp_size len); + /* multiply buffers in place */ +#endif +#if MP_SQUARE +mp_err s_mp_sqr(mp_int *a); /* magnitude square */ +#else +#define s_mp_sqr(a) s_mp_mul(a, a) +#endif +mp_err s_mp_div(mp_int *a, mp_int *b); /* magnitude divide */ +mp_err s_mp_2expt(mp_int *a, mp_digit k); /* a = 2^k */ +int s_mp_cmp(mp_int *a, mp_int *b); /* magnitude comparison */ +int s_mp_cmp_d(mp_int *a, mp_digit d); /* magnitude digit compare */ +int s_mp_ispow2(mp_int *v); /* is v a power of 2? */ +int s_mp_ispow2d(mp_digit d); /* is d a power of 2? */ + +int s_mp_tovalue(char ch, int r); /* convert ch to value */ +char s_mp_todigit(int val, int r, int low); /* convert val to digit */ +int s_mp_outlen(int bits, int r); /* output length in bytes */ + +/* }}} */ + +/* {{{ Default precision manipulation */ + +unsigned int mp_get_prec(void) +{ + return s_mp_defprec; + +} /* end mp_get_prec() */ + +void mp_set_prec(unsigned int prec) +{ + if(prec == 0) + s_mp_defprec = MP_DEFPREC; + else + s_mp_defprec = prec; + +} /* end mp_set_prec() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_init(mp) */ + +/* + mp_init(mp) + + Initialize a new zero-valued mp_int. Returns MP_OKAY if successful, + MP_MEM if memory could not be allocated for the structure. + */ + +mp_err mp_init(mp_int *mp) +{ + return mp_init_size(mp, s_mp_defprec); + +} /* end mp_init() */ + +/* }}} */ + +/* {{{ mp_init_array(mp[], count) */ + +mp_err mp_init_array(mp_int mp[], int count) +{ + mp_err res; + int pos; + + ARGCHK(mp !=NULL && count > 0, MP_BADARG); + + for(pos = 0; pos < count; ++pos) { + if((res = mp_init(&mp[pos])) != MP_OKAY) + goto CLEANUP; + } + + return MP_OKAY; + + CLEANUP: + while(--pos >= 0) + mp_clear(&mp[pos]); + + return res; + +} /* end mp_init_array() */ + +/* }}} */ + +/* {{{ mp_init_size(mp, prec) */ + +/* + mp_init_size(mp, prec) + + Initialize a new zero-valued mp_int with at least the given + precision; returns MP_OKAY if successful, or MP_MEM if memory could + not be allocated for the structure. + */ + +mp_err mp_init_size(mp_int *mp, mp_size prec) +{ + ARGCHK(mp != NULL && prec > 0, MP_BADARG); + + if((DIGITS(mp) = s_mp_alloc(prec, sizeof(mp_digit))) == NULL) + return MP_MEM; + + SIGN(mp) = MP_ZPOS; + USED(mp) = 1; + ALLOC(mp) = prec; + + return MP_OKAY; + +} /* end mp_init_size() */ + +/* }}} */ + +/* {{{ mp_init_copy(mp, from) */ + +/* + mp_init_copy(mp, from) + + Initialize mp as an exact copy of from. Returns MP_OKAY if + successful, MP_MEM if memory could not be allocated for the new + structure. + */ + +mp_err mp_init_copy(mp_int *mp, mp_int *from) +{ + ARGCHK(mp != NULL && from != NULL, MP_BADARG); + + if(mp == from) + return MP_OKAY; + + if((DIGITS(mp) = s_mp_alloc(USED(from), sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), DIGITS(mp), USED(from)); + USED(mp) = USED(from); + ALLOC(mp) = USED(from); + SIGN(mp) = SIGN(from); + + return MP_OKAY; + +} /* end mp_init_copy() */ + +/* }}} */ + +/* {{{ mp_copy(from, to) */ + +/* + mp_copy(from, to) + + Copies the mp_int 'from' to the mp_int 'to'. It is presumed that + 'to' has already been initialized (if not, use mp_init_copy() + instead). If 'from' and 'to' are identical, nothing happens. + */ + +mp_err mp_copy(mp_int *from, mp_int *to) +{ + ARGCHK(from != NULL && to != NULL, MP_BADARG); + + if(from == to) + return MP_OKAY; + + { /* copy */ + mp_digit *tmp; + + /* + If the allocated buffer in 'to' already has enough space to hold + all the used digits of 'from', we'll re-use it to avoid hitting + the memory allocater more than necessary; otherwise, we'd have + to grow anyway, so we just allocate a hunk and make the copy as + usual + */ + if(ALLOC(to) >= USED(from)) { + s_mp_setz(DIGITS(to) + USED(from), ALLOC(to) - USED(from)); + s_mp_copy(DIGITS(from), DIGITS(to), USED(from)); + + } else { + if((tmp = s_mp_alloc(USED(from), sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(from), tmp, USED(from)); + + if(DIGITS(to) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(to), ALLOC(to)); +#endif + s_mp_free(DIGITS(to)); + } + + DIGITS(to) = tmp; + ALLOC(to) = USED(from); + } + + /* Copy the precision and sign from the original */ + USED(to) = USED(from); + SIGN(to) = SIGN(from); + } /* end copy */ + + return MP_OKAY; + +} /* end mp_copy() */ + +/* }}} */ + +/* {{{ mp_exch(mp1, mp2) */ + +/* + mp_exch(mp1, mp2) + + Exchange mp1 and mp2 without allocating any intermediate memory + (well, unless you count the stack space needed for this call and the + locals it creates...). This cannot fail. + */ + +void mp_exch(mp_int *mp1, mp_int *mp2) +{ +#if MP_ARGCHK == 2 + assert(mp1 != NULL && mp2 != NULL); +#else + if(mp1 == NULL || mp2 == NULL) + return; +#endif + + s_mp_exch(mp1, mp2); + +} /* end mp_exch() */ + +/* }}} */ + +/* {{{ mp_clear(mp) */ + +/* + mp_clear(mp) + + Release the storage used by an mp_int, and void its fields so that + if someone calls mp_clear() again for the same int later, we won't + get tollchocked. + */ + +void mp_clear(mp_int *mp) +{ + if(mp == NULL) + return; + + if(DIGITS(mp) != NULL) { +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp)); + DIGITS(mp) = NULL; + } + + USED(mp) = 0; + ALLOC(mp) = 0; + +} /* end mp_clear() */ + +/* }}} */ + +/* {{{ mp_clear_array(mp[], count) */ + +void mp_clear_array(mp_int mp[], int count) +{ + ARGCHK(mp != NULL && count > 0, MP_BADARG); + + while(--count >= 0) + mp_clear(&mp[count]); + +} /* end mp_clear_array() */ + +/* }}} */ + +/* {{{ mp_zero(mp) */ + +/* + mp_zero(mp) + + Set mp to zero. Does not change the allocated size of the structure, + and therefore cannot fail (except on a bad argument, which we ignore) + */ +void mp_zero(mp_int *mp) +{ + if(mp == NULL) + return; + + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = MP_ZPOS; + +} /* end mp_zero() */ + +/* }}} */ + +/* {{{ mp_set(mp, d) */ + +void mp_set(mp_int *mp, mp_digit d) +{ + if(mp == NULL) + return; + + mp_zero(mp); + DIGIT(mp, 0) = d; + +} /* end mp_set() */ + +/* }}} */ + +/* {{{ mp_set_int(mp, z) */ + +mp_err mp_set_int(mp_int *mp, long z) +{ + int ix; + unsigned long v = abs(z); + mp_err res; + + ARGCHK(mp != NULL, MP_BADARG); + + mp_zero(mp); + if(z == 0) + return MP_OKAY; /* shortcut for zero */ + + for(ix = sizeof(long) - 1; ix >= 0; ix--) { + + if((res = s_mp_mul_2d(mp, CHAR_BIT)) != MP_OKAY) + return res; + + res = s_mp_add_d(mp, + (mp_digit)((v >> (ix * CHAR_BIT)) & UCHAR_MAX)); + if(res != MP_OKAY) + return res; + + } + + if(z < 0) + SIGN(mp) = MP_NEG; + + return MP_OKAY; + +} /* end mp_set_int() */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Digit arithmetic */ + +/* {{{ mp_add_d(a, d, b) */ + +/* + mp_add_d(a, d, b) + + Compute the sum b = a + d, for a single digit d. Respects the sign of + its primary addend (single digits are unsigned anyway). + */ + +mp_err mp_add_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res = MP_OKAY; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(SIGN(b) == MP_ZPOS) { + res = s_mp_add_d(b, d); + } else if(s_mp_cmp_d(b, d) >= 0) { + res = s_mp_sub_d(b, d); + } else { + SIGN(b) = MP_ZPOS; + + DIGIT(b, 0) = d - DIGIT(b, 0); + } + + return res; + +} /* end mp_add_d() */ + +/* }}} */ + +/* {{{ mp_sub_d(a, d, b) */ + +/* + mp_sub_d(a, d, b) + + Compute the difference b = a - d, for a single digit d. Respects the + sign of its subtrahend (single digits are unsigned anyway). + */ + +mp_err mp_sub_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(SIGN(b) == MP_NEG) { + if((res = s_mp_add_d(b, d)) != MP_OKAY) + return res; + + } else if(s_mp_cmp_d(b, d) >= 0) { + if((res = s_mp_sub_d(b, d)) != MP_OKAY) + return res; + + } else { + mp_neg(b, b); + + DIGIT(b, 0) = d - DIGIT(b, 0); + SIGN(b) = MP_NEG; + } + + if(s_mp_cmp_d(b, 0) == 0) + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sub_d() */ + +/* }}} */ + +/* {{{ mp_mul_d(a, d, b) */ + +/* + mp_mul_d(a, d, b) + + Compute the product b = a * d, for a single digit d. Respects the sign + of its multiplicand (single digits are unsigned anyway) + */ + +mp_err mp_mul_d(mp_int *a, mp_digit d, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if(d == 0) { + mp_zero(b); + return MP_OKAY; + } + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + res = s_mp_mul_d(b, d); + + return res; + +} /* end mp_mul_d() */ + +/* }}} */ + +/* {{{ mp_mul_2(a, c) */ + +mp_err mp_mul_2(mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + return s_mp_mul_2(c); + +} /* end mp_mul_2() */ + +/* }}} */ + +/* {{{ mp_div_d(a, d, q, r) */ + +/* + mp_div_d(a, d, q, r) + + Compute the quotient q = a / d and remainder r = a mod d, for a + single digit d. Respects the sign of its divisor (single digits are + unsigned anyway). + */ + +mp_err mp_div_d(mp_int *a, mp_digit d, mp_int *q, mp_digit *r) +{ + mp_err res; + mp_digit rem; + int pow; + + ARGCHK(a != NULL, MP_BADARG); + + if(d == 0) + return MP_RANGE; + + /* Shortcut for powers of two ... */ + if((pow = s_mp_ispow2d(d)) >= 0) { + mp_digit mask; + + mask = (1 << pow) - 1; + rem = DIGIT(a, 0) & mask; + + if(q) { + mp_copy(a, q); + s_mp_div_2d(q, pow); + } + + if(r) + *r = rem; + + return MP_OKAY; + } + + /* + If the quotient is actually going to be returned, we'll try to + avoid hitting the memory allocator by copying the dividend into it + and doing the division there. This can't be any _worse_ than + always copying, and will sometimes be better (since it won't make + another copy) + + If it's not going to be returned, we need to allocate a temporary + to hold the quotient, which will just be discarded. + */ + if(q) { + if((res = mp_copy(a, q)) != MP_OKAY) + return res; + + res = s_mp_div_d(q, d, &rem); + if(s_mp_cmp_d(q, 0) == MP_EQ) + SIGN(q) = MP_ZPOS; + + } else { + mp_int qp; + + if((res = mp_init_copy(&qp, a)) != MP_OKAY) + return res; + + res = s_mp_div_d(&qp, d, &rem); + if(s_mp_cmp_d(&qp, 0) == 0) + SIGN(&qp) = MP_ZPOS; + + mp_clear(&qp); + } + + if(r) + *r = rem; + + return res; + +} /* end mp_div_d() */ + +/* }}} */ + +/* {{{ mp_div_2(a, c) */ + +/* + mp_div_2(a, c) + + Compute c = a / 2, disregarding the remainder. + */ + +mp_err mp_div_2(mp_int *a, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + s_mp_div_2(c); + + return MP_OKAY; + +} /* end mp_div_2() */ + +/* }}} */ + +/* {{{ mp_expt_d(a, d, b) */ + +mp_err mp_expt_d(mp_int *a, mp_digit d, mp_int *c) +{ + mp_int s, x; + mp_err res; + mp_sign cs = MP_ZPOS; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + DIGIT(&s, 0) = 1; + + if((d % 2) == 1) + cs = SIGN(a); + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + SIGN(&s) = cs; + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt_d() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Full arithmetic */ + +/* {{{ mp_abs(a, b) */ + +/* + mp_abs(a, b) + + Compute b = |a|. 'a' and 'b' may be identical. + */ + +mp_err mp_abs(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_abs() */ + +/* }}} */ + +/* {{{ mp_neg(a, b) */ + +/* + mp_neg(a, b) + + Compute b = -a. 'a' and 'b' may be identical. + */ + +mp_err mp_neg(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if(s_mp_cmp_d(b, 0) == MP_EQ) + SIGN(b) = MP_ZPOS; + else + SIGN(b) = (SIGN(b) == MP_NEG) ? MP_ZPOS : MP_NEG; + + return MP_OKAY; + +} /* end mp_neg() */ + +/* }}} */ + +/* {{{ mp_add(a, b, c) */ + +/* + mp_add(a, b, c) + + Compute c = a + b. All parameters may be identical. + */ + +mp_err mp_add(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + int cmp; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(SIGN(a) == SIGN(b)) { /* same sign: add values, keep sign */ + + /* Commutativity of addition lets us do this in either order, + so we avoid having to use a temporary even if the result + is supposed to replace the output + */ + if(c == b) { + if((res = s_mp_add(c, a)) != MP_OKAY) + return res; + } else { + if(c != a && (res = mp_copy(a, c)) != MP_OKAY) + return res; + + if((res = s_mp_add(c, b)) != MP_OKAY) + return res; + } + + } else if((cmp = s_mp_cmp(a, b)) > 0) { /* different sign: a > b */ + + /* If the output is going to be clobbered, we will use a temporary + variable; otherwise, we'll do it without touching the memory + allocator at all, if possible + */ + if(c == b) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, b)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + + if(c != a && (res = mp_copy(a, c)) != MP_OKAY) + return res; + if((res = s_mp_sub(c, b)) != MP_OKAY) + return res; + + } + + } else if(cmp == 0) { /* different sign, a == b */ + + mp_zero(c); + return MP_OKAY; + + } else { /* different sign: a < b */ + + /* See above... */ + if(c == a) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, b)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, a)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + + if(c != b && (res = mp_copy(b, c)) != MP_OKAY) + return res; + if((res = s_mp_sub(c, a)) != MP_OKAY) + return res; + + } + } + + if(USED(c) == 1 && DIGIT(c, 0) == 0) + SIGN(c) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_add() */ + +/* }}} */ + +/* {{{ mp_sub(a, b, c) */ + +/* + mp_sub(a, b, c) + + Compute c = a - b. All parameters may be identical. + */ + +mp_err mp_sub(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + int cmp; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(SIGN(a) != SIGN(b)) { + if(c == a) { + if((res = s_mp_add(c, b)) != MP_OKAY) + return res; + } else { + if(c != b && ((res = mp_copy(b, c)) != MP_OKAY)) + return res; + if((res = s_mp_add(c, a)) != MP_OKAY) + return res; + SIGN(c) = SIGN(a); + } + + } else if((cmp = s_mp_cmp(a, b)) > 0) { /* Same sign, a > b */ + if(c == b) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, a)) != MP_OKAY) + return res; + if((res = s_mp_sub(&tmp, b)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + if(c != a && ((res = mp_copy(a, c)) != MP_OKAY)) + return res; + + if((res = s_mp_sub(c, b)) != MP_OKAY) + return res; + } + + } else if(cmp == 0) { /* Same sign, equal magnitude */ + mp_zero(c); + return MP_OKAY; + + } else { /* Same sign, b > a */ + if(c == a) { + mp_int tmp; + + if((res = mp_init_copy(&tmp, b)) != MP_OKAY) + return res; + + if((res = s_mp_sub(&tmp, a)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + s_mp_exch(&tmp, c); + mp_clear(&tmp); + + } else { + if(c != b && ((res = mp_copy(b, c)) != MP_OKAY)) + return res; + + if((res = s_mp_sub(c, a)) != MP_OKAY) + return res; + } + + SIGN(c) = !SIGN(b); + } + + if(USED(c) == 1 && DIGIT(c, 0) == 0) + SIGN(c) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sub() */ + +/* }}} */ + +/* {{{ mp_mul(a, b, c) */ + +/* + mp_mul(a, b, c) + + Compute c = a * b. All parameters may be identical. + */ + +mp_err mp_mul(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_sign sgn; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + sgn = (SIGN(a) == SIGN(b)) ? MP_ZPOS : MP_NEG; + + if(c == b) { + if((res = s_mp_mul(c, a)) != MP_OKAY) + return res; + + } else { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if((res = s_mp_mul(c, b)) != MP_OKAY) + return res; + } + + if(sgn == MP_ZPOS || s_mp_cmp_d(c, 0) == MP_EQ) + SIGN(c) = MP_ZPOS; + else + SIGN(c) = sgn; + + return MP_OKAY; + +} /* end mp_mul() */ + +/* }}} */ + +/* {{{ mp_mul_2d(a, d, c) */ + +/* + mp_mul_2d(a, d, c) + + Compute c = a * 2^d. a may be the same as c. + */ + +mp_err mp_mul_2d(mp_int *a, mp_digit d, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if(d == 0) + return MP_OKAY; + + return s_mp_mul_2d(c, d); + +} /* end mp_mul() */ + +/* }}} */ + +/* {{{ mp_sqr(a, b) */ + +#if MP_SQUARE +mp_err mp_sqr(mp_int *a, mp_int *b) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if((res = mp_copy(a, b)) != MP_OKAY) + return res; + + if((res = s_mp_sqr(b)) != MP_OKAY) + return res; + + SIGN(b) = MP_ZPOS; + + return MP_OKAY; + +} /* end mp_sqr() */ +#endif + +/* }}} */ + +/* {{{ mp_div(a, b, q, r) */ + +/* + mp_div(a, b, q, r) + + Compute q = a / b and r = a mod b. Input parameters may be re-used + as output parameters. If q or r is NULL, that portion of the + computation will be discarded (although it will still be computed) + + Pay no attention to the hacker behind the curtain. + */ + +mp_err mp_div(mp_int *a, mp_int *b, mp_int *q, mp_int *r) +{ + mp_err res; + mp_int qtmp, rtmp; + int cmp; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + if(mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + + /* If a <= b, we can compute the solution without division, and + avoid any memory allocation + */ + if((cmp = s_mp_cmp(a, b)) < 0) { + if(r) { + if((res = mp_copy(a, r)) != MP_OKAY) + return res; + } + + if(q) + mp_zero(q); + + return MP_OKAY; + + } else if(cmp == 0) { + + /* Set quotient to 1, with appropriate sign */ + if(q) { + int qneg = (SIGN(a) != SIGN(b)); + + mp_set(q, 1); + if(qneg) + SIGN(q) = MP_NEG; + } + + if(r) + mp_zero(r); + + return MP_OKAY; + } + + /* If we get here, it means we actually have to do some division */ + + /* Set up some temporaries... */ + if((res = mp_init_copy(&qtmp, a)) != MP_OKAY) + return res; + if((res = mp_init_copy(&rtmp, b)) != MP_OKAY) + goto CLEANUP; + + if((res = s_mp_div(&qtmp, &rtmp)) != MP_OKAY) + goto CLEANUP; + + /* Compute the signs for the output */ + SIGN(&rtmp) = SIGN(a); /* Sr = Sa */ + if(SIGN(a) == SIGN(b)) + SIGN(&qtmp) = MP_ZPOS; /* Sq = MP_ZPOS if Sa = Sb */ + else + SIGN(&qtmp) = MP_NEG; /* Sq = MP_NEG if Sa != Sb */ + + if(s_mp_cmp_d(&qtmp, 0) == MP_EQ) + SIGN(&qtmp) = MP_ZPOS; + if(s_mp_cmp_d(&rtmp, 0) == MP_EQ) + SIGN(&rtmp) = MP_ZPOS; + + /* Copy output, if it is needed */ + if(q) + s_mp_exch(&qtmp, q); + + if(r) + s_mp_exch(&rtmp, r); + +CLEANUP: + mp_clear(&rtmp); + mp_clear(&qtmp); + + return res; + +} /* end mp_div() */ + +/* }}} */ + +/* {{{ mp_div_2d(a, d, q, r) */ + +mp_err mp_div_2d(mp_int *a, mp_digit d, mp_int *q, mp_int *r) +{ + mp_err res; + + ARGCHK(a != NULL, MP_BADARG); + + if(q) { + if((res = mp_copy(a, q)) != MP_OKAY) + return res; + + s_mp_div_2d(q, d); + } + + if(r) { + if((res = mp_copy(a, r)) != MP_OKAY) + return res; + + s_mp_mod_2d(r, d); + } + + return MP_OKAY; + +} /* end mp_div_2d() */ + +/* }}} */ + +/* {{{ mp_expt(a, b, c) */ + +/* + mp_expt(a, b, c) + + Compute c = a ** b, that is, raise a to the b power. Uses a + standard iterative square-and-multiply technique. + */ + +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int s, x; + mp_err res; + mp_digit d; + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0) + return MP_RANGE; + + if((res = mp_init(&s)) != MP_OKAY) + return res; + + mp_set(&s, 1); + + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + /* Loop over low-order digits in ascending order */ + for(dig = 0; dig < (USED(b) - 1); dig++) { + d = DIGIT(b, dig); + + /* Loop over bits of each non-maximal digit */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Consider now the last digit... */ + d = DIGIT(b, dig); + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + } + + if(mp_iseven(b)) + SIGN(&s) = SIGN(a); + + res = mp_copy(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_expt() */ + +/* }}} */ + +/* {{{ mp_2expt(a, k) */ + +/* Compute a = 2^k */ + +mp_err mp_2expt(mp_int *a, mp_digit k) +{ + ARGCHK(a != NULL, MP_BADARG); + + return s_mp_2expt(a, k); + +} /* end mp_2expt() */ + +/* }}} */ + +/* {{{ mp_mod(a, m, c) */ + +/* + mp_mod(a, m, c) + + Compute c = a (mod m). Result will always be 0 <= c < m. + */ + +mp_err mp_mod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_err res; + int mag; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if(SIGN(m) == MP_NEG) + return MP_RANGE; + + /* + If |a| > m, we need to divide to get the remainder and take the + absolute value. + + If |a| < m, we don't need to do any division, just copy and adjust + the sign (if a is negative). + + If |a| == m, we can simply set the result to zero. + + This order is intended to minimize the average path length of the + comparison chain on common workloads -- the most frequent cases are + that |a| != m, so we do those first. + */ + if((mag = s_mp_cmp(a, m)) > 0) { + if((res = mp_div(a, m, NULL, c)) != MP_OKAY) + return res; + + if(SIGN(c) == MP_NEG) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + } + + } else if(mag < 0) { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + + if(mp_cmp_z(a) < 0) { + if((res = mp_add(c, m, c)) != MP_OKAY) + return res; + + } + + } else { + mp_zero(c); + + } + + return MP_OKAY; + +} /* end mp_mod() */ + +/* }}} */ + +/* {{{ mp_mod_d(a, d, c) */ + +/* + mp_mod_d(a, d, c) + + Compute c = a (mod d). Result will always be 0 <= c < d + */ +mp_err mp_mod_d(mp_int *a, mp_digit d, mp_digit *c) +{ + mp_err res; + mp_digit rem; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if(s_mp_cmp_d(a, d) > 0) { + if((res = mp_div_d(a, d, NULL, &rem)) != MP_OKAY) + return res; + + } else { + if(SIGN(a) == MP_NEG) + rem = d - DIGIT(a, 0); + else + rem = DIGIT(a, 0); + } + + if(c) + *c = rem; + + return MP_OKAY; + +} /* end mp_mod_d() */ + +/* }}} */ + +/* {{{ mp_sqrt(a, b) */ + +/* + mp_sqrt(a, b) + + Compute the integer square root of a, and store the result in b. + Uses an integer-arithmetic version of Newton's iterative linear + approximation technique to determine this value; the result has the + following two properties: + + b^2 <= a + (b+1)^2 >= a + + It is a range error to pass a negative value. + */ +mp_err mp_sqrt(mp_int *a, mp_int *b) +{ + mp_int x, t; + mp_err res; + + ARGCHK(a != NULL && b != NULL, MP_BADARG); + + /* Cannot take square root of a negative value */ + if(SIGN(a) == MP_NEG) + return MP_RANGE; + + /* Special cases for zero and one, trivial */ + if(mp_cmp_d(a, 0) == MP_EQ || mp_cmp_d(a, 1) == MP_EQ) + return mp_copy(a, b); + + /* Initialize the temporaries we'll use below */ + if((res = mp_init_size(&t, USED(a))) != MP_OKAY) + return res; + + /* Compute an initial guess for the iteration as a itself */ + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + for(;;) { + /* t = (x * x) - a */ + mp_copy(&x, &t); /* can't fail, t is big enough for original x */ + if((res = mp_sqr(&t, &t)) != MP_OKAY || + (res = mp_sub(&t, a, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = t / 2x */ + s_mp_mul_2(&x); + if((res = mp_div(&t, &x, &t, NULL)) != MP_OKAY) + goto CLEANUP; + s_mp_div_2(&x); + + /* Terminate the loop, if the quotient is zero */ + if(mp_cmp_z(&t) == MP_EQ) + break; + + /* x = x - t */ + if((res = mp_sub(&x, &t, &x)) != MP_OKAY) + goto CLEANUP; + + } + + /* Copy result to output parameter */ + mp_sub_d(&x, 1, &x); + s_mp_exch(&x, b); + + CLEANUP: + mp_clear(&x); + X: + mp_clear(&t); + + return res; + +} /* end mp_sqrt() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Modular arithmetic */ + +#if MP_MODARITH +/* {{{ mp_addmod(a, b, m, c) */ + +/* + mp_addmod(a, b, m, c) + + Compute c = (a + b) mod m + */ + +mp_err mp_addmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_add(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_submod(a, b, m, c) */ + +/* + mp_submod(a, b, m, c) + + Compute c = (a - b) mod m + */ + +mp_err mp_submod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sub(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_mulmod(a, b, m, c) */ + +/* + mp_mulmod(a, b, m, c) + + Compute c = (a * b) mod m + */ + +mp_err mp_mulmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && b != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_mul(a, b, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} + +/* }}} */ + +/* {{{ mp_sqrmod(a, m, c) */ + +#if MP_SQUARE +mp_err mp_sqrmod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_err res; + + ARGCHK(a != NULL && m != NULL && c != NULL, MP_BADARG); + + if((res = mp_sqr(a, c)) != MP_OKAY) + return res; + if((res = mp_mod(c, m, c)) != MP_OKAY) + return res; + + return MP_OKAY; + +} /* end mp_sqrmod() */ +#endif + +/* }}} */ + +/* {{{ mp_exptmod(a, b, m, c) */ + +/* + mp_exptmod(a, b, m, c) + + Compute c = (a ** b) mod m. Uses a standard square-and-multiply + method with modular reductions at each step. (This is basically the + same code as mp_expt(), except for the addition of the reductions) + + The modular reductions are done using Barrett's algorithm (see + s_mp_reduce() below for details) + */ + +mp_err mp_exptmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c) +{ + mp_int s, x, mu; + mp_err res; + mp_digit d, *db = DIGITS(b); + mp_size ub = USED(b); + int dig, bit; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(b) < 0 || mp_cmp_z(m) <= 0) + return MP_RANGE; + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + if((res = mp_mod(&x, m, &x)) != MP_OKAY || + (res = mp_init(&mu)) != MP_OKAY) + goto MU; + + mp_set(&s, 1); + + /* mu = b^2k / m */ + s_mp_add_d(&mu, 1); + s_mp_lshd(&mu, 2 * USED(m)); + if((res = mp_div(&mu, m, &mu, NULL)) != MP_OKAY) + goto CLEANUP; + + /* Loop over digits of b in ascending order, except highest order */ + for(dig = 0; dig < (ub - 1); dig++) { + d = *db++; + + /* Loop over the bits of the lower-order digits */ + for(bit = 0; bit < DIGIT_BIT; bit++) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + } + + /* Now do the last digit... */ + d = *db; + + while(d) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&s, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + d >>= 1; + + if((res = s_mp_sqr(&x)) != MP_OKAY) + goto CLEANUP; + if((res = s_mp_reduce(&x, m, &mu)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + + CLEANUP: + mp_clear(&mu); + MU: + mp_clear(&x); + X: + mp_clear(&s); + + return res; + +} /* end mp_exptmod() */ + +/* }}} */ + +/* {{{ mp_exptmod_d(a, d, m, c) */ + +mp_err mp_exptmod_d(mp_int *a, mp_digit d, mp_int *m, mp_int *c) +{ + mp_int s, x; + mp_err res; + + ARGCHK(a != NULL && c != NULL, MP_BADARG); + + if((res = mp_init(&s)) != MP_OKAY) + return res; + if((res = mp_init_copy(&x, a)) != MP_OKAY) + goto X; + + mp_set(&s, 1); + + while(d != 0) { + if(d & 1) { + if((res = s_mp_mul(&s, &x)) != MP_OKAY || + (res = mp_mod(&s, m, &s)) != MP_OKAY) + goto CLEANUP; + } + + d /= 2; + + if((res = s_mp_sqr(&x)) != MP_OKAY || + (res = mp_mod(&x, m, &x)) != MP_OKAY) + goto CLEANUP; + } + + s_mp_exch(&s, c); + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&s); + + return res; + +} /* end mp_exptmod_d() */ + +/* }}} */ +#endif /* if MP_MODARITH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Comparison functions */ + +/* {{{ mp_cmp_z(a) */ + +/* + mp_cmp_z(a) + + Compare a <=> 0. Returns <0 if a<0, 0 if a=0, >0 if a>0. + */ + +int mp_cmp_z(mp_int *a) +{ + if(SIGN(a) == MP_NEG) + return MP_LT; + else if(USED(a) == 1 && DIGIT(a, 0) == 0) + return MP_EQ; + else + return MP_GT; + +} /* end mp_cmp_z() */ + +/* }}} */ + +/* {{{ mp_cmp_d(a, d) */ + +/* + mp_cmp_d(a, d) + + Compare a <=> d. Returns <0 if a<d, 0 if a=d, >0 if a>d + */ + +int mp_cmp_d(mp_int *a, mp_digit d) +{ + ARGCHK(a != NULL, MP_EQ); + + if(SIGN(a) == MP_NEG) + return MP_LT; + + return s_mp_cmp_d(a, d); + +} /* end mp_cmp_d() */ + +/* }}} */ + +/* {{{ mp_cmp(a, b) */ + +int mp_cmp(mp_int *a, mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + if(SIGN(a) == SIGN(b)) { + int mag; + + if((mag = s_mp_cmp(a, b)) == MP_EQ) + return MP_EQ; + + if(SIGN(a) == MP_ZPOS) + return mag; + else + return -mag; + + } else if(SIGN(a) == MP_ZPOS) { + return MP_GT; + } else { + return MP_LT; + } + +} /* end mp_cmp() */ + +/* }}} */ + +/* {{{ mp_cmp_mag(a, b) */ + +/* + mp_cmp_mag(a, b) + + Compares |a| <=> |b|, and returns an appropriate comparison result + */ + +int mp_cmp_mag(mp_int *a, mp_int *b) +{ + ARGCHK(a != NULL && b != NULL, MP_EQ); + + return s_mp_cmp(a, b); + +} /* end mp_cmp_mag() */ + +/* }}} */ + +/* {{{ mp_cmp_int(a, z) */ + +/* + This just converts z to an mp_int, and uses the existing comparison + routines. This is sort of inefficient, but it's not clear to me how + frequently this wil get used anyway. For small positive constants, + you can always use mp_cmp_d(), and for zero, there is mp_cmp_z(). + */ +int mp_cmp_int(mp_int *a, long z) +{ + mp_int tmp; + int out; + + ARGCHK(a != NULL, MP_EQ); + + mp_init(&tmp); mp_set_int(&tmp, z); + out = mp_cmp(a, &tmp); + mp_clear(&tmp); + + return out; + +} /* end mp_cmp_int() */ + +/* }}} */ + +/* {{{ mp_isodd(a) */ + +/* + mp_isodd(a) + + Returns a true (non-zero) value if a is odd, false (zero) otherwise. + */ +int mp_isodd(mp_int *a) +{ + ARGCHK(a != NULL, 0); + + return (DIGIT(a, 0) & 1); + +} /* end mp_isodd() */ + +/* }}} */ + +/* {{{ mp_iseven(a) */ + +int mp_iseven(mp_int *a) +{ + return !mp_isodd(a); + +} /* end mp_iseven() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ Number theoretic functions */ + +#if MP_NUMTH +/* {{{ mp_gcd(a, b, c) */ + +/* + Like the old mp_gcd() function, except computes the GCD using the + binary algorithm due to Josef Stein in 1961 (via Knuth). + */ +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c) +{ + mp_err res; + mp_int u, v, t; + mp_size k = 0; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + if(mp_cmp_z(a) == MP_EQ && mp_cmp_z(b) == MP_EQ) + return MP_RANGE; + if(mp_cmp_z(a) == MP_EQ) { + if((res = mp_copy(b, c)) != MP_OKAY) + return res; + SIGN(c) = MP_ZPOS; return MP_OKAY; + } else if(mp_cmp_z(b) == MP_EQ) { + if((res = mp_copy(a, c)) != MP_OKAY) + return res; + SIGN(c) = MP_ZPOS; return MP_OKAY; + } + + if((res = mp_init(&t)) != MP_OKAY) + return res; + if((res = mp_init_copy(&u, a)) != MP_OKAY) + goto U; + if((res = mp_init_copy(&v, b)) != MP_OKAY) + goto V; + + SIGN(&u) = MP_ZPOS; + SIGN(&v) = MP_ZPOS; + + /* Divide out common factors of 2 until at least 1 of a, b is even */ + while(mp_iseven(&u) && mp_iseven(&v)) { + s_mp_div_2(&u); + s_mp_div_2(&v); + ++k; + } + + /* Initialize t */ + if(mp_isodd(&u)) { + if((res = mp_copy(&v, &t)) != MP_OKAY) + goto CLEANUP; + + /* t = -v */ + if(SIGN(&v) == MP_ZPOS) + SIGN(&t) = MP_NEG; + else + SIGN(&t) = MP_ZPOS; + + } else { + if((res = mp_copy(&u, &t)) != MP_OKAY) + goto CLEANUP; + + } + + for(;;) { + while(mp_iseven(&t)) { + s_mp_div_2(&t); + } + + if(mp_cmp_z(&t) == MP_GT) { + if((res = mp_copy(&t, &u)) != MP_OKAY) + goto CLEANUP; + + } else { + if((res = mp_copy(&t, &v)) != MP_OKAY) + goto CLEANUP; + + /* v = -t */ + if(SIGN(&t) == MP_ZPOS) + SIGN(&v) = MP_NEG; + else + SIGN(&v) = MP_ZPOS; + } + + if((res = mp_sub(&u, &v, &t)) != MP_OKAY) + goto CLEANUP; + + if(s_mp_cmp_d(&t, 0) == MP_EQ) + break; + } + + s_mp_2expt(&v, k); /* v = 2^k */ + res = mp_mul(&u, &v, c); /* c = u * v */ + + CLEANUP: + mp_clear(&v); + V: + mp_clear(&u); + U: + mp_clear(&t); + + return res; + +} /* end mp_bgcd() */ + +/* }}} */ + +/* {{{ mp_lcm(a, b, c) */ + +/* We compute the least common multiple using the rule: + + ab = [a, b](a, b) + + ... by computing the product, and dividing out the gcd. + */ + +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c) +{ + mp_int gcd, prod; + mp_err res; + + ARGCHK(a != NULL && b != NULL && c != NULL, MP_BADARG); + + /* Set up temporaries */ + if((res = mp_init(&gcd)) != MP_OKAY) + return res; + if((res = mp_init(&prod)) != MP_OKAY) + goto GCD; + + if((res = mp_mul(a, b, &prod)) != MP_OKAY) + goto CLEANUP; + if((res = mp_gcd(a, b, &gcd)) != MP_OKAY) + goto CLEANUP; + + res = mp_div(&prod, &gcd, c, NULL); + + CLEANUP: + mp_clear(&prod); + GCD: + mp_clear(&gcd); + + return res; + +} /* end mp_lcm() */ + +/* }}} */ + +/* {{{ mp_xgcd(a, b, g, x, y) */ + +/* + mp_xgcd(a, b, g, x, y) + + Compute g = (a, b) and values x and y satisfying Bezout's identity + (that is, ax + by = g). This uses the extended binary GCD algorithm + based on the Stein algorithm used for mp_gcd() + */ + +mp_err mp_xgcd(mp_int *a, mp_int *b, mp_int *g, mp_int *x, mp_int *y) +{ + mp_int gx, xc, yc, u, v, A, B, C, D; + mp_int *clean[9]; + mp_err res; + int last = -1; + + if(mp_cmp_z(b) == 0) + return MP_RANGE; + + /* Initialize all these variables we need */ + if((res = mp_init(&u)) != MP_OKAY) goto CLEANUP; + clean[++last] = &u; + if((res = mp_init(&v)) != MP_OKAY) goto CLEANUP; + clean[++last] = &v; + if((res = mp_init(&gx)) != MP_OKAY) goto CLEANUP; + clean[++last] = &gx; + if((res = mp_init(&A)) != MP_OKAY) goto CLEANUP; + clean[++last] = &A; + if((res = mp_init(&B)) != MP_OKAY) goto CLEANUP; + clean[++last] = &B; + if((res = mp_init(&C)) != MP_OKAY) goto CLEANUP; + clean[++last] = &C; + if((res = mp_init(&D)) != MP_OKAY) goto CLEANUP; + clean[++last] = &D; + if((res = mp_init_copy(&xc, a)) != MP_OKAY) goto CLEANUP; + clean[++last] = &xc; + mp_abs(&xc, &xc); + if((res = mp_init_copy(&yc, b)) != MP_OKAY) goto CLEANUP; + clean[++last] = &yc; + mp_abs(&yc, &yc); + + mp_set(&gx, 1); + + /* Divide by two until at least one of them is even */ + while(mp_iseven(&xc) && mp_iseven(&yc)) { + s_mp_div_2(&xc); + s_mp_div_2(&yc); + if((res = s_mp_mul_2(&gx)) != MP_OKAY) + goto CLEANUP; + } + + mp_copy(&xc, &u); + mp_copy(&yc, &v); + mp_set(&A, 1); mp_set(&D, 1); + + /* Loop through binary GCD algorithm */ + for(;;) { + while(mp_iseven(&u)) { + s_mp_div_2(&u); + + if(mp_iseven(&A) && mp_iseven(&B)) { + s_mp_div_2(&A); s_mp_div_2(&B); + } else { + if((res = mp_add(&A, &yc, &A)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&A); + if((res = mp_sub(&B, &xc, &B)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&B); + } + } + + while(mp_iseven(&v)) { + s_mp_div_2(&v); + + if(mp_iseven(&C) && mp_iseven(&D)) { + s_mp_div_2(&C); s_mp_div_2(&D); + } else { + if((res = mp_add(&C, &yc, &C)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&C); + if((res = mp_sub(&D, &xc, &D)) != MP_OKAY) goto CLEANUP; + s_mp_div_2(&D); + } + } + + if(mp_cmp(&u, &v) >= 0) { + if((res = mp_sub(&u, &v, &u)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&A, &C, &A)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&B, &D, &B)) != MP_OKAY) goto CLEANUP; + + } else { + if((res = mp_sub(&v, &u, &v)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&C, &A, &C)) != MP_OKAY) goto CLEANUP; + if((res = mp_sub(&D, &B, &D)) != MP_OKAY) goto CLEANUP; + + } + + /* If we're done, copy results to output */ + if(mp_cmp_z(&u) == 0) { + if(x) + if((res = mp_copy(&C, x)) != MP_OKAY) goto CLEANUP; + + if(y) + if((res = mp_copy(&D, y)) != MP_OKAY) goto CLEANUP; + + if(g) + if((res = mp_mul(&gx, &v, g)) != MP_OKAY) goto CLEANUP; + + break; + } + } + + CLEANUP: + while(last >= 0) + mp_clear(clean[last--]); + + return res; + +} /* end mp_xgcd() */ + +/* }}} */ + +/* {{{ mp_invmod(a, m, c) */ + +/* + mp_invmod(a, m, c) + + Compute c = a^-1 (mod m), if there is an inverse for a (mod m). + This is equivalent to the question of whether (a, m) = 1. If not, + MP_UNDEF is returned, and there is no inverse. + */ + +mp_err mp_invmod(mp_int *a, mp_int *m, mp_int *c) +{ + mp_int g, x; + mp_sign sa; + mp_err res; + + ARGCHK(a && m && c, MP_BADARG); + + if(mp_cmp_z(a) == 0 || mp_cmp_z(m) == 0) + return MP_RANGE; + + sa = SIGN(a); + + if((res = mp_init(&g)) != MP_OKAY) + return res; + if((res = mp_init(&x)) != MP_OKAY) + goto X; + + if((res = mp_xgcd(a, m, &g, &x, NULL)) != MP_OKAY) + goto CLEANUP; + + if(mp_cmp_d(&g, 1) != MP_EQ) { + res = MP_UNDEF; + goto CLEANUP; + } + + res = mp_mod(&x, m, c); + SIGN(c) = sa; + +CLEANUP: + mp_clear(&x); +X: + mp_clear(&g); + + return res; + +} /* end mp_invmod() */ + +/* }}} */ +#endif /* if MP_NUMTH */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ mp_print(mp, ofp) */ + +#if MP_IOFUNC +/* + mp_print(mp, ofp) + + Print a textual representation of the given mp_int on the output + stream 'ofp'. Output is generated using the internal radix. + */ + +void mp_print(mp_int *mp, FILE *ofp) +{ + int ix; + + if(mp == NULL || ofp == NULL) + return; + + fputc((SIGN(mp) == MP_NEG) ? '-' : '+', ofp); + + for(ix = USED(mp) - 1; ix >= 0; ix--) { + fprintf(ofp, DIGIT_FMT, DIGIT(mp, ix)); + } + +} /* end mp_print() */ + +#endif /* if MP_IOFUNC */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* {{{ More I/O Functions */ + +/* {{{ mp_read_signed_bin(mp, str, len) */ + +/* + mp_read_signed_bin(mp, str, len) + + Read in a raw value (base 256) into the given mp_int + */ + +mp_err mp_read_signed_bin(mp_int *mp, unsigned char *str, int len) +{ + mp_err res; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + if((res = mp_read_unsigned_bin(mp, str + 1, len - 1)) == MP_OKAY) { + /* Get sign from first byte */ + if(str[0]) + SIGN(mp) = MP_NEG; + else + SIGN(mp) = MP_ZPOS; + } + + return res; + +} /* end mp_read_signed_bin() */ + +/* }}} */ + +/* {{{ mp_signed_bin_size(mp) */ + +int mp_signed_bin_size(mp_int *mp) +{ + ARGCHK(mp != NULL, 0); + + return mp_unsigned_bin_size(mp) + 1; + +} /* end mp_signed_bin_size() */ + +/* }}} */ + +/* {{{ mp_to_signed_bin(mp, str) */ + +mp_err mp_to_signed_bin(mp_int *mp, unsigned char *str) +{ + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + + /* Caller responsible for allocating enough memory (use mp_raw_size(mp)) */ + str[0] = (char)SIGN(mp); + + return mp_to_unsigned_bin(mp, str + 1); + +} /* end mp_to_signed_bin() */ + +/* }}} */ + +/* {{{ mp_read_unsigned_bin(mp, str, len) */ + +/* + mp_read_unsigned_bin(mp, str, len) + + Read in an unsigned value (base 256) into the given mp_int + */ + +mp_err mp_read_unsigned_bin(mp_int *mp, unsigned char *str, int len) +{ + int ix; + mp_err res; + + ARGCHK(mp != NULL && str != NULL && len > 0, MP_BADARG); + + mp_zero(mp); + + for(ix = 0; ix < len; ix++) { + if((res = s_mp_mul_2d(mp, CHAR_BIT)) != MP_OKAY) + return res; + + if((res = mp_add_d(mp, str[ix], mp)) != MP_OKAY) + return res; + } + + return MP_OKAY; + +} /* end mp_read_unsigned_bin() */ + +/* }}} */ + +/* {{{ mp_unsigned_bin_size(mp) */ + +int mp_unsigned_bin_size(mp_int *mp) +{ + mp_digit topdig; + int count; + + ARGCHK(mp != NULL, 0); + + /* Special case for the value zero */ + if(USED(mp) == 1 && DIGIT(mp, 0) == 0) + return 1; + + count = (USED(mp) - 1) * sizeof(mp_digit); + topdig = DIGIT(mp, USED(mp) - 1); + + while(topdig != 0) { + ++count; + topdig >>= CHAR_BIT; + } + + return count; + +} /* end mp_unsigned_bin_size() */ + +/* }}} */ + +/* {{{ mp_to_unsigned_bin(mp, str) */ + +mp_err mp_to_unsigned_bin(mp_int *mp, unsigned char *str) +{ + mp_digit *dp, *end, d; + unsigned char *spos; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + + dp = DIGITS(mp); + end = dp + USED(mp) - 1; + spos = str; + + /* Special case for zero, quick test */ + if(dp == end && *dp == 0) { + *str = '\0'; + return MP_OKAY; + } + + /* Generate digits in reverse order */ + while(dp < end) { + int ix; + + d = *dp; + for(ix = 0; ix < sizeof(mp_digit); ++ix) { + *spos = d & UCHAR_MAX; + d >>= CHAR_BIT; + ++spos; + } + + ++dp; + } + + /* Now handle last digit specially, high order zeroes are not written */ + d = *end; + while(d != 0) { + *spos = d & UCHAR_MAX; + d >>= CHAR_BIT; + ++spos; + } + + /* Reverse everything to get digits in the correct order */ + while(--spos > str) { + unsigned char t = *str; + *str = *spos; + *spos = t; + + ++str; + } + + return MP_OKAY; + +} /* end mp_to_unsigned_bin() */ + +/* }}} */ + +/* {{{ mp_count_bits(mp) */ + +int mp_count_bits(mp_int *mp) +{ + int len; + mp_digit d; + + ARGCHK(mp != NULL, MP_BADARG); + + len = DIGIT_BIT * (USED(mp) - 1); + d = DIGIT(mp, USED(mp) - 1); + + while(d != 0) { + ++len; + d >>= 1; + } + + return len; + +} /* end mp_count_bits() */ + +/* }}} */ + +/* {{{ mp_read_radix(mp, str, radix) */ + +/* + mp_read_radix(mp, str, radix) + + Read an integer from the given string, and set mp to the resulting + value. The input is presumed to be in base 10. Leading non-digit + characters are ignored, and the function reads until a non-digit + character or the end of the string. + */ + +mp_err mp_read_radix(mp_int *mp, unsigned char *str, int radix) +{ + int ix = 0, val = 0; + mp_err res; + mp_sign sig = MP_ZPOS; + + ARGCHK(mp != NULL && str != NULL && radix >= 2 && radix <= MAX_RADIX, + MP_BADARG); + + mp_zero(mp); + + /* Skip leading non-digit characters until a digit or '-' or '+' */ + while(str[ix] && + (s_mp_tovalue(str[ix], radix) < 0) && + str[ix] != '-' && + str[ix] != '+') { + ++ix; + } + + if(str[ix] == '-') { + sig = MP_NEG; + ++ix; + } else if(str[ix] == '+') { + sig = MP_ZPOS; /* this is the default anyway... */ + ++ix; + } + + while((val = s_mp_tovalue(str[ix], radix)) >= 0) { + if((res = s_mp_mul_d(mp, radix)) != MP_OKAY) + return res; + if((res = s_mp_add_d(mp, val)) != MP_OKAY) + return res; + ++ix; + } + + if(s_mp_cmp_d(mp, 0) == MP_EQ) + SIGN(mp) = MP_ZPOS; + else + SIGN(mp) = sig; + + return MP_OKAY; + +} /* end mp_read_radix() */ + +/* }}} */ + +/* {{{ mp_radix_size(mp, radix) */ + +int mp_radix_size(mp_int *mp, int radix) +{ + int len; + ARGCHK(mp != NULL, 0); + + len = s_mp_outlen(mp_count_bits(mp), radix) + 1; /* for NUL terminator */ + + if(mp_cmp_z(mp) < 0) + ++len; /* for sign */ + + return len; + +} /* end mp_radix_size() */ + +/* }}} */ + +/* {{{ mp_value_radix_size(num, qty, radix) */ + +/* num = number of digits + qty = number of bits per digit + radix = target base + + Return the number of digits in the specified radix that would be + needed to express 'num' digits of 'qty' bits each. + */ +int mp_value_radix_size(int num, int qty, int radix) +{ + ARGCHK(num >= 0 && qty > 0 && radix >= 2 && radix <= MAX_RADIX, 0); + + return s_mp_outlen(num * qty, radix); + +} /* end mp_value_radix_size() */ + +/* }}} */ + +/* {{{ mp_toradix(mp, str, radix) */ + +mp_err mp_toradix(mp_int *mp, unsigned char *str, int radix) +{ + int ix, pos = 0; + + ARGCHK(mp != NULL && str != NULL, MP_BADARG); + ARGCHK(radix > 1 && radix <= MAX_RADIX, MP_RANGE); + + if(mp_cmp_z(mp) == MP_EQ) { + str[0] = '0'; + str[1] = '\0'; + } else { + mp_err res; + mp_int tmp; + mp_sign sgn; + mp_digit rem, rdx = (mp_digit)radix; + char ch; + + if((res = mp_init_copy(&tmp, mp)) != MP_OKAY) + return res; + + /* Save sign for later, and take absolute value */ + sgn = SIGN(&tmp); SIGN(&tmp) = MP_ZPOS; + + /* Generate output digits in reverse order */ + while(mp_cmp_z(&tmp) != 0) { + if((res = s_mp_div_d(&tmp, rdx, &rem)) != MP_OKAY) { + mp_clear(&tmp); + return res; + } + + /* Generate digits, use capital letters */ + ch = s_mp_todigit(rem, radix, 0); + + str[pos++] = ch; + } + + /* Add - sign if original value was negative */ + if(sgn == MP_NEG) + str[pos++] = '-'; + + /* Add trailing NUL to end the string */ + str[pos--] = '\0'; + + /* Reverse the digits and sign indicator */ + ix = 0; + while(ix < pos) { + char tmp = str[ix]; + + str[ix] = str[pos]; + str[pos] = tmp; + ++ix; + --pos; + } + + mp_clear(&tmp); + } + + return MP_OKAY; + +} /* end mp_toradix() */ + +/* }}} */ + +/* {{{ mp_char2value(ch, r) */ + +int mp_char2value(char ch, int r) +{ + return s_mp_tovalue(ch, r); + +} /* end mp_tovalue() */ + +/* }}} */ + +/* }}} */ + +/* {{{ mp_strerror(ec) */ + +/* + mp_strerror(ec) + + Return a string describing the meaning of error code 'ec'. The + string returned is allocated in static memory, so the caller should + not attempt to modify or free the memory associated with this + string. + */ +const char *mp_strerror(mp_err ec) +{ + int aec = (ec < 0) ? -ec : ec; + + /* Code values are negative, so the senses of these comparisons + are accurate */ + if(ec < MP_LAST_CODE || ec > MP_OKAY) { + return mp_err_string[0]; /* unknown error code */ + } else { + return mp_err_string[aec + 1]; + } + +} /* end mp_strerror() */ + +/* }}} */ + +/*========================================================================*/ +/*------------------------------------------------------------------------*/ +/* Static function definitions (internal use only) */ + +/* {{{ Memory management */ + +/* {{{ s_mp_grow(mp, min) */ + +/* Make sure there are at least 'min' digits allocated to mp */ +mp_err s_mp_grow(mp_int *mp, mp_size min) +{ + if(min > ALLOC(mp)) { + mp_digit *tmp; + + /* Set min to next nearest default precision block size */ + min = ((min + (s_mp_defprec - 1)) / s_mp_defprec) * s_mp_defprec; + + if((tmp = s_mp_alloc(min, sizeof(mp_digit))) == NULL) + return MP_MEM; + + s_mp_copy(DIGITS(mp), tmp, USED(mp)); + +#if MP_CRYPTO + s_mp_setz(DIGITS(mp), ALLOC(mp)); +#endif + s_mp_free(DIGITS(mp)); + DIGITS(mp) = tmp; + ALLOC(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_grow() */ + +/* }}} */ + +/* {{{ s_mp_pad(mp, min) */ + +/* Make sure the used size of mp is at least 'min', growing if needed */ +mp_err s_mp_pad(mp_int *mp, mp_size min) +{ + if(min > USED(mp)) { + mp_err res; + + /* Make sure there is room to increase precision */ + if(min > ALLOC(mp) && (res = s_mp_grow(mp, min)) != MP_OKAY) + return res; + + /* Increase precision; should already be 0-filled */ + USED(mp) = min; + } + + return MP_OKAY; + +} /* end s_mp_pad() */ + +/* }}} */ + +/* {{{ s_mp_setz(dp, count) */ + +#if MP_MACRO == 0 +/* Set 'count' digits pointed to by dp to be zeroes */ +void s_mp_setz(mp_digit *dp, mp_size count) +{ +#if MP_MEMSET == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = 0; +#else + memset(dp, 0, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_setz() */ +#endif + +/* }}} */ + +/* {{{ s_mp_copy(sp, dp, count) */ + +#if MP_MACRO == 0 +/* Copy 'count' digits from sp to dp */ +void s_mp_copy(mp_digit *sp, mp_digit *dp, mp_size count) +{ +#if MP_MEMCPY == 0 + int ix; + + for(ix = 0; ix < count; ix++) + dp[ix] = sp[ix]; +#else + memcpy(dp, sp, count * sizeof(mp_digit)); +#endif + +} /* end s_mp_copy() */ +#endif + +/* }}} */ + +/* {{{ s_mp_alloc(nb, ni) */ + +#if MP_MACRO == 0 +/* Allocate ni records of nb bytes each, and return a pointer to that */ +void *s_mp_alloc(size_t nb, size_t ni) +{ + return calloc(nb, ni); + +} /* end s_mp_alloc() */ +#endif + +/* }}} */ + +/* {{{ s_mp_free(ptr) */ + +#if MP_MACRO == 0 +/* Free the memory pointed to by ptr */ +void s_mp_free(void *ptr) +{ + if(ptr) + free(ptr); + +} /* end s_mp_free() */ +#endif + +/* }}} */ + +/* {{{ s_mp_clamp(mp) */ + +/* Remove leading zeroes from the given value */ +void s_mp_clamp(mp_int *mp) +{ + mp_size du = USED(mp); + mp_digit *zp = DIGITS(mp) + du - 1; + + while(du > 1 && !*zp--) + --du; + + if(du == 1 && *zp == 0) + SIGN(mp) = MP_ZPOS; + + USED(mp) = du; + +} /* end s_mp_clamp() */ + + +/* }}} */ + +/* {{{ s_mp_exch(a, b) */ + +/* Exchange the data for a and b; (b, a) = (a, b) */ +void s_mp_exch(mp_int *a, mp_int *b) +{ + mp_int tmp; + + tmp = *a; + *a = *b; + *b = tmp; + +} /* end s_mp_exch() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Arithmetic helpers */ + +/* {{{ s_mp_lshd(mp, p) */ + +/* + Shift mp leftward by p digits, growing if needed, and zero-filling + the in-shifted digits at the right end. This is a convenient + alternative to multiplication by powers of the radix + */ + +mp_err s_mp_lshd(mp_int *mp, mp_size p) +{ + mp_err res; + mp_size pos; + mp_digit *dp; + int ix; + + if(p == 0) + return MP_OKAY; + + if((res = s_mp_pad(mp, USED(mp) + p)) != MP_OKAY) + return res; + + pos = USED(mp) - 1; + dp = DIGITS(mp); + + /* Shift all the significant figures over as needed */ + for(ix = pos - p; ix >= 0; ix--) + dp[ix + p] = dp[ix]; + + /* Fill the bottom digits with zeroes */ + for(ix = 0; ix < p; ix++) + dp[ix] = 0; + + return MP_OKAY; + +} /* end s_mp_lshd() */ + +/* }}} */ + +/* {{{ s_mp_rshd(mp, p) */ + +/* + Shift mp rightward by p digits. Maintains the invariant that + digits above the precision are all zero. Digits shifted off the + end are lost. Cannot fail. + */ + +void s_mp_rshd(mp_int *mp, mp_size p) +{ + mp_size ix; + mp_digit *dp; + + if(p == 0) + return; + + /* Shortcut when all digits are to be shifted off */ + if(p >= USED(mp)) { + s_mp_setz(DIGITS(mp), ALLOC(mp)); + USED(mp) = 1; + SIGN(mp) = MP_ZPOS; + return; + } + + /* Shift all the significant figures over as needed */ + dp = DIGITS(mp); + for(ix = p; ix < USED(mp); ix++) + dp[ix - p] = dp[ix]; + + /* Fill the top digits with zeroes */ + ix -= p; + while(ix < USED(mp)) + dp[ix++] = 0; + + /* Strip off any leading zeroes */ + s_mp_clamp(mp); + +} /* end s_mp_rshd() */ + +/* }}} */ + +/* {{{ s_mp_div_2(mp) */ + +/* Divide by two -- take advantage of radix properties to do it fast */ +void s_mp_div_2(mp_int *mp) +{ + s_mp_div_2d(mp, 1); + +} /* end s_mp_div_2() */ + +/* }}} */ + +/* {{{ s_mp_mul_2(mp) */ + +mp_err s_mp_mul_2(mp_int *mp) +{ + int ix; + mp_digit kin = 0, kout, *dp = DIGITS(mp); + mp_err res; + + /* Shift digits leftward by 1 bit */ + for(ix = 0; ix < USED(mp); ix++) { + kout = (dp[ix] >> (DIGIT_BIT - 1)) & 1; + dp[ix] = (dp[ix] << 1) | kin; + + kin = kout; + } + + /* Deal with rollover from last digit */ + if(kin) { + if(ix >= ALLOC(mp)) { + if((res = s_mp_grow(mp, ALLOC(mp) + 1)) != MP_OKAY) + return res; + dp = DIGITS(mp); + } + + dp[ix] = kin; + USED(mp) += 1; + } + + return MP_OKAY; + +} /* end s_mp_mul_2() */ + +/* }}} */ + +/* {{{ s_mp_mod_2d(mp, d) */ + +/* + Remainder the integer by 2^d, where d is a number of bits. This + amounts to a bitwise AND of the value, and does not require the full + division code + */ +void s_mp_mod_2d(mp_int *mp, mp_digit d) +{ + unsigned int ndig = (d / DIGIT_BIT), nbit = (d % DIGIT_BIT); + unsigned int ix; + mp_digit dmask, *dp = DIGITS(mp); + + if(ndig >= USED(mp)) + return; + + /* Flush all the bits above 2^d in its digit */ + dmask = (1 << nbit) - 1; + dp[ndig] &= dmask; + + /* Flush all digits above the one with 2^d in it */ + for(ix = ndig + 1; ix < USED(mp); ix++) + dp[ix] = 0; + + s_mp_clamp(mp); + +} /* end s_mp_mod_2d() */ + +/* }}} */ + +/* {{{ s_mp_mul_2d(mp, d) */ + +/* + Multiply by the integer 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value, and does not require the + full multiplication code. + */ +mp_err s_mp_mul_2d(mp_int *mp, mp_digit d) +{ + mp_err res; + mp_digit save, next, mask, *dp; + mp_size used; + int ix; + + if((res = s_mp_lshd(mp, d / DIGIT_BIT)) != MP_OKAY) + return res; + + dp = DIGITS(mp); used = USED(mp); + d %= DIGIT_BIT; + + mask = (1 << d) - 1; + + /* If the shift requires another digit, make sure we've got one to + work with */ + if((dp[used - 1] >> (DIGIT_BIT - d)) & mask) { + if((res = s_mp_grow(mp, used + 1)) != MP_OKAY) + return res; + dp = DIGITS(mp); + } + + /* Do the shifting... */ + save = 0; + for(ix = 0; ix < used; ix++) { + next = (dp[ix] >> (DIGIT_BIT - d)) & mask; + dp[ix] = (dp[ix] << d) | save; + save = next; + } + + /* If, at this point, we have a nonzero carryout into the next + digit, we'll increase the size by one digit, and store it... + */ + if(save) { + dp[used] = save; + USED(mp) += 1; + } + + s_mp_clamp(mp); + return MP_OKAY; + +} /* end s_mp_mul_2d() */ + +/* }}} */ + +/* {{{ s_mp_div_2d(mp, d) */ + +/* + Divide the integer by 2^d, where d is a number of bits. This + amounts to a bitwise shift of the value, and does not require the + full division code (used in Barrett reduction, see below) + */ +void s_mp_div_2d(mp_int *mp, mp_digit d) +{ + int ix; + mp_digit save, next, mask, *dp = DIGITS(mp); + + s_mp_rshd(mp, d / DIGIT_BIT); + d %= DIGIT_BIT; + + mask = (1 << d) - 1; + + save = 0; + for(ix = USED(mp) - 1; ix >= 0; ix--) { + next = dp[ix] & mask; + dp[ix] = (dp[ix] >> d) | (save << (DIGIT_BIT - d)); + save = next; + } + + s_mp_clamp(mp); + +} /* end s_mp_div_2d() */ + +/* }}} */ + +/* {{{ s_mp_norm(a, b) */ + +/* + s_mp_norm(a, b) + + Normalize a and b for division, where b is the divisor. In order + that we might make good guesses for quotient digits, we want the + leading digit of b to be at least half the radix, which we + accomplish by multiplying a and b by a constant. This constant is + returned (so that it can be divided back out of the remainder at the + end of the division process). + + We multiply by the smallest power of 2 that gives us a leading digit + at least half the radix. By choosing a power of 2, we simplify the + multiplication and division steps to simple shifts. + */ +mp_digit s_mp_norm(mp_int *a, mp_int *b) +{ + mp_digit t, d = 0; + + t = DIGIT(b, USED(b) - 1); + while(t < (RADIX / 2)) { + t <<= 1; + ++d; + } + + if(d != 0) { + s_mp_mul_2d(a, d); + s_mp_mul_2d(b, d); + } + + return d; + +} /* end s_mp_norm() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive digit arithmetic */ + +/* {{{ s_mp_add_d(mp, d) */ + +/* Add d to |mp| in place */ +mp_err s_mp_add_d(mp_int *mp, mp_digit d) /* unsigned digit addition */ +{ + mp_word w, k = 0; + mp_size ix = 1, used = USED(mp); + mp_digit *dp = DIGITS(mp); + + w = dp[0] + d; + dp[0] = ACCUM(w); + k = CARRYOUT(w); + + while(ix < used && k) { + w = dp[ix] + k; + dp[ix] = ACCUM(w); + k = CARRYOUT(w); + ++ix; + } + + if(k != 0) { + mp_err res; + + if((res = s_mp_pad(mp, USED(mp) + 1)) != MP_OKAY) + return res; + + DIGIT(mp, ix) = k; + } + + return MP_OKAY; + +} /* end s_mp_add_d() */ + +/* }}} */ + +/* {{{ s_mp_sub_d(mp, d) */ + +/* Subtract d from |mp| in place, assumes |mp| > d */ +mp_err s_mp_sub_d(mp_int *mp, mp_digit d) /* unsigned digit subtract */ +{ + mp_word w, b = 0; + mp_size ix = 1, used = USED(mp); + mp_digit *dp = DIGITS(mp); + + /* Compute initial subtraction */ + w = (RADIX + dp[0]) - d; + b = CARRYOUT(w) ? 0 : 1; + dp[0] = ACCUM(w); + + /* Propagate borrows leftward */ + while(b && ix < used) { + w = (RADIX + dp[ix]) - b; + b = CARRYOUT(w) ? 0 : 1; + dp[ix] = ACCUM(w); + ++ix; + } + + /* Remove leading zeroes */ + s_mp_clamp(mp); + + /* If we have a borrow out, it's a violation of the input invariant */ + if(b) + return MP_RANGE; + else + return MP_OKAY; + +} /* end s_mp_sub_d() */ + +/* }}} */ + +/* {{{ s_mp_mul_d(a, d) */ + +/* Compute a = a * d, single digit multiplication */ +mp_err s_mp_mul_d(mp_int *a, mp_digit d) +{ + mp_word w, k = 0; + mp_size ix, max; + mp_err res; + mp_digit *dp = DIGITS(a); + + /* + Single-digit multiplication will increase the precision of the + output by at most one digit. However, we can detect when this + will happen -- if the high-order digit of a, times d, gives a + two-digit result, then the precision of the result will increase; + otherwise it won't. We use this fact to avoid calling s_mp_pad() + unless absolutely necessary. + */ + max = USED(a); + w = dp[max - 1] * d; + if(CARRYOUT(w) != 0) { + if((res = s_mp_pad(a, max + 1)) != MP_OKAY) + return res; + dp = DIGITS(a); + } + + for(ix = 0; ix < max; ix++) { + w = (dp[ix] * d) + k; + dp[ix] = ACCUM(w); + k = CARRYOUT(w); + } + + /* If there is a precision increase, take care of it here; the above + test guarantees we have enough storage to do this safely. + */ + if(k) { + dp[max] = k; + USED(a) = max + 1; + } + + s_mp_clamp(a); + + return MP_OKAY; + +} /* end s_mp_mul_d() */ + +/* }}} */ + +/* {{{ s_mp_div_d(mp, d, r) */ + +/* + s_mp_div_d(mp, d, r) + + Compute the quotient mp = mp / d and remainder r = mp mod d, for a + single digit d. If r is null, the remainder will be discarded. + */ + +mp_err s_mp_div_d(mp_int *mp, mp_digit d, mp_digit *r) +{ + mp_word w = 0, t; + mp_int quot; + mp_err res; + mp_digit *dp = DIGITS(mp), *qp; + int ix; + + if(d == 0) + return MP_RANGE; + + /* Make room for the quotient */ + if((res = mp_init_size(", USED(mp))) != MP_OKAY) + return res; + + USED(") = USED(mp); /* so clamping will work below */ + qp = DIGITS("); + + /* Divide without subtraction */ + for(ix = USED(mp) - 1; ix >= 0; ix--) { + w = (w << DIGIT_BIT) | dp[ix]; + + if(w >= d) { + t = w / d; + w = w % d; + } else { + t = 0; + } + + qp[ix] = t; + } + + /* Deliver the remainder, if desired */ + if(r) + *r = w; + + s_mp_clamp("); + mp_exch(", mp); + mp_clear("); + + return MP_OKAY; + +} /* end s_mp_div_d() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive full arithmetic */ + +/* {{{ s_mp_add(a, b) */ + +/* Compute a = |a| + |b| */ +mp_err s_mp_add(mp_int *a, mp_int *b) /* magnitude addition */ +{ + mp_word w = 0; + mp_digit *pa, *pb; + mp_size ix, used = USED(b); + mp_err res; + + /* Make sure a has enough precision for the output value */ + if((used > USED(a)) && (res = s_mp_pad(a, used)) != MP_OKAY) + return res; + + /* + Add up all digits up to the precision of b. If b had initially + the same precision as a, or greater, we took care of it by the + padding step above, so there is no problem. If b had initially + less precision, we'll have to make sure the carry out is duly + propagated upward among the higher-order digits of the sum. + */ + pa = DIGITS(a); + pb = DIGITS(b); + for(ix = 0; ix < used; ++ix) { + w += *pa + *pb++; + *pa++ = ACCUM(w); + w = CARRYOUT(w); + } + + /* If we run out of 'b' digits before we're actually done, make + sure the carries get propagated upward... + */ + used = USED(a); + while(w && ix < used) { + w += *pa; + *pa++ = ACCUM(w); + w = CARRYOUT(w); + ++ix; + } + + /* If there's an overall carry out, increase precision and include + it. We could have done this initially, but why touch the memory + allocator unless we're sure we have to? + */ + if(w) { + if((res = s_mp_pad(a, used + 1)) != MP_OKAY) + return res; + + DIGIT(a, ix) = w; /* pa may not be valid after s_mp_pad() call */ + } + + return MP_OKAY; + +} /* end s_mp_add() */ + +/* }}} */ + +/* {{{ s_mp_sub(a, b) */ + +/* Compute a = |a| - |b|, assumes |a| >= |b| */ +mp_err s_mp_sub(mp_int *a, mp_int *b) /* magnitude subtract */ +{ + mp_word w = 0; + mp_digit *pa, *pb; + mp_size ix, used = USED(b); + + /* + Subtract and propagate borrow. Up to the precision of b, this + accounts for the digits of b; after that, we just make sure the + carries get to the right place. This saves having to pad b out to + the precision of a just to make the loops work right... + */ + pa = DIGITS(a); + pb = DIGITS(b); + + for(ix = 0; ix < used; ++ix) { + w = (RADIX + *pa) - w - *pb++; + *pa++ = ACCUM(w); + w = CARRYOUT(w) ? 0 : 1; + } + + used = USED(a); + while(ix < used) { + w = RADIX + *pa - w; + *pa++ = ACCUM(w); + w = CARRYOUT(w) ? 0 : 1; + ++ix; + } + + /* Clobber any leading zeroes we created */ + s_mp_clamp(a); + + /* + If there was a borrow out, then |b| > |a| in violation + of our input invariant. We've already done the work, + but we'll at least complain about it... + */ + if(w) + return MP_RANGE; + else + return MP_OKAY; + +} /* end s_mp_sub() */ + +/* }}} */ + +/* {{{ s_mp_mul(a, b) */ + +/* Compute a = |a| * |b| */ +mp_err s_mp_mul(mp_int *a, mp_int *b) +{ + mp_word w, k = 0; + mp_int tmp; + mp_err res; + mp_size ix, jx, ua = USED(a), ub = USED(b); + mp_digit *pa, *pb, *pt, *pbt; + + if((res = mp_init_size(&tmp, ua + ub)) != MP_OKAY) + return res; + + /* This has the effect of left-padding with zeroes... */ + USED(&tmp) = ua + ub; + + /* We're going to need the base value each iteration */ + pbt = DIGITS(&tmp); + + /* Outer loop: Digits of b */ + + pb = DIGITS(b); + for(ix = 0; ix < ub; ++ix, ++pb) { + if(*pb == 0) + continue; + + /* Inner product: Digits of a */ + pa = DIGITS(a); + for(jx = 0; jx < ua; ++jx, ++pa) { + pt = pbt + ix + jx; + w = *pb * *pa + k + *pt; + *pt = ACCUM(w); + k = CARRYOUT(w); + } + + pbt[ix + jx] = k; + k = 0; + } + + s_mp_clamp(&tmp); + s_mp_exch(&tmp, a); + + mp_clear(&tmp); + + return MP_OKAY; + +} /* end s_mp_mul() */ + +/* }}} */ + +/* {{{ s_mp_kmul(a, b, out, len) */ + +#if 0 +void s_mp_kmul(mp_digit *a, mp_digit *b, mp_digit *out, mp_size len) +{ + mp_word w, k = 0; + mp_size ix, jx; + mp_digit *pa, *pt; + + for(ix = 0; ix < len; ++ix, ++b) { + if(*b == 0) + continue; + + pa = a; + for(jx = 0; jx < len; ++jx, ++pa) { + pt = out + ix + jx; + w = *b * *pa + k + *pt; + *pt = ACCUM(w); + k = CARRYOUT(w); + } + + out[ix + jx] = k; + k = 0; + } + +} /* end s_mp_kmul() */ +#endif + +/* }}} */ + +/* {{{ s_mp_sqr(a) */ + +/* + Computes the square of a, in place. This can be done more + efficiently than a general multiplication, because many of the + computation steps are redundant when squaring. The inner product + step is a bit more complicated, but we save a fair number of + iterations of the multiplication loop. + */ +#if MP_SQUARE +mp_err s_mp_sqr(mp_int *a) +{ + mp_word w, k = 0; + mp_int tmp; + mp_err res; + mp_size ix, jx, kx, used = USED(a); + mp_digit *pa1, *pa2, *pt, *pbt; + + if((res = mp_init_size(&tmp, 2 * used)) != MP_OKAY) + return res; + + /* Left-pad with zeroes */ + USED(&tmp) = 2 * used; + + /* We need the base value each time through the loop */ + pbt = DIGITS(&tmp); + + pa1 = DIGITS(a); + for(ix = 0; ix < used; ++ix, ++pa1) { + if(*pa1 == 0) + continue; + + w = DIGIT(&tmp, ix + ix) + (*pa1 * *pa1); + + pbt[ix + ix] = ACCUM(w); + k = CARRYOUT(w); + + /* + The inner product is computed as: + + (C, S) = t[i,j] + 2 a[i] a[j] + C + + This can overflow what can be represented in an mp_word, and + since C arithmetic does not provide any way to check for + overflow, we have to check explicitly for overflow conditions + before they happen. + */ + for(jx = ix + 1, pa2 = DIGITS(a) + jx; jx < used; ++jx, ++pa2) { + mp_word u = 0, v; + + /* Store this in a temporary to avoid indirections later */ + pt = pbt + ix + jx; + + /* Compute the multiplicative step */ + w = *pa1 * *pa2; + + /* If w is more than half MP_WORD_MAX, the doubling will + overflow, and we need to record a carry out into the next + word */ + u = (w >> (MP_WORD_BIT - 1)) & 1; + + /* Double what we've got, overflow will be ignored as defined + for C arithmetic (we've already noted if it is to occur) + */ + w *= 2; + + /* Compute the additive step */ + v = *pt + k; + + /* If we do not already have an overflow carry, check to see + if the addition will cause one, and set the carry out if so + */ + u |= ((MP_WORD_MAX - v) < w); + + /* Add in the rest, again ignoring overflow */ + w += v; + + /* Set the i,j digit of the output */ + *pt = ACCUM(w); + + /* Save carry information for the next iteration of the loop. + This is why k must be an mp_word, instead of an mp_digit */ + k = CARRYOUT(w) | (u << DIGIT_BIT); + + } /* for(jx ...) */ + + /* Set the last digit in the cycle and reset the carry */ + k = DIGIT(&tmp, ix + jx) + k; + pbt[ix + jx] = ACCUM(k); + k = CARRYOUT(k); + + /* If we are carrying out, propagate the carry to the next digit + in the output. This may cascade, so we have to be somewhat + circumspect -- but we will have enough precision in the output + that we won't overflow + */ + kx = 1; + while(k) { + k = pbt[ix + jx + kx] + 1; + pbt[ix + jx + kx] = ACCUM(k); + k = CARRYOUT(k); + ++kx; + } + } /* for(ix ...) */ + + s_mp_clamp(&tmp); + s_mp_exch(&tmp, a); + + mp_clear(&tmp); + + return MP_OKAY; + +} /* end s_mp_sqr() */ +#endif + +/* }}} */ + +/* {{{ s_mp_div(a, b) */ + +/* + s_mp_div(a, b) + + Compute a = a / b and b = a mod b. Assumes b > a. + */ + +mp_err s_mp_div(mp_int *a, mp_int *b) +{ + mp_int quot, rem, t; + mp_word q; + mp_err res; + mp_digit d; + int ix; + + if(mp_cmp_z(b) == 0) + return MP_RANGE; + + /* Shortcut if b is power of two */ + if((ix = s_mp_ispow2(b)) >= 0) { + mp_copy(a, b); /* need this for remainder */ + s_mp_div_2d(a, (mp_digit)ix); + s_mp_mod_2d(b, (mp_digit)ix); + + return MP_OKAY; + } + + /* Allocate space to store the quotient */ + if((res = mp_init_size(", USED(a))) != MP_OKAY) + return res; + + /* A working temporary for division */ + if((res = mp_init_size(&t, USED(a))) != MP_OKAY) + goto T; + + /* Allocate space for the remainder */ + if((res = mp_init_size(&rem, USED(a))) != MP_OKAY) + goto REM; + + /* Normalize to optimize guessing */ + d = s_mp_norm(a, b); + + /* Perform the division itself...woo! */ + ix = USED(a) - 1; + + while(ix >= 0) { + /* Find a partial substring of a which is at least b */ + while(s_mp_cmp(&rem, b) < 0 && ix >= 0) { + if((res = s_mp_lshd(&rem, 1)) != MP_OKAY) + goto CLEANUP; + + if((res = s_mp_lshd(", 1)) != MP_OKAY) + goto CLEANUP; + + DIGIT(&rem, 0) = DIGIT(a, ix); + s_mp_clamp(&rem); + --ix; + } + + /* If we didn't find one, we're finished dividing */ + if(s_mp_cmp(&rem, b) < 0) + break; + + /* Compute a guess for the next quotient digit */ + q = DIGIT(&rem, USED(&rem) - 1); + if(q <= DIGIT(b, USED(b) - 1) && USED(&rem) > 1) + q = (q << DIGIT_BIT) | DIGIT(&rem, USED(&rem) - 2); + + q /= DIGIT(b, USED(b) - 1); + + /* The guess can be as much as RADIX + 1 */ + if(q >= RADIX) + q = RADIX - 1; + + /* See what that multiplies out to */ + mp_copy(b, &t); + if((res = s_mp_mul_d(&t, q)) != MP_OKAY) + goto CLEANUP; + + /* + If it's too big, back it off. We should not have to do this + more than once, or, in rare cases, twice. Knuth describes a + method by which this could be reduced to a maximum of once, but + I didn't implement that here. + */ + while(s_mp_cmp(&t, &rem) > 0) { + --q; + s_mp_sub(&t, b); + } + + /* At this point, q should be the right next digit */ + if((res = s_mp_sub(&rem, &t)) != MP_OKAY) + goto CLEANUP; + + /* + Include the digit in the quotient. We allocated enough memory + for any quotient we could ever possibly get, so we should not + have to check for failures here + */ + DIGIT(", 0) = q; + } + + /* Denormalize remainder */ + if(d != 0) + s_mp_div_2d(&rem, d); + + s_mp_clamp("); + s_mp_clamp(&rem); + + /* Copy quotient back to output */ + s_mp_exch(", a); + + /* Copy remainder back to output */ + s_mp_exch(&rem, b); + +CLEANUP: + mp_clear(&rem); +REM: + mp_clear(&t); +T: + mp_clear("); + + return res; + +} /* end s_mp_div() */ + +/* }}} */ + +/* {{{ s_mp_2expt(a, k) */ + +mp_err s_mp_2expt(mp_int *a, mp_digit k) +{ + mp_err res; + mp_size dig, bit; + + dig = k / DIGIT_BIT; + bit = k % DIGIT_BIT; + + mp_zero(a); + if((res = s_mp_pad(a, dig + 1)) != MP_OKAY) + return res; + + DIGIT(a, dig) |= (1 << bit); + + return MP_OKAY; + +} /* end s_mp_2expt() */ + +/* }}} */ + +/* {{{ s_mp_reduce(x, m, mu) */ + +/* + Compute Barrett reduction, x (mod m), given a precomputed value for + mu = b^2k / m, where b = RADIX and k = #digits(m). This should be + faster than straight division, when many reductions by the same + value of m are required (such as in modular exponentiation). This + can nearly halve the time required to do modular exponentiation, + as compared to using the full integer divide to reduce. + + This algorithm was derived from the _Handbook of Applied + Cryptography_ by Menezes, Oorschot and VanStone, Ch. 14, + pp. 603-604. + */ + +mp_err s_mp_reduce(mp_int *x, mp_int *m, mp_int *mu) +{ + mp_int q; + mp_err res; + mp_size um = USED(m); + + if((res = mp_init_copy(&q, x)) != MP_OKAY) + return res; + + s_mp_rshd(&q, um - 1); /* q1 = x / b^(k-1) */ + s_mp_mul(&q, mu); /* q2 = q1 * mu */ + s_mp_rshd(&q, um + 1); /* q3 = q2 / b^(k+1) */ + + /* x = x mod b^(k+1), quick (no division) */ + s_mp_mod_2d(x, DIGIT_BIT * (um + 1)); + + /* q = q * m mod b^(k+1), quick (no division) */ + s_mp_mul(&q, m); + s_mp_mod_2d(&q, DIGIT_BIT * (um + 1)); + + /* x = x - q */ + if((res = mp_sub(x, &q, x)) != MP_OKAY) + goto CLEANUP; + + /* If x < 0, add b^(k+1) to it */ + if(mp_cmp_z(x) < 0) { + mp_set(&q, 1); + if((res = s_mp_lshd(&q, um + 1)) != MP_OKAY) + goto CLEANUP; + if((res = mp_add(x, &q, x)) != MP_OKAY) + goto CLEANUP; + } + + /* Back off if it's too big */ + while(mp_cmp(x, m) >= 0) { + if((res = s_mp_sub(x, m)) != MP_OKAY) + break; + } + + CLEANUP: + mp_clear(&q); + + return res; + +} /* end s_mp_reduce() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive comparisons */ + +/* {{{ s_mp_cmp(a, b) */ + +/* Compare |a| <=> |b|, return 0 if equal, <0 if a<b, >0 if a>b */ +int s_mp_cmp(mp_int *a, mp_int *b) +{ + mp_size ua = USED(a), ub = USED(b); + + if(ua > ub) + return MP_GT; + else if(ua < ub) + return MP_LT; + else { + int ix = ua - 1; + mp_digit *ap = DIGITS(a) + ix, *bp = DIGITS(b) + ix; + + while(ix >= 0) { + if(*ap > *bp) + return MP_GT; + else if(*ap < *bp) + return MP_LT; + + --ap; --bp; --ix; + } + + return MP_EQ; + } + +} /* end s_mp_cmp() */ + +/* }}} */ + +/* {{{ s_mp_cmp_d(a, d) */ + +/* Compare |a| <=> d, return 0 if equal, <0 if a<d, >0 if a>d */ +int s_mp_cmp_d(mp_int *a, mp_digit d) +{ + mp_size ua = USED(a); + mp_digit *ap = DIGITS(a); + + if(ua > 1) + return MP_GT; + + if(*ap < d) + return MP_LT; + else if(*ap > d) + return MP_GT; + else + return MP_EQ; + +} /* end s_mp_cmp_d() */ + +/* }}} */ + +/* {{{ s_mp_ispow2(v) */ + +/* + Returns -1 if the value is not a power of two; otherwise, it returns + k such that v = 2^k, i.e. lg(v). + */ +int s_mp_ispow2(mp_int *v) +{ + mp_digit d, *dp; + mp_size uv = USED(v); + int extra = 0, ix; + + d = DIGIT(v, uv - 1); /* most significant digit of v */ + + while(d && ((d & 1) == 0)) { + d >>= 1; + ++extra; + } + + if(d == 1) { + ix = uv - 2; + dp = DIGITS(v) + ix; + + while(ix >= 0) { + if(*dp) + return -1; /* not a power of two */ + + --dp; --ix; + } + + return ((uv - 1) * DIGIT_BIT) + extra; + } + + return -1; + +} /* end s_mp_ispow2() */ + +/* }}} */ + +/* {{{ s_mp_ispow2d(d) */ + +int s_mp_ispow2d(mp_digit d) +{ + int pow = 0; + + while((d & 1) == 0) { + ++pow; d >>= 1; + } + + if(d == 1) + return pow; + + return -1; + +} /* end s_mp_ispow2d() */ + +/* }}} */ + +/* }}} */ + +/* {{{ Primitive I/O helpers */ + +/* {{{ s_mp_tovalue(ch, r) */ + +/* + Convert the given character to its digit value, in the given radix. + If the given character is not understood in the given radix, -1 is + returned. Otherwise the digit's numeric value is returned. + + The results will be odd if you use a radix < 2 or > 62, you are + expected to know what you're up to. + */ +int s_mp_tovalue(char ch, int r) +{ + int val, xch; + + if(r > 36) + xch = ch; + else + xch = toupper(ch); + + if(isdigit(xch)) + val = xch - '0'; + else if(isupper(xch)) + val = xch - 'A' + 10; + else if(islower(xch)) + val = xch - 'a' + 36; + else if(xch == '+') + val = 62; + else if(xch == '/') + val = 63; + else + return -1; + + if(val < 0 || val >= r) + return -1; + + return val; + +} /* end s_mp_tovalue() */ + +/* }}} */ + +/* {{{ s_mp_todigit(val, r, low) */ + +/* + Convert val to a radix-r digit, if possible. If val is out of range + for r, returns zero. Otherwise, returns an ASCII character denoting + the value in the given radix. + + The results may be odd if you use a radix < 2 or > 64, you are + expected to know what you're doing. + */ + +char s_mp_todigit(int val, int r, int low) +{ + char ch; + + if(val < 0 || val >= r) + return 0; + + ch = s_dmap_1[val]; + + if(r <= 36 && low) + ch = tolower(ch); + + return ch; + +} /* end s_mp_todigit() */ + +/* }}} */ + +/* {{{ s_mp_outlen(bits, radix) */ + +/* + Return an estimate for how long a string is needed to hold a radix + r representation of a number with 'bits' significant bits. + + Does not include space for a sign or a NUL terminator. + */ +int s_mp_outlen(int bits, int r) +{ + return (int)((double)bits * LOG_V_2(r) + 0.5); + +} /* end s_mp_outlen() */ + +/* }}} */ + +/* }}} */ + +/*------------------------------------------------------------------------*/ +/* HERE THERE BE DRAGONS */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mpi/mpi.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,221 @@ +/* + mpi.h + + by Michael J. Fromberger <http://www.dartmouth.edu/~sting/> + Copyright (C) 1998 Michael J. Fromberger, All Rights Reserved + + Arbitrary precision integer arithmetic library + + $Id: mpi.h 14563 2005-11-29 23:31:40Z taliesein $ + */ + +#ifndef _H_MPI_ +#define _H_MPI_ + +#include "mpi-config.h" + +#if MP_DEBUG +#undef MP_IOFUNC +#define MP_IOFUNC 1 +#endif + +#if MP_IOFUNC +#include <stdio.h> +#include <ctype.h> +#endif + +#include <limits.h> + +#define MP_NEG 1 +#define MP_ZPOS 0 + +/* Included for compatibility... */ +#define NEG MP_NEG +#define ZPOS MP_ZPOS + +#define MP_OKAY 0 /* no error, all is well */ +#define MP_YES 0 /* yes (boolean result) */ +#define MP_NO -1 /* no (boolean result) */ +#define MP_MEM -2 /* out of memory */ +#define MP_RANGE -3 /* argument out of range */ +#define MP_BADARG -4 /* invalid parameter */ +#define MP_UNDEF -5 /* answer is undefined */ +#define MP_LAST_CODE MP_UNDEF + +#include "mpi-types.h" + +/* Included for compatibility... */ +#define DIGIT_BIT MP_DIGIT_BIT +#define DIGIT_MAX MP_DIGIT_MAX + +/* Macros for accessing the mp_int internals */ +#define SIGN(MP) ((MP)->sign) +#define USED(MP) ((MP)->used) +#define ALLOC(MP) ((MP)->alloc) +#define DIGITS(MP) ((MP)->dp) +#define DIGIT(MP,N) (MP)->dp[(N)] + +#if MP_ARGCHK == 1 +#define ARGCHK(X,Y) {if(!(X)){return (Y);}} +#elif MP_ARGCHK == 2 +#include <assert.h> +#define ARGCHK(X,Y) assert(X) +#else +#define ARGCHK(X,Y) /* */ +#endif + +/* This defines the maximum I/O base (minimum is 2) */ +#define MAX_RADIX 64 + +typedef struct { + mp_sign sign; /* sign of this quantity */ + mp_size alloc; /* how many digits allocated */ + mp_size used; /* how many digits used */ + mp_digit *dp; /* the digits themselves */ +} mp_int; + +/*------------------------------------------------------------------------*/ +/* Default precision */ + +unsigned int mp_get_prec(void); +void mp_set_prec(unsigned int prec); + +/*------------------------------------------------------------------------*/ +/* Memory management */ + +mp_err mp_init(mp_int *mp); +mp_err mp_init_array(mp_int mp[], int count); +mp_err mp_init_size(mp_int *mp, mp_size prec); +mp_err mp_init_copy(mp_int *mp, mp_int *from); +mp_err mp_copy(mp_int *from, mp_int *to); +void mp_exch(mp_int *mp1, mp_int *mp2); +void mp_clear(mp_int *mp); +void mp_clear_array(mp_int mp[], int count); +void mp_zero(mp_int *mp); +void mp_set(mp_int *mp, mp_digit d); +mp_err mp_set_int(mp_int *mp, long z); + +/*------------------------------------------------------------------------*/ +/* Single digit arithmetic */ + +mp_err mp_add_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_sub_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_d(mp_int *a, mp_digit d, mp_int *b); +mp_err mp_mul_2(mp_int *a, mp_int *c); +mp_err mp_div_d(mp_int *a, mp_digit d, mp_int *q, mp_digit *r); +mp_err mp_div_2(mp_int *a, mp_int *c); +mp_err mp_expt_d(mp_int *a, mp_digit d, mp_int *c); + +/*------------------------------------------------------------------------*/ +/* Sign manipulations */ + +mp_err mp_abs(mp_int *a, mp_int *b); +mp_err mp_neg(mp_int *a, mp_int *b); + +/*------------------------------------------------------------------------*/ +/* Full arithmetic */ + +mp_err mp_add(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_sub(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_mul(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_mul_2d(mp_int *a, mp_digit d, mp_int *c); +#if MP_SQUARE +mp_err mp_sqr(mp_int *a, mp_int *b); +#else +#define mp_sqr(a, b) mp_mul(a, a, b) +#endif +mp_err mp_div(mp_int *a, mp_int *b, mp_int *q, mp_int *r); +mp_err mp_div_2d(mp_int *a, mp_digit d, mp_int *q, mp_int *r); +mp_err mp_expt(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_2expt(mp_int *a, mp_digit k); +mp_err mp_sqrt(mp_int *a, mp_int *b); + +/*------------------------------------------------------------------------*/ +/* Modular arithmetic */ + +#if MP_MODARITH +mp_err mp_mod(mp_int *a, mp_int *m, mp_int *c); +mp_err mp_mod_d(mp_int *a, mp_digit d, mp_digit *c); +mp_err mp_addmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_submod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_mulmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +#if MP_SQUARE +mp_err mp_sqrmod(mp_int *a, mp_int *m, mp_int *c); +#else +#define mp_sqrmod(a, m, c) mp_mulmod(a, a, m, c) +#endif +mp_err mp_exptmod(mp_int *a, mp_int *b, mp_int *m, mp_int *c); +mp_err mp_exptmod_d(mp_int *a, mp_digit d, mp_int *m, mp_int *c); +#endif /* MP_MODARITH */ + +/*------------------------------------------------------------------------*/ +/* Comparisons */ + +int mp_cmp_z(mp_int *a); +int mp_cmp_d(mp_int *a, mp_digit d); +int mp_cmp(mp_int *a, mp_int *b); +int mp_cmp_mag(mp_int *a, mp_int *b); +int mp_cmp_int(mp_int *a, long z); +int mp_isodd(mp_int *a); +int mp_iseven(mp_int *a); + +/*------------------------------------------------------------------------*/ +/* Number theoretic */ + +#if MP_NUMTH +mp_err mp_gcd(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_lcm(mp_int *a, mp_int *b, mp_int *c); +mp_err mp_xgcd(mp_int *a, mp_int *b, mp_int *g, mp_int *x, mp_int *y); +mp_err mp_invmod(mp_int *a, mp_int *m, mp_int *c); +#endif /* end MP_NUMTH */ + +/*------------------------------------------------------------------------*/ +/* Input and output */ + +#if MP_IOFUNC +void mp_print(mp_int *mp, FILE *ofp); +#endif /* end MP_IOFUNC */ + +/*------------------------------------------------------------------------*/ +/* Base conversion */ + +#define BITS 1 +#define BYTES CHAR_BIT + +mp_err mp_read_signed_bin(mp_int *mp, unsigned char *str, int len); +int mp_signed_bin_size(mp_int *mp); +mp_err mp_to_signed_bin(mp_int *mp, unsigned char *str); + +mp_err mp_read_unsigned_bin(mp_int *mp, unsigned char *str, int len); +int mp_unsigned_bin_size(mp_int *mp); +mp_err mp_to_unsigned_bin(mp_int *mp, unsigned char *str); + +int mp_count_bits(mp_int *mp); + +#if MP_COMPAT_MACROS +#define mp_read_raw(mp, str, len) mp_read_signed_bin((mp), (str), (len)) +#define mp_raw_size(mp) mp_signed_bin_size(mp) +#define mp_toraw(mp, str) mp_to_signed_bin((mp), (str)) +#define mp_read_mag(mp, str, len) mp_read_unsigned_bin((mp), (str), (len)) +#define mp_mag_size(mp) mp_unsigned_bin_size(mp) +#define mp_tomag(mp, str) mp_to_unsigned_bin((mp), (str)) +#endif + +mp_err mp_read_radix(mp_int *mp, unsigned char *str, int radix); +int mp_radix_size(mp_int *mp, int radix); +int mp_value_radix_size(int num, int qty, int radix); +mp_err mp_toradix(mp_int *mp, unsigned char *str, int radix); + +int mp_char2value(char ch, int r); + +#define mp_tobinary(M, S) mp_toradix((M), (S), 2) +#define mp_tooctal(M, S) mp_toradix((M), (S), 8) +#define mp_todecimal(M, S) mp_toradix((M), (S), 10) +#define mp_tohex(M, S) mp_toradix((M), (S), 16) + +/*------------------------------------------------------------------------*/ +/* Error strings */ + +const char *mp_strerror(mp_err ec); + +#endif /* end _H_MPI_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_channel.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,366 @@ + +/* + 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 +*/ + +#ifndef _MW_CHANNEL_H +#define _MW_CHANNEL_H + + +#include <time.h> +#include "mw_common.h" + + +/** @file mw_channel.h + +Life-cycle of an outgoing channel: + +1: mwChannel_new is called. If there is a channel in the outgoing +collection in state NEW, then it is returned. Otherwise, a channel +is allocated, assigned a unique outgoing id, marked as NEW, and +returned. + +2: channel is set to INIT status (effectively earmarking it as in- +use). fields on the channel can then be set as necessary to +prepare it for creation. + +3: mwChannel_create is called. The channel is marked to WAIT status +and a message is sent to the server. The channel is also marked as +inactive as of that moment. + +4: the channel is accepted (step 5) or rejected (step 7) + +5: an accept message is received from the server, and the channel +is marked as OPEN, and the inactive mark is removed. And messages +in the in or out queues for that channel are processed. The channel +is now ready to be used. + +6: data is sent and received over the channel + +7: the channel is closed either by receipt of a close message or by +local action. If by local action, then a close message is sent to +the server. The channel is cleaned up, its queues dumped, and it +is set to NEW status to await re-use. + +Life-cycle of an incoming channel: + +1: a channel create message is received. A channel is allocated and +given an id matching the message. It is placed in status WAIT, and +marked as inactive as of that moment. The service matching that +channel is alerted of the incoming creation request. + +2: the service can either accept (step 3) or reject (step 5) the +channel + +3: mwChannel_accept is called. The channel is marked as OPEN, and +an accept message is sent to the server. And messages in the in or +out queues for that channel are processed. The channel is now ready +to be used. + +4: data is sent and received over the channel + +5: The channel is closed either by receipt of a close message or by +local action. If by local action, then a close message is sent to +the server. The channel is cleaned up, its queues dumped, and it +is deallocated. */ + + +/* place-holders */ +struct mwCipherInstance; +struct mwMsgChannelAccept; +struct mwMsgChannelCreate; +struct mwMsgChannelDestroy; +struct mwMsgChannelSend; +struct mwService; +struct mwSession; + + + +/** @struct mwChannel + Represents a channel to a service */ +struct mwChannel; + + +/** @struct mwChannelSet + Collection of channels */ +struct mwChannelSet; + + +/** special ID indicating the master channel */ +#define MW_MASTER_CHANNEL_ID 0x00000000 + + +/** non-zero if a channel id appears to be that of an outgoing channel */ +#define mwChannel_idIsOutgoing(id) \ + (! (0x80000000 & (id))) + +/** non-zero if a channel id appears to be that of an incoming channel */ +#define mwChannel_idIsIncoming(id) \ + (! mwChannel_idIsOutgoing(id)) + +/** non-zero if a channel appears to be an outgoing channel */ +#define mwChannel_isOutgoing(chan) \ + mwChannel_idIsOutgoing(mwChannel_getId(chan)) + +/** non-zero if a channel appears to be an incoming channel */ +#define mwChannel_isIncoming(chan) \ + mwChannel_idIsIncoming(mwChannel_getId(chan)) + + +/** channel status */ +enum mwChannelState { + mwChannel_NEW, /**< channel is newly allocated, in the pool */ + mwChannel_INIT, /**< channel is being prepared, out of the pool */ + mwChannel_WAIT, /**< channel is waiting for accept */ + mwChannel_OPEN, /**< channel is accepted and open */ + mwChannel_DESTROY, /**< channel is being destroyed */ + mwChannel_ERROR, /**< channel is being destroyed due to error */ + mwChannel_UNKNOWN, /**< unknown state, or error determining state */ +}; + + +#define mwChannel_isState(chan, state) \ + (mwChannel_getState(chan) == (state)) + + +/** channel statistic fields. + @see mwChannel_getStatistic */ +enum mwChannelStatField { + mwChannelStat_MSG_SENT, /**< total send-on-chan messages sent */ + mwChannelStat_MSG_RECV, /**< total send-on-chan messages received */ + mwChannelStat_U_BYTES_SENT, /**< total bytes sent, pre-encryption */ + mwChannelStat_U_BYTES_RECV, /**< total bytes received, post-decryption */ + mwChannelStat_OPENED_AT, /**< time when channel was opened */ + mwChannelStat_CLOSED_AT, /**< time when channel was closed */ +}; + + +/** @enum mwEncryptPolicy + + Policy for a channel, dictating what sort of encryption should be + used, if any, and when. +*/ +enum mwEncryptPolicy { + mwEncrypt_NONE = 0x0000, /**< encrypt none */ + mwEncrypt_WHATEVER = 0x0001, /**< encrypt whatever you want */ + mwEncrypt_ALL = 0x0002, /**< encrypt all, any cipher */ + mwEncrypt_RC2_40 = 0x1000, /**< encrypt all, RC2/40 cipher */ + mwEncrypt_RC2_128 = 0x2000, /**< encrypt all, RC2/128 cipher */ +}; + + +/** Allocate and initialize a channel set for a session */ +struct mwChannelSet *mwChannelSet_new(struct mwSession *); + + +/** Clear and deallocate a channel set. Closes, clears, and frees all + contained channels. */ +void mwChannelSet_free(struct mwChannelSet *); + + +/** Create an incoming channel with the given channel id. Channel's state + will be set to WAIT. Primarily for use in mw_session */ +struct mwChannel *mwChannel_newIncoming(struct mwChannelSet *, guint32 id); + + +/** Create an outgoing channel. Its channel ID will be generated by + the owning channel set. Channel's state will be set to INIT */ +struct mwChannel *mwChannel_newOutgoing(struct mwChannelSet *); + + +/** Obtain a reference to a channel by its id. + @returns the channel matching chan, or NULL */ +struct mwChannel *mwChannel_find(struct mwChannelSet *cs, guint32 chan); + + +/** get the ID for a channel. 0x00 indicates an error, as that is not + a permissible value */ +guint32 mwChannel_getId(struct mwChannel *); + + +/** get the session for a channel. */ +struct mwSession *mwChannel_getSession(struct mwChannel *); + + +/** get the ID of the service for a channel. This may be 0x00 for NEW + channels */ +guint32 mwChannel_getServiceId(struct mwChannel *); + + +/** get the service for a channel. This may be NULL for NEW + channels */ +struct mwService *mwChannel_getService(struct mwChannel *); + + +/** associate a channel with an owning service */ +void mwChannel_setService(struct mwChannel *chan, struct mwService *srvc); + + +/** get service-specific data. This is for use by service + implementations to easily associate information with the + channel */ +gpointer mwChannel_getServiceData(struct mwChannel *chan); + + +/** set service-specific data. This is for use by service + implementations to easily associate information with the + channel */ +void mwChannel_setServiceData(struct mwChannel *chan, + gpointer data, GDestroyNotify clean); + + +void mwChannel_removeServiceData(struct mwChannel *chan); + + +guint32 mwChannel_getProtoType(struct mwChannel *chan); + + +void mwChannel_setProtoType(struct mwChannel *chan, guint32 proto_type); + + +guint32 mwChannel_getProtoVer(struct mwChannel *chan); + + +void mwChannel_setProtoVer(struct mwChannel *chan, guint32 proto_ver); + + +/** Channel encryption policy. + + Cannot currently be set, used internally to automatically + negotiate ciphers. Future revisions may allow this to be specified + in a new channel to dictate channel encryption. + + @see enum mwEncryptPolicy +*/ +guint16 mwChannel_getEncryptPolicy(struct mwChannel *chan); + + +guint32 mwChannel_getOptions(struct mwChannel *chan); + + +void mwChannel_setOptions(struct mwChannel *chan, guint32 options); + + +/** User at the other end of the channel. The target user for outgoing + channels, the creator for incoming channels */ +struct mwLoginInfo *mwChannel_getUser(struct mwChannel *chan); + + +/** direct reference to the create addtl information for a channel */ +struct mwOpaque *mwChannel_getAddtlCreate(struct mwChannel *); + + +/** direct reference to the accept addtl information for a channel */ +struct mwOpaque *mwChannel_getAddtlAccept(struct mwChannel *); + + +/** automatically adds instances of all ciphers in the session to the + list of supported ciphers for a channel */ +void mwChannel_populateSupportedCipherInstances(struct mwChannel *chan); + + +/** add a cipher instance to a channel's list of supported + ciphers. Channel must be NEW. */ +void mwChannel_addSupportedCipherInstance(struct mwChannel *chan, + struct mwCipherInstance *ci); + + +/** the list of supported ciphers for a channel. This list will be + empty once a cipher has been selected for the channel */ +GList *mwChannel_getSupportedCipherInstances(struct mwChannel *chan); + + +/** select a cipher instance for a channel. A NULL instance indicates + that no encryption should be used. */ +void mwChannel_selectCipherInstance(struct mwChannel *chan, + struct mwCipherInstance *ci); + + +/** get the state of a channel */ +enum mwChannelState mwChannel_getState(struct mwChannel *); + + +/** obtain the value for a statistic field as a gpointer */ +gpointer mwChannel_getStatistic(struct mwChannel *chan, + enum mwChannelStatField stat); + + +/** Formally open a channel. + + For outgoing channels: instruct the session to send a channel + create message to the server, and to mark the channel (which must + be in INIT status) as being in WAIT status. + + For incoming channels: configures the channel according to options + in the channel create message. Marks the channel as being in WAIT + status +*/ +int mwChannel_create(struct mwChannel *chan); + + +/** Formally accept an incoming channel. Instructs the session to send + a channel accept message to the server, and to mark the channel as + being OPEN. */ +int mwChannel_accept(struct mwChannel *chan); + + +/** Destroy a channel. Sends a channel-destroy message to the server, + and perform cleanup to remove the channel. + + @param chan the channel to destroy + @param reason the reason code for closing the channel + @param data optional additional information +*/ +int mwChannel_destroy(struct mwChannel *chan, guint32 reason, + struct mwOpaque *data); + + +/** Compose a send-on-channel message, encrypt it as per the channel's + specification, and send it */ +int mwChannel_send(struct mwChannel *chan, guint32 msg_type, + struct mwOpaque *msg); + + +/** Compose a send-on-channel message, and if encrypt is TRUE, encrypt + it as per the channel's specification, and send it */ +int mwChannel_sendEncrypted(struct mwChannel *chan, + guint32 msg_type, struct mwOpaque *msg, + gboolean encrypt); + + +/** pass a create message to a channel for handling */ +void mwChannel_recvCreate(struct mwChannel *chan, + struct mwMsgChannelCreate *msg); + + +/** pass an accept message to a channel for handling */ +void mwChannel_recvAccept(struct mwChannel *chan, + struct mwMsgChannelAccept *msg); + + +/** pass a destroy message to a channel for handling */ +void mwChannel_recvDestroy(struct mwChannel *chan, + struct mwMsgChannelDestroy *msg); + + +/** Feed data into a channel. */ +void mwChannel_recv(struct mwChannel *chan, struct mwMsgChannelSend *msg); + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_cipher.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,294 @@ + +/* + 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 +*/ + +#ifndef _MW_CIPHER_H +#define _MW_CIPHER_H + + +#include <glib.h> +#include "mw_common.h" + + +/* place-holders */ +struct mwChannel; +struct mwSession; + + +/** @enum mwCipherType + Common cipher types */ +enum mwCipherType { + mwCipher_RC2_40 = 0x0000, + mwCipher_RC2_128 = 0x0001, +}; + + +struct mwCipher; +struct mwCipherInstance; + + +/** Obtain an instance of a given cipher, which can be used for the + processing of a single channel. */ +typedef struct mwCipherInstance *(*mwCipherInstantiator) + (struct mwCipher *cipher, struct mwChannel *chan); + + +/** Generate a descriptor for use in a channel create message to + indicate the availability of this cipher + + @todo remove for 1.0 +*/ +typedef struct mwEncryptItem *(*mwCipherDescriptor) + (struct mwCipherInstance *instance); + + +/** Process (encrypt or decrypt, depending) the given data. The passed + buffer may be freed in processing and be replaced with a freshly + allocated buffer. The post-processed buffer must in turn be freed + after use */ +typedef int (*mwCipherProcessor) + (struct mwCipherInstance *ci, struct mwOpaque *data); + + +/** A cipher. Ciphers are primarily used to provide cipher instances + for bi-directional encryption on channels, but some may be used + for other activities. Expand upon this structure to create a + custom encryption provider. + @see mwCipherInstance */ +struct mwCipher { + + /** service this cipher is providing for + @see mwCipher_getSession */ + struct mwSession *session; + + guint16 type; /**< @see mwCipher_getType */ + const char *(*get_name)(void); /**< @see mwCipher_getName */ + const char *(*get_desc)(void); /**< @see mwCipher_getDesc */ + + /** Generate a new Cipher Instance for use on a channel + @see mwCipher_newInstance */ + mwCipherInstantiator new_instance; + + void (*offered)(struct mwCipherInstance *ci, struct mwEncryptItem *item); + struct mwEncryptItem *(*offer)(struct mwCipherInstance *ci); + void (*accepted)(struct mwCipherInstance *ci, struct mwEncryptItem *item); + struct mwEncryptItem *(*accept)(struct mwCipherInstance *ci); + + mwCipherProcessor encrypt; /**< @see mwCipherInstance_encrypt */ + mwCipherProcessor decrypt; /**< @see mwCipherInstance_decrypt */ + + /** prepare this cipher for being free'd + @see mwCipher_free */ + void (*clear)(struct mwCipher *c); + + /** clean up a cipher instance before being free'd + @see mwCipherInstance_free */ + void (*clear_instance)(struct mwCipherInstance *ci); +}; + + +/** An instance of a cipher. Expand upon this structure to contain + necessary state data + @see mwCipher */ +struct mwCipherInstance { + + /** the parent cipher. + @see mwCipherInstance_getCipher */ + struct mwCipher *cipher; + + /** the channel this instances processes + @see mwCipherInstance_getChannel */ + struct mwChannel *channel; +}; + + +struct mwCipher *mwCipher_new_RC2_40(struct mwSession *s); + + +struct mwCipher *mwCipher_new_RC2_128(struct mwSession *s); + + +struct mwSession *mwCipher_getSession(struct mwCipher *cipher); + + +guint16 mwCipher_getType(struct mwCipher *cipher); + + +const char *mwCipher_getName(struct mwCipher *cipher); + + +const char *mwCipher_getDesc(struct mwCipher *cipher); + + +struct mwCipherInstance *mwCipher_newInstance(struct mwCipher *cipher, + struct mwChannel *channel); + + +/** destroy a cipher */ +void mwCipher_free(struct mwCipher* cipher); + + +/** reference the parent cipher of an instance */ +struct mwCipher *mwCipherInstance_getCipher(struct mwCipherInstance *ci); + + +/** Indicates a cipher has been offered to our channel */ +void mwCipherInstance_offered(struct mwCipherInstance *ci, + struct mwEncryptItem *item); + + +/** Offer a cipher */ +struct mwEncryptItem * +mwCipherInstance_offer(struct mwCipherInstance *ci); + + +/** Indicates an offered cipher has been accepted */ +void mwCipherInstance_accepted(struct mwCipherInstance *ci, + struct mwEncryptItem *item); + + +/** Accept a cipher offered to our channel */ +struct mwEncryptItem * +mwCipherInstance_accept(struct mwCipherInstance *ci); + + +/** encrypt data */ +int mwCipherInstance_encrypt(struct mwCipherInstance *ci, + struct mwOpaque *data); + + +/** decrypt data */ +int mwCipherInstance_decrypt(struct mwCipherInstance *ci, + struct mwOpaque *data); + + +/** destroy a cipher instance */ +void mwCipherInstance_free(struct mwCipherInstance *ci); + + +/** + @section General Cipher Functions + + These functions are reused where encryption is necessary outside of + a channel (eg. session authentication) +*/ +/* @{ */ + + +/** generate some pseudo-random bytes + @param keylen count of bytes to write into key + @param key buffer to write keys into +*/ +void mwKeyRandom(guchar *key, gsize keylen); + + +/** Setup an Initialization Vector. IV must be at least 8 bytes */ +void mwIV_init(guchar *iv); + + +/** Expand a variable-length key into a 128-byte key (represented as + an an array of 64 ints) */ +void mwKeyExpand(int *ekey, const guchar *key, gsize keylen); + + +/** Encrypt data using an already-expanded key */ +void mwEncryptExpanded(const int *ekey, guchar *iv, + struct mwOpaque *in, + struct mwOpaque *out); + + +/** Encrypt data using an expanded form of the given key */ +void mwEncrypt(const guchar *key, gsize keylen, guchar *iv, + struct mwOpaque *in, struct mwOpaque *out); + + +/** Decrypt data using an already expanded key */ +void mwDecryptExpanded(const int *ekey, guchar *iv, + struct mwOpaque *in, + struct mwOpaque *out); + + +/** Decrypt data using an expanded form of the given key */ +void mwDecrypt(const guchar *key, gsize keylen, guchar *iv, + struct mwOpaque *in, struct mwOpaque *out); + + +/* @} */ + + +/** + @section Diffie-Hellman Functions + + These functions are reused where DH Key negotiation is necessary + outside of a channel (eg. session authentication). These are + wrapping a full multiple-precision integer math library, but most of + the functionality there-of is not exposed. Currently, the math is + provided by a copy of the public domain libmpi. + + for more information on the used MPI Library, visit + http://www.cs.dartmouth.edu/~sting/mpi/ +*/ +/* @{ */ + + +/** @struct mwMpi */ +struct mwMpi; + + +/** prepare a new mpi value */ +struct mwMpi *mwMpi_new(void); + + +/** destroy an mpi value */ +void mwMpi_free(struct mwMpi *i); + + +/** Import a value from an opaque */ +void mwMpi_import(struct mwMpi *i, struct mwOpaque *o); + + +/** Export a value into an opaque */ +void mwMpi_export(struct mwMpi *i, struct mwOpaque *o); + + +/** initialize and set a big integer to the Sametime Prime value */ +void mwMpi_setDHPrime(struct mwMpi *i); + + +/** initialize and set a big integer to the Sametime Base value */ +void mwMpi_setDHBase(struct mwMpi *i); + + +/** sets private to a randomly generated value, and calculates public + using the Sametime Prime and Base */ +void mwMpi_randDHKeypair(struct mwMpi *private, struct mwMpi *public); + + +/** sets the shared key value based on the remote and private keys, + using the Sametime Prime and Base */ +void mwMpi_calculateDHShared(struct mwMpi *shared, struct mwMpi *remote, + struct mwMpi *private); + + +/* @} */ + + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_client.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,154 @@ + +#ifndef _MW_CLIENT_H +#define _MW_CLIENT_H + + +#include <glib.h> +#include <glib/glist.h> + + +/* place-holders */ +struct mwChannel; +struct mwClient; +struct mwClientHandler; +struct mwConnection; +struct mwConnectionHandler; +struct mwMessage; +struct mwService; +struct mwSession; + + + +/* @file mw_client.h + +*/ + + + +/* @section Client + +*/ +/*@{*/ + + +struct mwClient; + + +typedef (struct mwConnectionHandler *)(*mwClientConnect) + (struct mwClient *c, const char *host); + + +struct mwClientHandler { + mwClientConnect connect; +} + + +struct mwClient * +mwClient_new(struct mwClientHandler *h); + + +struct mwChannel * +mwClient_newChannel(struct mwClient *client, + struct mwService *srvc); + + +struct mwChannel * +mwClient_newMasterChannel(struct mwClient *client, + struct mwSession *session); + + +int mwClient_sendKeepAlive(struct mwClient *client); + + +void mwClient_setUsesCountByte(struct mwClient *client, + gboolean use); + + +gboolean mwClient_getUsesCountByte(struct mwClient *client); + + +void mwClient_destroy(struct mwClient *client); + + +/*@}*/ + + + +/* @section Connection + +*/ +/*{*/ + + +struct mwConnection; + + +typedef (int)(*mwConnectionWrite) + (struct mwConnection *c, const char *buf, gsize len); + + +typedef (void)(*mwConnectionClose) + (struct mwConnection *c); + + +struct mwConnectionHandler { + mwConnectionWrite write; + mwConnectionClose close; +} + + +struct mwConnection * +mwConnection_new(struct mwConnectionHandler *h); + + +void mwConnection_recv(struct mwConnection *connection, + const char *buf, + gsize len); + + +void mwConnection_destroy(struct mwConnection *connection); + + +/*@}*/ + + + +/* @section Channel + +*/ +/*@{*/ + + +struct mwChannel; + + +int mwChannel_sendMessage(struct mwChannel *channel, + struct mwMessage *msg); + + +int mwChannel_send(struct mwChannel *channel, + guint32 type, + guint32 options, + struct mwOpaque *data); + + +int mwChannel_destroy(struct mwChannel *channel, + guint32 reason, + struct mwOpaque *info); + + +gboolean mwChannel_isMasterChannel(struct mwChannel *channel); + + +guint32 mwChannel_getId(struct mwChannel *channel); + + +enum mwChannelState mwChannel_getState(struct mwChannel *channel); + + + +/*@}*/ + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_common.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,427 @@ + +/* + 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 +*/ + +#ifndef _MW_COMMON_H +#define _MW_COMMON_H + + +#include <glib.h> + + +/** @file mw_common.h + + Common data types and functions for handling those types. + + Functions in this file all fit into similar naming conventions of + <code>TYPE_ACTION</code> as per the activity they perform. The + following actions are available: + + <code>void TYPE_put(struct mwPutBuffer *b, TYPE *val)</code> + - marshalls val onto the buffer b. The buffer will grow as necessary + to fit all the data put into it. For guint16, guint32, and + gboolean, <code>TYPE val</code> is used instead of <code>TYPE + \*val</code>. + + <code>void TYPE_get(struct mwGetBuffer *b, TYPE *val)</code> + - unmarshals val from the buffer b. Failure (due to lack of + insufficient remaining buffer) is indicated in the buffer's error + field. A call to a _get function with a buffer in an error state + has to effect. + + <code>void TYPE_clear(TYPE *val)</code> + - zeros and frees internal members of val, but does not free val + itself. Needs to be called before free-ing any complex types which + have been unmarshalled from a TYPE_get or populated from a + TYPE_clone call to prevent memory leaks. + + <code>void TYPE_clone(TYPE *to, TYPE *from)</code> + - copies/clones members of from into to. May result in memory + allocation for some types. Note that to is not cleared + before-hand, it must already be in a pristine condition. + + <code>gboolean TYPE_equal(TYPE *y, TYPE *z)</code> + - simple equality test. +*/ + + +/** @struct mwPutBuffer + buffer to be written to */ +struct mwPutBuffer; + +/** @struct mwGetBuffer + buffer to be read from */ +struct mwGetBuffer; + + +/** A length of binary data, not null-terminated. */ +struct mwOpaque { + gsize len; /**< length of data. */ + guchar *data; /**< data, normally with no NULL termination */ +}; + + +/* 8.3.6 Login Types */ + +/** The type of login. Normally meaning the type of client code being + used to login with. + + If you know of any additional client identifiers, please add them + below or submit an RFE to the meanwhile tracker. +*/ +enum mwLoginType { + mwLogin_LIB = 0x1000, /**< official Lotus binary library */ + mwLogin_JAVA_WEB = 0x1001, /**< official Lotus Java applet */ + mwLogin_BINARY = 0x1002, /**< official Lotus binary application */ + mwLogin_JAVA_APP = 0x1003, /**< official Lotus Java application */ + mwLogin_LINKS = 0x100a, /**< official Sametime Links toolkit */ + + /* now we're getting crazy */ + mwLogin_NOTES_6_5 = 0x1200, + mwLogin_NOTES_6_5_3 = 0x1203, + mwLogin_NOTES_7_0_beta = 0x1210, + mwLogin_NOTES_7_0 = 0x1214, + mwLogin_ICT = 0x1300, + mwLogin_ICT_1_7_8_2 = 0x1302, + mwLogin_ICT_SIP = 0x1303, + mwLogin_NOTESBUDDY_4_14 = 0x1400, /**< 0xff00 mask? */ + mwLogin_NOTESBUDDY_4_15 = 0x1405, + mwLogin_NOTESBUDDY_4_16 = 0x1406, + mwLogin_SANITY = 0x1600, + mwLogin_ST_PERL = 0x1625, + mwLogin_PMR_ALERT = 0x1650, + mwLogin_TRILLIAN = 0x16aa, /**< http://sf.net/st-plugin/ */ + mwLogin_TRILLIAN_IBM = 0x16bb, + mwLogin_MEANWHILE = 0x1700, /**< Meanwhile library */ +}; + + +/* 8.2 Common Structures */ +/* 8.2.1 Login Info block */ + +struct mwLoginInfo { + char *login_id; /**< community-unique ID of the login */ + guint16 type; /**< @see mwLoginType */ + char *user_id; /**< community-unique ID of the user */ + char *user_name; /**< name of user (nick name, full name, etc) */ + char *community; /**< community name (usually domain name) */ + gboolean full; /**< if FALSE, following fields non-existant */ + char *desc; /**< implementation defined description */ + guint32 ip_addr; /**< ip addr of the login */ + char *server_id; /**< unique ID of login's server */ +}; + + +/* 8.2.2 Private Info Block */ + +struct mwUserItem { + gboolean full; /**< if FALSE, don't include name */ + char *id; /**< user id */ + char *community; /**< community */ + char *name; /**< user name */ +}; + + +struct mwPrivacyInfo { + gboolean deny; /**< deny (true) or allow (false) users */ + guint32 count; /**< count of users */ + struct mwUserItem *users; /**< the users list */ +}; + + +/* 8.3.5 User Status Types */ + +enum mwStatusType { + mwStatus_ACTIVE = 0x0020, + mwStatus_IDLE = 0x0040, + mwStatus_AWAY = 0x0060, + mwStatus_BUSY = 0x0080, +}; + + +/* 8.2.3 User Status Block */ + +struct mwUserStatus { + guint16 status; /**< @see mwStatusType */ + guint32 time; /**< last status change time in seconds */ + char *desc; /**< status description */ +}; + + +/* 8.2.4 ID Block */ + +struct mwIdBlock { + char *user; /**< user id (login id or empty for some services) */ + char *community; /**< community id (NULL for same community) */ +}; + + +/* 8.3.8.2 Awareness Presence Types */ + +/* @todo move mwAwareType, mwAwareIdBlock and mwAwareSnapshot into the + aware service and out of common */ + +/** type codes for mwAwareIdBlock */ +enum mwAwareType { + mwAware_USER = 0x0002, /**< a single user */ + mwAware_GROUP = 0x0003, /**< a group */ + mwAware_SERVER = 0x0008, /**< a server */ +}; + + +/* 8.4.2 Awareness Messages */ +/* 8.4.2.1 Awareness ID Block */ + +struct mwAwareIdBlock { + guint16 type; /**< @see mwAwareType */ + char *user; /**< user id */ + char *community; /**< community id (NULL for same community) */ +}; + + +/* 8.4.2.4 Snapshot */ + +struct mwAwareSnapshot { + struct mwAwareIdBlock id; + char *group; /**< group this id belongs to */ + gboolean online; /**< is this user online? */ + char *alt_id; /**< alternate ID, often same as id.user */ + struct mwUserStatus status; /**< status of this user */ + char *name; /**< Formatted version of ID */ +}; + + +/** encryption blocks */ +struct mwEncryptItem { + guint16 id; /**< cipher identifier */ + struct mwOpaque info; /**< cipher information */ +}; + + +/** @name buffer utility functions */ +/*@{*/ + + +/** allocate a new empty buffer */ +struct mwPutBuffer *mwPutBuffer_new(void); + + +/** write raw data to the put buffer */ +void mwPutBuffer_write(struct mwPutBuffer *b, gpointer data, gsize len); + + +/** destroy the buffer */ +void mwPutBuffer_free(struct mwPutBuffer *b); + + +/** move the buffer's data into an opaque, destroy the buffer */ +void mwPutBuffer_finalize(struct mwOpaque *to, struct mwPutBuffer *from); + + +/** allocate a new buffer with a copy of the given data */ +struct mwGetBuffer *mwGetBuffer_new(struct mwOpaque *data); + + +/** read len bytes of raw data from the get buffer into mem. If len is + greater than the count of bytes remaining in the buffer, the + buffer's error flag will NOT be set. + + @returns count of bytes successfully copied to mem */ +gsize mwGetBuffer_read(struct mwGetBuffer *b, gpointer mem, gsize len); + + +/** skip len bytes in the get buffer. If len is greater than the count + of bytes remaining in the buffer, the buffer's error flag will NOT + be set. + + @returns count of bytes successfully skipped */ +gsize mwGetBuffer_advance(struct mwGetBuffer *b, gsize len); + + +/** allocate a new buffer backed by the given data. Calling + mwGetBuffer_free will not result in the underlying data being + freed */ +struct mwGetBuffer *mwGetBuffer_wrap(const struct mwOpaque *data); + + +/** destroy the buffer */ +void mwGetBuffer_free(struct mwGetBuffer *b); + + +/** reset the buffer to the very beginning. Also clears the buffer's + error flag. */ +void mwGetBuffer_reset(struct mwGetBuffer *b); + + +/** count of remaining available bytes */ +gsize mwGetBuffer_remaining(struct mwGetBuffer *b); + + +/** TRUE if an error occurred while reading a basic type from this + buffer */ +gboolean mwGetBuffer_error(struct mwGetBuffer *b); + + +/*@}*/ + + +/** @name Basic Data Type Marshalling + The basic types are combined to construct the complex types. + */ +/*@{*/ + + +void guint16_put(struct mwPutBuffer *b, guint16 val); + +void guint16_get(struct mwGetBuffer *b, guint16 *val); + +guint16 guint16_peek(struct mwGetBuffer *b); + + +void guint32_put(struct mwPutBuffer *b, guint32 val); + +void guint32_get(struct mwGetBuffer *b, guint32 *val); + +guint32 guint32_peek(struct mwGetBuffer *b); + + +void gboolean_put(struct mwPutBuffer *b, gboolean val); + +void gboolean_get(struct mwGetBuffer *b, gboolean *val); + +gboolean gboolean_peek(struct mwGetBuffer *b); + + +void mwString_put(struct mwPutBuffer *b, const char *str); + +void mwString_get(struct mwGetBuffer *b, char **str); + + +void mwOpaque_put(struct mwPutBuffer *b, const struct mwOpaque *o); + +void mwOpaque_get(struct mwGetBuffer *b, struct mwOpaque *o); + +void mwOpaque_clear(struct mwOpaque *o); + +void mwOpaque_free(struct mwOpaque *o); + +void mwOpaque_clone(struct mwOpaque *to, const struct mwOpaque *from); + + +/*@}*/ + + +/** @name Complex Data Type Marshalling */ +/*@{*/ + + +void mwLoginInfo_put(struct mwPutBuffer *b, const struct mwLoginInfo *info); + +void mwLoginInfo_get(struct mwGetBuffer *b, struct mwLoginInfo *info); + +void mwLoginInfo_clear(struct mwLoginInfo *info); + +void mwLoginInfo_clone(struct mwLoginInfo *to, const struct mwLoginInfo *from); + + +void mwUserItem_put(struct mwPutBuffer *b, const struct mwUserItem *user); + +void mwUserItem_get(struct mwGetBuffer *b, struct mwUserItem *user); + +void mwUserItem_clear(struct mwUserItem *user); + +void mwUserItem_clone(struct mwUserItem *to, const struct mwUserItem *from); + + +void mwPrivacyInfo_put(struct mwPutBuffer *b, + const struct mwPrivacyInfo *info); + +void mwPrivacyInfo_get(struct mwGetBuffer *b, struct mwPrivacyInfo *info); + +void mwPrivacyInfo_clear(struct mwPrivacyInfo *info); + +void mwPrivacyInfo_clone(struct mwPrivacyInfo *to, + const struct mwPrivacyInfo *from); + + +void mwUserStatus_put(struct mwPutBuffer *b, + const struct mwUserStatus *stat); + +void mwUserStatus_get(struct mwGetBuffer *b, struct mwUserStatus *stat); + +void mwUserStatus_clear(struct mwUserStatus *stat); + +void mwUserStatus_clone(struct mwUserStatus *to, + const struct mwUserStatus *from); + + +void mwIdBlock_put(struct mwPutBuffer *b, const struct mwIdBlock *id); + +void mwIdBlock_get(struct mwGetBuffer *b, struct mwIdBlock *id); + +void mwIdBlock_clear(struct mwIdBlock *id); + +void mwIdBlock_clone(struct mwIdBlock *to, + const struct mwIdBlock *from); + +guint mwIdBlock_hash(const struct mwIdBlock *idb); + +gboolean mwIdBlock_equal(const struct mwIdBlock *a, + const struct mwIdBlock *b); + + +void mwAwareIdBlock_put(struct mwPutBuffer *b, + const struct mwAwareIdBlock *idb); + +void mwAwareIdBlock_get(struct mwGetBuffer *b, struct mwAwareIdBlock *idb); + +void mwAwareIdBlock_clear(struct mwAwareIdBlock *idb); + +void mwAwareIdBlock_clone(struct mwAwareIdBlock *to, + const struct mwAwareIdBlock *from); + +guint mwAwareIdBlock_hash(const struct mwAwareIdBlock *a); + +gboolean mwAwareIdBlock_equal(const struct mwAwareIdBlock *a, + const struct mwAwareIdBlock *b); + + +void mwAwareSnapshot_get(struct mwGetBuffer *b, + struct mwAwareSnapshot *idb); + +void mwAwareSnapshot_clear(struct mwAwareSnapshot *idb); + +void mwAwareSnapshot_clone(struct mwAwareSnapshot *to, + const struct mwAwareSnapshot *from); + + +void mwEncryptItem_put(struct mwPutBuffer *b, + const struct mwEncryptItem *item); + +void mwEncryptItem_get(struct mwGetBuffer *b, struct mwEncryptItem *item); + +void mwEncryptItem_clear(struct mwEncryptItem *item); + +void mwEncryptItem_free(struct mwEncryptItem *item); + + +/*@}*/ + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_debug.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,184 @@ + +/* + 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/gstring.h> + +#include "mw_debug.h" + + + +#define FRMT1 "%02x" +#define FRMT2 FRMT1 FRMT1 " " +#define FRMT4 FRMT2 FRMT2 +#define FRMT8 FRMT4 FRMT4 +#define FRMT16 FRMT8 FRMT8 + +#define ADVANCE(b, n, c) {b += c; n -= c;} + + + +/** writes hex pairs of buf to str */ +static void pretty_print(GString *str, const guchar *buf, gsize len) { + while(len >= 16) { + /* write a complete line */ + g_string_append_printf(str, FRMT16, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + ADVANCE(buf, len, 16); + + /* append \n to each line but the last */ + if(len) g_string_append(str, "\n"); + } + + /* write an incomplete line */ + if(len >= 8) { + g_string_append_printf(str, FRMT8, + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + ADVANCE(buf, len, 8); + } + + if(len >= 4) { + g_string_append_printf(str, FRMT4, + buf[0], buf[1], buf[2], buf[3]); + ADVANCE(buf, len, 4); + } + + if(len >= 2) { + g_string_append_printf(str, FRMT2, buf[0], buf[1]); + ADVANCE(buf, len, 2); + } + + if(len >= 1) { + g_string_append_printf(str, FRMT1, buf[0]); + ADVANCE(buf, len, 1); + } +} + + + +void mw_debug_datav(const guchar *buf, gsize len, + const char *msg, va_list args) { + GString *str; + + g_return_if_fail(buf != NULL || len == 0); + + str = g_string_new(NULL); + + if(msg) { + char *txt = g_strdup_vprintf(msg, args); + g_string_append_printf(str, "%s\n", txt); + g_free(txt); + } + pretty_print(str, buf, len); + + g_debug("%s", str->str); + g_string_free(str, TRUE); +} + + + +void mw_debug_data(const guchar *buf, gsize len, + const char *msg, ...) { + va_list args; + + g_return_if_fail(buf != NULL || len == 0); + + va_start(args, msg); + mw_debug_datav(buf, len, msg, args); + va_end(args); +} + + + +void mw_debug_opaquev(struct mwOpaque *o, const char *txt, va_list args) { + g_return_if_fail(o != NULL); + mw_debug_datav(o->data, o->len, txt, args); +} + + + +void mw_debug_opaque(struct mwOpaque *o, const char *txt, ...) { + va_list args; + + g_return_if_fail(o != NULL); + + va_start(args, txt); + mw_debug_opaquev(o, txt, args); + va_end(args); +} + + +void mw_mailme_datav(const guchar *buf, gsize len, + const char *info, va_list args) { + +#if defined(MW_MAILME) && MW_MAILME + GString *str; + char *txt; + + str = g_string_new(MW_MAILME_MESSAGE "\n" + " Please send mail to: " MW_MAILME_ADDRESS "\n" + MW_MAILME_CUT_START "\n"); + str = g_string_new(NULL); + + txt = g_strdup_vprintf(info, args); + g_string_append_printf(str, "%s\n", txt); + g_free(txt); + + if(buf && len) pretty_print(str, buf, len); + + g_string_append(str, MW_MAILME_CUT_STOP); + + g_debug(str->str); + g_string_free(str, TRUE); + +#else + mw_debug_datav(buf, len, info, args); + +#endif +} + + + +void mw_mailme_data(const guchar *buf, gsize len, + const char *info, ...) { + va_list args; + va_start(args, info); + mw_mailme_datav(buf, len, info, args); + va_end(args); +} + + + +void mw_mailme_opaquev(struct mwOpaque *o, const char *info, va_list args) { + mw_mailme_datav(o->data, o->len, info, args); +} + + + +void mw_mailme_opaque(struct mwOpaque *o, const char *info, ...) { + va_list args; + va_start(args, info); + mw_mailme_opaquev(o, info, args); + va_end(args); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_debug.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,128 @@ + +/* + 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 +*/ + +#ifndef _MW_DEBUG_H +#define _MW_DEBUG_H + + +#include <stdarg.h> +#include <glib.h> + +#include "mw_common.h" + + +/** replaces NULL strings with "(null)". useful for printf where + you're unsure that the %s will be non-NULL. Note that while the + linux printf will do this automatically, not all will. The others + will instead segfault */ +#define NSTR(str) ((str)? (str): "(null)") + + +#ifndef g_debug +#define g_debug(format...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format) +#endif + + +#ifndef g_info +#define g_info(format...) g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, format) +#endif + + +#ifndef MW_MAILME_ADDRESS +/** email address used in mw_debug_mailme. */ +#define MW_MAILME_ADDRESS "meanwhile-devel@lists.sourceforge.net" +#endif + + +#ifndef MW_MAILME_CUT_START +#define MW_MAILME_CUT_START "-------- begin copy --------" +#endif + + +#ifndef MW_MAILME_CUT_STOP +#define MW_MAILME_CUT_STOP "--------- end copy ---------" +#endif + + +#ifndef MW_MAILME_MESSAGE +/** message used in mw_debug_mailme instructing user on what to do + with the debugging output produced from that function */ +#define MW_MAILME_MESSAGE "\n" \ + " Greetings! It seems that you've run across protocol data that the\n" \ + "Meanwhile library does not yet know about. As such, there may be\n" \ + "some unexpected behaviour in this session. If you'd like to help\n" \ + "resolve this issue, please copy and paste the following block into\n" \ + "an email to the address listed below with a brief explanation of\n" \ + "what you were doing at the time of this message. Thanks a lot!" +#endif + + +void mw_debug_datav(const guchar *buf, gsize len, + const char *info, va_list args); + + +void mw_debug_data(const guchar *buf, gsize len, + const char *info, ...); + + +void mw_debug_opaquev(struct mwOpaque *o, const char *info, va_list args); + + +void mw_debug_opaque(struct mwOpaque *o, const char *info, ...); + + +void mw_mailme_datav(const guchar *buf, gsize len, + const char *info, va_list args); + +void mw_mailme_data(const guchar *buf, gsize len, + const char *info, ...); + + +/** Outputs a hex dump of a mwOpaque with debugging info and a + pre-defined message. Identical to mw_mailme_opaque, but taking a + va_list argument */ +void mw_mailme_opaquev(struct mwOpaque *o, const char *info, va_list args); + + + +/** Outputs a hex dump of a mwOpaque with debugging info and a + pre-defined message. + + if MW_MAILME is undefined or false, this function acts the same as + mw_mailme_opaque. + + @arg block data to be printed in a hex block + @arg info a printf-style format string + + The resulting message is in the following format: + @code + MW_MAILME_MESSAGE + " Please send mail to: " MW_MAILME_ADDRESS + MW_MAILME_CUT_START + info + block + MW_MAILME_CUT_STOP + @endcode + */ +void mw_mailme_opaque(struct mwOpaque *o, const char *info, ...); + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_error.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,163 @@ + +/* + 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 +*/ + +#ifndef _MW_ERROR_H +#define _MW_ERROR_H + +#include <glib.h> + + +/** @file mw_error.h + + Common error code constants used by Meanwhile. + + Not all of these error codes (or even many, really) will ever + actually appear from Meanwhile. These are taken directly from the + houri draft, along with the minimal explanation for each. +*/ + + +/** reference to a new string appropriate for the given error code.*/ +char* mwError(guint32 code); + + +/* 8.3 Constants */ +/* 8.3.1 Error Codes */ +/* 8.3.1.1 General error/success codes */ + +/** @enum ERR_GENERAL + general error codes */ +enum ERR_GENERAL { + ERR_SUCCESS = 0x00000000, + ERR_FAILURE = 0x80000000, + ERR_REQUEST_DELAY = 0x00000001, + ERR_REQUEST_INVALID = 0x80000001, + ERR_NOT_LOGGED_IN = 0x80000002, + ERR_NOT_AUTHORIZED = 0x80000003, + ERR_ABORT = 0x80000004, + ERR_NO_ELEMENT = 0x80000005, + ERR_NO_USER = 0x80000006, + ERR_BAD_DATA = 0x80000007, + ERR_NOT_IMPLEMENTED = 0x80000008, + ERR_UNKNOWN_ERROR = 0x80000009, /* what is this? */ + ERR_STARVING = 0x8000000a, + ERR_CHANNEL_NO_SUPPORT = 0x8000000b, + ERR_CHANNEL_EXISTS = 0x8000000c, + ERR_SERVICE_NO_SUPPORT = 0x8000000d, + ERR_PROTOCOL_NO_SUPPORT = 0x8000000e, + ERR_PROTOCOL_NO_SUPPORT2 = 0x8000000f, /* duplicate? */ + ERR_VERSION_NO_SUPPORT = 0x80000010, + ERR_USER_SKETCHY = 0x80000011, + ERR_ALREADY_INITIALIZED = 0x80000013, + ERR_NOT_OWNER = 0x80000014, + ERR_TOKEN_INVALID = 0x80000015, + ERR_TOKEN_EXPIRED = 0x80000016, + ERR_TOKEN_IP_MISMATCH = 0x80000017, + ERR_PORT_IN_USE = 0x80000018, + ERR_NETWORK_DEAD = 0x80000019, + ERR_NO_MASTER_CHANNEL = 0x8000001a, + ERR_ALREADY_SUBSCRIBED = 0x8000001b, + ERR_NOT_SUBSCRIBED = 0x8000001c, + ERR_ENCRYPT_NO_SUPPORT = 0x8000001d, + ERR_ENCRYPT_UNINITIALIZED = 0x8000001e, + ERR_ENCRYPT_UNACCEPTABLE = 0x8000001f, + ERR_ENCRYPT_INVALID = 0x80000020, + ERR_NO_COMMON_ENCRYPT = 0x80000021, + ERR_CHANNEL_DESTROYED = 0x80000022, + ERR_CHANNEL_REDIRECTED = 0x80000023 +}; + + +/* 8.3.1.2 Connection/disconnection errors */ + +#define VERSION_MISMATCH 0x80000200 +#define INSUF_BUFFER 0x80000201 +#define NOT_IN_USE 0x80000202 +#define INSUF_SOCKET 0x80000203 +#define HARDWARE_ERROR 0x80000204 +#define NETWORK_DOWN 0x80000205 +#define HOST_DOWN 0x80000206 +#define HOST_UNREACHABLE 0x80000207 +#define TCPIP_ERROR 0x80000208 +#define FAT_MESSAGE 0x80000209 +#define PROXY_ERROR 0x8000020A +#define SERVER_FULL 0x8000020B +#define SERVER_NORESPOND 0x8000020C +#define CANT_CONNECT 0x8000020D +#define USER_REMOVED 0x8000020E +#define PROTOCOL_ERROR 0x8000020F +#define USER_RESTRICTED 0x80000210 +#define INCORRECT_LOGIN 0x80000211 +#define ENCRYPT_MISMATCH 0x80000212 +#define USER_UNREGISTERED 0x80000213 +#define VERIFICATION_DOWN 0x80000214 +#define USER_TOO_IDLE 0x80000216 +#define GUEST_IN_USE 0x80000217 +#define USER_EXISTS 0x80000218 +#define USER_RE_LOGIN 0x80000219 +#define BAD_NAME 0x8000021A +#define REG_MODE_NS 0x8000021B +#define WRONG_USER_PRIV 0x8000021C +#define NEED_EMAIL 0x8000021D +#define DNS_ERROR 0x8000021E +#define DNS_FATAL_ERROR 0x8000021F +#define DNS_NOT_FOUND 0x80000220 +#define CONNECTION_BROKEN 0x80000221 +#define CONNECTION_ABORTED 0x80000222 +#define CONNECTION_REFUSED 0x80000223 +#define CONNECTION_RESET 0x80000224 +#define CONNECTION_TIMED 0x80000225 +#define CONNECTION_CLOSED 0x80000226 +#define MULTI_SERVER_LOGIN 0x80000227 +#define MULTI_SERVER_LOGIN2 0x80000228 +#define MULTI_LOGIN_COMP 0x80000229 +#define MUTLI_LOGIN_ALREADY 0x8000022A +#define SERVER_BROKEN 0x8000022B +#define SERVER_PATH_OLD 0x8000022C +#define APPLET_LOGOUT 0x8000022D + + +/* 8.3.1.3 Client error codes */ + +/** @enum ERR_CLIENT + Client error codes */ +enum ERR_CLIENT { + ERR_CLIENT_USER_GONE = 0x80002000, /* user isn't here */ + ERR_CLIENT_USER_DND = 0x80002001, /* user is DND */ + ERR_CLIENT_USER_ELSEWHERE = 0x80002002, /* already logged in elsewhere */ +}; + + +/* 8.3.1.4 IM error codes */ + +/** @enum ERR_IM + IM error codes */ +enum ERR_IM { + ERR_IM_COULDNT_REGISTER = 0x80002003, + ERR_IM_ALREADY_REGISTERED = 0x80002004, + + /** apparently, this is used to mean that the requested feature (per + the channel create addtl data) is not supported by the client on + the other end of the IM channel */ + ERR_IM_NOT_REGISTERED = 0x80002005, +}; + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_message.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,295 @@ + +/* + 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 +*/ + +#ifndef _MW_MESSAGE_H +#define _MW_MESSAGE_H + + +#include <glib/glist.h> +#include "mw_common.h" + + +/** Cast a pointer to a message subtype (eg, mwMsgHandshake, + mwMsgAdmin) into a pointer to a mwMessage */ +#define MW_MESSAGE(msg) (&msg->head) + + +/** Indicates the type of a message. */ +enum mwMessageType { + mwMessage_HANDSHAKE = 0x0000, /**< mwMsgHandshake */ + mwMessage_HANDSHAKE_ACK = 0x8000, /**< mwMsgHandshakeAck */ + mwMessage_LOGIN = 0x0001, /**< mwMsgLogin */ + mwMessage_LOGIN_ACK = 0x8001, /**< mwMsgLoginAck */ + mwMessage_LOGIN_REDIRECT = 0x0018, /**< mwMsgLoginRedirect */ + mwMessage_LOGIN_CONTINUE = 0x0016, /**< mwMsgLoginContinue */ + + mwMessage_CHANNEL_CREATE = 0x0002, /**< mwMsgChannelCreate */ + mwMessage_CHANNEL_DESTROY = 0x0003, /**< mwMsgChannelDestroy */ + mwMessage_CHANNEL_SEND = 0x0004, /**< mwMsgChannelSend */ + mwMessage_CHANNEL_ACCEPT = 0x0006, /**< mwMsgChannelAccept */ + + mwMessage_SET_USER_STATUS = 0x0009, /**< mwMsgSetUserStatus */ + mwMessage_SET_PRIVACY_LIST = 0x000b, /**< mwMsgSetPrivacyList */ + mwMessage_SENSE_SERVICE = 0x0011, /**< mwMsgSenseService */ + mwMessage_ADMIN = 0x0019, /**< mwMsgAdmin */ + mwMessage_ANNOUNCE = 0x0022, /**< mwMsgAnnounce */ +}; + + +enum mwMessageOption { + mwMessageOption_ENCRYPT = 0x4000, /**< message data is encrypted */ + mwMessageOption_HAS_ATTRIBS = 0x8000, /**< message has attributes */ +}; + + +/** @see mwMessageOption */ +#define MW_MESSAGE_HAS_OPTION(msg, opt) \ + ((msg)->options & (opt)) + + +struct mwMessage { + guint16 type; /**< @see mwMessageType */ + guint16 options; /**< @see mwMessageOption */ + guint32 channel; /**< ID of channel message is intended for */ + struct mwOpaque attribs; /**< optional message attributes */ +}; + + + +/** Allocate and initialize a new message of the specified type */ +struct mwMessage *mwMessage_new(enum mwMessageType type); + + +/** build a message from its representation */ +struct mwMessage *mwMessage_get(struct mwGetBuffer *b); + + +void mwMessage_put(struct mwPutBuffer *b, struct mwMessage *msg); + + +void mwMessage_free(struct mwMessage *msg); + + +/* 8.4 Messages */ +/* 8.4.1 Basic Community Messages */ +/* 8.4.1.1 Handshake */ + +struct mwMsgHandshake { + struct mwMessage head; + guint16 major; /**< client's major version number */ + guint16 minor; /**< client's minor version number */ + guint32 srvrcalc_addr; /**< 0.0.0.0 */ + guint16 login_type; /**< @see mwLoginType */ + guint32 loclcalc_addr; /**< local public IP */ + guint16 unknown_a; /**< normally 0x0100 */ + guint32 unknown_b; /**< normally 0x00000000 */ + char *local_host; /**< name of client host */ +}; + + +/* 8.4.1.2 HandshakeAck */ + +struct mwMsgHandshakeAck { + struct mwMessage head; + guint16 major; /**< server's major version number */ + guint16 minor; /**< server's minor version number */ + guint32 srvrcalc_addr; /**< server-calculated address */ + guint32 magic; /**< four bytes of something */ + struct mwOpaque data; /**< server's DH public key for auth */ +}; + + +/* 8.3.7 Authentication Types */ + +enum mwAuthType { + mwAuthType_PLAIN = 0x0000, + mwAuthType_TOKEN = 0x0001, + mwAuthType_ENCRYPT = 0x0002, /**< @todo remove for 1.0 */ + mwAuthType_RC2_40 = 0x0002, + mwAuthType_RC2_128 = 0x0004, +}; + + +/* 8.4.1.3 Login */ + +struct mwMsgLogin { + struct mwMessage head; + guint16 login_type; /**< @see mwLoginType */ + char *name; /**< user identification */ + guint16 auth_type; /**< @see mwAuthType */ + struct mwOpaque auth_data; /**< authentication data */ +}; + + +/* 8.4.1.4 LoginAck */ + +struct mwMsgLoginAck { + struct mwMessage head; + struct mwLoginInfo login; + struct mwPrivacyInfo privacy; + struct mwUserStatus status; +}; + + +/* 8.4.1.5 LoginCont */ + +struct mwMsgLoginContinue { + struct mwMessage head; +}; + + +/* 8.4.1.6 AuthPassed */ + +struct mwMsgLoginRedirect { + struct mwMessage head; + char *host; + char *server_id; +}; + + +/* 8.4.1.7 CreateCnl */ + +/** an offer of encryption items */ +struct mwEncryptOffer { + guint16 mode; /**< encryption mode */ + GList *items; /**< list of mwEncryptItem offered */ + guint16 extra; /**< encryption mode again? */ + gboolean flag; /**< unknown flag */ +}; + + +struct mwMsgChannelCreate { + struct mwMessage head; + guint32 reserved; /**< unknown reserved data */ + guint32 channel; /**< intended ID for new channel */ + struct mwIdBlock target; /**< User ID. for service use */ + guint32 service; /**< ID for the target service */ + guint32 proto_type; /**< protocol type for the service */ + guint32 proto_ver; /**< protocol version for the service */ + guint32 options; /**< options */ + struct mwOpaque addtl; /**< service-specific additional data */ + gboolean creator_flag; /**< indicate presence of creator information */ + struct mwLoginInfo creator; + struct mwEncryptOffer encrypt; +}; + + +/* 8.4.1.8 AcceptCnl */ + +/** a selected encryption item from those offered */ +struct mwEncryptAccept { + guint16 mode; /**< encryption mode */ + struct mwEncryptItem *item; /**< chosen mwEncryptItem (optional) */ + guint16 extra; /**< encryption mode again? */ + gboolean flag; /**< unknown flag */ +}; + + +struct mwMsgChannelAccept { + struct mwMessage head; + guint32 service; /**< ID for the channel's service */ + guint32 proto_type; /**< protocol type for the service */ + guint32 proto_ver; /**< protocol version for the service */ + struct mwOpaque addtl; /**< service-specific additional data */ + gboolean acceptor_flag; /**< indicate presence of acceptor information */ + struct mwLoginInfo acceptor; + struct mwEncryptAccept encrypt; +}; + + +/* 8.4.1.9 SendOnCnl */ + +struct mwMsgChannelSend { + struct mwMessage head; + + /** message type. each service defines its own send types. Type IDs + are only necessarily unique within a given service. */ + guint16 type; + + /** protocol data to be interpreted by the handling service */ + struct mwOpaque data; +}; + + +/* 8.4.1.10 DestroyCnl */ + +struct mwMsgChannelDestroy { + struct mwMessage head; + guint32 reason; /**< reason for closing the channel. */ + struct mwOpaque data; /**< additional information */ +}; + + +/* 8.4.1.11 SetUserStatus */ + +struct mwMsgSetUserStatus { + struct mwMessage head; + struct mwUserStatus status; +}; + + +/* 8.4.1.12 SetPrivacyList */ + +struct mwMsgSetPrivacyList { + struct mwMessage head; + struct mwPrivacyInfo privacy; +}; + + +/* Sense Service */ + +/** Sent to the server to request the presense of a service by its + ID. Sent to the client to indicate the presense of such a + service */ +struct mwMsgSenseService { + struct mwMessage head; + guint32 service; +}; + + +/* Admin */ + +/** An administrative broadcast message */ +struct mwMsgAdmin { + struct mwMessage head; + char *text; +}; + + +/* Announce */ + +/** An announcement between users */ +struct mwMsgAnnounce { + struct mwMessage head; + gboolean sender_present; /**< indicates presence of sender data */ + struct mwLoginInfo sender; /**< who sent the announcement */ + guint16 unknown_a; /**< unknown A. Usually 0x00 */ + gboolean may_reply; /**< replies allowed */ + char *text; /**< text of message */ + + /** list of (char *) indicating recipients. Recipient users are in + the format "@U username" and recipient NAB groups are in the + format "@G groupname" */ + GList *recipients; +}; + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_service.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,360 @@ + +/* + 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 +*/ + +#ifndef _MW_SERVICE_H +#define _MW_SERVICE_H + + +#include "mw_common.h" + + +/* place-holders */ +struct mwChannel; +struct mwService; +struct mwSession; +struct mwMsgChannelCreate; +struct mwMsgChannelAccept; +struct mwMsgChannelDestroy; + + +/** State-tracking for a service */ +enum mwServiceState { + mwServiceState_STOPPED, /**< the service is not active */ + mwServiceState_STOPPING, /**< the service is shutting down */ + mwServiceState_STARTED, /**< the service is active */ + mwServiceState_STARTING, /**< the service is starting up */ + mwServiceState_ERROR, /**< error in service, shutting down */ + mwServiceState_UNKNOWN, /**< error determining state */ +}; + + +/** Casts a concrete service (such as mwServiceAware) into a mwService */ +#define MW_SERVICE(srv) ((struct mwService *) srv) + + +#define MW_SERVICE_IS_STATE(srvc, state) \ + (mwService_getState(MW_SERVICE(srvc)) == (state)) + +#define MW_SERVICE_IS_STOPPED(srvc) \ + MW_SERVICE_IS_STATE(srvc, mwServiceState_STOPPED) + +#define MW_SERVICE_IS_STOPPING(srvc) \ + MW_SERVICE_IS_STATE(srvc, mwServiceState_STOPPING) + +#define MW_SERVICE_IS_STARTED(srvc) \ + MW_SERVICE_IS_STATE(srvc, mwServiceState_STARTED) + +#define MW_SERVICE_IS_STARTING(srvc) \ + MW_SERVICE_IS_STATE(srvc, mwServiceState_STARTING) + + +/** If a service is STARTING or STARTED, it's considered LIVE */ +#define MW_SERVICE_IS_LIVE(srvc) \ + (MW_SERVICE_IS_STARTING(srvc) || MW_SERVICE_IS_STARTED(srvc)) + +/** If a service is STOPPING or STOPPED, it's considered DEAD */ +#define MW_SERVICE_IS_DEAD(srvc) \ + (MW_SERVICE_IS_STOPPING(srvc) || MW_SERVICE_IS_STOPPED(srvc)) + + +typedef void (*mwService_funcStart)(struct mwService *service); + +typedef void (*mwService_funcStop)(struct mwService *service); + +typedef void (*mwService_funcClear)(struct mwService *service); + +typedef const char *(*mwService_funcGetName)(struct mwService *service); + +typedef const char *(*mwService_funcGetDesc)(struct mwService *service); + +/** @todo remove msg and replace with appropriate additional parameters */ +typedef void (*mwService_funcRecvCreate) + (struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelCreate *msg); + +/** @todo remove msg and replace with appropriate additional parameters */ +typedef void (*mwService_funcRecvAccept) + (struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelAccept *msg); + +/** @todo remove msg and replace with appropriate additional parameters */ +typedef void (*mwService_funcRecvDestroy) + (struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelDestroy *msg); + +typedef void (*mwService_funcRecv) + (struct mwService *service, + struct mwChannel *channel, + guint16 msg_type, + struct mwOpaque *data); + + +/** A service is the recipient of sendOnCnl messages sent over + channels marked with the corresponding service id. Services + provide functionality such as IM relaying, Awareness tracking and + notification, and Conference handling. It is a service's + responsibility to accept or destroy channels, and to process data + sent over those channels */ +struct mwService { + + /** the unique identifier by which this service is registered. The + type value also relates to those channels which will be directed + to this service */ + guint32 type; + + /** the state of this service. Determines whether or not the session + should call the start function upon receipt of a service + available message. Should not be set or checked by hand. + + @relates mwService_getState */ + enum mwServiceState state; + + /** session this service is attached to. + @relates mwService_getSession */ + struct mwSession *session; + + /** @return string short name of the service + @relates mwService_getName */ + mwService_funcGetName get_name; + + /** @return string short description of the service + @relates mwService_getDesc */ + mwService_funcGetDesc get_desc; + + /** The service's channel create handler. Called when the session + receives a channel create message with a service matching this + service's type. + + @relates mwService_recvCreate */ + mwService_funcRecvCreate recv_create; + + /** The service's channel accept handler. Called when the session + receives a channel accept message for a channel with a service + matching this service's type. + + @relates mwService_recvAccept */ + mwService_funcRecvAccept recv_accept; + + /** The service's channel destroy handler. Called when the session + receives a channel destroy message for a channel with a service + matching this service's type. + + @relates mwService_recvDestroy */ + mwService_funcRecvDestroy recv_destroy; + + /** The service's input handler. Called when the session receives + data on a channel belonging to this service + + @relates mwService_recv */ + mwService_funcRecv recv; + + /** The service's start handler. Called upon the receipt of a + service available message. + + @relates mwService_start */ + mwService_funcStart start; + + /** The service's stop handler. Called when the session is shutting + down, or when the service is free'd. + + @relates mwService_stop */ + mwService_funcStop stop; + + /** The service's cleanup handler. Service implementations should + presume that mwService::stop will be called first. The clear + handler is not for shutting down channels or generating + non-cleanup side-effects, it is only for handling tear-down of + the service, and will only be called once for any instance. + + @relates mwService_free */ + mwService_funcClear clear; + + /** Optional client data, not for use by service implementations + + @relates mwService_getClientData + @relates mwService_setClientData */ + gpointer client_data; + + /** Optional client data cleanup function. Called with client_data + from mwService_free + + @relates mwService_getClientData + @relates mwService_setClientData */ + GDestroyNotify client_cleanup; +}; + + +/** @name Service Extension API + + These functions are for use by service implementations */ +/*@{*/ + + +/** Prepares a newly allocated service for use. + + Intended for use by service implementations, rather than by code + utilizing a service. + + The service state will be initialized to STOPPED. + + @param service the service to initialize + @param session the service's owning session + @param service_type the service ID number */ +void mwService_init(struct mwService *service, + struct mwSession *session, + guint32 service_type); + + +/** Indicate that a service is started. To be used by service + implementations when the service is fully started. */ +void mwService_started(struct mwService *service); + + +/** Indicate that a service is stopped. To be used by service + implementations when the service is fully stopped. */ +void mwService_stopped(struct mwService *service); + + +/*@}*/ + + +/** @name General Services API + + These functions provide unified access to the general functions of + a client service, with some simple sanity-checking. */ +/*@{*/ + + +/** Triggers the recv_create handler on the service. + + @param service the service to handle the message + @param channel the channel being created + @param msg the channel create message */ +void mwService_recvCreate(struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelCreate *msg); + + +/** Triggers the recv_accept handler on the service. + + @param service the service to handle the message + @param channel the channel being accepted + @param msg the channel accept message */ +void mwService_recvAccept(struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelAccept *msg); + + +/** Triggers the recv_destroy handler on the service. + + @param service the service to handle the message + @param channel the channel being destroyed + @param msg the channel destroy message */ +void mwService_recvDestroy(struct mwService *service, + struct mwChannel *channel, + struct mwMsgChannelDestroy *msg); + + +/** Triggers the input handler on the service + + @param service the service to receive the input + @param channel the channel the input was received from + @param msg_type the service-dependant message type + @param data the message data */ +void mwService_recv(struct mwService *service, + struct mwChannel *channel, + guint16 msg_type, + struct mwOpaque *data); + + +/** @return the appropriate type id for the service */ +guint32 mwService_getType(struct mwService *); + + +/** @return string short name of the service */ +const char *mwService_getName(struct mwService *); + + +/** @return string short description of the service */ +const char *mwService_getDesc(struct mwService *); + + +/** @return the service's session */ +struct mwSession *mwService_getSession(struct mwService *service); + + +/** @returns the service's state +*/ +enum mwServiceState mwService_getState(struct mwService *service); + + +/** Triggers the start handler for the service. Normally called from + the session upon receipt of a service available message. Service + implementations should use this handler to open any necessary + channels, etc. Checks that the service is STOPPED, or returns. + + @param service The service to start +*/ +void mwService_start(struct mwService *service); + + +/** Triggers the stop handler for the service. Normally called from + the session before closing down the connection. Checks that the + service is STARTED or STARTING, or returns + + @param service The service to stop +*/ +void mwService_stop(struct mwService *service); + + +/** Frees memory used by a service. Will trigger the stop handler if + the service is STARTED or STARTING. Triggers clear handler to allow + cleanup. + + @param service The service to clear and free +*/ +void mwService_free(struct mwService *service); + + +/** Associates client data with service. If there is existing data, it + will not have its cleanup function called. Client data is not for + use by service implementations. Rather, it is for use by client + code which may later make use of those service implementations. */ +void mwService_setClientData(struct mwService *service, + gpointer data, GDestroyNotify cleanup); + + +/** Reference associated client data */ +gpointer mwService_getClientData(struct mwService *service); + + +/** Removes client data from service. If there is a cleanup function, + it will be called. */ +void mwService_removeClientData(struct mwService *service); + + +/*@}*/ + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_session.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,387 @@ + +/* + 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 +*/ + +#ifndef _MW_SESSION_H +#define _MW_SESSION_H + + +/** @file mw_session.h + + A client session with a Sametime server is encapsulated in the + mwSession structure. The session controls channels, provides + encryption ciphers, and manages services using messages over the + Master channel. + + A session does not directly communicate with a socket or stream, + instead the session is initialized from client code with an + instance of a mwSessionHandler structure. This session handler + provides functions as call-backs for common session events, and + provides functions for writing-to and closing the connection to + the server. + + A session does not perform reads on a socket directly. Instead, it + must be fed from an outside source via the mwSession_recv + function. The session will buffer and merge data passed to this + function to build complete protocol messages, and will act upon + each complete message accordingly. +*/ + + +#include "mw_common.h" + + +struct mwChannelSet; +struct mwCipher; +struct mwMessage; +struct mwService; + + +/** default protocol major version */ +#define MW_PROTOCOL_VERSION_MAJOR 0x001e + + +/** default protocol minor version */ +#define MW_PROTOCOL_VERSION_MINOR 0x001d + + +/** @section Session Properties + for use with mwSession_setProperty, et al. +*/ +/*@{*/ + +/** char *, session user ID */ +#define mwSession_AUTH_USER_ID "session.auth.user" + +/** char *, plaintext password */ +#define mwSession_AUTH_PASSWORD "session.auth.password" + +/** struct mwOpaque *, authentication token */ +#define mwSession_AUTH_TOKEN "session.auth.token" + +/** char *, hostname of client */ +#define mwSession_CLIENT_HOST "client.host" + +/** guint32, local IP of client */ +#define mwSession_CLIENT_IP "client.ip" + +/** guint16, major version of client protocol */ +#define mwSession_CLIENT_VER_MAJOR "client.version.major" + +/** guint16, minor version of client protocol */ +#define mwSession_CLIENT_VER_MINOR "client.version.minor" + +/** guint16, client type identifier */ +#define mwSession_CLIENT_TYPE_ID "client.id" + +/** guint16, major version of server protocol */ +#define mwSession_SERVER_VER_MAJOR "server.version.major" + +/** guint16, minor version of server protocol */ +#define mwSession_SERVER_VER_MINOR "server.version.minor" + +/*@}*/ + + +enum mwSessionState { + mwSession_STARTING, /**< session is starting */ + mwSession_HANDSHAKE, /**< session has sent handshake */ + mwSession_HANDSHAKE_ACK, /**< session has received handshake ack */ + mwSession_LOGIN, /**< session has sent login */ + mwSession_LOGIN_REDIR, /**< session has been redirected */ + mwSession_LOGIN_ACK, /**< session has received login ack */ + mwSession_STARTED, /**< session is active */ + mwSession_STOPPING, /**< session is shutting down */ + mwSession_STOPPED, /**< session is stopped */ + mwSession_UNKNOWN, /**< indicates an error determining state */ + mwSession_LOGIN_CONT, /**< session has sent a login continue */ +}; + + +#define mwSession_isState(session, state) \ + (mwSession_getState((session)) == (state)) + +#define mwSession_isStarting(s) \ + (mwSession_isState((s), mwSession_STARTING) || \ + mwSession_isState((s), mwSession_HANDSHAKE) || \ + mwSession_isState((s), mwSession_HANDSHAKE_ACK) || \ + mwSession_isState((s), mwSession_LOGIN) || \ + mwSession_isState((s), mwSession_LOGIN_ACK) || \ + mwSession_isState((s), mwSession_LOGIN_REDIR) || \ + mwSession_isState((s), mwSession_LOGIN_CONT)) + +#define mwSession_isStarted(s) \ + (mwSession_isState((s), mwSession_STARTED)) + +#define mwSession_isStopping(s) \ + (mwSession_isState((s), mwSession_STOPPING)) + +#define mwSession_isStopped(s) \ + (mwSession_isState((s), mwSession_STOPPED)) + + +/** @struct mwSession + + Represents a Sametime client session */ +struct mwSession; + + +/** @struct mwSessionHandler + + session handler. Structure which interfaces a session with client + code to provide I/O and event handling */ +struct mwSessionHandler { + + /** write data to the server connection. Required. Should return + zero for success, non-zero for error */ + int (*io_write)(struct mwSession *, const guchar *buf, gsize len); + + /** close the server connection. Required */ + void (*io_close)(struct mwSession *); + + /** triggered by mwSession_free. Optional. Put cleanup code here */ + void (*clear)(struct mwSession *); + + /** Called when the session has changed status. + + @see mwSession_getStateInfo for uses of info field + + @param s the session + @param state the session's state + @param info additional state information */ + void (*on_stateChange)(struct mwSession *s, + enum mwSessionState state, gpointer info); + + /** called when privacy information has been sent or received + + @see mwSession_getPrivacyInfo + */ + void (*on_setPrivacyInfo)(struct mwSession *); + + /** called when user status has changed + + @see mwSession_getUserStatus */ + void (*on_setUserStatus)(struct mwSession *); + + /** called when an admin messages has been received */ + void (*on_admin)(struct mwSession *, const char *text); + + /** called when an announcement arrives */ + void (*on_announce)(struct mwSession *, struct mwLoginInfo *from, + gboolean may_reply, const char *text); + +}; + + +/** allocate a new session */ +struct mwSession *mwSession_new(struct mwSessionHandler *); + + +/** stop, clear, free a session. Does not free contained ciphers or + services, these must be taken care of explicitly. */ +void mwSession_free(struct mwSession *); + + +/** obtain a reference to the session's handler */ +struct mwSessionHandler *mwSession_getHandler(struct mwSession *); + + +/** instruct the session to begin. This will result in the initial + handshake message being sent. */ +void mwSession_start(struct mwSession *); + + +/** instruct the session to shut down with the following reason + code. */ +void mwSession_stop(struct mwSession *, guint32 reason); + + +/** Data is buffered, unpacked, and parsed into a message, then + processed accordingly. */ +void mwSession_recv(struct mwSession *, const guchar *, gsize); + + +/** primarily used by services to have messages serialized and sent + @param s session to send message over + @param msg message to serialize and send + @returns 0 for success */ +int mwSession_send(struct mwSession *s, struct mwMessage *msg); + + +/** sends the keepalive byte */ +int mwSession_sendKeepalive(struct mwSession *s); + + +/** respond to a login redirect message by forcing the login sequence + to continue through the immediate server. */ +int mwSession_forceLogin(struct mwSession *s); + + +/** send an announcement to a list of users/groups. Targets of + announcement must be in the same community as the session. + + @param s session to send announcement from + @param may_reply permit clients to reply. Not all clients honor this. + @param text text of announcement + @param recipients list of recipients. Each recipient is specified + by a single string, prefix with "@U " for users + and "@G " for Notes Address Book groups. +*/ +int mwSession_sendAnnounce(struct mwSession *s, gboolean may_reply, + const char *text, const GList *recipients); + + +/** set the internal privacy information, and inform the server as + necessary. Triggers the on_setPrivacyInfo call-back. */ +int mwSession_setPrivacyInfo(struct mwSession *, struct mwPrivacyInfo *); + + +/** direct reference to the session's internal privacy structure */ +struct mwPrivacyInfo *mwSession_getPrivacyInfo(struct mwSession *); + + +/** reference the login information for the session */ +struct mwLoginInfo *mwSession_getLoginInfo(struct mwSession *); + + +/** set the internal user status state, and inform the server as + necessary. Triggers the on_setUserStatus call-back */ +int mwSession_setUserStatus(struct mwSession *, struct mwUserStatus *); + + +struct mwUserStatus *mwSession_getUserStatus(struct mwSession *); + + +/** current status of the session */ +enum mwSessionState mwSession_getState(struct mwSession *); + + +/** additional status-specific information. Depending on the state of + the session, this value has different meaning. + + @li @c mwSession_STOPPING guint32 error code causing + the session to shut down + + @li @c mwSession_STOPPED guint32 error code causing + the session to shut down + + @li @c mwSession_LOGIN_REDIR (char *) host to redirect + to +*/ +gpointer mwSession_getStateInfo(struct mwSession *); + + +struct mwChannelSet *mwSession_getChannels(struct mwSession *); + + +/** adds a service to the session. If the session is started (or when + the session is successfully started) and the service has a start + function, the session will request service availability from the + server. On receipt of the service availability notification, the + session will call the service's start function. + + @return TRUE if the session was added correctly */ +gboolean mwSession_addService(struct mwSession *, struct mwService *); + + +/** find a service by its type identifier */ +struct mwService *mwSession_getService(struct mwSession *, guint32 type); + + +/** removes a service from the session. If the session is started and + the service has a stop function, it will be called. Returns the + removed service */ +struct mwService *mwSession_removeService(struct mwSession *, guint32 type); + + +/** a GList of services in this session. The GList needs to be freed + after use */ +GList *mwSession_getServices(struct mwSession *); + + +/** instruct a STARTED session to check the server for the presense of + a given service. The service will be automatically started upon + receipt of an affirmative reply from the server. This function is + automatically called upon all services in a session when the + session is fully STARTED. + + Services which terminate due to an error may call this on + themselves to re-initialize when their server-side counterpart is + made available again. + + @param s owning session + @param type service type ID */ +void mwSession_senseService(struct mwSession *s, guint32 type); + + +/** adds a cipher to the session. */ +gboolean mwSession_addCipher(struct mwSession *, struct mwCipher *); + + +/** find a cipher by its type identifier */ +struct mwCipher *mwSession_getCipher(struct mwSession *, guint16 type); + + +/** remove a cipher from the session */ +struct mwCipher *mwSession_removeCipher(struct mwSession *, guint16 type); + + +/** a GList of ciphers in this session. The GList needs to be freed + after use */ +GList *mwSession_getCiphers(struct mwSession *); + + +/** associate a key:value pair with the session. If an existing value is + associated with the same key, it will have its clear function called + and will be replaced with the new value */ +void mwSession_setProperty(struct mwSession *, const char *key, + gpointer val, GDestroyNotify clear); + + +/** obtain the value of a previously set property, or NULL */ +gpointer mwSession_getProperty(struct mwSession *, const char *key); + + +/** remove a property, calling the optional GDestroyNotify function + indicated in mwSession_setProperty if applicable */ +void mwSession_removeProperty(struct mwSession *, const char *key); + + +/** associate arbitrary data with the session for use by the client + code. Only client applications should use this, never services. + + @param session the session to associate the data with + @param data arbitrary client data + @param clear optional cleanup function called on data from + mwSession_removeClientData and mwSession_free +*/ +void mwSession_setClientData(struct mwSession *session, + gpointer data, GDestroyNotify clear); + + +gpointer mwSession_getClientData(struct mwSession *session); + + +/** remove client data, calling the optional GDestroyNotify function + indicated in mwSession_setClientData if applicable */ +void mwSession_removeClientData(struct mwSession *session); + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_aware.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,270 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_AWARE_H +#define _MW_SRVC_AWARE_H + + +#include "mw_common.h" + + +/** @file mw_srvc_aware.h + + The aware service... + + @todo remove the whole idea of an instantiated mwAwareList and + instead use arbitrary pointers (including NULL) as keys to + internally stored lists. This removes the problem of the service + free'ing its lists and invalidating mwAwareList references from + client code. +*/ + + +/** Type identifier for the aware service */ +#define mwService_AWARE 0x00000011 + + +/** @struct mwServiceAware + + Instance of an Aware Service. The members of this structure are + not made available. Accessing the parts of an aware service should + be performed through the appropriate functions. Note that + instances of this structure can be safely cast to a mwService. +*/ +struct mwServiceAware; + + +/** @struct mwAwareList + + Instance of an Aware List. The members of this structure are not + made available. Access to the parts of an aware list should be + handled through the appropriate functions. + + Any references to an aware list are rendered invalid when the + parent service is free'd +*/ +struct mwAwareList; + + +/** @struct mwAwareAttribute + + Key/Opaque pair indicating an identity's attribute. + */ +struct mwAwareAttribute; + + +/** Predefined keys appropriate for a mwAwareAttribute + */ +enum mwAwareAttributeKeys { + mwAttribute_AV_PREFS_SET = 0x01, /**< A/V prefs specified, gboolean */ + mwAttribute_MICROPHONE = 0x02, /**< has a microphone, gboolean */ + mwAttribute_SPEAKERS = 0x03, /**< has speakers, gboolean */ + mwAttribute_VIDEO_CAMERA = 0x04, /**< has a video camera, gboolean */ + mwAttribute_FILE_TRANSFER = 0x06, /**< supports file transfers, gboolean */ +}; + + +typedef void (*mwAwareAttributeHandler) + (struct mwServiceAware *srvc, + struct mwAwareAttribute *attrib); + + +struct mwAwareHandler { + mwAwareAttributeHandler on_attrib; + void (*clear)(struct mwServiceAware *srvc); +}; + + +/** Appropriate function type for the on-aware signal + + @param list mwAwareList emiting the signal + @param id awareness status information + @param data user-specified data +*/ +typedef void (*mwAwareSnapshotHandler) + (struct mwAwareList *list, + struct mwAwareSnapshot *id); + + +/** Appropriate function type for the on-option signal. The option's + value may need to be explicitly loaded in some instances, + resulting in this handler being triggered again. + + @param list mwAwareList emiting the signal + @param id awareness the attribute belongs to + @param attrib attribute +*/ +typedef void (*mwAwareIdAttributeHandler) + (struct mwAwareList *list, + struct mwAwareIdBlock *id, + struct mwAwareAttribute *attrib); + + +struct mwAwareListHandler { + /** handle aware updates */ + mwAwareSnapshotHandler on_aware; + + /** handle attribute updates */ + mwAwareIdAttributeHandler on_attrib; + + /** optional. Called from mwAwareList_free */ + void (*clear)(struct mwAwareList *list); +}; + + +struct mwServiceAware * +mwServiceAware_new(struct mwSession *session, + struct mwAwareHandler *handler); + + +/** Set an attribute value for this session */ +int mwServiceAware_setAttribute(struct mwServiceAware *srvc, + guint32 key, struct mwOpaque *opaque); + + +int mwServiceAware_setAttributeBoolean(struct mwServiceAware *srvc, + guint32 key, gboolean val); + + +int mwServiceAware_setAttributeInteger(struct mwServiceAware *srvc, + guint32 key, guint32 val); + + +int mwServiceAware_setAttributeString(struct mwServiceAware *srvc, + guint32 key, const char *str); + + +/** Unset an attribute for this session */ +int mwServiceAware_unsetAttribute(struct mwServiceAware *srvc, + guint32 key); + + +guint32 mwAwareAttribute_getKey(const struct mwAwareAttribute *attrib); + + +gboolean mwAwareAttribute_asBoolean(const struct mwAwareAttribute *attrib); + + +guint32 mwAwareAttribute_asInteger(const struct mwAwareAttribute *attrib); + + +/** Copy of attribute string, must be g_free'd. If the attribute's + content cannot be loaded as a string, returns NULL */ +char *mwAwareAttribute_asString(const struct mwAwareAttribute *attrib); + + +/** Direct access to an attribute's underlying opaque */ +const struct mwOpaque * +mwAwareAttribute_asOpaque(const struct mwAwareAttribute *attrib); + + +/** Allocate and initialize an aware list */ +struct mwAwareList * +mwAwareList_new(struct mwServiceAware *srvc, + struct mwAwareListHandler *handler); + + +/** Clean and free an aware list */ +void mwAwareList_free(struct mwAwareList *list); + + +struct mwAwareListHandler *mwAwareList_getHandler(struct mwAwareList *list); + + +/** Add a collection of user IDs to an aware list. + @param list mwAwareList to add user ID to + @param id_list mwAwareIdBlock list of user IDs to add + @return 0 for success, non-zero to indicate an error. +*/ +int mwAwareList_addAware(struct mwAwareList *list, GList *id_list); + + +/** Remove a collection of user IDs from an aware list. + @param list mwAwareList to remove user ID from + @param id_list mwAwareIdBlock list of user IDs to remove + @return 0 for success, non-zero to indicate an error. +*/ +int mwAwareList_removeAware(struct mwAwareList *list, GList *id_list); + + +int mwAwareList_removeAllAware(struct mwAwareList *list); + + +/** watch an NULL terminated array of keys */ +int mwAwareList_watchAttributeArray(struct mwAwareList *list, + guint32 *keys); + + +/** watch a NULL terminated list of keys */ +int mwAwareList_watchAttributes(struct mwAwareList *list, + guint32 key, ...); + + +/** stop watching a NULL terminated array of keys */ +int mwAwareList_unwatchAttributeArray(struct mwAwareList *list, + guint32 *keys); + + +/** stop watching a NULL terminated list of keys */ +int mwAwareList_unwatchAttributes(struct mwAwareList *list, + guint32 key, ...); + + +/** remove all watched attributes */ +int mwAwareList_unwatchAllAttributes(struct mwAwareList *list); + + +guint32 *mwAwareList_getWatchedAttributes(struct mwAwareList *list); + + +void mwAwareList_setClientData(struct mwAwareList *list, + gpointer data, GDestroyNotify cleanup); + + +void mwAwareList_removeClientData(struct mwAwareList *list); + + +gpointer mwAwareList_getClientData(struct mwAwareList *list); + + +/** trigger a got_aware event constructed from the passed user and + status information. Useful for adding false users and having the + getText function work for them */ +void mwServiceAware_setStatus(struct mwServiceAware *srvc, + struct mwAwareIdBlock *user, + struct mwUserStatus *stat); + + +/** look up the status description for a user */ +const char *mwServiceAware_getText(struct mwServiceAware *srvc, + struct mwAwareIdBlock *user); + + +/** look up the last known copy of an attribute for a user by the + attribute's key */ +const struct mwAwareAttribute * +mwServiceAware_getAttribute(struct mwServiceAware *srvc, + struct mwAwareIdBlock *user, + guint32 key); + + +#endif + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_conf.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,199 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_CONF_H +#define _MW_SRVC_CONF_H + + +#include <glib/glist.h> +#include "mw_common.h" + + +/** Type identifier for the conference service */ +#define mwService_CONFERENCE 0x80000010 + + +enum mwConferenceState { + mwConference_NEW, /**< new outgoing conference */ + mwConference_PENDING, /**< outgoing conference pending creation */ + mwConference_INVITED, /**< invited to incoming conference */ + mwConference_OPEN, /**< conference open and active */ + mwConference_CLOSING, /**< conference is closing */ + mwConference_ERROR, /**< conference is closing due to error */ + mwConference_UNKNOWN, /**< unable to determine conference state */ +}; + + +/** @struct mwServiceConference + Instance of the multi-user conference service */ +struct mwServiceConference; + + +/** @struct mwConference + A multi-user chat */ +struct mwConference; + + +/** Handler structure used to provide callbacks for an instance of the + conferencing service */ +struct mwConferenceHandler { + + /** triggered when we receive a conference invitation. Call + mwConference_accept to accept the invitation and join the + conference, or mwConference_close to reject the invitation. + + @param conf the newly created conference + @param inviter the indentity of the user who sent the invitation + @param invite the invitation text + */ + void (*on_invited)(struct mwConference *conf, + struct mwLoginInfo *inviter, const char *invite); + + /** triggered when we enter the conference. Provides the initial + conference membership list as a GList of mwLoginInfo structures + + @param conf the conference just joined + @param members mwLoginInfo list of existing conference members + */ + void (*conf_opened)(struct mwConference *conf, GList *members); + + /** triggered when a conference is closed. This is typically when + we've left it */ + void (*conf_closed)(struct mwConference *, guint32 reason); + + /** triggered when someone joins the conference */ + void (*on_peer_joined)(struct mwConference *, struct mwLoginInfo *); + + /** triggered when someone leaves the conference */ + void (*on_peer_parted)(struct mwConference *, struct mwLoginInfo *); + + /** triggered when someone says something */ + void (*on_text)(struct mwConference *conf, + struct mwLoginInfo *who, const char *what); + + /** typing notification */ + void (*on_typing)(struct mwConference *conf, + struct mwLoginInfo *who, gboolean typing); + + /** optional. called from mwService_free */ + void (*clear)(struct mwServiceConference *srvc); +}; + + +/** Allocate a new conferencing service, attaching the given handler + @param sess owning session + @param handler handler providing call-back functions for the service + */ +struct mwServiceConference * +mwServiceConference_new(struct mwSession *sess, + struct mwConferenceHandler *handler); + + +/** @returns the conference handler for the service */ +struct mwConferenceHandler * +mwServiceConference_getHandler(struct mwServiceConference *srvc); + + +/** a mwConference list of the conferences in this service. The GList + will need to be destroyed with g_list_free after use */ +GList *mwServiceConference_getConferences(struct mwServiceConference *srvc); + + +/** Allocate a new conference, in state NEW with the given title. + @see mwConference_create */ +struct mwConference *mwConference_new(struct mwServiceConference *srvc, + const char *title); + + +/** @returns the owning service of a conference */ +struct mwServiceConference *mwConference_getService(struct mwConference *conf); + + +/** @returns unique conference name */ +const char *mwConference_getName(struct mwConference *conf); + + +/** @returns conference title */ +const char *mwConference_getTitle(struct mwConference *conf); + + +/** a mwIdBlock list of the members of the conference. The GList will + need to be free'd after use */ +GList *mwConference_getMembers(struct mwConference *conf); + + +/** Initiate a conference. Conference must be in state NEW. If no name + or title for the conference has been set, they will be + generated. Conference will be placed into state PENDING. */ +int mwConference_open(struct mwConference *conf); + + +/** Leave and close an existing conference, or reject an invitation. + Triggers mwServiceConfHandler::conf_closed and free's the + conference. + */ +int mwConference_destroy(struct mwConference *conf, + guint32 reason, const char *text); + + +#define mwConference_reject(c,r,t) \ + mwConference_destroy((c),(r),(t)) + + +/** accept a conference invitation. Conference must be in the state + INVITED. */ +int mwConference_accept(struct mwConference *conf); + + +/** invite another user to an ACTIVE conference + @param conf conference + @param who user to invite + @param text invitation message + */ +int mwConference_invite(struct mwConference *conf, + struct mwIdBlock *who, const char *text); + + +/** send a text message over an open conference */ +int mwConference_sendText(struct mwConference *conf, const char *text); + + +/** send typing notification over an open conference */ +int mwConference_sendTyping(struct mwConference *conf, gboolean typing); + + +/** associate arbitrary client data and an optional cleanup function + with a conference. If there is already client data with a clear + function, it will not be called. */ +void mwConference_setClientData(struct mwConference *conf, + gpointer data, GDestroyNotify clear); + + +/** reference associated client data */ +gpointer mwConference_getClientData(struct mwConference *conf); + + +/** remove associated client data if any, and call the cleanup + function on the data as necessary */ +void mwConference_removeClientData(struct mwConference *conf); + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_dir.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,202 @@ +/* + 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 +*/ + +#ifndef _MW_SRVC_DIR_H +#define _MW_SERV_DIR_H + + +#include <glib.h> +#include <glib/glist.h> + + +struct mwSession; + + +#define SERVICE_DIRECTORY 0x0000001a + + +/** @struct mwServiceDirectory + + the directory service. */ +struct mwServiceDirectory; + + +/** @struct mwAddressBook + + server-side collection of users and groups. Open a directory + based on an address book to search or list its contents */ +struct mwAddressBook; + + +/** @struct mwDirectory + + searchable directory, based off of an address book */ +struct mwDirectory; + + +enum mwDirectoryState { + mwDirectory_NEW, /**< directory is created, but not open */ + mwDirectory_PENDING, /**< directory has in the process of opening */ + mwDirectory_OPEN, /**< directory is open */ + mwDirectory_ERROR, /**< error opening or using directory */ + mwDirectory_UNKNOWN, /**< error determining directory state */ +}; + + +/** return value of directory searches that fail */ +#define DIR_SEARCH_ERROR 0x00000000 + + +#define MW_DIRECTORY_IS_STATE(dir, state) \ + (mwDirectory_getState(dir) == (state)) + +#define MW_DIRECTORY_IS_NEW(dir) \ + MW_DIRECTORY_IS_STATE((dir), mwDirectory_NEW) + +#define MW_DIRECTORY_IS_PENDING(dir) \ + MW_DIRECTORY_IS_STATE((dir), mwDirectory_PENDING) + +#define MW_DIRECTORY_IS_OPEN(dir) \ + MW_DIRECTORY_IS_STATE((dir), mwDirectory_OPEN) + + +enum mwDirectoryMemberType { + mwDirectoryMember_USER = 0x0000, + mwDirectoryMember_GROUP = 0x0001, +}; + + +struct mwDirectoryMember { + guint16 type; /**< @see mwDirectoryMemberType */ + char *id; /**< proper ID for member */ + char *long_name; /**< full name of member (USER type only) */ + char *short_name; /**< short name of member */ + guint16 foo; /**< unknown */ +}; + + +/** Appropriate function signature for handling directory search results */ +typedef void (*mwSearchHandler) + (struct mwDirectory *dir, + guint32 code, guint32 offset, GList *members); + + +/** handles asynchronous events for a directory service instance */ +struct mwDirectoryHandler { + + /** handle receipt of the address book list from the service. + Initially triggered by mwServiceDirectory_refreshAddressBooks + and at service startup */ + void (*on_book_list)(struct mwServiceDirectory *srvc, GList *books); + + /** triggered when a directory has been successfully opened */ + void (*dir_opened)(struct mwDirectory *dir); + + /** triggered when a directory has been closed */ + void (*dir_closed)(struct mwDirectory *dir, guint32 reason); + + /** optional. called from mwService_free */ + void (*clear)(struct mwServiceDirectory *srvc); +}; + + +/** Allocate a new directory service instance for use with session */ +struct mwServiceDirectory * +mwServiceDirectory_new(struct mwSession *session, + struct mwDirectoryHandler *handler); + + +/** the handler associated with the service at its creation */ +struct mwDirectoryHandler * +mwServiceDirectory_getHandler(struct mwServiceDirectory *srvc); + + +/** most recent list of address books available in service */ +GList *mwServiceDirectory_getAddressBooks(struct mwServiceDirectory *srvc); + + +/** submit a request to obtain an updated list of address books from + service */ +int mwServiceDirectory_refreshAddressBooks(struct mwServiceDirectory *srvc); + + +/** list of directories in the service */ +GList *mwServiceDirectory_getDirectories(struct mwServiceDirectory *srvc); + + +/** list of directories associated with address book. Note that the + returned GList will need to be free'd after use */ +GList *mwAddressBook_getDirectories(struct mwAddressBook *book); + + +/** the name of the address book */ +const char *mwAddressBook_getName(struct mwAddressBook *book); + + +/** allocate a new directory based off the given address book */ +struct mwDirectory *mwDirectory_new(struct mwAddressBook *book); + + +enum mwDirectoryState mwDirectory_getState(struct mwDirectory *dir); + + +/** set client data. If there is an existing clear function, it will + not be called */ +void mwDirectory_setClientData(struct mwDirectory *dir, + gpointer data, GDestroyNotify clear); + + +/** reference associated client data */ +gpointer mwDirectory_getClientData(struct mwDirectory *dir); + + +/** remove and cleanup user data */ +void mwDirectory_removeClientData(struct mwDirectory *dir); + + +/** reference owning service */ +struct mwServiceDirectory *mwDirectory_getService(struct mwDirectory *dir); + + +/** reference owning address book */ +struct mwAddressBook *mwDirectory_getAddressBook(struct mwDirectory *dir); + + +/** initialize a directory. */ +int mwDirectory_open(struct mwDirectory *dir, mwSearchHandler cb); + + +/** continue a search into its next results */ +int mwDirectory_next(struct mwDirectory *dir); + + +/** continue a search into its previous results */ +int mwDirectory_previous(struct mwDirectory *dir); + + +/** initiate a search on an open directory */ +int mwDirectory_search(struct mwDirectory *dir, const char *query); + + +/** close and free the directory, and unassociate it with its owning + address book and service */ +int mwDirectory_destroy(struct mwDirectory *dir); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_ft.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,239 @@ + +/* + 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 +*/ + + +#ifndef _MW_SRVC_FT_H +#define _MW_SRVC_FT_H + + +#include "mw_common.h" + + +/** @file mw_srvc_ft.h + + A file transfer is a simple way to get large chunks of binary data + from one client to another. +*/ + + +/** @struct mwServiceFileTransfer + File transfer service +*/ +struct mwServiceFileTransfer; + + +/** @struct mwFileTransfer + A single file trasfer session + */ +struct mwFileTransfer; + + +#define mwService_FILE_TRANSFER 0x00000038 + + +enum mwFileTransferState { + mwFileTransfer_NEW, /**< file transfer is not open */ + mwFileTransfer_PENDING, /**< file transfer is opening */ + mwFileTransfer_OPEN, /**< file transfer is open */ + mwFileTransfer_CANCEL_LOCAL, + mwFileTransfer_CANCEL_REMOTE, + mwFileTransfer_DONE, + mwFileTransfer_ERROR, /**< error in file transfer */ + mwFileTransfer_UNKNOWN, /**< unknown state */ +}; + + +#define mwFileTransfer_isState(ft, state) \ + (mwFileTransfer_getState(ft) == (state)) + +#define mwFileTransfer_isNew(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_NEW) + +#define mwFileTransfer_isPending(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_PENDING) + +#define mwFileTransfer_isOpen(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_OPEN) + +#define mwFileTransfer_isDone(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_DONE) + +#define mwFileTransfer_isCancelLocal(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_CANCEL_LOCAL) + +#define mwFileTransfer_isCancelRemote(ft) \ + mwFileTransfer_isState((ft), mwFileTransfer_CANCEL_REMOTE) + + +enum mwFileTranferCode { + mwFileTransfer_SUCCESS = 0x00000000, + mwFileTransfer_REJECTED = 0x08000606, +}; + + +struct mwFileTransferHandler { + + /** an incoming file transfer has been offered */ + void (*ft_offered)(struct mwFileTransfer *ft); + + /** a file transfer has been fully initiated */ + void (*ft_opened)(struct mwFileTransfer *ft); + + /** a file transfer has been closed. Check the status of the file + transfer to determine if the transfer was complete or if it had + been interrupted */ + void (*ft_closed)(struct mwFileTransfer *ft, guint32 code); + + /** receive a chunk of a file from an inbound file transfer. */ + void (*ft_recv)(struct mwFileTransfer *ft, struct mwOpaque *data); + + /** received an ack for a sent chunk on an outbound file transfer. + this indicates that a previous call to mwFileTransfer_send has + reached the target and that the target has responded. */ + void (*ft_ack)(struct mwFileTransfer *ft); + + /** optional. called from mwService_free */ + void (*clear)(struct mwServiceFileTransfer *srvc); +}; + + +struct mwServiceFileTransfer * +mwServiceFileTransfer_new(struct mwSession *session, + struct mwFileTransferHandler *handler); + + +struct mwFileTransferHandler * +mwServiceFileTransfer_getHandler(struct mwServiceFileTransfer *srvc); + + +const GList * +mwServiceFileTransfer_getTransfers(struct mwServiceFileTransfer *srvc); + + +struct mwFileTransfer * +mwFileTransfer_new(struct mwServiceFileTransfer *srvc, + const struct mwIdBlock *who, const char *msg, + const char *filename, guint32 filesize); + + +/** deallocate a file transfer. will call mwFileTransfer_close if + necessary */ +void +mwFileTransfer_free(struct mwFileTransfer *ft); + + +/** the status of this file transfer */ +enum mwFileTransferState +mwFileTransfer_getState(struct mwFileTransfer *ft); + + +struct mwServiceFileTransfer * +mwFileTransfer_getService(struct mwFileTransfer *ft); + + +/** the user on the other end of the file transfer */ +const struct mwIdBlock * +mwFileTransfer_getUser(struct mwFileTransfer *ft); + + +/** the message sent along with an offered file transfer */ +const char * +mwFileTransfer_getMessage(struct mwFileTransfer *ft); + + +/** the publicized file name. Not necessarily related to any actual + file on either system */ +const char * +mwFileTransfer_getFileName(struct mwFileTransfer *ft); + + +/** total bytes intended to be sent/received */ +guint32 mwFileTransfer_getFileSize(struct mwFileTransfer *ft); + + +/** bytes remaining to be received/send */ +guint32 mwFileTransfer_getRemaining(struct mwFileTransfer *ft); + + +/** count of bytes sent/received over this file transfer so far */ +#define mwFileTransfer_getSent(ft) \ + (mwFileTransfer_getFileSize(ft) - mwFileTransfer_getRemaining(ft)) + + +/** initiate an outgoing file transfer */ +int mwFileTransfer_offer(struct mwFileTransfer *ft); + + +/** accept an incoming file transfer */ +int mwFileTransfer_accept(struct mwFileTransfer *ft); + + +/** reject an incoming file transfer */ +#define mwFileTransfer_reject(ft) \ + mwFileTransfer_close((ft), mwFileTransfer_REJECTED) + + +/** cancel an open file transfer */ +#define mwFileTransfer_cancel(ft) \ + mwFileTransfer_close((ft), mwFileTransfer_SUCCESS); + + +/** Close a file transfer. This will trigger the ft_close function of the + session's handler. + + @see mwFileTransfer_reject + @see mwFileTransfer_cancel +*/ +int mwFileTransfer_close(struct mwFileTransfer *ft, guint32 code); + + +/** send a chunk of data over an outbound file transfer. The client at + the other end of the transfer should respond with an acknowledgement + message, which can be caught in the service's handler. + + @see mwFileTransferHandler::ft_ack +*/ +int mwFileTransfer_send(struct mwFileTransfer *ft, + struct mwOpaque *data); + + +/** acknowledge the receipt of a chunk of data from an inbound file + transfer. This should be done after every received chunk, or the + transfer will stall. However, not all clients will wait for an ack + after sending a chunk before sending the next chunk, so it is + possible to have the handler's ft_recv function triggered again + even if no ack was sent. + + @see mwFileTransferHandler::ft_recv +*/ +int mwFileTransfer_ack(struct mwFileTransfer *ft); + + +void mwFileTransfer_setClientData(struct mwFileTransfer *ft, + gpointer data, GDestroyNotify clean); + + +gpointer mwFileTransfer_getClientData(struct mwFileTransfer *ft); + + +void mwFileTransfer_removeClientData(struct mwFileTransfer *ft); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_im.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,267 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_IM_H +#define _MW_SRVC_IM_H + + +#include <glib.h> +#include "mw_common.h" + + +/** @file mw_srvc_im.h + + The IM service provides one-on-one communication between + users. Messages sent over conversations may relay different types + of information, in a variety of formats. The basic feature-set + provides plain-text chat with typing notification. More complex + features may be negotiated transparently by setting the IM Client + Type for a conversation, or for the service as a whole. + */ + + +/** Type identifier for the IM service */ +#define mwService_IM 0x00001000 + + +/** @struct mwServiceIm + + An instance of the IM service. This service provides simple + instant messaging functionality */ +struct mwServiceIm; + + +/** @struct mwConversation + + A conversation between the local service and a single other user */ +struct mwConversation; + + +enum mwImClientType { + mwImClient_PLAIN = 0x00000001, /**< text, typing */ + mwImClient_NOTESBUDDY = 0x00033453, /**< adds html, subject, mime */ + mwImClient_PRECONF = 0x00000019, /**< pre-conference, legacy */ + mwImClient_UNKNOWN = 0xffffffff, /**< trouble determining type */ +}; + + +/** + Types of supported messages. When a conversation is created, the + least common denominator of features between either side of the + conversation (based on what features are available in the IM + service itself) becomes the set of supported features for that + conversation. At any point, the feature set for the service may + change, without affecting any existing conversations. + + @see mwServiceIm_supports + @see mwServiceIm_setSupported + @see mwConversation_supports + @see mwConversation_send + @see mwServiceImHandler::conversation_recv + */ +enum mwImSendType { + mwImSend_PLAIN, /**< char *, plain-text message */ + mwImSend_TYPING, /**< gboolean, typing status */ + mwImSend_HTML, /**< char *, HTML formatted message (NOTESBUDDY) */ + mwImSend_SUBJECT, /**< char *, conversation subject (NOTESBUDDY) */ + mwImSend_MIME, /**< char *, MIME-encoded message (NOTESBUDDY) */ + mwImSend_TIMESTAMP, /**< char *, YYYY:MM:DD:HH:mm:SS format (NOTESBUDDY) */ +}; + + + +/** @see mwConversation_getState */ +enum mwConversationState { + mwConversation_CLOSED, /**< conversation is not open */ + mwConversation_PENDING, /**< conversation is opening */ + mwConversation_OPEN, /**< conversation is open */ + mwConversation_UNKNOWN, /**< unknown state */ +}; + + +#define mwConversation_isState(conv, state) \ + (mwConversation_getState(conv) == (state)) + +#define mwConversation_isClosed(conv) \ + mwConversation_isState((conv), mwConversation_CLOSED) + +#define mwConversation_isPending(conv) \ + mwConversation_isState((conv), mwConversation_PENDING) + +#define mwConversation_isOpen(conv) \ + mwConversation_isState((conv), mwConversation_OPEN) + + + +/** IM Service Handler. Provides functions for events triggered from an + IM service instance. */ +struct mwImHandler { + + /** A conversation has been successfully opened */ + void (*conversation_opened)(struct mwConversation *conv); + + /** A conversation has been closed */ + void (*conversation_closed)(struct mwConversation *conv, guint32 err); + + /** A message has been received on a conversation */ + void (*conversation_recv)(struct mwConversation *conv, + enum mwImSendType type, gconstpointer msg); + + /** Handle a Place invitation. Set this to NULL and we should end up + receiving a conference invitation instead. */ + void (*place_invite)(struct mwConversation *conv, + const char *message, + const char *title, const char *name); + + /** optional. called from mwService_free */ + void (*clear)(struct mwServiceIm *srvc); +}; + + +struct mwServiceIm *mwServiceIm_new(struct mwSession *session, + struct mwImHandler *handler); + + +struct mwImHandler *mwServiceIm_getHandler(struct mwServiceIm *srvc); + + +/** reference an existing conversation to target, or create a new + conversation to target if one does not already exist */ +struct mwConversation *mwServiceIm_getConversation(struct mwServiceIm *srvc, + struct mwIdBlock *target); + + +/** reference an existing conversation to target */ +struct mwConversation *mwServiceIm_findConversation(struct mwServiceIm *srvc, + struct mwIdBlock *target); + + +/** determine if the conversations created from this service will + support a given send type */ +gboolean mwServiceIm_supports(struct mwServiceIm *srvc, + enum mwImSendType type); + + +/** Set the default client type for the service. Newly created + conversations will attempt to meet this level of functionality + first. + + @param srvc the IM service + @param type the send type to enable/disable +*/ +void mwServiceIm_setClientType(struct mwServiceIm *srvc, + enum mwImClientType type); + + +enum mwImClientType mwServiceIm_getClientType(struct mwServiceIm *srvc); + + +/** attempt to open a conversation. If the conversation was not + already open and it is accepted, + mwServiceImHandler::conversation_opened will be triggered. Upon + failure, mwServiceImHandler::conversation_closed will be + triggered */ +void mwConversation_open(struct mwConversation *conv); + + +/** close a conversation. If the conversation was not already closed, + mwServiceImHandler::conversation_closed will be triggered */ +void mwConversation_close(struct mwConversation *conv, guint32 err); + + +/** determine whether a conversation supports the given message type */ +gboolean mwConversation_supports(struct mwConversation *conv, + enum mwImSendType type); + + +enum mwImClientType mwConversation_getClientType(struct mwConversation *conv); + + +/** get the state of a conversation + + @see mwConversation_isOpen + @see mwConversation_isClosed + @see mwConversation_isPending +*/ +enum mwConversationState mwConversation_getState(struct mwConversation *conv); + + +/** send a message over an open conversation */ +int mwConversation_send(struct mwConversation *conv, + enum mwImSendType type, gconstpointer send); + + +/** @returns owning service for a conversation */ +struct mwServiceIm *mwConversation_getService(struct mwConversation *conv); + + +/** login information for conversation partner. returns NULL if conversation + is not OPEN */ +struct mwLoginInfo *mwConversation_getTargetInfo(struct mwConversation *conv); + + +/** ID for conversation partner */ +struct mwIdBlock *mwConversation_getTarget(struct mwConversation *conv); + + +/** set whether outgoing messages should be encrypted using the + negotiated cipher, if any */ +void mwConversation_setEncrypted(struct mwConversation *conv, + gboolean useCipher); + + +/** determine whether outgoing messages are being encrypted */ +gboolean mwConversation_isEncrypted(struct mwConversation *conv); + + +/** Associates client data with a conversation. If there is existing data, + it will not have its cleanup function called. + + @see mwConversation_getClientData + @see mwConversation_removeClientData +*/ +void mwConversation_setClientData(struct mwConversation *conv, + gpointer data, GDestroyNotify clean); + + +/** Reference associated client data + + @see mwConversation_setClientData + @see mwConversation_removeClientData + */ +gpointer mwConversation_getClientData(struct mwConversation *conv); + + +/** Remove any associated client data, calling the optional cleanup + function if one was provided + + @see mwConversation_setClientData + @see mwConversation_getClientData +*/ +void mwConversation_removeClientData(struct mwConversation *conv); + + +/** close and destroy the conversation and its backing channel, and + call the optional client data cleanup function */ +void mwConversation_free(struct mwConversation *conv); + + +#endif +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_place.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,137 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_PLACE_H +#define _MW_SRVC_PLACE_H + + +#include <glib/glist.h> +#include "mw_common.h" + + +/** Type identifier for the place service */ +#define mwService_PLACE 0x80000022 + + +/** @struct mwServicePlace */ +struct mwServicePlace; + + +/** @struct mwPlace */ +struct mwPlace; + + +struct mwPlaceHandler { + void (*opened)(struct mwPlace *place); + void (*closed)(struct mwPlace *place, guint32 code); + + void (*peerJoined)(struct mwPlace *place, + const struct mwIdBlock *peer); + + void (*peerParted)(struct mwPlace *place, + const struct mwIdBlock *peer); + + void (*peerSetAttribute)(struct mwPlace *place, + const struct mwIdBlock *peer, + guint32 attr, struct mwOpaque *o); + + void (*peerUnsetAttribute)(struct mwPlace *place, + const struct mwIdBlock *peer, + guint32 attr); + + void (*message)(struct mwPlace *place, + const struct mwIdBlock *who, + const char *msg); + + void (*clear)(struct mwServicePlace *srvc); +}; + + +enum mwPlacePeerAttribute { + mwPlacePeer_TYPING = 0x00000008, +}; + + +struct mwServicePlace * +mwServicePlace_new(struct mwSession *session, + struct mwPlaceHandler *handler); + + +struct mwPlaceHandler * +mwServicePlace_getHandler(struct mwServicePlace *srvc); + + +const GList *mwServicePlace_getPlaces(struct mwServicePlace *srvc); + + +struct mwPlace *mwPlace_new(struct mwServicePlace *srvc, + const char *name, const char *title); + + +struct mwServicePlace *mwPlace_getService(struct mwPlace *place); + + +const char *mwPlace_getName(struct mwPlace *place); + + +const char *mwPlace_getTitle(struct mwPlace *place); + + +int mwPlace_open(struct mwPlace *place); + + +int mwPlace_destroy(struct mwPlace *place, guint32 code); + + +/** returns a GList* of struct mwIdBlock*. The GList will need to be + freed after use, the mwIdBlock structures should not be modified + or freed */ +GList *mwPlace_getMembers(struct mwPlace *place); + + +int mwPlace_sendText(struct mwPlace *place, const char *msg); + + +/** send a legacy invitation for this place to a user. The user will + receive an apparent invitation from a Conference (rather than a + Place) */ +int mwPlace_legacyInvite(struct mwPlace *place, + struct mwIdBlock *idb, + const char *message); + + +int mwPlace_setAttribute(struct mwPlace *place, guint32 attrib, + struct mwOpaque *data); + + +int mwPlace_unsetAttribute(struct mwPlace *place, guint32 attrib); + + +void mwPlace_setClientData(struct mwPlace *place, + gpointer data, GDestroyNotify clean); + + +gpointer mwPlace_getClientData(struct mwPlace *place); + + +void mwPlace_removeClientData(struct mwPlace *place); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_resolve.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,140 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_RESOLVE_H +#define _MW_SRVC_RESOLVE_H + + +#include <glib.h> +#include <glib/glist.h> + + +/** Type identifier for the conference service */ +#define mwService_RESOLVE 0x00000015 + + +/** Return value of mwServiceResolve_search indicating an error */ +#define SEARCH_ERROR 0x00 + + +/** @struct mwServiceResolve + User lookup service */ +struct mwServiceResolve; + + +enum mwResolveFlag { + /** return unique results or none at all */ + mwResolveFlag_UNIQUE = 0x00000001, + + /** return only the first result */ + mwResolveFlag_FIRST = 0x00000002, + + /** search all directories, not just the first with a match */ + mwResolveFlag_ALL_DIRS = 0x00000004, + + /** search for users */ + mwResolveFlag_USERS = 0x00000008, + + /** search for groups */ + mwResolveFlag_GROUPS = 0x00000010, +}; + + +/** @see mwResolveResult */ +enum mwResolveCode { + /** successful search */ + mwResolveCode_SUCCESS = 0x00000000, + + /** only some of the nested searches were successful */ + mwResolveCode_PARTIAL = 0x00010000, + + /** more than one result (occurs when mwResolveFlag_UNIQUE is used + and more than one result would have been otherwise returned) */ + mwResolveCode_MULTIPLE = 0x80020000, + + /** the name is not resolvable due to its format */ + mwResolveCode_BAD_FORMAT = 0x80030000, +}; + + +enum mwResolveMatchType { + mwResolveMatch_USER = 0x00000001, + mwResolveMatch_GROUP = 0x00000002, +}; + + +struct mwResolveMatch { + char *id; /**< user id */ + char *name; /**< user name */ + char *desc; /**< description */ + guint32 type; /**< @see mwResolveMatchType */ +}; + + +struct mwResolveResult { + guint32 code; /**< @see mwResolveCode */ + char *name; /**< name of the result */ + GList *matches; /**< list of mwResolveMatch */ +}; + + +/** Handle the results of a resolve request. If there was a cleanup + function specified to mwServiceResolve_search, it will be called + upon the user data after this callback returns. + + @param srvc the resolve service + @param id the resolve request ID + @param code return code + @param results list of mwResolveResult + @param data optional user data attached to the request +*/ +typedef void (*mwResolveHandler) + (struct mwServiceResolve *srvc, + guint32 id, guint32 code, GList *results, + gpointer data); + + +/** Allocate a new resolve service */ +struct mwServiceResolve *mwServiceResolve_new(struct mwSession *); + + +/** Inisitate a resolve request. + + @param srvc the resolve service + @param queries list query strings + @param flags search flags + @param handler result handling function + @param data optional user data attached to the request + @param cleanup optional function to clean up user data + @return generated ID for the search request, or SEARCH_ERROR +*/ +guint32 mwServiceResolve_resolve(struct mwServiceResolve *srvc, + GList *queries, enum mwResolveFlag flags, + mwResolveHandler handler, + gpointer data, GDestroyNotify cleanup); + + +/** Cancel a resolve request by its generated ID. The handler function + will not be called, and the optional cleanup function will be + called upon the optional user data for the request */ +void mwServiceResolve_cancelResolve(struct mwServiceResolve *, guint32); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_store.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,186 @@ + +/* + 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 +*/ + +#ifndef _MW_SRVC_STORE_H +#define _MW_SRVC_STORE_H + + +#include <glib.h> +#include "mw_common.h" + + +/** Type identifier for the storage service */ +#define mwService_STORAGE 0x00000018 + + +/** @struct mwServiceStorage + @see mwServiceStorage_new + + Instance of the storage service */ +struct mwServiceStorage; + + +/** @struct mwStorage + + Unit Represents information intended for loading from or saving to + the storage service */ +struct mwStorageUnit; + + +/** The upper limit of reserved Lotus keys */ +#define LOTUS_RESERVED_LIMIT 0x186a0 + + +/** Check if a key is in the range of Lotus reserved keys */ +#define KEY_IS_LOTUS_RESERVED(key) \ + (((guint32) key) <= (LOTUS_RESERVED_LIMIT)) + + +/** Some common keys storage keys. Anything in the range 0x00 to + 0x186a0 (100000) is reserved for use by the Lotus + clients. */ +enum mwStorageKey { + + /** The buddy list, in the Sametime .dat file format. String */ + mwStore_AWARE_LIST = 0x00000000, + + /** Default text for chat invitations. String */ + mwStore_INVITE_CHAT = 0x00000006, + + /** Default text for meeting invitations. String */ + mwStore_INVITE_MEETING = 0x0000000e, + + /** Last five Away messages, separated by semicolon. String */ + mwStore_AWAY_MESSAGES = 0x00000050, + + /** Last five Busy (DND) messages, separated by semicolon. String */ + mwStore_BUSY_MESSAGES = 0x0000005a, + + /** Last five Active messages, separated by semicolon. String */ + mwStore_ACTIVE_MESSAGES = 0x00000064, +}; + + +/** Appropriate function type for load and store callbacks. + @param srvc the storage service + @param result the result value of the load or store call + @param item the storage unit loaded or saved + @param data optional user data +*/ +typedef void (*mwStorageCallback) + (struct mwServiceStorage *srvc, + guint32 result, struct mwStorageUnit *item, + gpointer data); + + +/** Allocates and initializes a storage service instance for use on + the passed session. */ +struct mwServiceStorage *mwServiceStorage_new(struct mwSession *); + + +/** create an empty storage unit */ +struct mwStorageUnit *mwStorageUnit_new(guint32 key); + + +/** creates a storage unit with the passed key, and a copy of data. */ +struct mwStorageUnit *mwStorageUnit_newOpaque(guint32 key, + struct mwOpaque *data); + + +/** creates a storage unit with the passed key, and an encapsulated + boolean value */ +struct mwStorageUnit *mwStorageUnit_newBoolean(guint32 key, + gboolean val); + + +struct mwStorageUnit *mwStorageUnit_newInteger(guint32 key, + guint32 val); + + +/** creates a storage unit with the passed key, and an encapsulated + string value. */ +struct mwStorageUnit *mwStorageUnit_newString(guint32 key, + const char *str); + + +/** get the key for the given storage unit */ +guint32 mwStorageUnit_getKey(struct mwStorageUnit *); + + +/** attempts to obtain a boolean value from a storage unit. If the + unit is empty, or does not contain the type in a recongnizable + format, val is returned instead */ +gboolean mwStorageUnit_asBoolean(struct mwStorageUnit *, gboolean val); + + +/** attempts to obtain a guint32 value from a storage unit. If the + unit is empty, or does not contain the type in a recognizable + format, val is returned instead */ +guint32 mwStorageUnit_asInteger(struct mwStorageUnit *, guint32 val); + + +/** attempts to obtain a string value from a storage unit. If the unit + is empty, or does not contain the type in a recognizable format, + NULL is returned instead. Note that the string returned is a copy, + and will need to be deallocated at some point. */ +char *mwStorageUnit_asString(struct mwStorageUnit *); + + +/** direct access to the opaque data backing the storage unit */ +struct mwOpaque *mwStorageUnit_asOpaque(struct mwStorageUnit *); + + +/** clears and frees a storage unit */ +void mwStorageUnit_free(struct mwStorageUnit *); + + +/** Initiates a load call to the storage service. If the service is + not currently available, the call will be cached and processed + when the service is started. + + @param srvc the storage service + @param item storage unit to load + @param cb callback function when the load call completes + @param data user data for callback + @param data_free optional cleanup function for user data +*/ +void mwServiceStorage_load(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify data_free); + + +/** Initiates a store call to the storage service. If the service is + not currently available, the call will be cached and processed + when the service is started. + + @param srvc the storage service + @param item storage unit to save + @param cb callback function when the load call completes + @param data optional user data for callback + @param data_free optional cleanup function for user data + */ +void mwServiceStorage_save(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify data_free); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_st_list.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,200 @@ + +/* + 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 +*/ + +#ifndef _MW_ST_LIST_H +#define _MW_ST_LIST_H + + +#include <glib.h> +#include <glib/glist.h> +#include "mw_common.h" + + +#define ST_LIST_MAJOR 3 +#define ST_LIST_MINOR 1 +#define ST_LIST_MICRO 3 + + +enum mwSametimeGroupType { + mwSametimeGroup_NORMAL = 1, /**< a normal group of users */ + mwSametimeGroup_DYNAMIC = 2, /**< a server-side group */ + mwSametimeGroup_UNKNOWN = 0, /**< error determining group type */ +}; + + +enum mwSametimeUserType { + mwSametimeUser_NORMAL = 1, /**< user on same community */ + mwSametimeUser_EXTERNAL = 2, /**< external user */ + mwSametimeUser_UNKNOWN = 0, /**< error determining user type */ +}; + + +/** @struct mwSametimeList + + Represents a group-based buddy list. */ +struct mwSametimeList; + + +/** @struct mwSametimeGroup + + Represents a group in a buddy list */ +struct mwSametimeGroup; + + +/** @struct mwSametimeUser + + Represents a user in a group in a buddy list */ +struct mwSametimeUser; + + +/** Create a new list */ +struct mwSametimeList *mwSametimeList_new(void); + + +/** Free the list, all of its groups, and all of the groups' members */ +void mwSametimeList_free(struct mwSametimeList *l); + + +/** Load a sametime list from a buffer. The list must be encapsulated + as a string (eg, the first two bytes in the buffer should be the + length of the string) */ +void mwSametimeList_get(struct mwGetBuffer *b, struct mwSametimeList *l); + + +/** Write a sametime list onto a buffer. The list will be encapsulated + in a string (the first two bytes written will be the length of the + rest of the written list data) */ +void mwSametimeList_put(struct mwPutBuffer *b, struct mwSametimeList *l); + + +/** convert a plain string into a sametime list */ +struct mwSametimeList *mwSametimeList_load(const char *str); + + +/** convert a sametime list into a string */ +char *mwSametimeList_store(struct mwSametimeList *l); + + +void mwSametimeList_setMajor(struct mwSametimeList *l, guint v); + + +guint mwSametimeList_getMajor(struct mwSametimeList *l); + + +void mwSametimeList_setMinor(struct mwSametimeList *l, guint v); + + +guint mwSametimeList_getMinor(struct mwSametimeList *l); + + +void mwSametimeList_setMicro(struct mwSametimeList *l, guint v); + + +guint mwSametimeList_getMicro(struct mwSametimeList *l); + + +/** Get a GList snapshot of the groups in a list */ +GList *mwSametimeList_getGroups(struct mwSametimeList *l); + + +struct mwSametimeGroup * +mwSametimeList_findGroup(struct mwSametimeList *l, + const char *name); + + +/** Create a new group in a list */ +struct mwSametimeGroup * +mwSametimeGroup_new(struct mwSametimeList *l, + enum mwSametimeGroupType type, + const char *name); + + +/** Remove a group from its list, and free it. Also frees all users + contained in the group */ +void mwSametimeGroup_free(struct mwSametimeGroup *g); + + +enum mwSametimeGroupType mwSametimeGroup_getType(struct mwSametimeGroup *g); + + +const char *mwSametimeGroup_getName(struct mwSametimeGroup *g); + + +void mwSametimeGroup_setAlias(struct mwSametimeGroup *g, + const char *alias); + + +const char *mwSametimeGroup_getAlias(struct mwSametimeGroup *g); + + +void mwSametimeGroup_setOpen(struct mwSametimeGroup *g, gboolean open); + + +gboolean mwSametimeGroup_isOpen(struct mwSametimeGroup *g); + + +struct mwSametimeList *mwSametimeGroup_getList(struct mwSametimeGroup *g); + + +/** Get a GList snapshot of the users in a list */ +GList *mwSametimeGroup_getUsers(struct mwSametimeGroup *g); + + +struct mwSametimeUser * +mwSametimeGroup_findUser(struct mwSametimeGroup *g, + struct mwIdBlock *user); + + +/** Create a user in a group */ +struct mwSametimeUser * +mwSametimeUser_new(struct mwSametimeGroup *g, + enum mwSametimeUserType type, + struct mwIdBlock *user); + + +/** Remove user from its group, and free it */ +void mwSametimeUser_free(struct mwSametimeUser *u); + + +struct mwSametimeGroup *mwSametimeUser_getGroup(struct mwSametimeUser *u); + + +enum mwSametimeUserType mwSametimeUser_getType(struct mwSametimeUser *u); + + +const char *mwSametimeUser_getUser(struct mwSametimeUser *u); + + +const char *mwSametimeUser_getCommunity(struct mwSametimeUser *u); + + +void mwSametimeUser_setShortName(struct mwSametimeUser *u, const char *name); + + +const char *mwSametimeUser_getShortName(struct mwSametimeUser *u); + + +void mwSametimeUser_setAlias(struct mwSametimeUser *u, const char *alias); + + +const char *mwSametimeUser_getAlias(struct mwSametimeUser *u); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_util.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,80 @@ + +/* + 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 "mw_util.h" + + +static void collect_keys(gpointer key, gpointer val, gpointer data) { + GList **list = data; + *list = g_list_append(*list, key); +} + + +GList *map_collect_keys(GHashTable *ht) { + GList *ret = NULL; + g_hash_table_foreach(ht, collect_keys, &ret); + return ret; +} + + +static void collect_values(gpointer key, gpointer val, gpointer data) { + GList **list = data; + *list = g_list_append(*list, val); +} + + +GList *map_collect_values(GHashTable *ht) { + GList *ret = NULL; + g_hash_table_foreach(ht, collect_values, &ret); + return ret; +} + + +struct mw_datum *mw_datum_new(gpointer data, GDestroyNotify clear) { + struct mw_datum *d = g_new(struct mw_datum, 1); + mw_datum_set(d, data, clear); + return d; +} + + +void mw_datum_set(struct mw_datum *d, gpointer data, GDestroyNotify clear) { + d->data = data; + d->clear = clear; +} + + +gpointer mw_datum_get(struct mw_datum *d) { + return d->data; +} + + +void mw_datum_clear(struct mw_datum *d) { + if(d->clear) { + d->clear(d->data); + d->clear = NULL; + } + d->data = NULL; +} + + +void mw_datum_free(struct mw_datum *d) { + mw_datum_clear(d); + g_free(d); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_util.h Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,85 @@ + +/* + 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 +*/ + +#ifndef _MW_UTIL_H +#define _MW_UTIL_H + + +#include <glib.h> +#include <glib/ghash.h> +#include <glib/glist.h> + + +#define map_guint_new() \ + g_hash_table_new(g_direct_hash, g_direct_equal) + + +#define map_guint_new_full(valfree) \ + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (valfree)) + + +#define map_guint_insert(ht, key, val) \ + g_hash_table_insert((ht), GUINT_TO_POINTER((guint)(key)), (val)) + + +#define map_guint_replace(ht, key, val) \ + g_hash_table_replace((ht), GUINT_TO_POINTER((guint)(key)), (val)) + + +#define map_guint_lookup(ht, key) \ + g_hash_table_lookup((ht), GUINT_TO_POINTER((guint)(key))) + + +#define map_guint_remove(ht, key) \ + g_hash_table_remove((ht), GUINT_TO_POINTER((guint)(key))) + + +#define map_guint_steal(ht, key) \ + g_hash_table_steal((ht), GUINT_TO_POINTER((guint)(key))) + + +GList *map_collect_keys(GHashTable *ht); + + +GList *map_collect_values(GHashTable *ht); + + +struct mw_datum { + gpointer data; + GDestroyNotify clear; +}; + + +struct mw_datum *mw_datum_new(gpointer data, GDestroyNotify clear); + + +void mw_datum_set(struct mw_datum *d, gpointer data, GDestroyNotify clear); + + +gpointer mw_datum_get(struct mw_datum *d); + + +void mw_datum_clear(struct mw_datum *d); + + +void mw_datum_free(struct mw_datum *d); + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/service.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,267 @@ + +/* + 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 "mw_channel.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" + + +/* I tried to be explicit with the g_return_* calls, to make the debug + logging a bit more sensible. Hence all the explicit "foo != NULL" + checks. */ + + +void mwService_recvCreate(struct mwService *s, struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* ensure none are null, ensure that the service and channel belong + to the same session, and ensure that the message belongs on the + channel */ + g_return_if_fail(s != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(s->session == mwChannel_getSession(chan)); + g_return_if_fail(mwChannel_getId(chan) == msg->channel); + + if(s->recv_create) { + s->recv_create(s, chan, msg); + } else { + mwChannel_destroy(chan, ERR_FAILURE, NULL); + } +} + + +void mwService_recvAccept(struct mwService *s, struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + /* ensure none are null, ensure that the service and channel belong + to the same session, and ensure that the message belongs on the + channel */ + g_return_if_fail(s != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(s->session == mwChannel_getSession(chan)); + g_return_if_fail(mwChannel_getId(chan) == msg->head.channel); + + if(s->recv_accept) + s->recv_accept(s, chan, msg); +} + + +void mwService_recvDestroy(struct mwService *s, struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + /* ensure none are null, ensure that the service and channel belong + to the same session, and ensure that the message belongs on the + channel */ + g_return_if_fail(s != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(s->session == mwChannel_getSession(chan)); + g_return_if_fail(mwChannel_getId(chan) == msg->head.channel); + + if(s->recv_destroy) + s->recv_destroy(s, chan, msg); +} + + +void mwService_recv(struct mwService *s, struct mwChannel *chan, + guint16 msg_type, struct mwOpaque *data) { + + /* ensure that none are null. zero-length messages are acceptable */ + g_return_if_fail(s != NULL); + g_return_if_fail(chan != NULL); + g_return_if_fail(data != NULL); + g_return_if_fail(s->session == mwChannel_getSession(chan)); + + /* + g_message("mwService_recv: session = %p, service = %p, b = %p, n = %u", + mwService_getSession(s), s, data->data, data->len); + */ + + if(s->recv) + s->recv(s, chan, msg_type, data); +} + + +guint32 mwService_getType(struct mwService *s) { + g_return_val_if_fail(s != NULL, 0x00); + return s->type; +} + + +const char *mwService_getName(struct mwService *s) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->get_name != NULL, NULL); + + return s->get_name(s); +} + + +const char *mwService_getDesc(struct mwService *s) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->get_desc != NULL, NULL); + + return s->get_desc(s); +} + + +struct mwSession *mwService_getSession(struct mwService *s) { + g_return_val_if_fail(s != NULL, NULL); + return s->session; +} + + +void mwService_init(struct mwService *srvc, struct mwSession *sess, + guint32 type) { + + /* ensure nothing is null, and there's no such thing as a zero + service type */ + g_return_if_fail(srvc != NULL); + g_return_if_fail(sess != NULL); + g_return_if_fail(type != 0x00); + + srvc->session = sess; + srvc->type = type; + srvc->state = mwServiceState_STOPPED; +} + + +enum mwServiceState mwService_getState(struct mwService *srvc) { + g_return_val_if_fail(srvc != NULL, mwServiceState_STOPPED); + return srvc->state; +} + + +void mwService_start(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + if(! MW_SERVICE_IS_STOPPED(srvc)) + return; + + srvc->state = mwServiceState_STARTING; + g_message("starting service %s", NSTR(mwService_getName(srvc))); + + if(srvc->start) { + srvc->start(srvc); + } else { + mwService_started(srvc); + } +} + + +void mwService_started(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + srvc->state = mwServiceState_STARTED; + g_message("started service %s", NSTR(mwService_getName(srvc))); +} + + +void mwService_error(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + if(MW_SERVICE_IS_DEAD(srvc)) + return; + + srvc->state = mwServiceState_ERROR; + g_message("error in service %s", NSTR(mwService_getName(srvc))); + + if(srvc->stop) { + srvc->stop(srvc); + } else { + mwService_stopped(srvc); + } +} + + +void mwService_stop(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + if(MW_SERVICE_IS_DEAD(srvc)) + return; + + srvc->state = mwServiceState_STOPPING; + g_message("stopping service %s", NSTR(mwService_getName(srvc))); + + if(srvc->stop) { + srvc->stop(srvc); + } else { + mwService_stopped(srvc); + } +} + + +void mwService_stopped(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + if(srvc->state != mwServiceState_STOPPED) { + srvc->state = mwServiceState_STOPPED; + g_message("stopped service %s", NSTR(mwService_getName(srvc))); + } +} + + +void mwService_free(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + mwService_stop(srvc); + + if(srvc->clear) + srvc->clear(srvc); + + if(srvc->client_cleanup) + srvc->client_cleanup(srvc->client_data); + + g_free(srvc); +} + + +/** @todo switch the following to using mw_datum */ + +void mwService_setClientData(struct mwService *srvc, + gpointer data, GDestroyNotify cleanup) { + + g_return_if_fail(srvc != NULL); + + srvc->client_data = data; + srvc->client_cleanup = cleanup; +} + + +gpointer mwService_getClientData(struct mwService *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->client_data; +} + + +void mwService_removeClientData(struct mwService *srvc) { + g_return_if_fail(srvc != NULL); + + if(srvc->client_cleanup) { + srvc->client_cleanup(srvc->client_data); + srvc->client_cleanup = NULL; + } + + srvc->client_data = NULL; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/session.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,1206 @@ + +/* + 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.h> +#include <string.h> + +#include "mw_channel.h" +#include "mw_cipher.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_util.h" + + +/** the hash table key for a service, for mwSession::services */ +#define SERVICE_KEY(srvc) mwService_getType(srvc) + +/** the hash table key for a cipher, for mwSession::ciphers */ +#define CIPHER_KEY(ciph) mwCipher_getType(ciph) + + +#define GPOINTER(val) (GUINT_TO_POINTER((guint) (val))) +#define GUINT(val) (GPOINTER_TO_UINT((val))) + + +struct mwSession { + + /** provides I/O and callback functions */ + struct mwSessionHandler *handler; + + enum mwSessionState state; /**< session state */ + gpointer state_info; /**< additional state info */ + + /* input buffering for an incoming message */ + guchar *buf; /**< buffer for incoming message data */ + gsize buf_len; /**< length of buf */ + gsize buf_used; /**< offset to last-used byte of buf */ + + struct mwLoginInfo login; /**< login information */ + struct mwUserStatus status; /**< user status */ + struct mwPrivacyInfo privacy; /**< privacy list */ + + /** the collection of channels */ + struct mwChannelSet *channels; + + /** the collection of services, keyed to guint32 service id */ + GHashTable *services; + + /** the collection of ciphers, keyed to guint16 cipher type */ + GHashTable *ciphers; + + /** arbitrary key:value pairs */ + GHashTable *attributes; + + /** optional user data */ + struct mw_datum client_data; +}; + + +static void property_set(struct mwSession *s, const char *key, + gpointer val, GDestroyNotify clean) { + + g_hash_table_insert(s->attributes, g_strdup(key), + mw_datum_new(val, clean)); +} + + +static gpointer property_get(struct mwSession *s, const char *key) { + struct mw_datum *p = g_hash_table_lookup(s->attributes, key); + return p? p->data: NULL; +} + + +static void property_del(struct mwSession *s, const char *key) { + g_hash_table_remove(s->attributes, key); +} + + +/** + set up the default properties for a newly created session +*/ +static void session_defaults(struct mwSession *s) { + property_set(s, mwSession_CLIENT_VER_MAJOR, + GPOINTER(MW_PROTOCOL_VERSION_MAJOR), NULL); + + property_set(s, mwSession_CLIENT_VER_MINOR, + GPOINTER(MW_PROTOCOL_VERSION_MINOR), NULL); + + property_set(s, mwSession_CLIENT_TYPE_ID, + GPOINTER(mwLogin_MEANWHILE), NULL); +} + + +struct mwSession *mwSession_new(struct mwSessionHandler *handler) { + struct mwSession *s; + + g_return_val_if_fail(handler != NULL, NULL); + + /* consider io_write and io_close to be absolute necessities */ + g_return_val_if_fail(handler->io_write != NULL, NULL); + g_return_val_if_fail(handler->io_close != NULL, NULL); + + s = g_new0(struct mwSession, 1); + + s->state = mwSession_STOPPED; + + s->handler = handler; + + s->channels = mwChannelSet_new(s); + s->services = map_guint_new(); + s->ciphers = map_guint_new(); + + s->attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify) mw_datum_free); + + session_defaults(s); + + return s; +} + + +/** free and reset the session buffer */ +static void session_buf_free(struct mwSession *s) { + g_return_if_fail(s != NULL); + + g_free(s->buf); + s->buf = NULL; + s->buf_len = 0; + s->buf_used = 0; +} + + +/** a polite string version of the session state enum */ +static const char *state_str(enum mwSessionState state) { + switch(state) { + case mwSession_STARTING: return "starting"; + case mwSession_HANDSHAKE: return "handshake sent"; + case mwSession_HANDSHAKE_ACK: return "handshake acknowledged"; + case mwSession_LOGIN: return "login sent"; + case mwSession_LOGIN_REDIR: return "login redirected"; + case mwSession_LOGIN_CONT: return "forcing login"; + case mwSession_LOGIN_ACK: return "login acknowledged"; + case mwSession_STARTED: return "started"; + case mwSession_STOPPING: return "stopping"; + case mwSession_STOPPED: return "stopped"; + + case mwSession_UNKNOWN: /* fall-through */ + default: return "UNKNOWN"; + } +} + + +void mwSession_free(struct mwSession *s) { + struct mwSessionHandler *h; + + g_return_if_fail(s != NULL); + + if(! mwSession_isStopped(s)) { + g_debug("session is not stopped (state: %s), proceeding with free", + state_str(s->state)); + } + + h = s->handler; + if(h && h->clear) h->clear(s); + s->handler = NULL; + + session_buf_free(s); + + mwChannelSet_free(s->channels); + g_hash_table_destroy(s->services); + g_hash_table_destroy(s->ciphers); + g_hash_table_destroy(s->attributes); + + mwLoginInfo_clear(&s->login); + mwUserStatus_clear(&s->status); + mwPrivacyInfo_clear(&s->privacy); + + g_free(s); +} + + +/** write data to the session handler */ +static int io_write(struct mwSession *s, const guchar *buf, gsize len) { + g_return_val_if_fail(s != NULL, -1); + g_return_val_if_fail(s->handler != NULL, -1); + g_return_val_if_fail(s->handler->io_write != NULL, -1); + + return s->handler->io_write(s, buf, len); +} + + +/** close the session handler */ +static void io_close(struct mwSession *s) { + g_return_if_fail(s != NULL); + g_return_if_fail(s->handler != NULL); + g_return_if_fail(s->handler->io_close != NULL); + + s->handler->io_close(s); +} + + +static void state(struct mwSession *s, enum mwSessionState state, + gpointer info) { + + struct mwSessionHandler *sh; + + g_return_if_fail(s != NULL); + g_return_if_fail(s->handler != NULL); + + if(mwSession_isState(s, state)) return; + + s->state = state; + s->state_info = info; + + switch(state) { + case mwSession_STOPPING: + case mwSession_STOPPED: + g_message("session state: %s (0x%08x)", state_str(state), + GPOINTER_TO_UINT(info)); + break; + + case mwSession_LOGIN_REDIR: + g_message("session state: %s (%s)", state_str(state), + (char *)info); + break; + + default: + g_message("session state: %s", state_str(state)); + } + + sh = s->handler; + if(sh && sh->on_stateChange) + sh->on_stateChange(s, state, info); +} + + +void mwSession_start(struct mwSession *s) { + struct mwMsgHandshake *msg; + int ret; + + g_return_if_fail(s != NULL); + g_return_if_fail(mwSession_isStopped(s)); + + if(mwSession_isStarted(s) || mwSession_isStarting(s)) { + g_debug("attempted to start session that is already started/starting"); + return; + } + + state(s, mwSession_STARTING, 0); + + msg = (struct mwMsgHandshake *) mwMessage_new(mwMessage_HANDSHAKE); + msg->major = GUINT(property_get(s, mwSession_CLIENT_VER_MAJOR)); + msg->minor = GUINT(property_get(s, mwSession_CLIENT_VER_MINOR)); + msg->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID)); + + msg->loclcalc_addr = GUINT(property_get(s, mwSession_CLIENT_IP)); + + if(msg->major >= 0x001e && msg->minor >= 0x001d) { + msg->unknown_a = 0x0100; + msg->local_host = (char *) property_get(s, mwSession_CLIENT_HOST); + } + + ret = mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + if(ret) { + mwSession_stop(s, CONNECTION_BROKEN); + } else { + state(s, mwSession_HANDSHAKE, 0); + } +} + + +void mwSession_stop(struct mwSession *s, guint32 reason) { + GList *list, *l = NULL; + struct mwMsgChannelDestroy *msg; + + g_return_if_fail(s != NULL); + + if(mwSession_isStopped(s) || mwSession_isStopping(s)) { + g_debug("attempted to stop session that is already stopped/stopping"); + return; + } + + state(s, mwSession_STOPPING, GUINT_TO_POINTER(reason)); + + for(list = l = mwSession_getServices(s); l; l = l->next) + mwService_stop(MW_SERVICE(l->data)); + g_list_free(list); + + msg = (struct mwMsgChannelDestroy *) + mwMessage_new(mwMessage_CHANNEL_DESTROY); + + msg->head.channel = MW_MASTER_CHANNEL_ID; + msg->reason = reason; + + /* don't care if this fails, we're closing the connection anyway */ + mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + session_buf_free(s); + + /* close the connection */ + io_close(s); + + state(s, mwSession_STOPPED, GUINT_TO_POINTER(reason)); +} + + +/** compose authentication information into an opaque based on the + password, encrypted via RC2/40 */ +static void compose_auth_rc2_40(struct mwOpaque *auth, const char *pass) { + guchar iv[8], key[5]; + struct mwOpaque a, b, z; + struct mwPutBuffer *p; + + /* get an IV and a random five-byte key */ + mwIV_init(iv); + mwKeyRandom(key, 5); + + /* the opaque with the key */ + a.len = 5; + a.data = key; + + /* the opaque to receive the encrypted pass */ + b.len = 0; + b.data = NULL; + + /* the plain-text pass dressed up as an opaque */ + z.len = strlen(pass); + z.data = (guchar *) pass; + + /* the opaque with the encrypted pass */ + mwEncrypt(a.data, a.len, iv, &z, &b); + + /* an opaque containing the other two opaques */ + p = mwPutBuffer_new(); + mwOpaque_put(p, &a); + mwOpaque_put(p, &b); + mwPutBuffer_finalize(auth, p); + + /* this is the only one to clear, as the other uses a static buffer */ + mwOpaque_clear(&b); +} + + +static void compose_auth_rc2_128(struct mwOpaque *auth, const char *pass, + guint32 magic, struct mwOpaque *rkey) { + + guchar iv[8]; + struct mwOpaque a, b, c; + struct mwPutBuffer *p; + + struct mwMpi *private, *public; + struct mwMpi *remote; + struct mwMpi *shared; + + private = mwMpi_new(); + public = mwMpi_new(); + remote = mwMpi_new(); + shared = mwMpi_new(); + + mwIV_init(iv); + + mwMpi_randDHKeypair(private, public); + mwMpi_import(remote, rkey); + mwMpi_calculateDHShared(shared, remote, private); + + /* put the password in opaque a */ + p = mwPutBuffer_new(); + guint32_put(p, magic); + mwString_put(p, pass); + mwPutBuffer_finalize(&a, p); + + /* put the shared key in opaque b */ + mwMpi_export(shared, &b); + + /* encrypt the password (a) using the shared key (b), put the result + in opaque c */ + mwEncrypt(b.data+(b.len-16), 16, iv, &a, &c); + + /* don't need the shared key anymore, re-use opaque (b) as the + export of the public key */ + mwOpaque_clear(&b); + mwMpi_export(public, &b); + + p = mwPutBuffer_new(); + guint16_put(p, 0x0001); /* XXX: unknown */ + mwOpaque_put(p, &b); + mwOpaque_put(p, &c); + mwPutBuffer_finalize(auth, p); + + mwOpaque_clear(&a); + mwOpaque_clear(&b); + mwOpaque_clear(&c); + + mwMpi_free(private); + mwMpi_free(public); + mwMpi_free(remote); + mwMpi_free(shared); +} + + +/** handle the receipt of a handshake_ack message by sending the login + message */ +static void HANDSHAKE_ACK_recv(struct mwSession *s, + struct mwMsgHandshakeAck *msg) { + struct mwMsgLogin *log; + int ret; + + g_return_if_fail(s != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(mwSession_isState(s, mwSession_HANDSHAKE) || + mwSession_isState(s, mwSession_LOGIN_CONT)); + + if(mwSession_isState(s, mwSession_LOGIN_CONT)) { + /* this is a login continuation, don't re-send the login. We + should receive a login ack in a moment */ + + state(s, mwSession_HANDSHAKE_ACK, 0); + state(s, mwSession_LOGIN, 0); + return; + + } else { + state(s, mwSession_HANDSHAKE_ACK, 0); + } + + /* record the major/minor versions from the server */ + property_set(s, mwSession_SERVER_VER_MAJOR, GPOINTER(msg->major), NULL); + property_set(s, mwSession_SERVER_VER_MINOR, GPOINTER(msg->minor), NULL); + + /* compose the login message */ + log = (struct mwMsgLogin *) mwMessage_new(mwMessage_LOGIN); + log->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID)); + log->name = g_strdup(property_get(s, mwSession_AUTH_USER_ID)); + + /** @todo default to password for now. later use token optionally */ + { + const char *pw; + pw = (const char *) property_get(s, mwSession_AUTH_PASSWORD); + + if(msg->data.len >= 64) { + /* good login encryption */ + log->auth_type = mwAuthType_RC2_128; + compose_auth_rc2_128(&log->auth_data, pw, msg->magic, &msg->data); + + } else { + /* BAD login encryption */ + log->auth_type = mwAuthType_RC2_40; + compose_auth_rc2_40(&log->auth_data, pw); + } + } + + /* send the login message */ + ret = mwSession_send(s, MW_MESSAGE(log)); + mwMessage_free(MW_MESSAGE(log)); + + if(! ret) { + /* sent login OK, set state appropriately */ + state(s, mwSession_LOGIN, 0); + } +} + + +/** handle the receipt of a login_ack message. This completes the + startup sequence for the session */ +static void LOGIN_ACK_recv(struct mwSession *s, + struct mwMsgLoginAck *msg) { + GList *ll, *l; + + g_return_if_fail(s != NULL); + g_return_if_fail(msg != NULL); + g_return_if_fail(mwSession_isState(s, mwSession_LOGIN)); + + /* store the login information in the session */ + mwLoginInfo_clear(&s->login); + mwLoginInfo_clone(&s->login, &msg->login); + + state(s, mwSession_LOGIN_ACK, 0); + + /* start up our services */ + for(ll = l = mwSession_getServices(s); l; l = l->next) { + mwService_start(l->data); + } + g_list_free(ll); + + /* @todo any further startup stuff? */ + + state(s, mwSession_STARTED, 0); +} + + +static void CHANNEL_CREATE_recv(struct mwSession *s, + struct mwMsgChannelCreate *msg) { + struct mwChannel *chan; + chan = mwChannel_newIncoming(s->channels, msg->channel); + + /* hand off to channel */ + mwChannel_recvCreate(chan, msg); +} + + +static void CHANNEL_ACCEPT_recv(struct mwSession *s, + struct mwMsgChannelAccept *msg) { + struct mwChannel *chan; + chan = mwChannel_find(s->channels, msg->head.channel); + + g_return_if_fail(chan != NULL); + + /* hand off to channel */ + mwChannel_recvAccept(chan, msg); +} + + +static void CHANNEL_DESTROY_recv(struct mwSession *s, + struct mwMsgChannelDestroy *msg) { + + /* the server can indicate that we should close the session by + destroying the zero channel */ + if(msg->head.channel == MW_MASTER_CHANNEL_ID) { + mwSession_stop(s, msg->reason); + + } else { + struct mwChannel *chan; + chan = mwChannel_find(s->channels, msg->head.channel); + + /* we don't have any such channel... so I guess we destroyed it. + This is to remove a warning from timing errors when two clients + both try to close a channel at about the same time. */ + if(! chan) return; + + /* hand off to channel */ + mwChannel_recvDestroy(chan, msg); + } +} + + +static void CHANNEL_SEND_recv(struct mwSession *s, + struct mwMsgChannelSend *msg) { + struct mwChannel *chan; + chan = mwChannel_find(s->channels, msg->head.channel); + + /* if we don't have any such channel, we're certainly not going to + accept data from it */ + if(! chan) return; + + /* hand off to channel */ + mwChannel_recv(chan, msg); +} + + +static void SET_PRIVACY_LIST_recv(struct mwSession *s, + struct mwMsgSetPrivacyList *msg) { + struct mwSessionHandler *sh = s->handler; + + g_info("SET_PRIVACY_LIST"); + + mwPrivacyInfo_clear(&s->privacy); + mwPrivacyInfo_clone(&s->privacy, &msg->privacy); + + if(sh && sh->on_setPrivacyInfo) + sh->on_setPrivacyInfo(s); +} + + +static void SET_USER_STATUS_recv(struct mwSession *s, + struct mwMsgSetUserStatus *msg) { + struct mwSessionHandler *sh = s->handler; + + mwUserStatus_clear(&s->status); + mwUserStatus_clone(&s->status, &msg->status); + + if(sh && sh->on_setUserStatus) + sh->on_setUserStatus(s); +} + + +static void SENSE_SERVICE_recv(struct mwSession *s, + struct mwMsgSenseService *msg) { + struct mwService *srvc; + + srvc = mwSession_getService(s, msg->service); + if(srvc) mwService_start(srvc); +} + + +static void ADMIN_recv(struct mwSession *s, struct mwMsgAdmin *msg) { + struct mwSessionHandler *sh = s->handler; + + if(sh && sh->on_admin) + sh->on_admin(s, msg->text); +} + + +static void ANNOUNCE_recv(struct mwSession *s, struct mwMsgAnnounce *msg) { + struct mwSessionHandler *sh = s->handler; + + if(sh && sh->on_announce) + sh->on_announce(s, &msg->sender, msg->may_reply, msg->text); +} + + +static void LOGIN_REDIRECT_recv(struct mwSession *s, + struct mwMsgLoginRedirect *msg) { + + state(s, mwSession_LOGIN_REDIR, msg->host); +} + + +#define CASE(var, type) \ +case mwMessage_ ## var: \ + var ## _recv(s, (struct type *) msg); \ + break; + + +static void session_process(struct mwSession *s, + const guchar *buf, gsize len) { + + struct mwOpaque o = { .len = len, .data = (guchar *) buf }; + struct mwGetBuffer *b; + struct mwMessage *msg; + + g_return_if_fail(s != NULL); + g_return_if_fail(buf != NULL); + + /* ignore zero-length messages */ + if(len == 0) return; + + /* wrap up buf */ + b = mwGetBuffer_wrap(&o); + + /* attempt to parse the message. */ + msg = mwMessage_get(b); + + if(mwGetBuffer_error(b)) { + mw_mailme_opaque(&o, "parsing of message failed"); + } + + mwGetBuffer_free(b); + + g_return_if_fail(msg != NULL); + + /* handle each of the appropriate incoming types of mwMessage */ + switch(msg->type) { + CASE(HANDSHAKE_ACK, mwMsgHandshakeAck); + CASE(LOGIN_REDIRECT, mwMsgLoginRedirect); + CASE(LOGIN_ACK, mwMsgLoginAck); + CASE(CHANNEL_CREATE, mwMsgChannelCreate); + CASE(CHANNEL_DESTROY, mwMsgChannelDestroy); + CASE(CHANNEL_SEND, mwMsgChannelSend); + CASE(CHANNEL_ACCEPT, mwMsgChannelAccept); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SET_USER_STATUS, mwMsgSetUserStatus); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ADMIN, mwMsgAdmin); + CASE(ANNOUNCE, mwMsgAnnounce); + + default: + g_warning("unknown message type 0x%04x, no handler", msg->type); + } + + mwMessage_free(msg); +} + + +#undef CASE + + +#define ADVANCE(b, n, count) { b += count; n -= count; } + + +/* handle input to complete an existing buffer */ +static gsize session_recv_cont(struct mwSession *s, + const guchar *b, gsize n) { + + /* determine how many bytes still required */ + gsize x = s->buf_len - s->buf_used; + + /* g_message(" session_recv_cont: session = %p, b = %p, n = %u", + s, b, n); */ + + if(n < x) { + /* not quite enough; still need some more */ + memcpy(s->buf+s->buf_used, b, n); + s->buf_used += n; + return 0; + + } else { + /* enough to finish the buffer, at least */ + memcpy(s->buf+s->buf_used, b, x); + ADVANCE(b, n, x); + + if(s->buf_len == 4) { + /* if only the length bytes were being buffered, we'll now try + to complete an actual message */ + + struct mwOpaque o = { 4, s->buf }; + struct mwGetBuffer *gb = mwGetBuffer_wrap(&o); + x = guint32_peek(gb); + mwGetBuffer_free(gb); + + if(n < x) { + /* there isn't enough to meet the demands of the length, so + we'll buffer it for next time */ + + guchar *t; + x += 4; + t = (guchar *) g_malloc(x); + memcpy(t, s->buf, 4); + memcpy(t+4, b, n); + + session_buf_free(s); + + s->buf = t; + s->buf_len = x; + s->buf_used = n + 4; + return 0; + + } else { + /* there's enough (maybe more) for a full message. don't need + the old session buffer (which recall, was only the length + bytes) any more */ + + session_buf_free(s); + session_process(s, b, x); + ADVANCE(b, n, x); + } + + } else { + /* process the now-complete buffer. remember to skip the first + four bytes, since they're just the size count */ + session_process(s, s->buf+4, s->buf_len-4); + session_buf_free(s); + } + } + + return n; +} + + +/* handle input when there's nothing previously buffered */ +static gsize session_recv_empty(struct mwSession *s, + const guchar *b, gsize n) { + + struct mwOpaque o = { n, (guchar *) b }; + struct mwGetBuffer *gb; + gsize x; + + if(n < 4) { + /* uh oh. less than four bytes means we've got an incomplete + length indicator. Have to buffer to get the rest of it. */ + s->buf = (guchar *) g_malloc0(4); + memcpy(s->buf, b, n); + s->buf_len = 4; + s->buf_used = n; + return 0; + } + + /* peek at the length indicator. if it's a zero length message, + don't process, just skip it */ + gb = mwGetBuffer_wrap(&o); + x = guint32_peek(gb); + mwGetBuffer_free(gb); + if(! x) return n - 4; + + if(n < (x + 4)) { + /* if the total amount of data isn't enough to cover the length + bytes and the length indicated by those bytes, then we'll need + to buffer. This is where the DOS mentioned below in + session_recv takes place */ + + x += 4; + s->buf = (guchar *) g_malloc(x); + memcpy(s->buf, b, n); + s->buf_len = x; + s->buf_used = n; + return 0; + + } else { + /* advance past length bytes */ + ADVANCE(b, n, 4); + + /* process and advance */ + session_process(s, b, x); + ADVANCE(b, n, x); + + /* return left-over count */ + return n; + } +} + + +static gsize session_recv(struct mwSession *s, + const guchar *b, gsize n) { + + /* This is messy and kind of confusing. I'd like to simplify it at + some point, but the constraints are as follows: + + - buffer up to a single full message on the session buffer + - buffer must contain the four length bytes + - the four length bytes indicate how much we'll need to buffer + - the four length bytes might not arrive all at once, so it's + possible that we'll need to buffer to get them. + - since our buffering includes the length bytes, we know we + still have an incomplete length if the buffer length is only + four. */ + + /** @todo we should allow a compiled-in upper limit to message + sizes, and just drop messages over that size. However, to do that + we'd need to keep track of the size of a message and keep + dropping bytes until we'd fulfilled the entire length. eg: if we + receive a message size of 10MB, we need to pass up exactly 10MB + before it's safe to start processing the rest as a new + message. As it stands, a malicious packet from the server can run + us out of memory by indicating it's going to send us some + obscenely long message (even if it never actually sends it) */ + + /* g_message(" session_recv: session = %p, b = %p, n = %u", + s, b, n); */ + + if(s->buf_len == 0) { + while(n && (*b & 0x80)) { + /* keep-alive and series bytes are ignored */ + ADVANCE(b, n, 1); + } + } + + if(n == 0) { + return 0; + + } else if(s->buf_len > 0) { + return session_recv_cont(s, b, n); + + } else { + return session_recv_empty(s, b, n); + } +} + + +#undef ADVANCE + + +void mwSession_recv(struct mwSession *s, const guchar *buf, gsize n) { + guchar *b = (guchar *) buf; + gsize remain = 0; + + g_return_if_fail(s != NULL); + + while(n > 0) { + remain = session_recv(s, b, n); + b += (n - remain); + n = remain; + } +} + + +int mwSession_send(struct mwSession *s, struct mwMessage *msg) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret = 0; + + g_return_val_if_fail(s != NULL, -1); + + /* writing nothing is easy */ + if(! msg) return 0; + + /* first we render the message into an opaque */ + b = mwPutBuffer_new(); + mwMessage_put(b, msg); + mwPutBuffer_finalize(&o, b); + + /* then we render the opaque into... another opaque! */ + b = mwPutBuffer_new(); + mwOpaque_put(b, &o); + mwOpaque_clear(&o); + mwPutBuffer_finalize(&o, b); + + /* then we use that opaque's data and length to write to the socket */ + ret = io_write(s, o.data, o.len); + mwOpaque_clear(&o); + + /* ensure we could actually write the message */ + if(! ret) { + + /* special case, as the server doesn't always respond to user + status messages. Thus, we trigger the event when we send the + messages as well as when we receive them */ + if(msg->type == mwMessage_SET_USER_STATUS) { + SET_USER_STATUS_recv(s, (struct mwMsgSetUserStatus *) msg); + } + } + + return ret; +} + + +int mwSession_sendKeepalive(struct mwSession *s) { + const guchar b = 0x80; + + g_return_val_if_fail(s != NULL, -1); + return io_write(s, &b, 1); +} + + +int mwSession_forceLogin(struct mwSession *s) { + struct mwMsgLoginContinue *msg; + int ret; + + g_return_val_if_fail(s != NULL, -1); + g_return_val_if_fail(mwSession_isState(s, mwSession_LOGIN_REDIR), -1); + + state(s, mwSession_LOGIN_CONT, 0x00); + + msg = (struct mwMsgLoginContinue *) + mwMessage_new(mwMessage_LOGIN_CONTINUE); + + ret = mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + return ret; +} + + +int mwSession_sendAnnounce(struct mwSession *s, gboolean may_reply, + const char *text, const GList *recipients) { + + struct mwMsgAnnounce *msg; + int ret; + + g_return_val_if_fail(s != NULL, -1); + g_return_val_if_fail(mwSession_isStarted(s), -1); + + msg = (struct mwMsgAnnounce *) mwMessage_new(mwMessage_ANNOUNCE); + + msg->recipients = (GList *) recipients; + msg->may_reply = may_reply; + msg->text = g_strdup(text); + + ret = mwSession_send(s, MW_MESSAGE(msg)); + + msg->recipients = NULL; /* don't kill our recipients param */ + mwMessage_free(MW_MESSAGE(msg)); + + return ret; +} + + +struct mwSessionHandler *mwSession_getHandler(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + return s->handler; +} + + +struct mwLoginInfo *mwSession_getLoginInfo(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + return &s->login; +} + + +int mwSession_setPrivacyInfo(struct mwSession *s, + struct mwPrivacyInfo *privacy) { + + struct mwMsgSetPrivacyList *msg; + int ret; + + g_return_val_if_fail(s != NULL, -1); + g_return_val_if_fail(privacy != NULL, -1); + + msg = (struct mwMsgSetPrivacyList *) + mwMessage_new(mwMessage_SET_PRIVACY_LIST); + + mwPrivacyInfo_clone(&msg->privacy, privacy); + + ret = mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + return ret; +} + + +struct mwPrivacyInfo *mwSession_getPrivacyInfo(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + return &s->privacy; +} + + +int mwSession_setUserStatus(struct mwSession *s, + struct mwUserStatus *stat) { + + struct mwMsgSetUserStatus *msg; + int ret; + + g_return_val_if_fail(s != NULL, -1); + g_return_val_if_fail(stat != NULL, -1); + + msg = (struct mwMsgSetUserStatus *) + mwMessage_new(mwMessage_SET_USER_STATUS); + + mwUserStatus_clone(&msg->status, stat); + + ret = mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + return ret; +} + + +struct mwUserStatus *mwSession_getUserStatus(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + return &s->status; +} + + +enum mwSessionState mwSession_getState(struct mwSession *s) { + g_return_val_if_fail(s != NULL, mwSession_UNKNOWN); + return s->state; +} + + +gpointer mwSession_getStateInfo(struct mwSession *s) { + g_return_val_if_fail(s != NULL, 0); + return s->state_info; +} + + +struct mwChannelSet *mwSession_getChannels(struct mwSession *session) { + g_return_val_if_fail(session != NULL, NULL); + return session->channels; +} + + +gboolean mwSession_addService(struct mwSession *s, struct mwService *srv) { + g_return_val_if_fail(s != NULL, FALSE); + g_return_val_if_fail(srv != NULL, FALSE); + g_return_val_if_fail(s->services != NULL, FALSE); + + if(map_guint_lookup(s->services, SERVICE_KEY(srv))) { + return FALSE; + + } else { + map_guint_insert(s->services, SERVICE_KEY(srv), srv); + if(mwSession_isState(s, mwSession_STARTED)) + mwSession_senseService(s, mwService_getType(srv)); + return TRUE; + } +} + + +struct mwService *mwSession_getService(struct mwSession *s, guint32 srv) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->services != NULL, NULL); + + return map_guint_lookup(s->services, srv); +} + + +struct mwService *mwSession_removeService(struct mwSession *s, guint32 srv) { + struct mwService *svc; + + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->services != NULL, NULL); + + svc = map_guint_lookup(s->services, srv); + if(svc) map_guint_remove(s->services, srv); + return svc; +} + + +GList *mwSession_getServices(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->services != NULL, NULL); + + return map_collect_values(s->services); +} + + +void mwSession_senseService(struct mwSession *s, guint32 srvc) { + struct mwMsgSenseService *msg; + + g_return_if_fail(s != NULL); + g_return_if_fail(srvc != 0x00); + g_return_if_fail(mwSession_isStarted(s)); + + msg = (struct mwMsgSenseService *) + mwMessage_new(mwMessage_SENSE_SERVICE); + msg->service = srvc; + + mwSession_send(s, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); +} + + +gboolean mwSession_addCipher(struct mwSession *s, struct mwCipher *c) { + g_return_val_if_fail(s != NULL, FALSE); + g_return_val_if_fail(c != NULL, FALSE); + g_return_val_if_fail(s->ciphers != NULL, FALSE); + + if(map_guint_lookup(s->ciphers, mwCipher_getType(c))) { + g_message("cipher %s is already added, apparently", + NSTR(mwCipher_getName(c))); + return FALSE; + + } else { + g_message("adding cipher %s", NSTR(mwCipher_getName(c))); + map_guint_insert(s->ciphers, mwCipher_getType(c), c); + return TRUE; + } +} + + +struct mwCipher *mwSession_getCipher(struct mwSession *s, guint16 c) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->ciphers != NULL, NULL); + + return map_guint_lookup(s->ciphers, c); +} + + +struct mwCipher *mwSession_removeCipher(struct mwSession *s, guint16 c) { + struct mwCipher *ciph; + + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->ciphers != NULL, NULL); + + ciph = map_guint_lookup(s->ciphers, c); + if(ciph) map_guint_remove(s->ciphers, c); + return ciph; +} + + +GList *mwSession_getCiphers(struct mwSession *s) { + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->ciphers != NULL, NULL); + + return map_collect_values(s->ciphers); +} + + +void mwSession_setProperty(struct mwSession *s, const char *key, + gpointer val, GDestroyNotify clean) { + + g_return_if_fail(s != NULL); + g_return_if_fail(s->attributes != NULL); + g_return_if_fail(key != NULL); + + property_set(s, key, val, clean); +} + + +gpointer mwSession_getProperty(struct mwSession *s, const char *key) { + + g_return_val_if_fail(s != NULL, NULL); + g_return_val_if_fail(s->attributes != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + + return property_get(s, key); +} + + +void mwSession_removeProperty(struct mwSession *s, const char *key) { + g_return_if_fail(s != NULL); + g_return_if_fail(s->attributes != NULL); + g_return_if_fail(key != NULL); + + property_del(s, key); +} + + +void mwSession_setClientData(struct mwSession *session, + gpointer data, GDestroyNotify clear) { + + g_return_if_fail(session != NULL); + mw_datum_set(&session->client_data, data, clear); +} + + +gpointer mwSession_getClientData(struct mwSession *session) { + g_return_val_if_fail(session != NULL, NULL); + return mw_datum_get(&session->client_data); +} + + +void mwSession_removeClientData(struct mwSession *session) { + g_return_if_fail(session != NULL); + mw_datum_clear(&session->client_data); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_aware.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,1318 @@ + +/* + 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.h> +#include <glib/ghash.h> +#include <glib/glist.h> +#include <string.h> + +#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); + } +} + + +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; +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_conf.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,865 @@ + +/* + 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.h> +#include <glib/ghash.h> +#include <glib/glist.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#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_conf.h" +#include "mw_util.h" + + +/* This thing needs a re-write. More than anything else, I need to + re-examine the conferencing service protocol from more modern + clients */ + + +#define PROTOCOL_TYPE 0x00000010 +#define PROTOCOL_VER 0x00000002 + + +/** @see mwMsgChannelSend::type + @see recv */ +enum msg_type { + msg_WELCOME = 0x0000, /**< welcome message */ + msg_INVITE = 0x0001, /**< outgoing invitation */ + msg_JOIN = 0x0002, /**< someone joined */ + msg_PART = 0x0003, /**< someone left */ + msg_MESSAGE = 0x0004, /**< conference message */ +}; + + +/** the conferencing service */ +struct mwServiceConference { + struct mwService service; + + /** call-back handler for this service */ + struct mwConferenceHandler *handler; + + /** collection of conferences in this service */ + GList *confs; +}; + + +/** a conference and its members */ +struct mwConference { + enum mwConferenceState state; /**< state of the conference */ + struct mwServiceConference *service; /**< owning service */ + struct mwChannel *channel; /**< conference's channel */ + + char *name; /**< server identifier for the conference */ + char *title; /**< topic for the conference */ + + struct mwLoginInfo owner; /**< person who created this conference */ + GHashTable *members; /**< mapping guint16:mwLoginInfo */ + struct mw_datum client_data; +}; + + +#define MEMBER_FIND(conf, id) \ + g_hash_table_lookup(conf->members, GUINT_TO_POINTER((guint) id)) + + +#define MEMBER_ADD(conf, id, member) \ + g_hash_table_insert(conf->members, GUINT_TO_POINTER((guint) id), member) + + +#define MEMBER_REM(conf, id) \ + g_hash_table_remove(conf->members, GUINT_TO_POINTER((guint) id)); + + +/** clear and free a login info block */ +static void login_free(struct mwLoginInfo *li) { + mwLoginInfo_clear(li); + g_free(li); +} + + +/** generates a random conference name built around a user name */ +static char *conf_generate_name(const char *user) { + guint a, b; + char *ret; + + user = user? user: ""; + + srand(clock() + rand()); + a = ((rand() & 0xff) << 8) | (rand() & 0xff); + b = time(NULL); + + ret = g_strdup_printf("%s(%08x,%04x)", user, b, a); + g_debug("generated random conference name: '%s'", ret); + return ret; +} + + + + + +static struct mwConference *conf_new(struct mwServiceConference *srvc) { + + struct mwConference *conf; + struct mwSession *session; + const char *user; + + conf = g_new0(struct mwConference, 1); + conf->state = mwConference_NEW; + conf->service = srvc; + conf->members = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + (GDestroyNotify) login_free); + + session = mwService_getSession(MW_SERVICE(srvc)); + user = mwSession_getProperty(session, mwSession_AUTH_USER_ID); + + srvc->confs = g_list_prepend(srvc->confs, conf); + + return conf; +} + + +/** clean and free a conference structure */ +static void conf_free(struct mwConference *conf) { + struct mwServiceConference *srvc; + + /* this shouldn't ever happen, but just to be sure */ + g_return_if_fail(conf != NULL); + + srvc = conf->service; + + if(conf->members) + g_hash_table_destroy(conf->members); + + g_list_remove_all(srvc->confs, conf); + + mw_datum_clear(&conf->client_data); + + g_free(conf->name); + g_free(conf->title); + g_free(conf); +} + + +static struct mwConference *conf_find(struct mwServiceConference *srvc, + struct mwChannel *chan) { + GList *l; + + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(chan != NULL, NULL); + + for(l = srvc->confs; l; l = l->next) { + struct mwConference *conf = l->data; + if(conf->channel == chan) return conf; + } + + return NULL; +} + + +static const char *conf_state_str(enum mwConferenceState state) { + switch(state) { + case mwConference_NEW: return "new"; + case mwConference_PENDING: return "pending"; + case mwConference_INVITED: return "invited"; + case mwConference_OPEN: return "open"; + case mwConference_CLOSING: return "closing"; + case mwConference_ERROR: return "error"; + + case mwConference_UNKNOWN: /* fall through */ + default: return "UNKNOWN"; + } +} + + +static void conf_state(struct mwConference *conf, + enum mwConferenceState state) { + g_return_if_fail(conf != NULL); + + if(conf->state == state) return; + + conf->state = state; + g_message("conference %s state: %s", + NSTR(conf->name), conf_state_str(state)); +} + + +static void recv_channelCreate(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* - this is how we really receive invitations + - create a conference and associate it with the channel + - obtain the invite data from the msg addtl info + - mark the conference as INVITED + - trigger the got_invite event + */ + + struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc; + struct mwConference *conf; + + struct mwGetBuffer *b; + + char *invite = NULL; + guint tmp; + + conf = conf_new(srvc_conf); + conf->channel = chan; + + b = mwGetBuffer_wrap(&msg->addtl); + + guint32_get(b, &tmp); + mwString_get(b, &conf->name); + mwString_get(b, &conf->title); + guint32_get(b, &tmp); + mwLoginInfo_get(b, &conf->owner); + guint32_get(b, &tmp); + mwString_get(b, &invite); + + if(mwGetBuffer_error(b)) { + g_warning("failure parsing addtl for conference invite"); + mwConference_destroy(conf, ERR_FAILURE, NULL); + + } else { + struct mwConferenceHandler *h = srvc_conf->handler; + conf_state(conf, mwConference_INVITED); + if(h->on_invited) + h->on_invited(conf, &conf->owner, invite); + } + + mwGetBuffer_free(b); + g_free(invite); +} + + +static void recv_channelAccept(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + ; +} + + +static void recv_channelDestroy(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + /* - find conference from channel + - trigger got_closed + - remove conference, dealloc + */ + + struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc; + struct mwConference *conf = conf_find(srvc_conf, chan); + struct mwConferenceHandler *h = srvc_conf->handler; + + /* if there's no such conference, then I guess there's nothing to worry + about. Except of course for the fact that we should never receive a + channel destroy for a conference that doesn't exist. */ + if(! conf) return; + + conf->channel = NULL; + + conf_state(conf, msg->reason? mwConference_ERROR: mwConference_CLOSING); + + if(h->conf_closed) + h->conf_closed(conf, msg->reason); + + mwConference_destroy(conf, ERR_SUCCESS, NULL); +} + + +static void WELCOME_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwGetBuffer *b) { + + struct mwConferenceHandler *h; + guint16 tmp16; + guint32 tmp32; + guint32 count; + GList *l = NULL; + + /* re-read name and title */ + g_free(conf->name); + g_free(conf->title); + conf->name = NULL; + conf->title = NULL; + mwString_get(b, &conf->name); + mwString_get(b, &conf->title); + + /* some numbers we don't care about, then a count of members */ + guint16_get(b, &tmp16); + guint32_get(b, &tmp32); + guint32_get(b, &count); + + if(mwGetBuffer_error(b)) { + g_warning("error parsing welcome message for conference"); + mwConference_destroy(conf, ERR_FAILURE, NULL); + return; + } + + while(count--) { + guint16 member_id; + struct mwLoginInfo *member = g_new0(struct mwLoginInfo, 1); + + guint16_get(b, &member_id); + mwLoginInfo_get(b, member); + + if(mwGetBuffer_error(b)) { + login_free(member); + break; + } + + MEMBER_ADD(conf, member_id, member); + l = g_list_append(l, member); + } + + conf_state(conf, mwConference_OPEN); + + h = srvc->handler; + if(h->conf_opened) + h->conf_opened(conf, l); + + /* get rid of the GList, but not its contents */ + g_list_free(l); +} + + +static void JOIN_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwGetBuffer *b) { + + struct mwConferenceHandler *h; + guint16 m_id; + struct mwLoginInfo *m; + + /* for some inane reason, conferences we create will send a join + message for ourselves before the welcome message. Since the + welcome message will list our ID among those in the channel, + we're going to just pretend that these join messages don't + exist */ + if(conf->state == mwConference_PENDING) + return; + + m = g_new0(struct mwLoginInfo, 1); + + guint16_get(b, &m_id); + mwLoginInfo_get(b, m); + + if(mwGetBuffer_error(b)) { + g_warning("failed parsing JOIN message in conference"); + login_free(m); + return; + } + + MEMBER_ADD(conf, m_id, m); + + h = srvc->handler; + if(h->on_peer_joined) + h->on_peer_joined(conf, m); +} + + +static void PART_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwGetBuffer *b) { + + /* - parse who left + - look up their membership + - remove them from the members list + - trigger the event + */ + + struct mwConferenceHandler *h; + guint16 id = 0; + struct mwLoginInfo *m; + + guint16_get(b, &id); + + if(mwGetBuffer_error(b)) return; + + m = MEMBER_FIND(conf, id); + if(! m) return; + + h = srvc->handler; + if(h->on_peer_parted) + h->on_peer_parted(conf, m); + + MEMBER_REM(conf, id); +} + + +static void text_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwLoginInfo *m, + struct mwGetBuffer *b) { + + /* this function acts a lot like receiving an IM Text message. The text + message contains only a string */ + + char *text = NULL; + struct mwConferenceHandler *h; + + mwString_get(b, &text); + + if(mwGetBuffer_error(b)) { + g_warning("failed to parse text message in conference"); + g_free(text); + return; + } + + h = srvc->handler; + if(text && h->on_text) { + h->on_text(conf, m, text); + } + + g_free(text); +} + + +static void data_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwLoginInfo *m, + struct mwGetBuffer *b) { + + /* this function acts a lot like receiving an IM Data message. The + data message has a type, a subtype, and an opaque. We only + support typing notification though. */ + + /** @todo it's possible that some clients send text in a data + message, as we've seen rarely in the IM service. Have to add + support for that here */ + + guint32 type, subtype; + struct mwConferenceHandler *h; + + guint32_get(b, &type); + guint32_get(b, &subtype); + + if(mwGetBuffer_error(b)) return; + + /* don't know how to deal with any others yet */ + if(type != 0x01) { + g_message("unknown data message type (0x%08x, 0x%08x)", type, subtype); + return; + } + + h = srvc->handler; + if(h->on_typing) { + h->on_typing(conf, m, !subtype); + } +} + + +static void MESSAGE_recv(struct mwServiceConference *srvc, + struct mwConference *conf, + struct mwGetBuffer *b) { + + /* - look up who send the message by their id + - trigger the event + */ + + guint16 id; + guint32 type; + struct mwLoginInfo *m; + + /* an empty buffer isn't an error, just ignored */ + if(! mwGetBuffer_remaining(b)) return; + + guint16_get(b, &id); + guint32_get(b, &type); /* reuse type variable */ + guint32_get(b, &type); + + if(mwGetBuffer_error(b)) return; + + m = MEMBER_FIND(conf, id); + if(! m) { + g_warning("received message type 0x%04x from" + " unknown conference member %u", type, id); + return; + } + + switch(type) { + case 0x01: /* type is text */ + text_recv(srvc, conf, m, b); + break; + + case 0x02: /* type is data */ + data_recv(srvc, conf, m, b); + break; + + default: + g_warning("unknown message type 0x%4x received in conference", type); + } +} + + +static void recv(struct mwService *srvc, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + struct mwServiceConference *srvc_conf = (struct mwServiceConference *) srvc; + struct mwConference *conf = conf_find(srvc_conf, chan); + struct mwGetBuffer *b; + + g_return_if_fail(conf != NULL); + + b = mwGetBuffer_wrap(data); + + switch(type) { + case msg_WELCOME: + WELCOME_recv(srvc_conf, conf, b); + break; + + case msg_JOIN: + JOIN_recv(srvc_conf, conf, b); + break; + + case msg_PART: + PART_recv(srvc_conf, conf, b); + break; + + case msg_MESSAGE: + MESSAGE_recv(srvc_conf, conf, b); + break; + + default: + ; /* hrm. should log this. TODO */ + } +} + + +static void clear(struct mwServiceConference *srvc) { + struct mwConferenceHandler *h; + + while(srvc->confs) + conf_free(srvc->confs->data); + + h = srvc->handler; + if(h && h->clear) + h->clear(srvc); + srvc->handler = NULL; +} + + +static const char *name(struct mwService *srvc) { + return "Basic Conferencing"; +} + + +static const char *desc(struct mwService *srvc) { + return "Multi-user plain-text conferencing"; +} + + +static void start(struct mwService *srvc) { + mwService_started(srvc); +} + + +static void stop(struct mwServiceConference *srvc) { + while(srvc->confs) + mwConference_destroy(srvc->confs->data, ERR_SUCCESS, NULL); + + mwService_stopped(MW_SERVICE(srvc)); +} + + +struct mwServiceConference * +mwServiceConference_new(struct mwSession *session, + struct mwConferenceHandler *handler) { + + struct mwServiceConference *srvc_conf; + struct mwService *srvc; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(handler != NULL, NULL); + + srvc_conf = g_new0(struct mwServiceConference, 1); + srvc = &srvc_conf->service; + + mwService_init(srvc, session, mwService_CONFERENCE); + srvc->start = start; + srvc->stop = (mwService_funcStop) stop; + srvc->recv_create = recv_channelCreate; + srvc->recv_accept = recv_channelAccept; + srvc->recv_destroy = recv_channelDestroy; + srvc->recv = recv; + srvc->clear = (mwService_funcClear) clear; + srvc->get_name = name; + srvc->get_desc = desc; + + srvc_conf->handler = handler; + + return srvc_conf; +} + + +struct mwConference *mwConference_new(struct mwServiceConference *srvc, + const char *title) { + struct mwConference *conf; + + g_return_val_if_fail(srvc != NULL, NULL); + + conf = conf_new(srvc); + conf->title = g_strdup(title); + + return conf; +} + + +struct mwServiceConference * +mwConference_getService(struct mwConference *conf) { + g_return_val_if_fail(conf != NULL, NULL); + return conf->service; +} + + +const char *mwConference_getName(struct mwConference *conf) { + g_return_val_if_fail(conf != NULL, NULL); + return conf->name; +} + + +const char *mwConference_getTitle(struct mwConference *conf) { + g_return_val_if_fail(conf != NULL, NULL); + return conf->title; +} + + +GList *mwConference_memebers(struct mwConference *conf) { + g_return_val_if_fail(conf != NULL, NULL); + g_return_val_if_fail(conf->members != NULL, NULL); + + return map_collect_values(conf->members); +} + + +int mwConference_open(struct mwConference *conf) { + struct mwSession *session; + struct mwChannel *chan; + struct mwPutBuffer *b; + int ret; + + g_return_val_if_fail(conf != NULL, -1); + g_return_val_if_fail(conf->service != NULL, -1); + g_return_val_if_fail(conf->state == mwConference_NEW, -1); + g_return_val_if_fail(conf->channel == NULL, -1); + + session = mwService_getSession(MW_SERVICE(conf->service)); + g_return_val_if_fail(session != NULL, -1); + + if(! conf->name) { + char *user = mwSession_getProperty(session, mwSession_AUTH_USER_ID); + conf->name = conf_generate_name(user? user: "meanwhile"); + } + + chan = mwChannel_newOutgoing(mwSession_getChannels(session)); + mwChannel_setService(chan, MW_SERVICE(conf->service)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + /* offer all known ciphers */ + mwChannel_populateSupportedCipherInstances(chan); + + b = mwPutBuffer_new(); + mwString_put(b, conf->name); + mwString_put(b, conf->title); + guint32_put(b, 0x00); + mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); + + ret = mwChannel_create(chan); + if(ret) { + conf_state(conf, mwConference_ERROR); + } else { + conf_state(conf, mwConference_PENDING); + conf->channel = chan; + } + + return ret; +} + + +int mwConference_destroy(struct mwConference *conf, + guint32 reason, const char *text) { + + struct mwServiceConference *srvc; + struct mwOpaque info = { 0, 0 }; + int ret = 0; + + g_return_val_if_fail(conf != NULL, -1); + + srvc = conf->service; + g_return_val_if_fail(srvc != NULL, -1); + + /* remove conference from the service */ + srvc->confs = g_list_remove_all(srvc->confs, conf); + + /* close the channel if applicable */ + if(conf->channel) { + if(text && *text) { + info.len = strlen(text); + info.data = (guchar *) text; + } + + ret = mwChannel_destroy(conf->channel, reason, &info); + } + + /* free the conference */ + conf_free(conf); + + return ret; +} + + +int mwConference_accept(struct mwConference *conf) { + /* - if conference is not INVITED, return -1 + - accept the conference channel + - send an empty JOIN message + */ + + struct mwChannel *chan; + int ret; + + g_return_val_if_fail(conf != NULL, -1); + g_return_val_if_fail(conf->state == mwConference_INVITED, -1); + + chan = conf->channel; + ret = mwChannel_accept(chan); + + if(! ret) + ret = mwChannel_sendEncrypted(chan, msg_JOIN, NULL, FALSE); + + return ret; +} + + +int mwConference_invite(struct mwConference *conf, + struct mwIdBlock *who, + const char *text) { + + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(conf != NULL, -1); + g_return_val_if_fail(conf->channel != NULL, -1); + g_return_val_if_fail(who != NULL, -1); + + b = mwPutBuffer_new(); + + mwIdBlock_put(b, who); + guint16_put(b, 0x00); + guint32_put(b, 0x00); + mwString_put(b, text); + mwString_put(b, who->user); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_sendEncrypted(conf->channel, msg_INVITE, &o, FALSE); + mwOpaque_clear(&o); + + return ret; +} + + +int mwConference_sendText(struct mwConference *conf, const char *text) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(conf != NULL, -1); + g_return_val_if_fail(conf->channel != NULL, -1); + + b = mwPutBuffer_new(); + + guint32_put(b, 0x01); + mwString_put(b, text); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE); + mwOpaque_clear(&o); + + return ret; +} + + +int mwConference_sendTyping(struct mwConference *conf, gboolean typing) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(conf != NULL, -1); + g_return_val_if_fail(conf->channel != NULL, -1); + g_return_val_if_fail(conf->state == mwConference_OPEN, -1); + + b = mwPutBuffer_new(); + + guint32_put(b, 0x02); + guint32_put(b, 0x01); + guint32_put(b, !typing); + mwOpaque_put(b, NULL); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_sendEncrypted(conf->channel, msg_MESSAGE, &o, FALSE); + mwOpaque_clear(&o); + + return ret; +} + + +void mwConference_setClientData(struct mwConference *conference, + gpointer data, GDestroyNotify clear) { + + g_return_if_fail(conference != NULL); + mw_datum_set(&conference->client_data, data, clear); +} + + +gpointer mwConference_getClientData(struct mwConference *conference) { + g_return_val_if_fail(conference != NULL, NULL); + return mw_datum_get(&conference->client_data); +} + + +void mwConference_removeClientData(struct mwConference *conference) { + g_return_if_fail(conference != NULL); + mw_datum_clear(&conference->client_data); +} + + +struct mwConferenceHandler * +mwServiceConference_getHandler(struct mwServiceConference *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +GList *mwServiceConference_getConferences(struct mwServiceConference *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return g_list_copy(srvc->confs); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_dir.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,664 @@ + +/* + 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_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_dir.h" +#include "mw_util.h" + + +#define PROTOCOL_TYPE 0x0000001c +#define PROTOCOL_VER 0x00000005 + + +enum dir_action { + action_list = 0x0000, /**< list address books */ + action_open = 0x0001, /**< open an addressbook as a directory */ + action_close = 0x0002, /**< close a directory */ + action_search = 0x0003, /**< search an open directory */ +}; + + +struct mwServiceDirectory { + struct mwService service; + + struct mwDirectoryHandler *handler; + + struct mwChannel *channel; + + guint32 counter; /**< counter of request IDs */ + GHashTable *requests; /**< map of request ID:directory */ + GHashTable *books; /**< book->name:mwAddressBook */ +}; + + +struct mwAddressBook { + struct mwServiceDirectory *service; + + guint32 id; /**< id or type or something */ + char *name; /**< name of address book */ + GHashTable *dirs; /**< dir->id:mwDirectory */ +}; + + +struct mwDirectory { + struct mwServiceDirectory *service; + struct mwAddressBook *book; + + enum mwDirectoryState state; + + guint32 id; /**< id of directory, assigned by server */ + guint32 search_id; /**< id of current search, from srvc->counter++ */ + + mwSearchHandler handler; + struct mw_datum client_data; +}; + + +#define next_request_id(srvc) ( ++((srvc)->counter) ) + + +static guint32 map_request(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc = dir->service; + guint32 id = next_request_id(srvc); + + dir->search_id = id; + map_guint_insert(srvc->requests, id, dir); + + return id; +} + + +/** called when directory is removed from the service directory map */ +static void dir_free(struct mwDirectory *dir) { + map_guint_remove(dir->service->requests, dir->search_id); + g_free(dir); +} + + +/** remove the directory from the service list and its owning address + book, then frees the directory */ +static void dir_remove(struct mwDirectory *dir) { + struct mwAddressBook *book = dir->book; + map_guint_remove(book->dirs, dir->id); +} + + +__attribute__((used)) +static struct mwDirectory *dir_new(struct mwAddressBook *book, guint32 id) { + struct mwDirectory *dir = g_new0(struct mwDirectory, 1); + dir->service = book->service; + dir->book = book; + dir->id = id; + map_guint_insert(book->dirs, id, dir); + return dir; +} + + +/** called when book is removed from the service book map. Removed all + directories as well */ +static void book_free(struct mwAddressBook *book) { + g_hash_table_destroy(book->dirs); + g_free(book->name); +} + + +__attribute__((used)) +static void book_remove(struct mwAddressBook *book) { + struct mwServiceDirectory *srvc = book->service; + g_hash_table_remove(srvc->books, book->name); +} + + +static struct mwAddressBook *book_new(struct mwServiceDirectory *srvc, + const char *name, guint32 id) { + struct mwAddressBook *book = g_new0(struct mwAddressBook, 1); + book->service = srvc; + book->id = id; + book->name = g_strdup(name); + book->dirs = map_guint_new_full((GDestroyNotify) dir_free); + g_hash_table_insert(srvc->books, book->name, book); + return book; +} + + +static const char *getName(struct mwService *srvc) { + return "Address Book and Directory"; +} + + +static const char *getDesc(struct mwService *srvc) { + return "Address book directory service for user and group lookups"; +} + + +static struct mwChannel *make_channel(struct mwServiceDirectory *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 mwServiceDirectory *srvc) { + struct mwChannel *chan; + + chan = make_channel(srvc); + if(chan) { + srvc->channel = chan; + } else { + mwService_stopped(MW_SERVICE(srvc)); + return; + } +} + + +static void stop(struct mwServiceDirectory *srvc) { + /* XXX */ + + if(srvc->channel) { + mwChannel_destroy(srvc->channel, ERR_SUCCESS, NULL); + srvc->channel = NULL; + } +} + + +static void clear(struct mwServiceDirectory *srvc) { + struct mwDirectoryHandler *handler; + + if(srvc->books) { + g_hash_table_destroy(srvc->books); + srvc->books = NULL; + } + + /* clear the handler */ + handler = srvc->handler; + if(handler && handler->clear) + handler->clear(srvc); + srvc->handler = NULL; +} + + +static void recv_create(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* no way man, we call the shots around here */ + mwChannel_destroy(chan, ERR_FAILURE, NULL); +} + + +static void recv_accept(struct mwServiceDirectory *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(srvc)) { + mwService_started(MW_SERVICE(srvc)); + + } else { + mwChannel_destroy(chan, ERR_FAILURE, NULL); + } +} + + +static void recv_destroy(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + srvc->channel = NULL; + mwService_stop(MW_SERVICE(srvc)); + /** @todo session sense service */ +} + + +static void recv_list(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + struct mwGetBuffer *b; + guint32 request, code, count; + gboolean foo_1; + guint16 foo_2; + + b = mwGetBuffer_wrap(data); + + guint32_get(b, &request); + guint32_get(b, &code); + guint32_get(b, &count); + + gboolean_get(b, &foo_1); + guint16_get(b, &foo_2); + + if(foo_1 || foo_2) { + mw_debug_mailme(data, "received strange address book list"); + mwGetBuffer_free(b); + return; + } + + while(!mwGetBuffer_error(b) && count--) { + guint32 id; + char *name = NULL; + + guint32_get(b, &id); + mwString_get(b, &name); + + book_new(srvc, name, id); + g_free(name); + } +} + + +static void recv_open(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + /* look up the directory associated with this request id, + mark it as open, and trigger the event */ +} + + +static void recv_search(struct mwServiceDirectory *srvc, + struct mwOpaque *data) { + + /* look up the directory associated with this request id, + trigger the event */ +} + + +static void recv(struct mwServiceDirectory *srvc, + struct mwChannel *chan, + guint16 msg_type, struct mwOpaque *data) { + + 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); + + switch(msg_type) { + case action_list: + recv_list(srvc, data); + break; + + case action_open: + recv_open(srvc, data); + break; + + case action_close: + ; /* I don't think we should receive these */ + break; + + case action_search: + recv_search(srvc, data); + break; + + default: + mw_debug_mailme(data, "msg type 0x%04x in directory service", msg_type); + } +} + + +struct mwServiceDirectory * +mwServiceDirectory_new(struct mwSession *session, + struct mwDirectoryHandler *handler) { + + struct mwServiceDirectory *srvc; + struct mwService *service; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(handler != NULL, NULL); + + srvc = g_new0(struct mwServiceDirectory, 1); + service = MW_SERVICE(srvc); + + mwService_init(service, session, SERVICE_DIRECTORY); + service->get_name = getName; + service->get_desc = getDesc; + service->start = (mwService_funcStart) start; + service->stop = (mwService_funcStop) stop; + service->clear = (mwService_funcClear) clear; + service->recv_create = (mwService_funcRecvCreate) recv_create; + service->recv_accept = (mwService_funcRecvAccept) recv_accept; + service->recv_destroy = (mwService_funcRecvDestroy) recv_destroy; + service->recv = (mwService_funcRecv) recv; + + srvc->handler = handler; + srvc->requests = map_guint_new(); + srvc->books = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, (GDestroyNotify) book_free); + return srvc; +} + + +struct mwDirectoryHandler * +mwServiceDirectory_getHandler(struct mwServiceDirectory *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +int mwServiceDirectory_refreshAddressBooks(struct mwServiceDirectory *srvc) { + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, next_request_id(srvc)); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_list, &o); + mwOpaque_clear(&o); + + return ret; +} + + +GList *mwServiceDirectory_getAddressBooks(struct mwServiceDirectory *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(srvc->books != NULL, NULL); + + return map_collect_values(srvc->books); +} + + +GList *mwServiceDirectory_getDirectories(struct mwServiceDirectory *srvc) { + GList *bl, *ret = NULL; + + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(srvc->books != NULL, NULL); + + bl = map_collect_values(srvc->books); + for( ; bl; bl = g_list_delete_link(bl, bl)) { + struct mwAddressBook *book = bl->data; + ret = g_list_concat(ret, map_collect_values(book->dirs)); + } + + return ret; +} + + +GList *mwAddressBook_getDirectories(struct mwAddressBook *book) { + g_return_val_if_fail(book != NULL, NULL); + g_return_val_if_fail(book->dirs != NULL, NULL); + + return map_collect_values(book->dirs); +} + + +const char *mwAddressBook_getName(struct mwAddressBook *book) { + g_return_val_if_fail(book != NULL, NULL); + return book->name; +} + + +struct mwDirectory *mwDirectory_new(struct mwAddressBook *book) { + struct mwDirectory *dir; + + g_return_val_if_fail(book != NULL, NULL); + g_return_val_if_fail(book->service != NULL, NULL); + + dir = g_new0(struct mwDirectory, 1); + dir->service = book->service; + dir->book = book; + dir->state = mwDirectory_NEW; + + return dir; +} + + +enum mwDirectoryState mwDirectory_getState(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, mwDirectory_UNKNOWN); + return dir->state; +} + + +void mwDirectory_setClientData(struct mwDirectory *dir, + gpointer data, GDestroyNotify clear) { + + g_return_if_fail(dir != NULL); + mw_datum_set(&dir->client_data, data, clear); +} + + +gpointer mwDirectory_getClientData(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + return mw_datum_get(&dir->client_data); +} + + +void mwDirectory_removeClientData(struct mwDirectory *dir) { + g_return_if_fail(dir != NULL); + mw_datum_clear(&dir->client_data); +} + + +struct mwServiceDirectory *mwDirectory_getService(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + g_return_val_if_fail(dir->book != NULL, NULL); + return dir->book->service; +} + + +struct mwAddressBook *mwDirectory_getAddressBook(struct mwDirectory *dir) { + g_return_val_if_fail(dir != NULL, NULL); + return dir->book; +} + + +static int dir_open(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + + /* unsure about these three bytes */ + gboolean_put(b, FALSE); + guint16_put(b, 0x0000); + + guint32_put(b, dir->book->id); + mwString_put(b, dir->book->name); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_open, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_open(struct mwDirectory *dir, mwSearchHandler cb) { + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(cb != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_NEW(dir), -1); + + dir->state = mwDirectory_PENDING; + dir->handler = cb; + + return dir_open(dir); +} + + +int mwDirectory_next(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0xffff); /* some magic? */ + guint32_put(b, 0x00000000); /* next results */ + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_previous(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0x0061); /* some magic? */ + guint32_put(b, 0x00000001); /* prev results */ + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_search(struct mwDirectory *dir, const char *query) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + g_return_val_if_fail(MW_DIRECTORY_IS_OPEN(dir), -1); + g_return_val_if_fail(query != NULL, -1); + g_return_val_if_fail(*query != '\0', -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, map_request(dir)); + guint32_put(b, dir->id); + guint16_put(b, 0x0061); /* some magic? */ + guint32_put(b, 0x00000008); /* seek results */ + mwString_put(b, query); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_search, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int dir_close(struct mwDirectory *dir) { + struct mwServiceDirectory *srvc; + struct mwChannel *chan; + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + g_return_val_if_fail(dir != NULL, -1); + + srvc = dir->service; + g_return_val_if_fail(srvc != NULL, -1); + + chan = srvc->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + guint32_put(b, next_request_id(dir->service)); + guint32_put(b, dir->id); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, action_close, &o); + mwOpaque_clear(&o); + + return ret; +} + + +int mwDirectory_destroy(struct mwDirectory *dir) { + int ret = 0; + + g_return_val_if_fail(dir != NULL, -1); + + if(MW_DIRECTORY_IS_OPEN(dir) || MW_DIRECTORY_IS_PENDING(dir)) { + ret = dir_close(dir); + } + dir_remove(dir); + + return ret; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_ft.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,654 @@ + +/* + 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/glist.h> + +#include "mw_channel.h" +#include "mw_common.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_ft.h" +#include "mw_util.h" + + +#define PROTOCOL_TYPE 0x00000000 +#define PROTOCOL_VER 0x00000001 + + +/** send-on-channel type: FT transfer data */ +#define msg_TRANSFER 0x0001 + + +/** ack received transfer data */ +#define msg_RECEIVED 0x0002 + + +struct mwServiceFileTransfer { + struct mwService service; + + struct mwFileTransferHandler *handler; + GList *transfers; +}; + + +struct mwFileTransfer { + struct mwServiceFileTransfer *service; + + struct mwChannel *channel; + struct mwIdBlock who; + + enum mwFileTransferState state; + + char *filename; + char *message; + + guint32 size; + guint32 remaining; + + struct mw_datum client_data; +}; + + +/** momentarily places a mwLoginInfo into a mwIdBlock */ +static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) { + to->user = from->user_id; + to->community = from->community; +} + + +static const char *ft_state_str(enum mwFileTransferState state) { + switch(state) { + case mwFileTransfer_NEW: + return "new"; + + case mwFileTransfer_PENDING: + return "pending"; + + case mwFileTransfer_OPEN: + return "open"; + + case mwFileTransfer_CANCEL_LOCAL: + return "cancelled locally"; + + case mwFileTransfer_CANCEL_REMOTE: + return "cancelled remotely"; + + case mwFileTransfer_DONE: + return "done"; + + case mwFileTransfer_ERROR: + return "error"; + + case mwFileTransfer_UNKNOWN: + default: + return "UNKNOWN"; + } +} + + +static void ft_state(struct mwFileTransfer *ft, + enum mwFileTransferState state) { + + g_return_if_fail(ft != NULL); + + if(ft->state == state) return; + + g_info("setting ft (%s, %s) state: %s", + NSTR(ft->who.user), NSTR(ft->who.community), + ft_state_str(state)); + + ft->state = state; +} + + +static void recv_channelCreate(struct mwServiceFileTransfer *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + struct mwFileTransferHandler *handler; + struct mwGetBuffer *b; + + char *fnm, *txt; + guint32 size, junk; + gboolean b_err; + + g_return_if_fail(srvc->handler != NULL); + handler = srvc->handler; + + b = mwGetBuffer_wrap(&msg->addtl); + + guint32_get(b, &junk); /* unknown */ + mwString_get(b, &fnm); /* offered filename */ + mwString_get(b, &txt); /* offering message */ + guint32_get(b, &size); /* size of offered file */ + guint32_get(b, &junk); /* unknown */ + /* and we just skip an unknown guint16 at the end */ + + b_err = mwGetBuffer_error(b); + mwGetBuffer_free(b); + + if(b_err) { + g_warning("bad/malformed addtl in File Transfer service"); + mwChannel_destroy(chan, ERR_FAILURE, NULL); + + } else { + struct mwIdBlock idb; + struct mwFileTransfer *ft; + + login_into_id(&idb, mwChannel_getUser(chan)); + ft = mwFileTransfer_new(srvc, &idb, txt, fnm, size); + ft->channel = chan; + ft_state(ft, mwFileTransfer_PENDING); + + mwChannel_setServiceData(chan, ft, NULL); + + if(handler->ft_offered) + handler->ft_offered(ft); + } + + g_free(fnm); + g_free(txt); +} + + +static void recv_channelAccept(struct mwServiceFileTransfer *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + struct mwFileTransferHandler *handler; + struct mwFileTransfer *ft; + + g_return_if_fail(srvc->handler != NULL); + handler = srvc->handler; + + ft = mwChannel_getServiceData(chan); + g_return_if_fail(ft != NULL); + + ft_state(ft, mwFileTransfer_OPEN); + + if(handler->ft_opened) + handler->ft_opened(ft); +} + + +static void recv_channelDestroy(struct mwServiceFileTransfer *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwFileTransferHandler *handler; + struct mwFileTransfer *ft; + guint32 code; + + code = msg->reason; + + g_return_if_fail(srvc->handler != NULL); + handler = srvc->handler; + + ft = mwChannel_getServiceData(chan); + g_return_if_fail(ft != NULL); + + ft->channel = NULL; + + if(! mwFileTransfer_isDone(ft)) + ft_state(ft, mwFileTransfer_CANCEL_REMOTE); + + mwFileTransfer_close(ft, code); +} + + +static void recv_TRANSFER(struct mwFileTransfer *ft, + struct mwOpaque *data) { + + struct mwServiceFileTransfer *srvc; + struct mwFileTransferHandler *handler; + + srvc = ft->service; + handler = srvc->handler; + + g_return_if_fail(mwFileTransfer_isOpen(ft)); + + if(data->len > ft->remaining) { + /* @todo handle error */ + + } else { + ft->remaining -= data->len; + + if(! ft->remaining) + ft_state(ft, mwFileTransfer_DONE); + + if(handler->ft_recv) + handler->ft_recv(ft, data); + } +} + + +static void recv_RECEIVED(struct mwFileTransfer *ft, + struct mwOpaque *data) { + + struct mwServiceFileTransfer *srvc; + struct mwFileTransferHandler *handler; + + srvc = ft->service; + handler = srvc->handler; + + if(! ft->remaining) + ft_state(ft, mwFileTransfer_DONE); + + if(handler->ft_ack) + handler->ft_ack(ft); + + if(! ft->remaining) + mwFileTransfer_close(ft, mwFileTransfer_SUCCESS); +} + + +static void recv(struct mwService *srvc, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + struct mwFileTransfer *ft; + + ft = mwChannel_getServiceData(chan); + g_return_if_fail(ft != NULL); + + switch(type) { + case msg_TRANSFER: + recv_TRANSFER(ft, data); + break; + + case msg_RECEIVED: + recv_RECEIVED(ft, data); + break; + + default: + mw_mailme_opaque(data, "unknown message in ft service: 0x%04x", type); + } +} + + +static void clear(struct mwServiceFileTransfer *srvc) { + struct mwFileTransferHandler *h; + + h = srvc->handler; + if(h && h->clear) + h->clear(srvc); + srvc->handler = NULL; +} + + +static const char *name(struct mwService *srvc) { + return "File Transfer"; +} + + +static const char *desc(struct mwService *srvc) { + return "Provides file transfer capabilities through the community server"; +} + + +static void start(struct mwService *srvc) { + mwService_started(srvc); +} + + +static void stop(struct mwServiceFileTransfer *srvc) { + while(srvc->transfers) { + mwFileTransfer_free(srvc->transfers->data); + } + + mwService_stopped(MW_SERVICE(srvc)); +} + + +struct mwServiceFileTransfer * +mwServiceFileTransfer_new(struct mwSession *session, + struct mwFileTransferHandler *handler) { + + struct mwServiceFileTransfer *srvc_ft; + struct mwService *srvc; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(handler != NULL, NULL); + + srvc_ft = g_new0(struct mwServiceFileTransfer, 1); + srvc = MW_SERVICE(srvc_ft); + + mwService_init(srvc, session, mwService_FILE_TRANSFER); + srvc->recv_create = (mwService_funcRecvCreate) recv_channelCreate; + srvc->recv_accept = (mwService_funcRecvAccept) recv_channelAccept; + srvc->recv_destroy = (mwService_funcRecvDestroy) recv_channelDestroy; + srvc->recv = recv; + srvc->clear = (mwService_funcClear) clear; + srvc->get_name = name; + srvc->get_desc = desc; + srvc->start = start; + srvc->stop = (mwService_funcStop) stop; + + srvc_ft->handler = handler; + + return srvc_ft; +} + + +struct mwFileTransferHandler * +mwServiceFileTransfer_getHandler(struct mwServiceFileTransfer *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +const GList * +mwServiceFileTransfer_getTransfers(struct mwServiceFileTransfer *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->transfers; +} + + +struct mwFileTransfer * +mwFileTransfer_new(struct mwServiceFileTransfer *srvc, + const struct mwIdBlock *who, const char *msg, + const char *filename, guint32 filesize) { + + struct mwFileTransfer *ft; + + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(who != NULL, NULL); + + ft = g_new0(struct mwFileTransfer, 1); + ft->service = srvc; + mwIdBlock_clone(&ft->who, who); + ft->filename = g_strdup(filename); + ft->message = g_strdup(msg); + ft->size = ft->remaining = filesize; + + ft_state(ft, mwFileTransfer_NEW); + + /* stick a reference in the service */ + srvc->transfers = g_list_prepend(srvc->transfers, ft); + + return ft; +} + + +struct mwServiceFileTransfer * +mwFileTransfer_getService(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, NULL); + return ft->service; +} + + +enum mwFileTransferState +mwFileTransfer_getState(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, mwFileTransfer_UNKNOWN); + return ft->state; +} + + +const struct mwIdBlock * +mwFileTransfer_getUser(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, NULL); + return &ft->who; +} + + +const char * +mwFileTransfer_getMessage(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, NULL); + return ft->message; +} + + +const char * +mwFileTransfer_getFileName(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, NULL); + return ft->filename; +} + + +guint32 mwFileTransfer_getFileSize(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, 0); + return ft->size; +} + + +guint32 mwFileTransfer_getRemaining(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, 0); + return ft->remaining; +} + + +int mwFileTransfer_accept(struct mwFileTransfer *ft) { + struct mwServiceFileTransfer *srvc; + struct mwFileTransferHandler *handler; + int ret; + + g_return_val_if_fail(ft != NULL, -1); + g_return_val_if_fail(ft->channel != NULL, -1); + g_return_val_if_fail(mwFileTransfer_isPending(ft), -1); + g_return_val_if_fail(mwChannel_isIncoming(ft->channel), -1); + g_return_val_if_fail(mwChannel_isState(ft->channel, mwChannel_WAIT), -1); + + g_return_val_if_fail(ft->service != NULL, -1); + srvc = ft->service; + + g_return_val_if_fail(srvc->handler != NULL, -1); + handler = srvc->handler; + + ret = mwChannel_accept(ft->channel); + + if(ret) { + mwFileTransfer_close(ft, ERR_FAILURE); + + } else { + ft_state(ft, mwFileTransfer_OPEN); + if(handler->ft_opened) + handler->ft_opened(ft); + } + + return ret; +} + + +static void ft_create_chan(struct mwFileTransfer *ft) { + struct mwSession *s; + struct mwChannelSet *cs; + struct mwChannel *chan; + struct mwLoginInfo *login; + struct mwPutBuffer *b; + + /* we only should be calling this if there isn't a channel already + associated with the conversation */ + g_return_if_fail(ft != NULL); + g_return_if_fail(mwFileTransfer_isNew(ft)); + g_return_if_fail(ft->channel == NULL); + + s = mwService_getSession(MW_SERVICE(ft->service)); + cs = mwSession_getChannels(s); + + chan = mwChannel_newOutgoing(cs); + mwChannel_setService(chan, MW_SERVICE(ft->service)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + /* offer all known ciphers */ + mwChannel_populateSupportedCipherInstances(chan); + + /* set the target */ + login = mwChannel_getUser(chan); + login->user_id = g_strdup(ft->who.user); + login->community = g_strdup(ft->who.community); + + /* compose the addtl create */ + b = mwPutBuffer_new(); + guint32_put(b, 0x00); + mwString_put(b, ft->filename); + mwString_put(b, ft->message); + guint32_put(b, ft->size); + guint32_put(b, 0x00); + guint16_put(b, 0x00); + + mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); + + ft->channel = mwChannel_create(chan)? NULL: chan; + if(ft->channel) { + mwChannel_setServiceData(ft->channel, ft, NULL); + } +} + + +int mwFileTransfer_offer(struct mwFileTransfer *ft) { + struct mwServiceFileTransfer *srvc; + struct mwFileTransferHandler *handler; + + g_return_val_if_fail(ft != NULL, -1); + g_return_val_if_fail(ft->channel == NULL, -1); + g_return_val_if_fail(mwFileTransfer_isNew(ft), -1); + + g_return_val_if_fail(ft->service != NULL, -1); + srvc = ft->service; + + g_return_val_if_fail(srvc->handler != NULL, -1); + handler = srvc->handler; + + ft_create_chan(ft); + if(ft->channel) { + ft_state(ft, mwFileTransfer_PENDING); + } else { + ft_state(ft, mwFileTransfer_ERROR); + mwFileTransfer_close(ft, ERR_FAILURE); + } + + return 0; +} + + +int mwFileTransfer_close(struct mwFileTransfer *ft, guint32 code) { + struct mwServiceFileTransfer *srvc; + struct mwFileTransferHandler *handler; + int ret = 0; + + g_return_val_if_fail(ft != NULL, -1); + + if(mwFileTransfer_isOpen(ft)) + ft_state(ft, mwFileTransfer_CANCEL_LOCAL); + + if(ft->channel) { + ret = mwChannel_destroy(ft->channel, code, NULL); + ft->channel = NULL; + } + + srvc = ft->service; + g_return_val_if_fail(srvc != NULL, ret); + + handler = srvc->handler; + g_return_val_if_fail(handler != NULL, ret); + + if(handler->ft_closed) + handler->ft_closed(ft, code); + + return ret; +} + + +void mwFileTransfer_free(struct mwFileTransfer *ft) { + struct mwServiceFileTransfer *srvc; + + if(! ft) return; + + srvc = ft->service; + if(srvc) + srvc->transfers = g_list_remove(srvc->transfers, ft); + + if(ft->channel) { + mwChannel_destroy(ft->channel, mwFileTransfer_SUCCESS, NULL); + ft->channel = NULL; + } + + mwFileTransfer_removeClientData(ft); + + mwIdBlock_clear(&ft->who); + g_free(ft->filename); + g_free(ft->message); + g_free(ft); +} + + +int mwFileTransfer_send(struct mwFileTransfer *ft, + struct mwOpaque *data) { + + struct mwChannel *chan; + int ret; + + g_return_val_if_fail(ft != NULL, -1); + g_return_val_if_fail(mwFileTransfer_isOpen(ft), -1); + g_return_val_if_fail(ft->channel != NULL, -1); + chan = ft->channel; + + g_return_val_if_fail(mwChannel_isOutgoing(chan), -1); + + if(data->len > ft->remaining) { + /* @todo handle error */ + return -1; + } + + ret = mwChannel_send(chan, msg_TRANSFER, data); + if(! ret) ft->remaining -= data->len; + + /* we're not done until we receive an ACK for the last piece of + outgoing data */ + + return ret; +} + + +int mwFileTransfer_ack(struct mwFileTransfer *ft) { + struct mwChannel *chan; + + g_return_val_if_fail(ft != NULL, -1); + + chan = ft->channel; + g_return_val_if_fail(chan != NULL, -1); + g_return_val_if_fail(mwChannel_isIncoming(chan), -1); + + return mwChannel_sendEncrypted(chan, msg_RECEIVED, NULL, FALSE); +} + + +void mwFileTransfer_setClientData(struct mwFileTransfer *ft, + gpointer data, GDestroyNotify clean) { + g_return_if_fail(ft != NULL); + mw_datum_set(&ft->client_data, data, clean); +} + + +gpointer mwFileTransfer_getClientData(struct mwFileTransfer *ft) { + g_return_val_if_fail(ft != NULL, NULL); + return mw_datum_get(&ft->client_data); +} + + +void mwFileTransfer_removeClientData(struct mwFileTransfer *ft) { + g_return_if_fail(ft != NULL); + mw_datum_clear(&ft->client_data); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_im.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,1055 @@ + +/* + 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.h> +#include <glib/glist.h> +#include <string.h> + +#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_im.h" +#include "mw_util.h" + + +#define PROTOCOL_TYPE 0x00001000 +#define PROTOCOL_VER 0x00000003 + + +/* data for the addtl blocks of channel creation */ +#define mwImAddtlA_NORMAL 0x00000001 + +#define mwImAddtlB_NORMAL 0x00000001 /**< standard */ +#define mwImAddtlB_PRECONF 0x00000019 /**< pre-conference chat */ +#define mwImAddtlB_NOTESBUDDY 0x00033453 /**< notesbuddy */ + +#define mwImAddtlC_NORMAL 0x00000002 + + +/* send-on-channel message type */ +#define msg_MESSAGE 0x0064 /**< IM message */ + + +#define BREAKUP 2048 + + +/* which type of im? */ +enum mwImType { + mwIm_TEXT = 0x00000001, /**< text message */ + mwIm_DATA = 0x00000002, /**< status message (usually) */ +}; + + +/* which type of data im? */ +enum mwImDataType { + mwImData_TYPING = 0x00000001, /**< common use typing indicator */ + mwImData_SUBJECT = 0x00000003, /**< notesbuddy IM topic */ + mwImData_HTML = 0x00000004, /**< notesbuddy HTML message */ + mwImData_MIME = 0x00000005, /**< notesbuddy MIME message, w/image */ + mwImData_TIMESTAMP = 0x00000006, /**< notesbuddy timestamp */ + + mwImData_INVITE = 0x0000000a, /**< Places invitation */ + + mwImData_MULTI_START = 0x00001388, + mwImData_MULTI_STOP = 0x00001389, +}; + + +/** @todo might be appropriate to make a couple of hashtables to + reference conversations by channel and target */ +struct mwServiceIm { + struct mwService service; + + enum mwImClientType features; + + struct mwImHandler *handler; + GList *convs; /**< list of struct im_convo */ +}; + + +struct mwConversation { + struct mwServiceIm *service; /**< owning service */ + struct mwChannel *channel; /**< channel */ + struct mwIdBlock target; /**< conversation target */ + + gboolean ext_id; /**< special treatment, external ID */ + + /** state of the conversation, based loosely on the state of its + underlying channel */ + enum mwConversationState state; + + enum mwImClientType features; + + GString *multi; /**< buffer for multi-chunk message */ + enum mwImSendType multi_type; /**< type of incoming multi-chunk message */ + + struct mw_datum client_data; +}; + + +/** momentarily places a mwLoginInfo into a mwIdBlock */ +static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) { + to->user = from->user_id; + to->community = from->community; +} + + +static struct mwConversation *convo_find_by_user(struct mwServiceIm *srvc, + struct mwIdBlock *to) { + GList *l; + + for(l = srvc->convs; l; l = l->next) { + struct mwConversation *c = l->data; + if(mwIdBlock_equal(&c->target, to)) + return c; + } + + return NULL; +} + + +static const char *conv_state_str(enum mwConversationState state) { + switch(state) { + case mwConversation_CLOSED: + return "closed"; + + case mwConversation_OPEN: + return "open"; + + case mwConversation_PENDING: + return "pending"; + + case mwConversation_UNKNOWN: + default: + return "UNKNOWN"; + } +} + + +static void convo_set_state(struct mwConversation *conv, + enum mwConversationState state) { + + g_return_if_fail(conv != NULL); + + if(conv->state != state) { + g_info("setting conversation (%s, %s) state: %s", + NSTR(conv->target.user), NSTR(conv->target.community), + conv_state_str(state)); + conv->state = state; + } +} + + +struct mwConversation *mwServiceIm_findConversation(struct mwServiceIm *srvc, + struct mwIdBlock *to) { + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(to != NULL, NULL); + + return convo_find_by_user(srvc, to); +} + + +struct mwConversation *mwServiceIm_getConversation(struct mwServiceIm *srvc, + struct mwIdBlock *to) { + struct mwConversation *c; + + g_return_val_if_fail(srvc != NULL, NULL); + g_return_val_if_fail(to != NULL, NULL); + + c = convo_find_by_user(srvc, to); + if(! c) { + c = g_new0(struct mwConversation, 1); + c->service = srvc; + mwIdBlock_clone(&c->target, to); + c->state = mwConversation_CLOSED; + c->features = srvc->features; + + /* mark external users */ + /* c->ext_id = g_str_has_prefix(to->user, "@E "); */ + + srvc->convs = g_list_prepend(srvc->convs, c); + } + + return c; +} + + +static void convo_create_chan(struct mwConversation *c) { + struct mwSession *s; + struct mwChannelSet *cs; + struct mwChannel *chan; + struct mwLoginInfo *login; + struct mwPutBuffer *b; + + /* we only should be calling this if there isn't a channel already + associated with the conversation */ + g_return_if_fail(c != NULL); + g_return_if_fail(mwConversation_isPending(c)); + g_return_if_fail(c->channel == NULL); + + s = mwService_getSession(MW_SERVICE(c->service)); + cs = mwSession_getChannels(s); + + chan = mwChannel_newOutgoing(cs); + mwChannel_setService(chan, MW_SERVICE(c->service)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + /* offer all known ciphers */ + mwChannel_populateSupportedCipherInstances(chan); + + /* set the target */ + login = mwChannel_getUser(chan); + login->user_id = g_strdup(c->target.user); + login->community = g_strdup(c->target.community); + + /* compose the addtl create, with optional FANCY HTML! */ + b = mwPutBuffer_new(); + guint32_put(b, mwImAddtlA_NORMAL); + guint32_put(b, c->features); + mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); + + c->channel = mwChannel_create(chan)? NULL: chan; + if(c->channel) { + mwChannel_setServiceData(c->channel, c, NULL); + } +} + + +void mwConversation_open(struct mwConversation *conv) { + g_return_if_fail(conv != NULL); + g_return_if_fail(mwConversation_isClosed(conv)); + + convo_set_state(conv, mwConversation_PENDING); + convo_create_chan(conv); +} + + +static void convo_opened(struct mwConversation *conv) { + struct mwImHandler *h = NULL; + + g_return_if_fail(conv != NULL); + g_return_if_fail(conv->service != NULL); + + convo_set_state(conv, mwConversation_OPEN); + h = conv->service->handler; + + g_return_if_fail(h != NULL); + + if(h->conversation_opened) + h->conversation_opened(conv); +} + + +static void convo_free(struct mwConversation *conv) { + struct mwServiceIm *srvc; + + mwConversation_removeClientData(conv); + + srvc = conv->service; + srvc->convs = g_list_remove_all(srvc->convs, conv); + + mwIdBlock_clear(&conv->target); + g_free(conv); +} + + +static int send_accept(struct mwConversation *c) { + + struct mwChannel *chan = c->channel; + struct mwSession *s = mwChannel_getSession(chan); + struct mwUserStatus *stat = mwSession_getUserStatus(s); + + struct mwPutBuffer *b; + struct mwOpaque *o; + + b = mwPutBuffer_new(); + guint32_put(b, mwImAddtlA_NORMAL); + guint32_put(b, c->features); + guint32_put(b, mwImAddtlC_NORMAL); + mwUserStatus_put(b, stat); + + o = mwChannel_getAddtlAccept(chan); + mwOpaque_clear(o); + mwPutBuffer_finalize(o, b); + + return mwChannel_accept(chan); +} + + +static void recv_channelCreate(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelCreate *msg) { + + /* - ensure it's the right service,proto,and proto ver + - check the opaque for the right opaque junk + - if not, close channel + - compose & send a channel accept + */ + + struct mwServiceIm *srvc_im = (struct mwServiceIm *) srvc; + struct mwImHandler *handler; + struct mwSession *s; + struct mwUserStatus *stat; + guint32 x, y, z; + struct mwGetBuffer *b; + struct mwConversation *c = NULL; + struct mwIdBlock idb; + + handler = srvc_im->handler; + s = mwChannel_getSession(chan); + stat = mwSession_getUserStatus(s); + + /* ensure the appropriate service/proto/ver */ + x = mwChannel_getServiceId(chan); + y = mwChannel_getProtoType(chan); + z = mwChannel_getProtoVer(chan); + + if( (x != mwService_IM) || (y != PROTOCOL_TYPE) || (z != PROTOCOL_VER) ) { + g_warning("unacceptable service, proto, ver:" + " 0x%08x, 0x%08x, 0x%08x", x, y, z); + mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); + return; + } + + /* act upon the values in the addtl block */ + b = mwGetBuffer_wrap(&msg->addtl); + guint32_get(b, &x); + guint32_get(b, &y); + z = (guint) mwGetBuffer_error(b); + mwGetBuffer_free(b); + + if(z /* buffer error, BOOM! */ ) { + g_warning("bad/malformed addtl in IM service"); + mwChannel_destroy(chan, ERR_FAILURE, NULL); + return; + + } else if(x != mwImAddtlA_NORMAL) { + g_message("unknown params: 0x%08x, 0x%08x", x, y); + mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); + return; + + } else if(y == mwImAddtlB_PRECONF) { + if(! handler->place_invite) { + g_info("rejecting place-invite channel"); + mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); + return; + + } else { + g_info("accepting place-invite channel"); + } + + } else if(y != mwImClient_PLAIN && y != srvc_im->features) { + /** reject what we do not understand */ + mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); + return; + + } else if(stat->status == mwStatus_BUSY) { + g_info("rejecting IM channel due to DND status"); + mwChannel_destroy(chan, ERR_CLIENT_USER_DND, NULL); + return; + } + + login_into_id(&idb, mwChannel_getUser(chan)); + +#if 0 + c = convo_find_by_user(srvc_im, &idb); +#endif + + if(! c) { + c = g_new0(struct mwConversation, 1); + c->service = srvc_im; + srvc_im->convs = g_list_prepend(srvc_im->convs, c); + } + +#if 0 + /* we're going to re-associate any existing conversations with this + new channel. That means closing any existing ones */ + if(c->channel) { + g_info("closing existing IM channel 0x%08x", mwChannel_getId(c->channel)); + mwConversation_close(c, ERR_SUCCESS); + } +#endif + + /* set up the conversation with this channel, target, and be fancy + if the other side requested it */ + c->channel = chan; + mwIdBlock_clone(&c->target, &idb); + c->features = y; + convo_set_state(c, mwConversation_PENDING); + mwChannel_setServiceData(c->channel, c, NULL); + + if(send_accept(c)) { + g_warning("sending IM channel accept failed"); + mwConversation_free(c); + + } else { + convo_opened(c); + } +} + + +static void recv_channelAccept(struct mwService *srvc, struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + struct mwConversation *conv; + + conv = mwChannel_getServiceData(chan); + if(! conv) { + g_warning("received channel accept for non-existant conversation"); + mwChannel_destroy(chan, ERR_FAILURE, NULL); + return; + } + + convo_opened(conv); +} + + +static void recv_channelDestroy(struct mwService *srvc, struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwConversation *c; + + c = mwChannel_getServiceData(chan); + g_return_if_fail(c != NULL); + + c->channel = NULL; + + if(mwChannel_isState(chan, mwChannel_ERROR)) { + + /* checking for failure on the receiving end to accept html + messages. Fail-over to a non-html format on a new channel for + the convo */ + if(c->features != mwImClient_PLAIN + && (msg->reason == ERR_IM_NOT_REGISTERED || + msg->reason == ERR_SERVICE_NO_SUPPORT)) { + + g_debug("falling back on a plaintext conversation"); + c->features = mwImClient_PLAIN; + convo_create_chan(c); + return; + } + } + + mwConversation_close(c, msg->reason); +} + + +static void convo_recv(struct mwConversation *conv, enum mwImSendType type, + gconstpointer msg) { + + struct mwServiceIm *srvc; + struct mwImHandler *handler; + + g_return_if_fail(conv != NULL); + + srvc = conv->service; + g_return_if_fail(srvc != NULL); + + handler = srvc->handler; + if(handler && handler->conversation_recv) + handler->conversation_recv(conv, type, msg); +} + + +static void convo_multi_start(struct mwConversation *conv) { + g_return_if_fail(conv->multi == NULL); + conv->multi = g_string_new(NULL); +} + + +static void convo_multi_stop(struct mwConversation *conv) { + + g_return_if_fail(conv->multi != NULL); + + /* actually send it */ + convo_recv(conv, conv->multi_type, conv->multi->str); + + /* clear up the multi buffer */ + g_string_free(conv->multi, TRUE); + conv->multi = NULL; +} + + +static void recv_text(struct mwServiceIm *srvc, struct mwChannel *chan, + struct mwGetBuffer *b) { + + struct mwConversation *c; + char *text = NULL; + + mwString_get(b, &text); + + if(! text) return; + + c = mwChannel_getServiceData(chan); + if(c) { + if(c->multi) { + g_string_append(c->multi, text); + + } else { + convo_recv(c, mwImSend_PLAIN, text); + } + } + + g_free(text); +} + + +static void convo_invite(struct mwConversation *conv, + struct mwOpaque *o) { + + struct mwServiceIm *srvc; + struct mwImHandler *handler; + + struct mwGetBuffer *b; + char *title, *name, *msg; + + g_info("convo_invite"); + + srvc = conv->service; + handler = srvc->handler; + + g_return_if_fail(handler != NULL); + g_return_if_fail(handler->place_invite != NULL); + + b = mwGetBuffer_wrap(o); + mwGetBuffer_advance(b, 4); + mwString_get(b, &title); + mwString_get(b, &msg); + mwGetBuffer_advance(b, 19); + mwString_get(b, &name); + + if(! mwGetBuffer_error(b)) { + handler->place_invite(conv, msg, title, name); + } + + mwGetBuffer_free(b); + g_free(msg); + g_free(title); + g_free(name); +} + + +static void recv_data(struct mwServiceIm *srvc, struct mwChannel *chan, + struct mwGetBuffer *b) { + + struct mwConversation *conv; + guint32 type, subtype; + struct mwOpaque o = { 0, 0 }; + char *x; + + guint32_get(b, &type); + guint32_get(b, &subtype); + mwOpaque_get(b, &o); + + if(mwGetBuffer_error(b)) { + mwOpaque_clear(&o); + return; + } + + conv = mwChannel_getServiceData(chan); + if(! conv) return; + + switch(type) { + case mwImData_TYPING: + convo_recv(conv, mwImSend_TYPING, GINT_TO_POINTER(! subtype)); + break; + + case mwImData_HTML: + if(o.len) { + if(conv->multi) { + g_string_append_len(conv->multi, (char *) o.data, o.len); + conv->multi_type = mwImSend_HTML; + + } else { + x = g_strndup((char *) o.data, o.len); + convo_recv(conv, mwImSend_HTML, x); + g_free(x); + } + } + break; + + case mwImData_SUBJECT: + x = g_strndup((char *) o.data, o.len); + convo_recv(conv, mwImSend_SUBJECT, x); + g_free(x); + break; + + case mwImData_MIME: + if(conv->multi) { + g_string_append_len(conv->multi, (char *) o.data, o.len); + conv->multi_type = mwImSend_MIME; + + } else { + x = g_strndup((char *) o.data, o.len); + convo_recv(conv, mwImSend_MIME, x); + g_free(x); + } + break; + + case mwImData_TIMESTAMP: + /* todo */ + break; + + case mwImData_INVITE: + convo_invite(conv, &o); + break; + + case mwImData_MULTI_START: + convo_multi_start(conv); + break; + + case mwImData_MULTI_STOP: + convo_multi_stop(conv); + break; + + default: + + mw_mailme_opaque(&o, "unknown data message type in IM service:" + " (0x%08x, 0x%08x)", type, subtype); + } + + mwOpaque_clear(&o); +} + + +static void recv(struct mwService *srvc, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + /* - ensure message type is something we want + - parse message type into either mwIMText or mwIMData + - handle + */ + + struct mwGetBuffer *b; + guint32 mt; + + g_return_if_fail(type == msg_MESSAGE); + + b = mwGetBuffer_wrap(data); + guint32_get(b, &mt); + + if(mwGetBuffer_error(b)) { + g_warning("failed to parse message for IM service"); + mwGetBuffer_free(b); + return; + } + + switch(mt) { + case mwIm_TEXT: + recv_text((struct mwServiceIm *) srvc, chan, b); + break; + + case mwIm_DATA: + recv_data((struct mwServiceIm *) srvc, chan, b); + break; + + default: + g_warning("unknown message type 0x%08x for IM service", mt); + } + + if(mwGetBuffer_error(b)) + g_warning("failed to parse message type 0x%08x for IM service", mt); + + mwGetBuffer_free(b); +} + + +static void clear(struct mwServiceIm *srvc) { + struct mwImHandler *h; + + while(srvc->convs) + convo_free(srvc->convs->data); + + h = srvc->handler; + if(h && h->clear) + h->clear(srvc); + srvc->handler = NULL; +} + + +static const char *name(struct mwService *srvc) { + return "Instant Messaging"; +} + + +static const char *desc(struct mwService *srvc) { + return "IM service with Standard and NotesBuddy features"; +} + + +static void start(struct mwService *srvc) { + mwService_started(srvc); +} + + +static void stop(struct mwServiceIm *srvc) { + + while(srvc->convs) + mwConversation_free(srvc->convs->data); + + mwService_stopped(MW_SERVICE(srvc)); +} + + +struct mwServiceIm *mwServiceIm_new(struct mwSession *session, + struct mwImHandler *hndl) { + + struct mwServiceIm *srvc_im; + struct mwService *srvc; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(hndl != NULL, NULL); + + srvc_im = g_new0(struct mwServiceIm, 1); + srvc = MW_SERVICE(srvc_im); + + mwService_init(srvc, session, mwService_IM); + srvc->recv_create = recv_channelCreate; + srvc->recv_accept = recv_channelAccept; + srvc->recv_destroy = recv_channelDestroy; + srvc->recv = recv; + srvc->clear = (mwService_funcClear) clear; + srvc->get_name = name; + srvc->get_desc = desc; + srvc->start = start; + srvc->stop = (mwService_funcStop) stop; + + srvc_im->features = mwImClient_PLAIN; + srvc_im->handler = hndl; + + return srvc_im; +} + + +struct mwImHandler *mwServiceIm_getHandler(struct mwServiceIm *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +gboolean mwServiceIm_supports(struct mwServiceIm *srvc, + enum mwImSendType type) { + + g_return_val_if_fail(srvc != NULL, FALSE); + + switch(type) { + case mwImSend_PLAIN: + case mwImSend_TYPING: + return TRUE; + + case mwImSend_SUBJECT: + case mwImSend_HTML: + case mwImSend_MIME: + case mwImSend_TIMESTAMP: + return srvc->features == mwImClient_NOTESBUDDY; + + default: + return FALSE; + } +} + + +void mwServiceIm_setClientType(struct mwServiceIm *srvc, + enum mwImClientType type) { + + g_return_if_fail(srvc != NULL); + srvc->features = type; +} + + +enum mwImClientType mwServiceIm_getClientType(struct mwServiceIm *srvc) { + g_return_val_if_fail(srvc != NULL, mwImClient_UNKNOWN); + return srvc->features; +} + + +static int convo_send_data(struct mwConversation *conv, + guint32 type, guint32 subtype, + struct mwOpaque *data) { + struct mwPutBuffer *b; + struct mwOpaque o; + struct mwChannel *chan; + int ret; + + chan = conv->channel; + g_return_val_if_fail(chan != NULL, -1); + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_DATA); + guint32_put(b, type); + guint32_put(b, subtype); + mwOpaque_put(b, data); + + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_sendEncrypted(chan, msg_MESSAGE, &o, !conv->ext_id); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_send_multi_start(struct mwConversation *conv) { + return convo_send_data(conv, mwImData_MULTI_START, 0x00, NULL); +} + + +static int convo_send_multi_stop(struct mwConversation *conv) { + return convo_send_data(conv, mwImData_MULTI_STOP, 0x00, NULL); +} + + +/* breaks up a large message into segments, sends a start_segment + message, then sends each segment in turn, then sends a stop_segment + message */ +static int +convo_sendSegmented(struct mwConversation *conv, const char *message, + int (*send)(struct mwConversation *conv, + const char *msg)) { + char *buf = (char *) message; + gsize len; + int ret = 0; + + len = strlen(buf); + ret = convo_send_multi_start(conv); + + while(len && !ret) { + char tail; + gsize seg; + + seg = BREAKUP; + if(len < BREAKUP) + seg = len; + + /* temporarily NUL-terminate this segment */ + tail = buf[seg]; + buf[seg] = 0x00; + + ret = send(conv, buf); + + /* restore this segment */ + buf[seg] = tail; + + buf += seg; + len -= seg; + } + + if(! ret) + ret = convo_send_multi_stop(conv); + + return ret; +} + + +static int convo_sendText(struct mwConversation *conv, const char *text) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_TEXT); + mwString_put(b, text); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_sendEncrypted(conv->channel, msg_MESSAGE, &o, !conv->ext_id); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_sendTyping(struct mwConversation *conv, gboolean typing) { + return convo_send_data(conv, mwImData_TYPING, !typing, NULL); +} + + +static int convo_sendSubject(struct mwConversation *conv, + const char *subject) { + struct mwOpaque o; + + o.len = strlen(subject); + o.data = (guchar *) subject; + + return convo_send_data(conv, mwImData_SUBJECT, 0x00, &o); +} + + +static int convo_sendHtml(struct mwConversation *conv, const char *html) { + struct mwOpaque o; + + o.len = strlen(html); + o.data = (guchar *) html; + + if(o.len > BREAKUP) { + return convo_sendSegmented(conv, html, convo_sendHtml); + } else { + return convo_send_data(conv, mwImData_HTML, 0x00, &o); + } +} + + +static int convo_sendMime(struct mwConversation *conv, const char *mime) { + struct mwOpaque o; + + o.len = strlen(mime); + o.data = (guchar *) mime; + + if(o.len > BREAKUP) { + return convo_sendSegmented(conv, mime, convo_sendMime); + } else { + return convo_send_data(conv, mwImData_MIME, 0x00, &o); + } +} + + +int mwConversation_send(struct mwConversation *conv, enum mwImSendType type, + gconstpointer msg) { + + g_return_val_if_fail(conv != NULL, -1); + g_return_val_if_fail(mwConversation_isOpen(conv), -1); + g_return_val_if_fail(conv->channel != NULL, -1); + + switch(type) { + case mwImSend_PLAIN: + return convo_sendText(conv, msg); + case mwImSend_TYPING: + return convo_sendTyping(conv, GPOINTER_TO_INT(msg)); + case mwImSend_SUBJECT: + return convo_sendSubject(conv, msg); + case mwImSend_HTML: + return convo_sendHtml(conv, msg); + case mwImSend_MIME: + return convo_sendMime(conv, msg); + + default: + g_warning("unsupported IM Send Type, 0x%x", type); + return -1; + } +} + + +enum mwConversationState mwConversation_getState(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, mwConversation_UNKNOWN); + return conv->state; +} + + +struct mwServiceIm *mwConversation_getService(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, NULL); + return conv->service; +} + + +gboolean mwConversation_supports(struct mwConversation *conv, + enum mwImSendType type) { + g_return_val_if_fail(conv != NULL, FALSE); + + switch(type) { + case mwImSend_PLAIN: + case mwImSend_TYPING: + return TRUE; + + case mwImSend_SUBJECT: + case mwImSend_HTML: + case mwImSend_MIME: + return conv->features == mwImClient_NOTESBUDDY; + + default: + return FALSE; + } +} + + +enum mwImClientType +mwConversation_getClientType(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, mwImClient_UNKNOWN); + return conv->features; +} + + +struct mwIdBlock *mwConversation_getTarget(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, NULL); + return &conv->target; +} + + +struct mwLoginInfo *mwConversation_getTargetInfo(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, NULL); + g_return_val_if_fail(conv->channel != NULL, NULL); + return mwChannel_getUser(conv->channel); +} + + +void mwConversation_setClientData(struct mwConversation *conv, + gpointer data, GDestroyNotify clean) { + g_return_if_fail(conv != NULL); + mw_datum_set(&conv->client_data, data, clean); +} + + +gpointer mwConversation_getClientData(struct mwConversation *conv) { + g_return_val_if_fail(conv != NULL, NULL); + return mw_datum_get(&conv->client_data); +} + + +void mwConversation_removeClientData(struct mwConversation *conv) { + g_return_if_fail(conv != NULL); + mw_datum_clear(&conv->client_data); +} + + +void mwConversation_close(struct mwConversation *conv, guint32 reason) { + struct mwServiceIm *srvc; + struct mwImHandler *h; + + g_return_if_fail(conv != NULL); + + convo_set_state(conv, mwConversation_CLOSED); + + srvc = conv->service; + g_return_if_fail(srvc != NULL); + + h = srvc->handler; + if(h && h->conversation_closed) + h->conversation_closed(conv, reason); + + if(conv->channel) { + mwChannel_destroy(conv->channel, reason, NULL); + conv->channel = NULL; + } +} + + +void mwConversation_free(struct mwConversation *conv) { + g_return_if_fail(conv != NULL); + + if(! mwConversation_isClosed(conv)) + mwConversation_close(conv, ERR_SUCCESS); + + convo_free(conv); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_place.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,1075 @@ + +/* + 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.h> +#include <glib/ghash.h> +#include <glib/glist.h> + +#include <stdio.h> +#include <stdlib.h> + +#include "mw_channel.h" +#include "mw_common.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_place.h" +#include "mw_util.h" + + +#define PROTOCOL_TYPE 0x00 +#define PROTOCOL_VER 0x05 + + +enum incoming_msg { + msg_in_JOIN_RESPONSE = 0x0000, /* ? */ + msg_in_INFO = 0x0002, + msg_in_MESSAGE = 0x0004, + msg_in_SECTION = 0x0014, /* see in_section_subtype */ + msg_in_UNKNOWNa = 0x0015, +}; + + +enum in_section_subtype { + msg_in_SECTION_LIST = 0x0000, /* list of section members */ + msg_in_SECTION_PEER = 0x0001, /* see in_section_peer_subtye */ + msg_in_SECTION_PART = 0x0003, +}; + + +enum in_section_peer_subtype { + msg_in_SECTION_PEER_JOIN = 0x0000, + msg_in_SECTION_PEER_PART = 0x0001, /* after msg_in_SECTION_PART */ + msg_in_SECTION_PEER_CLEAR_ATTR = 0x0003, + msg_in_SECTION_PEER_SET_ATTR = 0x0004, +}; + + +enum outgoing_msg { + msg_out_JOIN_PLACE = 0x0000, /* ? */ + msg_out_PEER_INFO = 0x0002, /* ? */ + msg_out_MESSAGE = 0x0003, + msg_out_OLD_INVITE = 0x0005, /* old-style conf. invitation */ + msg_out_SET_ATTR = 0x000a, + msg_out_CLEAR_ATTR = 0x000b, + msg_out_SECTION = 0x0014, /* see out_section_subtype */ + msg_out_UNKNOWNb = 0x001e, /* ? maybe enter stage ? */ +}; + + +enum out_section_subtype { + msg_out_SECTION_LIST = 0x0002, /* req list of members */ + msg_out_SECTION_PART = 0x0003, +}; + + +/* + : allocate section + : state = NEW + + : create channel + : state = PENDING + + : channel accepted + : msg_out_JOIN_PLACE (maybe create?) + : state = JOINING + + : msg_in_JOIN_RESPONSE (contains our place member ID and section ID) + : msg_in_INFO (for place, not peer) + : state = JOINED + + : msg_out_SECTION_LIST (asking for all sections) (optional) + : msg_in_SECTION_LIST (listing all sections, as requested above) + + : msg_out_PEER_INFO (with our place member ID) (optional) + : msg_in_INFO (peer info as requested above) + + : msg_out_SECTION_LIST (with our section ID) (sorta optional) + : msg_in_SECTION_LIST (section listing as requested above) + + : msg_out_UNKNOWNb + : msg_in_SECTION_PEER_JOINED (empty, with our place member ID) + : state = OPEN + + : stuff... (invites, joins, parts, messages, attr) + + : state = CLOSING + : msg_out_SECTION_PART + : destroy channel + : deallocate section +*/ + + +struct mwServicePlace { + struct mwService service; + struct mwPlaceHandler *handler; + GList *places; +}; + + +enum mwPlaceState { + mwPlace_NEW, + mwPlace_PENDING, + mwPlace_JOINING, + mwPlace_JOINED, + mwPlace_OPEN, + mwPlace_CLOSING, + mwPlace_ERROR, + mwPlace_UNKNOWN, +}; + + +struct mwPlace { + struct mwServicePlace *service; + + enum mwPlaceState state; + struct mwChannel *channel; + + char *name; + char *title; + GHashTable *members; /* mapping of member ID: place_member */ + guint32 our_id; /* our member ID */ + guint32 section; /* the section we're using */ + + guint32 requests; /* counter for requests */ + + struct mw_datum client_data; +}; + + +struct place_member { + guint32 place_id; + guint16 member_type; + struct mwIdBlock idb; + char *login_id; + char *name; + guint16 login_type; + guint32 unknown_a; + guint32 unknown_b; +}; + + +#define GET_MEMBER(place, id) \ + (g_hash_table_lookup(place->members, GUINT_TO_POINTER(id))) + + +#define PUT_MEMBER(place, member) \ + (g_hash_table_insert(place->members, \ + GUINT_TO_POINTER(member->place_id), member)) + + +#define REMOVE_MEMBER_ID(place, id) \ + (g_hash_table_remove(place->members, GUINT_TO_POINTER(id))) + + +#define REMOVE_MEMBER(place, member) \ + REMOVE_MEMBER_ID(place, member->place_id) + + +static void member_free(struct place_member *p) { + mwIdBlock_clear(&p->idb); + g_free(p->login_id); + g_free(p->name); + g_free(p); +} + + +__attribute__((used)) +static const struct mwLoginInfo * +member_as_login_info(struct place_member *p) { + static struct mwLoginInfo li; + + li.login_id = p->login_id; + li.type = p->login_type; + li.user_id = p->idb.user; + li.user_name = p->name; + li.community = p->idb.community; + li.full = FALSE; + + return &li; +} + + +static const char *place_state_str(enum mwPlaceState s) { + switch(s) { + case mwPlace_NEW: return "new"; + case mwPlace_PENDING: return "pending"; + case mwPlace_JOINING: return "joining"; + case mwPlace_JOINED: return "joined"; + case mwPlace_OPEN: return "open"; + case mwPlace_CLOSING: return "closing"; + case mwPlace_ERROR: return "error"; + + case mwPlace_UNKNOWN: /* fall-through */ + default: return "UNKNOWN"; + } +} + + +static void place_state(struct mwPlace *place, enum mwPlaceState s) { + g_return_if_fail(place != NULL); + + if(place->state == s) return; + + place->state = s; + g_message("place %s state: %s", NSTR(place->name), place_state_str(s)); +} + + +static void place_free(struct mwPlace *place) { + struct mwServicePlace *srvc; + + if(! place) return; + + srvc = place->service; + g_return_if_fail(srvc != NULL); + + srvc->places = g_list_remove_all(srvc->places, place); + + mw_datum_clear(&place->client_data); + + g_hash_table_destroy(place->members); + + g_free(place->name); + g_free(place->title); + g_free(place); +} + + +static int recv_JOIN_RESPONSE(struct mwPlace *place, + struct mwGetBuffer *b) { + + int ret = 0; + guint32 our_id, section; + + guint32_get(b, &our_id); + guint32_get(b, §ion); + + place->our_id = our_id; + place->section = section; + + return ret; +} + + +static int send_SECTION_LIST(struct mwPlace *place, guint32 section) { + int ret = 0; + struct mwOpaque o = {0, 0}; + struct mwPutBuffer *b; + + b = mwPutBuffer_new(); + guint16_put(b, msg_out_SECTION_LIST); + guint32_put(b, section); + gboolean_put(b, FALSE); + guint32_put(b, ++place->requests); + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_send(place->channel, msg_out_SECTION, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int recv_INFO(struct mwPlace *place, + struct mwGetBuffer *b) { + + int ret = 0; + guint32 skip = 0; + guint32 section = 0; + + guint32_get(b, &skip); + guint32_get(b, §ion); + mwGetBuffer_advance(b, skip); + + if(! section) { + /* this is a place info rather than member info */ + if(place->title) g_free(place->title); + mwGetBuffer_advance(b, 2); + mwString_get(b, &place->title); + + place_state(place, mwPlace_JOINED); + ret = send_SECTION_LIST(place, place->section); + } + + return ret; +} + + +static int recv_MESSAGE(struct mwPlace *place, + struct mwGetBuffer *b) { + + struct mwServicePlace *srvc; + guint32 pm_id; + guint32 unkn_a, unkn_b, ign; + struct place_member *pm; + char *msg = NULL; + int ret = 0; + + srvc = place->service; + + /* no messages before becoming fully open, please */ + g_return_val_if_fail(place->state == mwPlace_OPEN, -1); + + /* regarding unkn_a and unkn_b: + + they're probably a section indicator and a message count, I'm + just not sure which is which. Until this implementation supports + place sections in the API, it really doesn't matter. */ + + guint32_get(b, &pm_id); + pm = GET_MEMBER(place, pm_id); + g_return_val_if_fail(pm != NULL, -1); + + guint32_get(b, &unkn_a); + guint32_get(b, &ign); /* actually an opaque length */ + + if(! ign) return ret; + + guint32_get(b, &unkn_b); + mwString_get(b, &msg); + + if(srvc->handler && srvc->handler->message) + srvc->handler->message(place, &pm->idb, msg); + + g_free(msg); + + return ret; +} + + +static void place_opened(struct mwPlace *place) { + struct mwServicePlace *srvc; + + place_state(place, mwPlace_OPEN); + + srvc = place->service; + if(srvc->handler && srvc->handler->opened) + srvc->handler->opened(place); +} + + +static int recv_SECTION_PEER_JOIN(struct mwPlace *place, + struct mwGetBuffer *b) { + struct mwServicePlace *srvc; + struct place_member *pm; + guint32 section; + int ret = 0; + + srvc = place->service; + + guint32_get(b, §ion); + if(! section) { + g_info("SECTION_PEER_JOIN with section 0x00"); + return 0; + } + + mwGetBuffer_advance(b, 4); + + pm = g_new0(struct place_member, 1); + guint32_get(b, &pm->place_id); + guint16_get(b, &pm->member_type); + mwIdBlock_get(b, &pm->idb); + mwString_get(b, &pm->login_id); + mwString_get(b, &pm->name); + guint16_get(b, &pm->login_type); + guint32_get(b, &pm->unknown_a); + guint32_get(b, &pm->unknown_b); + + PUT_MEMBER(place, pm); + if(srvc->handler && srvc->handler->peerJoined) + srvc->handler->peerJoined(place, &pm->idb); + + if(pm->place_id == place->our_id) + place_opened(place); + + return ret; +} + + +static int recv_SECTION_PEER_PART(struct mwPlace *place, + struct mwGetBuffer *b) { + struct mwServicePlace *srvc; + int ret = 0; + guint32 section, id; + struct place_member *pm; + + srvc = place->service; + + guint32_get(b, §ion); + g_return_val_if_fail(section == place->section, 0); + + guint32_get(b, &id); + pm = GET_MEMBER(place, id); + + /* SECTION_PART may have been called already */ + if(! pm) return 0; + + if(srvc->handler && srvc->handler->peerParted) + srvc->handler->peerParted(place, &pm->idb); + + REMOVE_MEMBER(place, pm); + + return ret; +} + + +static int recv_SECTION_PEER_CLEAR_ATTR(struct mwPlace *place, + struct mwGetBuffer *b) { + struct mwServicePlace *srvc; + int ret = 0; + guint32 id, attr; + struct place_member *pm; + + srvc = place->service; + + guint32_get(b, &id); + guint32_get(b, &attr); + + pm = GET_MEMBER(place, id); + g_return_val_if_fail(pm != NULL, -1); + + if(srvc->handler && srvc->handler->peerUnsetAttribute) + srvc->handler->peerUnsetAttribute(place, &pm->idb, attr); + + return ret; +} + + +static int recv_SECTION_PEER_SET_ATTR(struct mwPlace *place, + struct mwGetBuffer *b) { + struct mwServicePlace *srvc; + int ret = 0; + guint32 id, attr; + struct mwOpaque o = {0,0}; + struct place_member *pm; + + srvc = place->service; + + guint32_get(b, &id); + mwGetBuffer_advance(b, 4); + mwOpaque_get(b, &o); + mwGetBuffer_advance(b, 4); + guint32_get(b, &attr); + + pm = GET_MEMBER(place, id); + g_return_val_if_fail(pm != NULL, -1); + + if(srvc->handler && srvc->handler->peerSetAttribute) + srvc->handler->peerSetAttribute(place, &pm->idb, attr, &o); + + mwOpaque_clear(&o); + + return ret; +} + + +static int recv_SECTION_PEER(struct mwPlace *place, + struct mwGetBuffer *b) { + guint16 subtype; + int res; + + guint16_get(b, &subtype); + + g_return_val_if_fail(! mwGetBuffer_error(b), -1); + + switch(subtype) { + case msg_in_SECTION_PEER_JOIN: + res = recv_SECTION_PEER_JOIN(place, b); + break; + + case msg_in_SECTION_PEER_PART: + res = recv_SECTION_PEER_PART(place, b); + break; + + case msg_in_SECTION_PEER_CLEAR_ATTR: + res = recv_SECTION_PEER_CLEAR_ATTR(place, b); + break; + + case msg_in_SECTION_PEER_SET_ATTR: + res = recv_SECTION_PEER_SET_ATTR(place, b); + break; + + default: + res = -1; + } + + return res; +} + + +static int recv_SECTION_LIST(struct mwPlace *place, + struct mwGetBuffer *b) { + int ret = 0; + guint32 sec, count; + + mwGetBuffer_advance(b, 4); + guint32_get(b, &sec); + + g_return_val_if_fail(sec == place->section, -1); + + mwGetBuffer_advance(b, 8); + guint32_get(b, &count); + mwGetBuffer_advance(b, 8); + + while(count--) { + struct place_member *m; + + m = g_new0(struct place_member, 1); + mwGetBuffer_advance(b, 4); + guint32_get(b, &m->place_id); + guint16_get(b, &m->member_type); + mwIdBlock_get(b, &m->idb); + mwString_get(b, &m->login_id); + mwString_get(b, &m->name); + guint16_get(b, &m->login_type); + guint32_get(b, &m->unknown_a); + guint32_get(b, &m->unknown_b); + + PUT_MEMBER(place, m); + } + + if(place->state != mwPlace_OPEN) + place_opened(place); + + return ret; +} + + +static int recv_SECTION_PART(struct mwPlace *place, + struct mwGetBuffer *b) { + /* look up user in place + remove user from place + trigger event */ + + struct mwServicePlace *srvc; + guint32 pm_id; + struct place_member *pm; + + srvc = place->service; + + guint32_get(b, &pm_id); + pm = GET_MEMBER(place, pm_id); + + /* SECTION_PEER_PART may have been called already */ + if(! pm) return 0; + + if(srvc->handler && srvc->handler->peerParted) + srvc->handler->peerParted(place, &pm->idb); + + REMOVE_MEMBER(place, pm); + + return 0; +} + + +static int recv_SECTION(struct mwPlace *place, struct mwGetBuffer *b) { + guint16 subtype; + int res; + + guint16_get(b, &subtype); + + g_return_val_if_fail(! mwGetBuffer_error(b), -1); + + switch(subtype) { + case msg_in_SECTION_LIST: + res = recv_SECTION_LIST(place, b); + break; + + case msg_in_SECTION_PEER: + res = recv_SECTION_PEER(place, b); + break; + + case msg_in_SECTION_PART: + res = recv_SECTION_PART(place, b); + break; + + default: + res = -1; + } + + return res; +} + + +static int recv_UNKNOWNa(struct mwPlace *place, struct mwGetBuffer *b) { + int res = 0; + + if(place->state == mwPlace_JOINING) { + ; + /* place_state(place, mwPlace_JOINED); + res = send_SECTION_LIST(place, place->section); */ + + } else if(place->state == mwPlace_JOINED) { + ; + /* if(GET_MEMBER(place, place->our_id)) + place_opened(place); */ + } + + return res; +} + + +static void recv(struct mwService *service, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + struct mwPlace *place; + struct mwGetBuffer *b; + int res = 0; + + place = mwChannel_getServiceData(chan); + g_return_if_fail(place != NULL); + + b = mwGetBuffer_wrap(data); + switch(type) { + case msg_in_JOIN_RESPONSE: + res = recv_JOIN_RESPONSE(place, b); + break; + + case msg_in_INFO: + res = recv_INFO(place, b); + break; + + case msg_in_MESSAGE: + res = recv_MESSAGE(place, b); + break; + + case msg_in_SECTION: + res = recv_SECTION(place, b); + break; + + case msg_in_UNKNOWNa: + res = recv_UNKNOWNa(place, b); + break; + + default: + mw_mailme_opaque(data, "Received unknown message type 0x%x on place %s", + type, NSTR(place->name)); + } + + if(res) { + mw_mailme_opaque(data, "Troubling parsing message type 0x0%x on place %s", + type, NSTR(place->name)); + } + + mwGetBuffer_free(b); +} + + +static void stop(struct mwServicePlace *srvc) { + while(srvc->places) + mwPlace_destroy(srvc->places->data, ERR_SUCCESS); + + mwService_stopped(MW_SERVICE(srvc)); +} + + +static int send_JOIN_PLACE(struct mwPlace *place) { + struct mwOpaque o = {0, 0}; + struct mwPutBuffer *b; + int ret; + + b = mwPutBuffer_new(); + gboolean_put(b, FALSE); + guint16_put(b, 0x01); + guint16_put(b, 0x02); /* 0x01 */ + guint16_put(b, 0x01); /* 0x00 */ + + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_send(place->channel, msg_out_JOIN_PLACE, &o); + + mwOpaque_clear(&o); + + if(ret) { + place_state(place, mwPlace_ERROR); + } else { + place_state(place, mwPlace_JOINING); + } + + return ret; +} + + +static void recv_channelAccept(struct mwService *service, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + struct mwServicePlace *srvc; + struct mwPlace *place; + int res; + + srvc = (struct mwServicePlace *) service; + g_return_if_fail(srvc != NULL); + + place = mwChannel_getServiceData(chan); + g_return_if_fail(place != NULL); + + res = send_JOIN_PLACE(place); +} + + +static void recv_channelDestroy(struct mwService *service, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + struct mwServicePlace *srvc; + struct mwPlace *place; + + srvc = (struct mwServicePlace *) service; + g_return_if_fail(srvc != NULL); + + place = mwChannel_getServiceData(chan); + g_return_if_fail(place != NULL); + + place_state(place, mwPlace_ERROR); + + place->channel = NULL; + + if(srvc->handler && srvc->handler->closed) + srvc->handler->closed(place, msg->reason); + + mwPlace_destroy(place, msg->reason); +} + + +static void clear(struct mwServicePlace *srvc) { + + if(srvc->handler && srvc->handler->clear) + srvc->handler->clear(srvc); + + while(srvc->places) + place_free(srvc->places->data); +} + + +static const char *get_name(struct mwService *srvc) { + return "Places Conferencing"; +} + + +static const char *get_desc(struct mwService *srvc) { + return "Barebones conferencing via Places"; +} + + +struct mwServicePlace * +mwServicePlace_new(struct mwSession *session, + struct mwPlaceHandler *handler) { + + struct mwServicePlace *srvc_place; + struct mwService *srvc; + + g_return_val_if_fail(session != NULL, NULL); + g_return_val_if_fail(handler != NULL, NULL); + + srvc_place = g_new0(struct mwServicePlace, 1); + srvc_place->handler = handler; + + srvc = MW_SERVICE(srvc_place); + mwService_init(srvc, session, mwService_PLACE); + srvc->start = NULL; + srvc->stop = (mwService_funcStop) stop; + srvc->recv_create = NULL; + srvc->recv_accept = recv_channelAccept; + srvc->recv_destroy = recv_channelDestroy; + srvc->recv = recv; + srvc->clear = (mwService_funcClear) clear; + srvc->get_name = get_name; + srvc->get_desc = get_desc; + + return srvc_place; +} + + +struct mwPlaceHandler * +mwServicePlace_getHandler(struct mwServicePlace *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->handler; +} + + +const GList *mwServicePlace_getPlaces(struct mwServicePlace *srvc) { + g_return_val_if_fail(srvc != NULL, NULL); + return srvc->places; +} + + +struct mwPlace *mwPlace_new(struct mwServicePlace *srvc, + const char *name, const char *title) { + struct mwPlace *place; + + g_return_val_if_fail(srvc != NULL, NULL); + + place = g_new0(struct mwPlace, 1); + place->service = srvc; + place->name = g_strdup(name); + place->title = g_strdup(title); + place->state = mwPlace_NEW; + + place->members = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify) member_free); + + srvc->places = g_list_prepend(srvc->places, place); + + return place; +} + + +struct mwServicePlace *mwPlace_getService(struct mwPlace *place) { + g_return_val_if_fail(place != NULL, NULL); + return place->service; +} + + +static char *place_generate_name(const char *user) { + guint a, b; + char *ret; + + user = user? user: "meanwhile"; + + srand(clock() + rand()); + a = ((rand() & 0xff) << 8) | (rand() & 0xff); + b = time(NULL); + + ret = g_strdup_printf("%s(%08x,%04x)", user, b, a); + g_debug("generated random conference name: '%s'", ret); + return ret; +} + + +const char *mwPlace_getName(struct mwPlace *place) { + g_return_val_if_fail(place != NULL, NULL); + + if(! place->name) { + struct mwSession *session; + struct mwLoginInfo *li; + + session = mwService_getSession(MW_SERVICE(place->service)); + li = mwSession_getLoginInfo(session); + + place->name = place_generate_name(li? li->user_id: NULL); + } + + return place->name; +} + + +static char *place_generate_title(const char *user) { + char *ret; + + user = user? user: "Meanwhile"; + ret = g_strdup_printf("%s's Conference", user); + g_debug("generated conference title: %s", ret); + + return ret; +} + + +const char *mwPlace_getTitle(struct mwPlace *place) { + g_return_val_if_fail(place != NULL, NULL); + + if(! place->title) { + struct mwSession *session; + struct mwLoginInfo *li; + + session = mwService_getSession(MW_SERVICE(place->service)); + li = mwSession_getLoginInfo(session); + + place->title = place_generate_title(li? li->user_name: NULL); + } + + return place->title; +} + + +int mwPlace_open(struct mwPlace *p) { + struct mwSession *session; + struct mwChannelSet *cs; + struct mwChannel *chan; + struct mwPutBuffer *b; + int ret; + + g_return_val_if_fail(p != NULL, -1); + g_return_val_if_fail(p->service != NULL, -1); + + session = mwService_getSession(MW_SERVICE(p->service)); + g_return_val_if_fail(session != NULL, -1); + + cs = mwSession_getChannels(session); + g_return_val_if_fail(cs != NULL, -1); + + chan = mwChannel_newOutgoing(cs); + mwChannel_setService(chan, MW_SERVICE(p->service)); + mwChannel_setProtoType(chan, PROTOCOL_TYPE); + mwChannel_setProtoVer(chan, PROTOCOL_VER); + + mwChannel_populateSupportedCipherInstances(chan); + + b = mwPutBuffer_new(); + mwString_put(b, mwPlace_getName(p)); + mwString_put(b, mwPlace_getTitle(p)); + guint32_put(b, 0x00); /* ? */ + + mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); + + ret = mwChannel_create(chan); + if(ret) { + place_state(p, mwPlace_ERROR); + } else { + place_state(p, mwPlace_PENDING); + p->channel = chan; + mwChannel_setServiceData(chan, p, NULL); + } + + return ret; +} + + +int mwPlace_destroy(struct mwPlace *p, guint32 code) { + int ret = 0; + + place_state(p, mwPlace_CLOSING); + + if(p->channel) { + ret = mwChannel_destroy(p->channel, code, NULL); + p->channel = NULL; + } + + place_free(p); + + return ret; +} + + +GList *mwPlace_getMembers(struct mwPlace *place) { + GList *l, *ll; + + g_return_val_if_fail(place != NULL, NULL); + g_return_val_if_fail(place->members != NULL, NULL); + + ll = map_collect_values(place->members); + for(l = ll; l; l = l->next) { + struct place_member *pm = l->data; + l->data = &pm->idb; + g_info("collected member %u: %s, %s", pm->place_id, + NSTR(pm->idb.user), NSTR(pm->idb.community)); + } + + return ll; +} + + +int mwPlace_sendText(struct mwPlace *place, const char *msg) { + struct mwOpaque o = {0,0}; + struct mwPutBuffer *b; + int ret; + + b = mwPutBuffer_new(); + guint32_put(b, 0x01); /* probably a message type */ + mwString_put(b, msg); + mwPutBuffer_finalize(&o, b); + + b = mwPutBuffer_new(); + guint32_put(b, place->section); + mwOpaque_put(b, &o); + mwOpaque_clear(&o); + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_send(place->channel, msg_out_MESSAGE, &o); + mwOpaque_clear(&o); + return ret; +} + + +int mwPlace_legacyInvite(struct mwPlace *place, + struct mwIdBlock *idb, + const char *message) { + + struct mwOpaque o = {0,0}; + struct mwPutBuffer *b; + int ret; + + b = mwPutBuffer_new(); + mwIdBlock_put(b, idb); + mwString_put(b, idb->user); + mwString_put(b, idb->user); + mwString_put(b, message); + gboolean_put(b, FALSE); + mwPutBuffer_finalize(&o, b); + + ret = mwChannel_send(place->channel, msg_out_OLD_INVITE, &o); + mwOpaque_clear(&o); + return ret; +} + + +int mwPlace_setAttribute(struct mwPlace *place, guint32 attrib, + struct mwOpaque *data) { + + struct mwOpaque o = {0,0}; + struct mwPutBuffer *b; + int ret; + + b = mwPutBuffer_new(); + guint32_put(b, place->our_id); + guint32_put(b, 0x00); + guint32_put(b, attrib); + mwOpaque_put(b, data); + + ret = mwChannel_send(place->channel, msg_out_SET_ATTR, &o); + mwOpaque_clear(&o); + return ret; +} + + +int mwPlace_unsetAttribute(struct mwPlace *place, guint32 attrib) { + struct mwOpaque o = {0,0}; + struct mwPutBuffer *b; + int ret; + + b = mwPutBuffer_new(); + guint32_put(b, place->our_id); + guint32_put(b, attrib); + + ret = mwChannel_send(place->channel, msg_out_SET_ATTR, &o); + mwOpaque_clear(&o); + return ret; +} + + +void mwPlace_setClientData(struct mwPlace *place, + gpointer data, GDestroyNotify clear) { + + g_return_if_fail(place != NULL); + mw_datum_set(&place->client_data, data, clear); +} + + +gpointer mwPlace_getClientData(struct mwPlace *place) { + g_return_val_if_fail(place != NULL, NULL); + return mw_datum_get(&place->client_data); +} + + +void mwPlace_removeClientData(struct mwPlace *place) { + g_return_if_fail(place != NULL); + mw_datum_clear(&place->client_data); +}
--- /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)); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_store.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,608 @@ + +/* + 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/glist.h> + +#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_store.h" + + +#define PROTOCOL_TYPE 0x00000025 +#define PROTOCOL_VER 0x00000001 + + +enum storage_action { + action_load = 0x0004, + action_loaded = 0x0005, + action_save = 0x0006, + action_saved = 0x0007, +}; + + +struct mwStorageUnit { + /** key by which data is referenced in service + @see mwStorageKey */ + guint32 key; + + /** Data associated with key in service */ + struct mwOpaque data; +}; + + +struct mwStorageReq { + guint32 id; /**< unique id for this request */ + guint32 result_code; /**< result code for completed request */ + enum storage_action action; /**< load or save */ + struct mwStorageUnit *item; /**< the key/data pair */ + mwStorageCallback cb; /**< callback to notify upon completion */ + gpointer data; /**< user data to pass with callback */ + GDestroyNotify data_free; /**< optionally frees user data */ +}; + + +struct mwServiceStorage { + struct mwService service; + + /** collection of mwStorageReq */ + GList *pending; + + /** current service channel */ + struct mwChannel *channel; + + /** keep track of the counter */ + guint32 id_counter; +}; + + +static void request_get(struct mwGetBuffer *b, struct mwStorageReq *req) { + guint32 id, count, junk; + + if(mwGetBuffer_error(b)) return; + + guint32_get(b, &id); + guint32_get(b, &req->result_code); + + if(req->action == action_loaded) { + guint32_get(b, &count); + + if(count > 0) { + guint32_get(b, &junk); + guint32_get(b, &req->item->key); + + mwOpaque_clear(&req->item->data); + mwOpaque_get(b, &req->item->data); + } + } +} + + +static void request_put(struct mwPutBuffer *b, struct mwStorageReq *req) { + + guint32_put(b, req->id); + guint32_put(b, 1); + + if(req->action == action_save) { + guint32_put(b, 20 + req->item->data.len); /* ugh, offset garbage */ + guint32_put(b, req->item->key); + mwOpaque_put(b, &req->item->data); + + } else { + guint32_put(b, req->item->key); + } +} + + +static int request_send(struct mwChannel *chan, struct mwStorageReq *req) { + struct mwPutBuffer *b; + struct mwOpaque o = { 0, 0 }; + int ret; + + b = mwPutBuffer_new(); + request_put(b, req); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(chan, req->action, &o); + mwOpaque_clear(&o); + + if(! ret) { + if(req->action == action_save) { + req->action = action_saved; + } else if(req->action == action_load) { + req->action = action_loaded; + } + } + + return ret; +} + + +static struct mwStorageReq *request_find(struct mwServiceStorage *srvc, + guint32 id) { + GList *l; + + for(l = srvc->pending; l; l = l->next) { + struct mwStorageReq *r = l->data; + if(r->id == id) return r; + } + + return NULL; +} + + +static const char *action_str(enum storage_action act) { + switch(act) { + case action_load: return "load"; + case action_loaded: return "loaded"; + case action_save: return "save"; + case action_saved: return "saved"; + default: return "UNKNOWN"; + } +} + + +static void request_trigger(struct mwServiceStorage *srvc, + struct mwStorageReq *req) { + + struct mwStorageUnit *item = req->item; + + g_message("storage request %s: key = 0x%x, result = 0x%x, length = %u", + action_str(req->action), + item->key, req->result_code, (guint) item->data.len); + + if(req->cb) + req->cb(srvc, req->result_code, item, req->data); +} + + +static void request_free(struct mwStorageReq *req) { + if(req->data_free) { + req->data_free(req->data); + req->data = NULL; + req->data_free = NULL; + } + + mwStorageUnit_free(req->item); + g_free(req); +} + + +static void request_remove(struct mwServiceStorage *srvc, + struct mwStorageReq *req) { + + srvc->pending = g_list_remove_all(srvc->pending, req); + request_free(req); +} + + +static const char *get_name(struct mwService *srvc) { + return "User Storage"; +} + + +static const char *get_desc(struct mwService *srvc) { + return "Stores user data and settings on the server"; +} + + +static struct mwChannel *make_channel(struct mwServiceStorage *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 mwService *srvc) { + struct mwServiceStorage *srvc_store; + struct mwChannel *chan; + + g_return_if_fail(srvc != NULL); + srvc_store = (struct mwServiceStorage *) srvc; + + chan = make_channel(srvc_store); + if(chan) { + srvc_store->channel = chan; + } else { + mwService_stopped(srvc); + } +} + + +static void stop(struct mwService *srvc) { + + struct mwServiceStorage *srvc_store; + GList *l; + + g_return_if_fail(srvc != NULL); + srvc_store = (struct mwServiceStorage *) srvc; + + if(srvc_store->channel) { + mwChannel_destroy(srvc_store->channel, ERR_SUCCESS, NULL); + srvc_store->channel = NULL; + } + +#if 1 + /* the new way */ + /* remove pending requests. Sometimes we can crash the storage + service, and when that happens, we end up resending the killer + request over and over again, and the service never stays up */ + for(l = srvc_store->pending; l; l = l->next) + request_free(l->data); + + g_list_free(srvc_store->pending); + srvc_store->pending = NULL; + + srvc_store->id_counter = 0; + +#else + /* the old way */ + /* reset all of the started requests to their unstarted states */ + for(l = srvc_store->pending; l; l = l->next) { + struct mwStorageReq *req = l->data; + + if(req->action == action_loaded) { + req->action = action_load; + } else if(req->action == action_saved) { + req->action = action_save; + } + } +#endif + + mwService_stopped(srvc); +} + + +static void recv_channelAccept(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelAccept *msg) { + + struct mwServiceStorage *srvc_stor; + GList *l; + + g_return_if_fail(srvc != NULL); + srvc_stor = (struct mwServiceStorage *) srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc_stor->channel); + + /* send all pending requests */ + for(l = srvc_stor->pending; l; l = l->next) { + struct mwStorageReq *req = l->data; + + if(req->action == action_save || req->action == action_load) { + request_send(chan, req); + } + } + + mwService_started(srvc); +} + + +static void recv_channelDestroy(struct mwService *srvc, + struct mwChannel *chan, + struct mwMsgChannelDestroy *msg) { + + struct mwSession *session; + struct mwServiceStorage *srvc_stor; + + g_return_if_fail(srvc != NULL); + g_return_if_fail(chan != NULL); + + session = mwService_getSession(srvc); + g_return_if_fail(session != NULL); + + srvc_stor = (struct mwServiceStorage *) srvc; + srvc_stor->channel = NULL; + + mwService_stop(srvc); + mwSession_senseService(session, mwService_getType(srvc)); +} + + +static void recv(struct mwService *srvc, struct mwChannel *chan, + guint16 type, struct mwOpaque *data) { + + /* process into results, trigger callbacks */ + + struct mwGetBuffer *b; + struct mwServiceStorage *srvc_stor; + struct mwStorageReq *req; + guint32 id; + + g_return_if_fail(srvc != NULL); + srvc_stor = (struct mwServiceStorage *) srvc; + + g_return_if_fail(chan != NULL); + g_return_if_fail(chan == srvc_stor->channel); + g_return_if_fail(data != NULL); + + b = mwGetBuffer_wrap(data); + + id = guint32_peek(b); + req = request_find(srvc_stor, id); + + if(! req) { + g_warning("couldn't find request 0x%x in storage service", id); + mwGetBuffer_free(b); + return; + } + + g_return_if_fail(req->action == type); + request_get(b, req); + + if(mwGetBuffer_error(b)) { + mw_mailme_opaque(data, "storage request 0x%x, type: 0x%x", id, type); + + } else { + request_trigger(srvc_stor, req); + } + + mwGetBuffer_free(b); + request_remove(srvc_stor, req); +} + + +static void clear(struct mwService *srvc) { + struct mwServiceStorage *srvc_stor; + GList *l; + + srvc_stor = (struct mwServiceStorage *) srvc; + + for(l = srvc_stor->pending; l; l = l->next) + request_free(l->data); + + g_list_free(srvc_stor->pending); + srvc_stor->pending = NULL; + + srvc_stor->id_counter = 0; +} + + +struct mwServiceStorage *mwServiceStorage_new(struct mwSession *session) { + struct mwServiceStorage *srvc_store; + struct mwService *srvc; + + srvc_store = g_new0(struct mwServiceStorage, 1); + srvc = MW_SERVICE(srvc_store); + + mwService_init(srvc, session, mwService_STORAGE); + srvc->get_name = get_name; + srvc->get_desc = get_desc; + srvc->recv_accept = recv_channelAccept; + srvc->recv_destroy = recv_channelDestroy; + srvc->recv = recv; + srvc->start = start; + srvc->stop = stop; + srvc->clear = clear; + + return srvc_store; +} + + +struct mwStorageUnit *mwStorageUnit_new(guint32 key) { + struct mwStorageUnit *u; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newOpaque(guint32 key, + struct mwOpaque *data) { + struct mwStorageUnit *u; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + if(data) + mwOpaque_clone(&u->data, data); + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newBoolean(guint32 key, + gboolean val) { + + return mwStorageUnit_newInteger(key, (guint32) val); +} + + +struct mwStorageUnit *mwStorageUnit_newInteger(guint32 key, + guint32 val) { + struct mwStorageUnit *u; + struct mwPutBuffer *b; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + b = mwPutBuffer_new(); + guint32_put(b, val); + mwPutBuffer_finalize(&u->data, b); + + return u; +} + + +struct mwStorageUnit *mwStorageUnit_newString(guint32 key, + const char *str) { + struct mwStorageUnit *u; + struct mwPutBuffer *b; + + u = g_new0(struct mwStorageUnit, 1); + u->key = key; + + b = mwPutBuffer_new(); + mwString_put(b, str); + mwPutBuffer_finalize(&u->data, b); + + return u; +} + + +guint32 mwStorageUnit_getKey(struct mwStorageUnit *item) { + g_return_val_if_fail(item != NULL, 0x00); /* feh, unsafe */ + return item->key; +} + + +gboolean mwStorageUnit_asBoolean(struct mwStorageUnit *item, + gboolean val) { + + return !! mwStorageUnit_asInteger(item, (guint32) val); +} + + +guint32 mwStorageUnit_asInteger(struct mwStorageUnit *item, + guint32 val) { + struct mwGetBuffer *b; + guint32 v; + + g_return_val_if_fail(item != NULL, val); + + b = mwGetBuffer_wrap(&item->data); + + guint32_get(b, &v); + if(! mwGetBuffer_error(b)) val = v; + mwGetBuffer_free(b); + + return val; +} + + +char *mwStorageUnit_asString(struct mwStorageUnit *item) { + struct mwGetBuffer *b; + char *c = NULL; + + g_return_val_if_fail(item != NULL, NULL); + + b = mwGetBuffer_wrap(&item->data); + + mwString_get(b, &c); + + if(mwGetBuffer_error(b)) + g_debug("error obtaining string value from opaque"); + + mwGetBuffer_free(b); + + return c; +} + + +struct mwOpaque *mwStorageUnit_asOpaque(struct mwStorageUnit *item) { + g_return_val_if_fail(item != NULL, NULL); + return &item->data; +} + + +void mwStorageUnit_free(struct mwStorageUnit *item) { + if(! item) return; + + mwOpaque_clear(&item->data); + g_free(item); +} + + +static struct mwStorageReq *request_new(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify df) { + + struct mwStorageReq *req = g_new0(struct mwStorageReq, 1); + + req->id = ++srvc->id_counter; + req->item = item; + req->cb = cb; + req->data = data; + req->data_free = df; + + return req; +} + + +void mwServiceStorage_load(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify d_free) { + + /* - construct a request + - put request at end of pending + - if channel is open and connected + - compose the load message + - send message + - set request to sent + - else + - start service + */ + + struct mwStorageReq *req; + + req = request_new(srvc, item, cb, data, d_free); + req->action = action_load; + + srvc->pending = g_list_append(srvc->pending, req); + + if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) + request_send(srvc->channel, req); +} + + +void mwServiceStorage_save(struct mwServiceStorage *srvc, + struct mwStorageUnit *item, + mwStorageCallback cb, + gpointer data, GDestroyNotify d_free) { + + /* - construct a request + - put request at end of pending + - if channel is open and connected + - compose the save message + - send message + - set request to sent + - else + - start service + */ + + struct mwStorageReq *req; + + req = request_new(srvc, item, cb, data, d_free); + req->action = action_save; + + srvc->pending = g_list_append(srvc->pending, req); + + if(MW_SERVICE_IS_STARTED(MW_SERVICE(srvc))) + request_send(srvc->channel, req); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/st_list.c Fri Jan 20 00:19:53 2006 +0000 @@ -0,0 +1,668 @@ + +/* + 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 <stdio.h> +#include <string.h> +#include <glib/gstring.h> + +#include "mw_debug.h" +#include "mw_util.h" +#include "mw_st_list.h" + + +struct mwSametimeList { + guint ver_major; + guint ver_minor; + guint ver_micro; + + GList *groups; +}; + + +struct mwSametimeGroup { + struct mwSametimeList *list; + + enum mwSametimeGroupType type; + char *name; + char *alias; + gboolean open; + + GList *users; +}; + + +struct mwSametimeUser { + struct mwSametimeGroup *group; + + enum mwSametimeUserType type; + struct mwIdBlock id; + char *name; + char *alias; +}; + + +static void user_free(struct mwSametimeUser *u) { + struct mwSametimeGroup *g; + + g = u->group; + g->users = g_list_remove(g->users, u); + + mwIdBlock_clear(&u->id); + g_free(u->name); + g_free(u->alias); + g_free(u); +} + + +static void group_free(struct mwSametimeGroup *g) { + struct mwSametimeList *l; + + l = g->list; + l->groups = g_list_remove(l->groups, g); + + while(g->users) + mwSametimeUser_free(g->users->data); + + g_free(g->name); + g_free(g->alias); + g_free(g); +} + + +static void list_free(struct mwSametimeList *l) { + while(l->groups) + mwSametimeGroup_free(l->groups->data); + + g_free(l); +} + + +struct mwSametimeList * +mwSametimeList_new() { + + struct mwSametimeList *stl; + + stl = g_new0(struct mwSametimeList, 1); + stl->ver_major = ST_LIST_MAJOR; + stl->ver_minor = ST_LIST_MINOR; + stl->ver_micro = ST_LIST_MICRO; + + return stl; +} + + +void mwSametimeList_setMajor(struct mwSametimeList *l, guint v) { + g_return_if_fail(l != NULL); + l->ver_major = v; +} + + +guint mwSametimeList_getMajor(struct mwSametimeList *l) { + g_return_val_if_fail(l != NULL, 0); + return l->ver_major; +} + + +void mwSametimeList_setMinor(struct mwSametimeList *l, guint v) { + g_return_if_fail(l != NULL); + l->ver_minor = v; +} + + +guint mwSametimeList_getMinor(struct mwSametimeList *l) { + g_return_val_if_fail(l != NULL, 0); + return l->ver_minor; +} + + +void mwSametimeList_setMicro(struct mwSametimeList *l, guint v) { + g_return_if_fail(l != NULL); + l->ver_micro = v; +} + + +guint mwSametimeList_getMicro(struct mwSametimeList *l) { + g_return_val_if_fail(l != NULL, 0); + return l->ver_micro; +} + + +GList *mwSametimeList_getGroups(struct mwSametimeList *l) { + g_return_val_if_fail(l != NULL, NULL); + return g_list_copy(l->groups); +} + + +struct mwSametimeGroup * +mwSametimeList_findGroup(struct mwSametimeList *l, + const char *name) { + GList *s; + + g_return_val_if_fail(l != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(*name != '\0', NULL); + + for(s = l->groups; s; s = s->next) { + struct mwSametimeGroup *g = s->data; + if(! strcmp(g->name, name)) return g; + } + + return NULL; +} + + +void mwSametimeList_free(struct mwSametimeList *l) { + g_return_if_fail(l != NULL); + list_free(l); +} + + +struct mwSametimeGroup * +mwSametimeGroup_new(struct mwSametimeList *list, + enum mwSametimeGroupType type, + const char *name) { + + struct mwSametimeGroup *stg; + + g_return_val_if_fail(list != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + g_return_val_if_fail(*name != '\0', NULL); + + stg = g_new0(struct mwSametimeGroup, 1); + stg->list = list; + stg->type = type; + stg->name = g_strdup(name); + + list->groups = g_list_append(list->groups, stg); + + return stg; +} + + +enum mwSametimeGroupType mwSametimeGroup_getType(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, mwSametimeGroup_UNKNOWN); + return g->type; +} + + +const char *mwSametimeGroup_getName(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, NULL); + return g->name; +} + + +void mwSametimeGroup_setAlias(struct mwSametimeGroup *g, + const char *alias) { + g_return_if_fail(g != NULL); + + g_free(g->alias); + g->alias = g_strdup(alias); +} + + +const char *mwSametimeGroup_getAlias(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, NULL); + return g->alias; +} + + +void mwSametimeGroup_setOpen(struct mwSametimeGroup *g, gboolean open) { + g_return_if_fail(g != NULL); + g->open = open; +} + + +gboolean mwSametimeGroup_isOpen(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, FALSE); + return g->open; +} + + +struct mwSametimeList *mwSametimeGroup_getList(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, NULL); + return g->list; +} + + +GList *mwSametimeGroup_getUsers(struct mwSametimeGroup *g) { + g_return_val_if_fail(g != NULL, NULL); + return g_list_copy(g->users); +} + + +struct mwSametimeUser * +mwSametimeGroup_findUser(struct mwSametimeGroup *g, + struct mwIdBlock *user) { + GList *s; + + g_return_val_if_fail(g != NULL, NULL); + g_return_val_if_fail(user != NULL, NULL); + + for(s = g->users; s; s = s->next) { + struct mwSametimeUser *u = s->data; + if(mwIdBlock_equal(user, &u->id)) return u; + } + + return NULL; +} + + +void mwSametimeGroup_free(struct mwSametimeGroup *g) { + g_return_if_fail(g != NULL); + g_return_if_fail(g->list != NULL); + group_free(g); +} + + +struct mwSametimeUser * +mwSametimeUser_new(struct mwSametimeGroup *group, + enum mwSametimeUserType type, + struct mwIdBlock *id) { + + struct mwSametimeUser *stu; + + g_return_val_if_fail(group != NULL, NULL); + g_return_val_if_fail(id != NULL, NULL); + + stu = g_new0(struct mwSametimeUser, 1); + stu->group = group; + stu->type = type; + mwIdBlock_clone(&stu->id, id); + + group->users = g_list_append(group->users, stu); + + return stu; +} + + +struct mwSametimeGroup *mwSametimeUser_getGroup(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, NULL); + return u->group; +} + + +enum mwSametimeUserType mwSametimeUser_getType(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, mwSametimeUser_UNKNOWN); + return u->type; +} + + +const char *mwSametimeUser_getUser(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, NULL); + return u->id.user; +} + + +const char *mwSametimeUser_getCommunity(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, NULL); + return u->id.community; +} + + +void mwSametimeUser_setShortName(struct mwSametimeUser *u, const char *name) { + g_return_if_fail(u != NULL); + g_free(u->name); + u->name = g_strdup(name); +} + + +const char *mwSametimeUser_getShortName(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, NULL); + return u->name; +} + + +void mwSametimeUser_setAlias(struct mwSametimeUser *u, const char *alias) { + g_return_if_fail(u != NULL); + g_free(u->alias); + u->alias = g_strdup(alias); +} + + +const char *mwSametimeUser_getAlias(struct mwSametimeUser *u) { + g_return_val_if_fail(u != NULL, NULL); + return u->alias; +} + + +void mwSametimeUser_free(struct mwSametimeUser *u) { + g_return_if_fail(u != NULL); + g_return_if_fail(u->group != NULL); + user_free(u); +} + + +static void str_replace(char *str, char from, char to) { + if(! str) return; + for(; *str; str++) if(*str == from) *str = to; +} + + +static char user_type_to_char(enum mwSametimeUserType type) { + switch(type) { + case mwSametimeUser_NORMAL: return '1'; + case mwSametimeUser_EXTERNAL: return '2'; + case mwSametimeUser_UNKNOWN: + default: return '9'; + } +} + + +static enum mwSametimeUserType user_char_to_type(char type) { + switch(type) { + case '1': return mwSametimeUser_NORMAL; + case '2': return mwSametimeUser_EXTERNAL; + default: return mwSametimeUser_UNKNOWN; + } +} + + +static void user_put(GString *str, struct mwSametimeUser *u) { + char *id, *name, *alias; + char type; + + id = g_strdup(u->id.user); + name = g_strdup(u->name); + alias = g_strdup(u->alias); + type = user_type_to_char(u->type); + + if(id) str_replace(id, ' ', ';'); + if(name) str_replace(name, ' ', ';'); + if(alias) str_replace(alias, ' ', ';'); + + if(!name && alias) { + name = alias; + alias = NULL; + } + + g_string_append_printf(str, "U %s%c:: %s,%s\r\n", + id, type, (name? name: ""), (alias? alias: "")); + + g_free(id); + g_free(name); + g_free(alias); +} + + +static char group_type_to_char(enum mwSametimeGroupType type) { + switch(type) { + case mwSametimeGroup_NORMAL: return '2'; + case mwSametimeGroup_DYNAMIC: return '3'; + case mwSametimeGroup_UNKNOWN: + default: return '9'; + } +} + + +static enum mwSametimeGroupType group_char_to_type(char type) { + switch(type) { + case '2': return mwSametimeGroup_NORMAL; + case '3': return mwSametimeGroup_DYNAMIC; + default: return mwSametimeGroup_UNKNOWN; + } +} + + +static void group_put(GString *str, struct mwSametimeGroup *g) { + char *name, *alias; + char type; + GList *gl; + + name = g_strdup(g->name); + alias = g_strdup((g->alias)? g->alias: name); + type = group_type_to_char(g->type); + + str_replace(name, ' ', ';'); + str_replace(alias, ' ', ';'); + + g_string_append_printf(str, "G %s%c %s %c\r\n", + name, type, alias, (g->open? 'O':'C')); + + for(gl = g->users; gl; gl = gl->next) { + user_put(str, gl->data); + } + + g_free(name); + g_free(alias); +} + + +/** composes a GString with the written contents of a sametime list */ +static GString *list_store(struct mwSametimeList *l) { + GString *str; + GList *gl; + + g_return_val_if_fail(l != NULL, NULL); + + str = g_string_new(NULL); + g_string_append_printf(str, "Version=%u.%u.%u\r\n", + l->ver_major, l->ver_minor, l->ver_micro); + + for(gl = l->groups; gl; gl = gl->next) { + group_put(str, gl->data); + } + + return str; +} + + +char *mwSametimeList_store(struct mwSametimeList *l) { + GString *str; + char *s; + + g_return_val_if_fail(l != NULL, NULL); + + str = list_store(l); + s = str->str; + g_string_free(str, FALSE); + return s; +} + + +void mwSametimeList_put(struct mwPutBuffer *b, struct mwSametimeList *l) { + GString *str; + guint16 len; + + g_return_if_fail(l != NULL); + g_return_if_fail(b != NULL); + + str = list_store(l); + len = (guint16) str->len; + guint16_put(b, len); + mwPutBuffer_write(b, str->str, len); + + g_string_free(str, TRUE); +} + + +static void get_version(const char *line, struct mwSametimeList *l) { + guint major = 0, minor = 0, micro = 0; + int ret; + + ret = sscanf(line, "Version=%u.%u.%u\n", &major, &minor, µ); + if(ret != 3) { + g_warning("strange sametime list version line:\n%s", line); + } + + l->ver_major = major; + l->ver_minor = minor; + l->ver_micro = micro; +} + + +static struct mwSametimeGroup *get_group(const char *line, + struct mwSametimeList *l) { + struct mwSametimeGroup *group; + char *name, *alias; + char type = '2', open = 'O'; + int ret; + + ret = strlen(line); + name = g_malloc0(ret); + alias = g_malloc0(ret); + + ret = sscanf(line, "G %s %s %c\n", + name, alias, &open); + + if(ret < 3) { + g_warning("strange sametime list group line:\n%s", line); + } + + str_replace(name, ';', ' '); + str_replace(alias, ';', ' '); + + if(name && *name) { + int l = strlen(name)-1; + type = name[l]; + name[l] = '\0'; + } + + group = g_new0(struct mwSametimeGroup, 1); + group->list = l; + group->name = name; + group->type = group_char_to_type(type); + group->alias = alias; + group->open = (open == 'O'); + + l->groups = g_list_append(l->groups, group); + + return group; +} + + +static void get_user(const char *line, struct mwSametimeGroup *g) { + struct mwSametimeUser *user; + struct mwIdBlock idb = { 0, 0 }; + char *name, *alias = NULL; + char type = '1'; + int ret; + + ret = strlen(line); + idb.user = g_malloc0(ret); + name = g_malloc0(ret); + + ret = sscanf(line, "U %s %s", + idb.user, name); + + if(ret < 2) { + g_warning("strange sametime list user line:\n%s", line); + } + + str_replace(idb.user, ';', ' '); + str_replace(name, ';', ' '); + + if(idb.user && *idb.user) { + char *tmp = strstr(idb.user, "::"); + if(tmp--) { + type = *(tmp); + *tmp = '\0'; + } + } + + if(name && *name) { + char *tmp = strrchr(name, ','); + if(tmp) { + *tmp++ = '\0'; + if(*tmp) alias = tmp; + } + } + + user = g_new0(struct mwSametimeUser, 1); + user->group = g; + user->id.user = idb.user; + user->type = user_char_to_type(type); + user->name = name; + user->alias = g_strdup(alias); + + g->users = g_list_append(g->users, user); +} + + +/** returns a line from str, and advances str */ +static char *fetch_line(char **str) { + char *start = *str; + char *end; + + /* move to first non-whitespace character */ + while(*start && g_ascii_isspace(*start)) start++; + if(! *start) return NULL; + + for(end = start + 1; *end; end++) { + if(*end == '\n' || *end == '\r') { + *(end++) = '\0'; + break; + } + } + + *str = end; + return start; +} + + +void list_get(const char *lines, struct mwSametimeList *l) { + char *s = (char *) lines; + char *line; + + struct mwSametimeGroup *g = NULL; + + while( (line = fetch_line(&s)) ) { + switch(*line) { + case 'V': + get_version(line, l); + break; + + case 'G': + g = get_group(line, l); + break; + + case 'U': + get_user(line, g); + break; + + default: + g_warning("unknown sametime list data line:\n%s", line); + } + } +} + + +struct mwSametimeList *mwSametimeList_load(const char *data) { + struct mwSametimeList *l; + + g_return_val_if_fail(data != NULL, NULL); + + l = mwSametimeList_new(); + list_get(data, l); + + return l; +} + + +void mwSametimeList_get(struct mwGetBuffer *b, struct mwSametimeList *l) { + char *str = NULL; + + g_return_if_fail(l != NULL); + g_return_if_fail(b != NULL); + + mwString_get(b, &str); + list_get(str, l); + g_free(str); +} +