# HG changeset patch # User Christopher O'Brien # Date 1117939813 0 # Node ID 3ef77720e57784e497885c8ee5ce4cb879309166 # Parent e0d5038fbb7ee84c22dfdef57f76286be4613141 [gaim-migrate @ 12790] importing meanwhile library for use in the sametime plugin committer: Tailor Script diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/channel.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/channel.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,878 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include + +#include "mw_channel.h" +#include "mw_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; + + /** 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) { + g_return_if_fail(chan != NULL); + + if(chan->state == state) return; + + chan->state = state; + 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); + + 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); + + 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; +} + + +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); + + for(list = l = mwChannel_getSupportedCipherInstances(chan); l; l = l->next) { + struct mwEncryptItem *ei = mwCipherInstance_newItem(l->data); + msg->encrypt.items = g_list_append(msg->encrypt.items, ei); + } + if(list) { + msg->encrypt.mode = 0x1000; + msg->encrypt.extra = 0x1000; + } + g_list_free(list); + + ret = mwSession_send(chan->session, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + state(chan, (ret)? mwChannel_ERROR: mwChannel_WAIT); + + return ret; +} + + +static void channel_open(struct mwChannel *chan) { + state(chan, mwChannel_OPEN); + timestamp_stat(chan, mwChannelStat_OPENED_AT); + flush_channel(chan); +} + + +int mwChannel_accept(struct mwChannel *chan) { + struct mwSession *session; + struct mwMsgChannelAccept *msg; + 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); + + /* if nobody selected a cipher, we'll just pick the first that comes + handy */ + if(chan->supported) { + GList *l = mwChannel_getSupportedCipherInstances(chan); + if(l) { + mwChannel_selectCipherInstance(chan, l->data); + g_list_free(l); + } else { + mwChannel_selectCipherInstance(chan, NULL); + } + } + + if(chan->cipher) { + mwCipherInstance_accept(chan->cipher); + + msg->encrypt.item = mwCipherInstance_newItem(chan->cipher); + + /** @todo figure out encrypt modes */ + msg->encrypt.mode = 0x1000; + msg->encrypt.extra = 0x1000; + } + + ret = mwSession_send(session, MW_MESSAGE(msg)); + mwMessage_free(MW_MESSAGE(msg)); + + if(ret) { + state(chan, mwChannel_ERROR); + } 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); + + 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; + } + + 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; + } + + if(! msg->encrypt.mode || ! msg->encrypt.item) { + 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); + + /* 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); + + 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)); + + g_message("channel 0x%08x selected cipher %s", + chan->id, NSTR(mwCipher_getName(c))); + } else { + g_message("channel 0x%08x selected no cipher", chan->id); + } + + g_hash_table_destroy(chan->supported); + chan->supported = NULL; +} + + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/cipher.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/cipher.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,598 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include + +#include "mw_channel.h" +#include "mw_cipher.h" +#include "mw_debug.h" +#include "mw_session.h" + + +/** From RFC2268 */ +static unsigned char 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 +}; + + +void rand_key(char *key, gsize keylen) { + srand(clock()); + while(keylen--) key[keylen] = rand() & 0xff; +} + + +void mwIV_init(char *iv) { + static unsigned char normal_iv[] = { + 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef + }; + memcpy(iv, normal_iv, 8); + + /* + *iv++ = 0x01; *iv++ = 0x23; + *iv++ = 0x45; *iv++ = 0x67; + *iv++ = 0x89; *iv++ = 0xab; + *iv++ = 0xcd; *iv = 0xef; + */ +} + + +/* 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 char *key, gsize keylen) { + char tmp[128]; + int i, j; + + /* + g_message("expanding key from:"); + pretty_print(key, keylen); + */ + + if(keylen > 128) keylen = 128; + memcpy(tmp, key, keylen); + + 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, char *out) { + int a = (out[7] << 8) | (out[6] & 0xff); + int b = (out[5] << 8) | (out[4] & 0xff); + int c = (out[3] << 8) | (out[2] & 0xff); + int d = (out[1] << 8) | (out[0] & 0xff); + + int i, j; + + 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, char *iv, + struct mwOpaque *in_data, + struct mwOpaque *out_data) { + + char *i = in_data->data; + gsize i_len = in_data->len; + + char *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 */ + 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); + memcpy(iv, o, 8); + o += 8; + } +} + + +void mwEncrypt(const char *key, gsize keylen, char *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, char *out) { + int a = (out[7] << 8) | (out[6] & 0xff); + int b = (out[5] << 8) | (out[4] & 0xff); + int c = (out[3] << 8) | (out[2] & 0xff); + int d = (out[1] << 8) | (out[0] & 0xff); + + int i, j; + + 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, char *iv, + struct mwOpaque *in_data, + struct mwOpaque *out_data) { + + char *i = in_data->data; + gsize i_len = in_data->len; + + char *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; + 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]); + 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 char *key, gsize keylen, char *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]; + char outgoing_iv[8]; + char 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; + + cir = g_new0(struct mwCipherInstance_RC2_40, 1); + ci = &cir->instance; + + ci->cipher = cipher; + ci->channel = chan; + + /* a bit of lazy initialization here */ + if(! cr->ready) { + struct mwLoginInfo *info = mwSession_getLoginInfo(cipher->session); + mwKeyExpand(cr->session_key, info->login_id, 5); + cr->ready = TRUE; + } + + 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 = g_new0(struct mwEncryptItem, 1); + e->id = mwCipher_RC2_40; + return e; +} + + +static void accept_RC2_40(struct mwCipherInstance *ci) { + struct mwCipherInstance_RC2_40 *cir; + struct mwLoginInfo *info = mwChannel_getUser(ci->channel); + + cir = (struct mwCipherInstance_RC2_40 *) ci; + mwKeyExpand(cir->incoming_key, info->login_id, 5); +} + + +static void accepted_RC2_40(struct mwCipherInstance *ci, + struct mwEncryptItem *item) { + accept_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->new_item = new_item_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 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, 0x00); + 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; +} + + +struct mwEncryptItem *mwCipherInstance_newItem(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); + g_return_val_if_fail(cipher->new_item != NULL, NULL); + + return cipher->new_item(ci); +} + + +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); +} + + +void mwCipherInstance_offer(struct mwCipherInstance *ci) { + struct mwCipher *cipher; + + g_return_if_fail(ci != NULL); + + cipher = ci->cipher; + g_return_if_fail(cipher != NULL); + + if(cipher->offer) 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); +} + + +void mwCipherInstance_accept(struct mwCipherInstance *ci) { + struct mwCipher *cipher; + + g_return_if_fail(ci != NULL); + + cipher = ci->cipher; + g_return_if_fail(cipher != NULL); + + if(cipher->accept) 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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/common.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/common.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,982 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "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 { + char *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + char *ptr; /**< offset to first unused byte */ + gsize rem; /**< count of unused bytes remaining */ +}; + + +struct mwGetBuffer { + char *buf; /**< head of buffer */ + gsize len; /**< length of buffer */ + + char *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); + char *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 = (char *) 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) { + char *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) { + char *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); +} + + +/* 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)); +} + + +const char *mwLoginType_getName(enum mwLoginType type) { + switch(type) { + case mwLogin_LIB: + return "Lotus Binary Library"; + + case mwLogin_JAVA_WEB: + return "Lotus Java Client Applet"; + + case mwLogin_BINARY: + return "Lotus Sametime"; + + case mwLogin_JAVA_APP: + return "Lotus Java Client Application"; + + case mwLogin_NOTES_6_5: + return "Lotus Notes Client 6.5.2+"; + + case mwLogin_NOTES_7_0: + return "Lotus Notes Client 7"; + + case mwLogin_ICT: + return "IBM Community Tools (ICT)"; + + case mwLogin_NOTESBUDDY: + case mwLogin_NOTESBUDDY_4_15: + return "Alphaworks NotesBuddy"; + + case mwLogin_SANITY: + return "Sanity"; + + case mwLogin_ST_PERL: + return "ST-Send-Message"; + + case mwLogin_PMR_ALERT: + return "PMR Alert"; + + case mwLogin_TRILLIAN: + case mwLogin_TRILLIAN_IBM: + return "Trillian"; + + case mwLogin_MEANWHILE: + return "Meanwhile"; + + case mwLogin_MW_PYTHON: + return "Meanwhile Python"; + + case mwLogin_MW_GAIM: + return "Meanwhile Gaim"; + + case mwLogin_MW_ADIUM: + return "Meanwhile Adium"; + + case mwLogin_MW_KOPETE: + return "Meanwhile Kopete"; + + default: + return NULL; + } +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/error.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/error.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,96 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "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"); + + /* 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/message.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/message.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,715 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +#include "mw_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); +} + + +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->unknown); + mwOpaque_get(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 draft?? */ + mwOpaque_put(b, &msg->auth_data); + guint16_put(b, msg->auth_type); +} + + +static void LOGIN_clear(struct mwMsgLogin *msg) { + g_free(msg->name); + 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_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_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) { + char tail = 0x07; + + 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); + } + + guint32_put(b, 0x00); + guint32_put(b, 0x00); + gboolean_put(b, FALSE); + mwPutBuffer_write(b, &tail, 1); +} + + +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); +} + + +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) { + + char tail = 0x07; + + 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); + } + + guint32_put(b, 0x00); + guint32_put(b, 0x00); + gboolean_put(b, FALSE); + + mwPutBuffer_write(b, &tail, 1); +} + + +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); +} + + +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; +} + + +#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); + + 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_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_USER_STATUS, mwMsgSetUserStatus); + CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList); + CASE(SENSE_SERVICE, mwMsgSenseService); + CASE(ADMIN, mwMsgAdmin); + + 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(LOGIN, mwMsgLogin); + 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); + + 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); + + default: + ; /* hrm. */ + } + + g_free(msg); +} + + +#undef CASE + + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_channel.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_channel.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,341 @@ + +/* + 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 +#include "mw_common.h" + + +/** @file 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 */ +}; + + +/** 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); + + +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 + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_cipher.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_cipher.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,249 @@ + +/* + 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 +#include "mw_common.h" + + +/* place-holders */ +struct mwChannel; +struct mwSession; + + + +/** 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 */ +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)(); /**< @see mwCipher_getName */ + const char *(*get_desc)(); /**< @see mwCipher_getDesc */ + + /** Generate a new Cipher Instance for use on a channel + @see mwCipher_newInstance */ + mwCipherInstantiator new_instance; + + /** @see mwCipher_newItem */ + mwCipherDescriptor new_item; + + void (*offered)(struct mwCipherInstance *ci, struct mwEncryptItem *item); + void (*offer)(struct mwCipherInstance *ci); + void (*accepted)(struct mwCipherInstance *ci, struct mwEncryptItem *item); + void (*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); + + +#if 0 +/* @todo write this */ +struct mwCipher *mwCipher_new_DH_RC2_128(struct mwSession *s); +#endif + + +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); + + +struct mwEncryptItem *mwCipherInstance_newItem(struct mwCipherInstance *ci); + + +/** Indicates a cipher has been offered to our channel */ +void mwCipherInstance_offered(struct mwCipherInstance *ci, + struct mwEncryptItem *item); + + +/** Offer a cipher */ +void 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 */ +void 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 + + This set of functions is a broken sort of RC2 implementation. But it + works with sametime, so we're all happy, right? Primary change to + functionality appears in the mwKeyExpand function. Hypothetically, + using a key expanded here (but breaking it into a 128-char array + rather than 64 ints), one could pass it at that length to openssl + and no further key expansion would occur. + + I'm not certain if replacing this with a wrapper for calls to some + other crypto library is a good idea or not. Proven software versus + added dependencies... +*/ +/* @{ */ + + +/** generate some pseudo-random bytes + @param keylen count of bytes to write into key + @param key buffer to write keys into +*/ +void rand_key(char *key, gsize keylen); + + +/** Setup an Initialization Vector */ +void mwIV_init(char *iv); + + +/** Expand a variable-length key into a 128-byte key (represented as + an an array of 64 ints) */ +void mwKeyExpand(int *ekey, const char *key, gsize keylen); + + +/** Encrypt data using an already-expanded key */ +void mwEncryptExpanded(const int *ekey, char *iv, + struct mwOpaque *in, + struct mwOpaque *out); + + +/** Encrypt data using an expanded form of the given key */ +void mwEncrypt(const char *key, gsize keylen, char *iv, + struct mwOpaque *in, struct mwOpaque *out); + + +/** Decrypt data using an already expanded key */ +void mwDecryptExpanded(const int *ekey, char *iv, + struct mwOpaque *in, + struct mwOpaque *out); + + +/** Decrypt data using an expanded form of the given key */ +void mwDecrypt(const char *key, gsize keylen, char *iv, + struct mwOpaque *in, struct mwOpaque *out); + + +/* @} */ + + +#endif + + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_client.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_client.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,154 @@ + +#ifndef _MW_CLIENT_H +#define _MW_CLIENT_H + + +#include +#include + + +/* 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_common.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_common.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,433 @@ + +/* + 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 + + +/** @file common.h + + Common data types and functions for handling those types. + + Functions in this file all fit into similar naming conventions of + TYPE_ACTION as per the activity they perform. The + following actions are available: + + void TYPE_put(struct mwPutBuffer *b, TYPE *val) + - 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, TYPE val is used instead of TYPE + \*val. + + void TYPE_get(struct mwGetBuffer *b, TYPE *val) + - 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. + + void TYPE_clear(TYPE *val) + - 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. + + void TYPE_clone(TYPE *to, TYPE *from) + - 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. + + gboolean TYPE_equal(TYPE *y, TYPE *z) + - 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. */ + char *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. + + If you are using Meanwhile in your client code and would like to + differentiate yourself, please email siege at preoccupied dot net + with all the relevant information you can think of. I intend to be + pretty liberal with 'em. +*/ +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 */ + + /* now we're getting crazy */ + mwLogin_NOTES_6_5 = 0x1200, + mwLogin_NOTES_7_0 = 0x1210, + mwLogin_ICT = 0x1300, + mwLogin_NOTESBUDDY = 0x1400, /**< 0xff00 mask? */ + mwLogin_NOTESBUDDY_4_15 = 0x1405, + 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 */ + + /* these aren't ready for use yet, DO NOT USE WHILE THIS COMMENT + EXISTS HERE, it will only cause you trouble */ + mwLogin_MW_PYTHON = 0x1701, /**< Meanwhile Python */ + mwLogin_MW_GAIM = 0x1702, /**< gaim-meanwhile */ + mwLogin_MW_ADIUM = 0x1703, /**< adium-meanwhile */ + mwLogin_MW_KOPETE = 0x1704, /**< kopete-meanwhile */ +}; + + +/* 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(); + + +/** 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); + + +/*@}*/ + + +/** provides a textual name for a given login type. If the type is not + known by name, returns NULL */ +const char *mwLoginType_getName(enum mwLoginType type); + + +/** @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 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); + + +/*@}*/ + + +#endif diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_debug.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_debug.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,129 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include + +#include "mw_debug.h" + + +#define FRM "%02x" +#define FRMT "%02x%02x " +#define BUF(n) ((unsigned char) buf[n]) +#define ADVANCE(b, n, c) {b += c; n -= c;} + + +#ifdef DEBUG +/** writes hex pairs of buf to str */ +static void t_pretty_print(GString *str, const char *buf, gsize len) { + while(len) { + if(len >= 16) { + g_string_append_printf(str, + FRMT FRMT FRMT FRMT FRMT FRMT FRMT FRMT "\n", + 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); + + } else if(len == 2) { + g_string_append_printf(str, FRMT "\n", BUF(0), BUF(1)); + ADVANCE(buf, len, 2); + + } else if(len > 1) { + g_string_append_printf(str, FRMT, BUF(0), BUF(1)); + ADVANCE(buf, len, 2); + + } else { + g_string_append_printf(str, FRM "\n", BUF(0)); + ADVANCE(buf, len, 1); + } + } +} +#endif + + +void pretty_print(const char *buf, gsize len) { +#ifdef DEBUG + GString *str; + + if(! len) return; + + g_return_if_fail(buf != NULL); + + str = g_string_new(NULL); + t_pretty_print(str, buf, len); + g_debug(str->str); + g_string_free(str, TRUE); +#endif + ; +} + + +void pretty_print_opaque(struct mwOpaque *o) { + if(! o) return; + pretty_print(o->data, o->len); +} + + +void mw_debug_mailme_v(struct mwOpaque *block, + const char *info, va_list args) { + /* + MW_MAILME_MESSAGE + begin here + info % args + pretty_print + end here + */ + +#ifdef DEBUG + 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"); + + txt = g_strdup_vprintf(info, args); + g_string_append(str, txt); + g_free(txt); + + g_string_append(str, "\n"); + + if(block) { + t_pretty_print(str, block->data, block->len); + } + + g_string_append(str, MW_MAILME_CUT_STOP); + + g_debug(str->str); + g_string_free(str, TRUE); +#endif + ; +} + + +void mw_debug_mailme(struct mwOpaque *block, + const char *info, ...) { + va_list args; + va_start(args, info); + mw_debug_mailme_v(block, info, args); + va_end(args); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_debug.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_debug.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,112 @@ + +/* + 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 +#include + +#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 + + +/** logs buf as hex pairs. Requires DEBUG enabled during build */ +void pretty_print(const char *buf, gsize len); + + +/** logs block as hex pairs. Requires DEBUG enabled during build */ +void pretty_print_opaque(struct mwOpaque *block); + + +#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 + + +/** Outputs a hex dump of a mwOpaque with debugging info and a + pre-defined message. Identical to mw_debug_mailme, but taking a + va_list argument */ +void mw_debug_mailme_v(struct mwOpaque *block, + const char *info, va_list args); + + +/** Outputs a hex dump of a mwOpaque with debugging info and a + pre-defined message. + + @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_debug_mailme(struct mwOpaque *block, const char *info, ...); + + +#endif + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_error.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_error.h Sun Jun 05 02:50:13 2005 +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 + + +/** @file 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_message.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_message.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,271 @@ + +/* + 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 +#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 */ +}; + + +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; /**< */ + guint16 login_type; /**< @see mwLoginType */ + guint32 loclcalc_addr; /**< */ +}; + + +/* 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 unknown; /**< four bytes of something */ + struct mwOpaque data; /**< some stuff */ +}; + + +/* 8.3.7 Authentication Types */ + +enum mwAuthType { + mwAuthType_PLAIN = 0x0000, + mwAuthType_TOKEN = 0x0001, + mwAuthType_ENCRYPT = 0x0002, +}; + + +/* 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; +}; + + +#endif + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_service.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_service.h Sun Jun 05 02:50:13 2005 +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_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 + @relates MW_SERVICE_IS_STARTING + @relates MW_SERVICE_IS_STARTED + @relates MW_SERVICE_IS_STOPPING + @relates MW_SERVICE_IS_STOPPED + @relates MW_SERVICE_IS_LIVE + @relates MW_SERVICE_IS_DEAD +*/ +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 + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_session.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_session.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,357 @@ + +/* + 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 + + +#include "mw_common.h" + + +/** @file 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. +*/ + + +struct mwCipher; +struct mwMessage; + + +/** default protocol major version */ +#define MW_PROTOCOL_VERSION_MAJOR 0x001e + + +/** default protocol minor version */ +#define MW_PROTOCOL_VERSION_MINOR 0x001d + + +/** @section Session Properties + ... +*/ +/*@{*/ + +/** 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" + +/** 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 char *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. + + Uses of the info param: + - STOPPING error code causing the session to shut down + + @todo change info to a gpointer + + @param s the session + @param state the session's state + @param info additional state info. */ + void (*on_stateChange)(struct mwSession *s, + enum mwSessionState state, guint32 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 a login redirect message is received + + @todo remove in favour of on_stateChange, passing host as a + gpointer in info */ + void (*on_loginRedirect)(struct mwSession *, const char *host); +}; + + +/** 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 char *, 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); + + +/** set the internal privacy information, and inform the server as + necessary. Triggers the on_setPrivacyInfo call-back. */ +int mwSession_setPrivacyInfo(struct mwSession *, struct mwPrivacyInfo *); + + +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 */ +guint32 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 + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_aware.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_aware.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,261 @@ + +/* + 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... +*/ + + +/** Type identifier for the aware service */ +#define SERVICE_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. +*/ +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 + + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_conf.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,198 @@ + +/* + 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 +#include "mw_common.h" + + +/** Type identifier for the conference service */ +#define SERVICE_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 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 + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_dir.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_dir.h Sun Jun 05 02:50:13 2005 +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 +#include + + +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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_ft.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_ft.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,235 @@ + +/* + 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); + + +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. + + @relates mwFileTransfer_reject + @relates 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. + + @relates 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. + + @relates 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_im.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_im.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,266 @@ + +/* + 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 +#include "mw_common.h" + + +/** @file 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 SERVICE_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. + + @relates mwServiceIm_supports + @relates mwServiceIm_setSupported + @relates mwConversation_supports + @relates mwConversation_send + @relates 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) */ +}; + + + +/** @relates 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 + + @relates mwConversation_isOpen + @relates mwConversation_isClosed + @relates 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. + + @relates mwConversation_getClientData + @relates mwConversation_removeClientData +*/ +void mwConversation_setClientData(struct mwConversation *conv, + gpointer data, GDestroyNotify clean); + + +/** Reference associated client data + + @relates mwConversation_setClientData + @relates mwConversation_removeClientData + */ +gpointer mwConversation_getClientData(struct mwConversation *conv); + + +/** Remove any associated client data, calling the optional cleanup + function if one was provided + + @relates mwConversation_setClientData + @relates 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 + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_place.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_place.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,71 @@ + +/* + 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 +#include "mw_common.h" + + +/** Type identifier for the place service */ +#define SERVICE_PLACE 0x80000022 + + +/** @struct mwServicePlace */ +struct mwServicePlace; + + +/** @struct mwPlace */ +struct mwPlace; + + +/** @struct mwPlaceSection */ +struct mwPlaceSection; + + +/** @struct mwPlaceAction */ +struct mwPlaceAction; + + +struct mwPlaceHandler { + +}; + + +struct mwServicePlace *mwServicePlace_new(struct mwSession *session, + struct mwPlaceHandler *handler); + + +struct mwPlace *mwPlace_new(struct mwServicePlace *place, + const char *title, const char *name); + + +int mwPlace_open(struct mwPlace *place); + + +int mwPlace_close(struct mwPlace *place); + + +void mwPlace_free(struct mwPlace *place); + + +const GList *mwPlace_getSections(struct mwPlace *place); diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_resolve.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_resolve.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,139 @@ + +/* + 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 +#include + + +/** Type identifier for the conference service */ +#define SERVICE_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 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_srvc_store.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_srvc_store.h Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,182 @@ + +/* + 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 +#include "mw_common.h" + + +/** Type identifier for the storage service */ +#define SERVICE_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 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 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_st_list.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_st_list.h Sun Jun 05 02:50:13 2005 +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 +#include +#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(); + + +/** 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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_util.c Sun Jun 05 02:50:13 2005 +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); +} diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/mw_util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/mw_util.h Sun Jun 05 02:50:13 2005 +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 +#include +#include + + +#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 diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/service.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/service.c Sun Jun 05 02:50:13 2005 +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; +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/session.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/session.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,1087 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include + +#include "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 */ + guint32 state_info; /**< additional state info */ + + /* input buffering for an incoming message */ + char *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(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 char *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); +} + + +/** set the state of the session, and trigger the session handler's + on_stateChange function. Has no effect if the session is already + in the specified state (ignores additional state info) + + @param s the session + @param state the state to set + @param info additional state info +*/ +static void state(struct mwSession *s, enum mwSessionState state, + guint32 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; + + if(info) { + g_message("session state: %s (0x%08x)", state_str(state), info); + } else { + g_message("session state: %s", state_str(state)); + } + + sh = s->handler; + if(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)); + + 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)); + + 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); + g_return_if_fail(! mwSession_isStopping(s)); + g_return_if_fail(! mwSession_isStopped(s)); + + state(s, mwSession_STOPPING, 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, reason); +} + + +/** compose authentication information into an opaque based on the + password */ +static void compose_auth(struct mwOpaque *auth, const char *pass) { + char iv[8], key[5]; + struct mwOpaque a, b, z; + struct mwPutBuffer *p; + + /* get an IV and a random five-byte key */ + mwIV_init((char *) iv); + rand_key((char *) 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 = (char *) 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); +} + + +/** 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 */ + log->auth_type = mwAuthType_ENCRYPT; + compose_auth(&log->auth_data, property_get(s, mwSession_AUTH_PASSWORD)); + + /* 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 LOGIN_REDIRECT_recv(struct mwSession *s, + struct mwMsgLoginRedirect *msg) { + struct mwSessionHandler *sh = s->handler; + + state(s, mwSession_LOGIN_REDIR, 0); + + if(sh && sh->on_loginRedirect) + sh->on_loginRedirect(s, msg->host); +} + + +#define CASE(var, type) \ +case mwMessage_ ## var: \ + var ## _recv(s, (struct type *) msg); \ + break; + + +static void session_process(struct mwSession *s, + const char *buf, gsize len) { + + struct mwOpaque o = { len, (char *) buf }; + struct mwGetBuffer *b; + struct mwMessage *msg; + + g_assert(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); + 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); + + default: + g_warning("unknown message type 0x%04x, no handler", msg->type); + } + + if(mwGetBuffer_error(b)) { + struct mwOpaque o = { .data = (char *) buf, .len = len }; + mw_debug_mailme(&o, "parsing of message type 0x%04x failed", msg->type); + } + + mwGetBuffer_free(b); + 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 char *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 */ + + char *t; + x += 4; + t = (char *) 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 char *b, gsize n) { + struct mwOpaque o = { n, (char *) 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 = (char *) 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 = (char *) 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 char *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(n && (s->buf_len == 0) && (*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 char *buf, gsize n) { + char *b = (char *) buf; + gsize remain = 0; + + g_return_if_fail(s != NULL); + + /* g_message(" mwSession_recv: session = %p, b = %p, n = %u", + s, b, n); */ + + 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; + gsize len; + 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 */ + len = o.len; + 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 char 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; +} + + +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; +} + + +guint32 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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_aware.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_aware.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,1283 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include + +#include "mw_channel.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_aware.h" +#include "mw_util.h" + + +struct mwServiceAware { + struct mwService service; + + struct mwAwareHandler *handler; + + /** map of ENTRY_KEY(aware_entry):aware_entry */ + GHashTable *entries; + + /** set of guint32:attrib_watch_entry attribute keys */ + GHashTable *attribs; + + /** collection of lists of awareness for this service. Each item is + a mwAwareList */ + GList *lists; + + /** the buddy list channel */ + struct mwChannel *channel; +}; + + +struct mwAwareList { + + /** the owning service */ + struct mwServiceAware *service; + + /** map of ENTRY_KEY(aware_entry):aware_entry */ + GHashTable *entries; + + /** set of guint32:attrib_watch_entry attribute keys */ + GHashTable *attribs; + + struct mwAwareListHandler *handler; + struct mw_datum client_data; +}; + + +struct mwAwareAttribute { + guint32 key; + struct mwOpaque data; +}; + + +struct attrib_entry { + guint32 key; + GList *membership; +}; + + +/** an actual awareness entry, belonging to any number of aware lists */ +struct aware_entry { + struct mwAwareSnapshot aware; + + /** list of mwAwareList containing this entry */ + GList *membership; + + /** collection of attribute values for this entry. + map of ATTRIB_KEY(mwAwareAttribute):mwAwareAttribute */ + GHashTable *attribs; +}; + + +#define ENTRY_KEY(entry) &entry->aware.id + + +/** the channel send types used by this service */ +enum msg_types { + msg_AWARE_ADD = 0x0068, /**< remove an aware */ + msg_AWARE_REMOVE = 0x0069, /**< add an aware */ + + msg_OPT_DO_SET = 0x00c9, /**< set an attribute */ + msg_OPT_DO_UNSET = 0x00ca, /**< unset an attribute */ + msg_OPT_WATCH = 0x00cb, /**< set the attribute watch list */ + + msg_AWARE_SNAPSHOT = 0x01f4, /**< recv aware snapshot */ + msg_AWARE_UPDATE = 0x01f5, /**< recv aware update */ + msg_AWARE_GROUP = 0x01f6, /**< recv group aware */ + + msg_OPT_GOT_SET = 0x0259, /**< recv attribute set update */ + msg_OPT_GOT_UNSET = 0x025a, /**< recv attribute unset update */ + + msg_OPT_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_assert(srvc != NULL); + g_assert(srvc->entries != 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; + + 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; + + 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; + + 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->entries) { + 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 send_attrib_list(srvc); +} + + +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; + 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); + + 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 && 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); + + 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_DID_SET: + case msg_OPT_DID_UNSET: + case msg_OPT_DID_ERROR: + break; + + default: + mw_debug_mailme(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; +} + + +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; + + 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; + mwChannel_destroy(srvc_aware->channel, ERR_SUCCESS, 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, SERVICE_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->entries = g_hash_table_new((GHashFunc) mwAwareIdBlock_hash, + (GEqualFunc) mwAwareIdBlock_equal); + al->attribs = g_hash_table_new(g_direct_hash, g_direct_equal); + al->handler = handler; + + 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->entries != NULL); + g_return_if_fail(list->service != NULL); + + handler = list->handler; + if(handler && handler->clear) { + handler->clear(list); + list->handler = NULL; + } + + mw_datum_clear(&list->client_data); + + srvc = list->service; + srvc->lists = g_list_remove(srvc->lists, list); + + mwAwareList_unwatchAllAttributes(list); + mwAwareList_removeAllAware(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(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; + gpointer k = GUINT_TO_POINTER(key); + + 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); + + remove_unused_attrib(list->service); + return send_attrib_list(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); + + remove_unused_attrib(list->service); + return send_attrib_list(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; + + g_hash_table_foreach(list->attribs, (GHFunc) dismember_attrib, list); + g_hash_table_destroy(list->attribs); + list->attribs = g_hash_table_new(g_direct_hash, g_direct_equal); + + remove_unused_attrib(srvc); + return send_attrib_list(list->service); +} + + +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 */ + g_hash_table_foreach(list->entries, (GHFunc) dismember_aware, list); + g_hash_table_destroy(list->entries); + g_free(list); + + 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); + g_return_val_if_fail(aware != NULL, NULL); + + return aware->aware.status.desc; +} + + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_conf.c Sun Jun 05 02:50:13 2005 +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 +#include +#include + +#include +#include +#include +#include + +#include "mw_channel.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_message.h" +#include "mw_service.h" +#include "mw_session.h" +#include "mw_srvc_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 c[16]; /* limited space. Used only to hold sprintf output */ + char *ret; + + user = user? user: ""; + + srand(clock()); + a = ((rand() & 0xff) << 8) | (rand() & 0xff); + b = time(NULL); + sprintf(c, "(%08x,%04x)", b, a); + + ret = g_strconcat(user, c, NULL); + + 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, SERVICE_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_assert(session != NULL); + + 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 = (char *) 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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_dir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_dir.c Sun Jun 05 02:50:13 2005 +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 + +#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; +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_ft.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_ft.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,647 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + + +#include + +#include "mw_channel.h" +#include "mw_common.h" +#include "mw_debug.h" +#include "mw_error.h" +#include "mw_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_debug_mailme(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; +} + + +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; + + 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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_im.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_im.c Sun Jun 05 02:50:13 2005 +0000 @@ -0,0 +1,1015 @@ + +/* + Meanwhile - Unofficial Lotus Sametime Community Client Library + Copyright (C) 2004 Christopher (siege) O'Brien + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include + +#include "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 */ + + +/* 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_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 */ + + /** 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; + + 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(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 != SERVICE_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) { + + 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, o.data, o.len); + conv->multi_type = mwImSend_HTML; + + } else { + x = g_strndup(o.data, o.len); + convo_recv(conv, mwImSend_HTML, x); + g_free(x); + } + } + break; + + case mwImData_SUBJECT: + x = g_strndup(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, o.data, o.len); + conv->multi_type = mwImSend_MIME; + + } else { + x = g_strndup(o.data, o.len); + convo_recv(conv, mwImSend_MIME, x); + g_free(x); + } + 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_debug_mailme(&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 = &srvc_im->service; + + mwService_init(srvc, session, SERVICE_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: + 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_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_send(conv->channel, msg_MESSAGE, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_sendHtml(struct mwConversation *conv, const char *html) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_DATA); + guint32_put(b, mwImData_HTML); + guint32_put(b, 0x00); + + /* use o first as a shell of an opaque for the text */ + o.len = strlen(html); + o.data = (char *) html; + mwOpaque_put(b, &o); + + /* use o again as the holder of the buffer's finalized data */ + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(conv->channel, msg_MESSAGE, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_sendSubject(struct mwConversation *conv, + const char *subject) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_DATA); + guint32_put(b, mwImData_SUBJECT); + guint32_put(b, 0x00); + + /* use o first as a shell of an opaque for the text */ + o.len = strlen(subject); + o.data = (char *) subject; + mwOpaque_put(b, &o); + + /* use o again as the holder of the buffer's finalized data */ + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(conv->channel, msg_MESSAGE, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_sendTyping(struct mwConversation *conv, gboolean typing) { + struct mwPutBuffer *b; + struct mwOpaque o = { 0, NULL }; + int ret; + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_DATA); + guint32_put(b, mwImData_TYPING); + guint32_put(b, !typing); + + /* not to be confusing, but we're re-using o first as an empty + opaque, and later as the contents of the finalized buffer */ + mwOpaque_put(b, &o); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(conv->channel, msg_MESSAGE, &o); + mwOpaque_clear(&o); + + return ret; +} + + +static int convo_sendMime(struct mwConversation *conv, + const char *mime) { + struct mwPutBuffer *b; + struct mwOpaque o; + int ret; + + b = mwPutBuffer_new(); + + guint32_put(b, mwIm_DATA); + guint32_put(b, mwImData_MIME); + guint32_put(b, 0x00); + + o.len = strlen(mime); + o.data = (char *) mime; + mwOpaque_put(b, &o); + + mwPutBuffer_finalize(&o, b); + ret = mwChannel_send(conv->channel, msg_MESSAGE, &o); + mwOpaque_clear(&o); + + return ret; +} + + +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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_resolve.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_resolve.c Sun Jun 05 02:50:13 2005 +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 + +#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_debug_mailme(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, SERVICE_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)); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/srvc_store.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/srvc_store.c Sun Jun 05 02:50:13 2005 +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 + +#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, 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_debug_mailme(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, SERVICE_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); +} + diff -r e0d5038fbb7e -r 3ef77720e577 src/protocols/sametime/meanwhile/st_list.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/protocols/sametime/meanwhile/st_list.c Sun Jun 05 02:50:13 2005 +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 +#include +#include + +#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) { + int l = strlen(idb.user) - 3; + type = idb.user[l]; + idb.user[l] = '\0'; + } + + if(name && *name) { + char *tmp; + + 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); +} +