--- a/src/protocols/sametime/meanwhile/srvc_im.c Fri Jan 20 00:19:53 2006 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1055 +0,0 @@ - -/* - Meanwhile - Unofficial Lotus Sametime Community Client Library - Copyright (C) 2004 Christopher (siege) O'Brien - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include <glib.h> -#include <glib/glist.h> -#include <string.h> - -#include "mw_channel.h" -#include "mw_debug.h" -#include "mw_error.h" -#include "mw_message.h" -#include "mw_service.h" -#include "mw_session.h" -#include "mw_srvc_im.h" -#include "mw_util.h" - - -#define PROTOCOL_TYPE 0x00001000 -#define PROTOCOL_VER 0x00000003 - - -/* data for the addtl blocks of channel creation */ -#define mwImAddtlA_NORMAL 0x00000001 - -#define mwImAddtlB_NORMAL 0x00000001 /**< standard */ -#define mwImAddtlB_PRECONF 0x00000019 /**< pre-conference chat */ -#define mwImAddtlB_NOTESBUDDY 0x00033453 /**< notesbuddy */ - -#define mwImAddtlC_NORMAL 0x00000002 - - -/* send-on-channel message type */ -#define msg_MESSAGE 0x0064 /**< IM message */ - - -#define BREAKUP 2048 - - -/* which type of im? */ -enum mwImType { - mwIm_TEXT = 0x00000001, /**< text message */ - mwIm_DATA = 0x00000002, /**< status message (usually) */ -}; - - -/* which type of data im? */ -enum mwImDataType { - mwImData_TYPING = 0x00000001, /**< common use typing indicator */ - mwImData_SUBJECT = 0x00000003, /**< notesbuddy IM topic */ - mwImData_HTML = 0x00000004, /**< notesbuddy HTML message */ - mwImData_MIME = 0x00000005, /**< notesbuddy MIME message, w/image */ - mwImData_TIMESTAMP = 0x00000006, /**< notesbuddy timestamp */ - - mwImData_INVITE = 0x0000000a, /**< Places invitation */ - - mwImData_MULTI_START = 0x00001388, - mwImData_MULTI_STOP = 0x00001389, -}; - - -/** @todo might be appropriate to make a couple of hashtables to - reference conversations by channel and target */ -struct mwServiceIm { - struct mwService service; - - enum mwImClientType features; - - struct mwImHandler *handler; - GList *convs; /**< list of struct im_convo */ -}; - - -struct mwConversation { - struct mwServiceIm *service; /**< owning service */ - struct mwChannel *channel; /**< channel */ - struct mwIdBlock target; /**< conversation target */ - - gboolean ext_id; /**< special treatment, external ID */ - - /** state of the conversation, based loosely on the state of its - underlying channel */ - enum mwConversationState state; - - enum mwImClientType features; - - GString *multi; /**< buffer for multi-chunk message */ - enum mwImSendType multi_type; /**< type of incoming multi-chunk message */ - - struct mw_datum client_data; -}; - - -/** momentarily places a mwLoginInfo into a mwIdBlock */ -static void login_into_id(struct mwIdBlock *to, struct mwLoginInfo *from) { - to->user = from->user_id; - to->community = from->community; -} - - -static struct mwConversation *convo_find_by_user(struct mwServiceIm *srvc, - struct mwIdBlock *to) { - GList *l; - - for(l = srvc->convs; l; l = l->next) { - struct mwConversation *c = l->data; - if(mwIdBlock_equal(&c->target, to)) - return c; - } - - return NULL; -} - - -static const char *conv_state_str(enum mwConversationState state) { - switch(state) { - case mwConversation_CLOSED: - return "closed"; - - case mwConversation_OPEN: - return "open"; - - case mwConversation_PENDING: - return "pending"; - - case mwConversation_UNKNOWN: - default: - return "UNKNOWN"; - } -} - - -static void convo_set_state(struct mwConversation *conv, - enum mwConversationState state) { - - g_return_if_fail(conv != NULL); - - if(conv->state != state) { - g_info("setting conversation (%s, %s) state: %s", - NSTR(conv->target.user), NSTR(conv->target.community), - conv_state_str(state)); - conv->state = state; - } -} - - -struct mwConversation *mwServiceIm_findConversation(struct mwServiceIm *srvc, - struct mwIdBlock *to) { - g_return_val_if_fail(srvc != NULL, NULL); - g_return_val_if_fail(to != NULL, NULL); - - return convo_find_by_user(srvc, to); -} - - -struct mwConversation *mwServiceIm_getConversation(struct mwServiceIm *srvc, - struct mwIdBlock *to) { - struct mwConversation *c; - - g_return_val_if_fail(srvc != NULL, NULL); - g_return_val_if_fail(to != NULL, NULL); - - c = convo_find_by_user(srvc, to); - if(! c) { - c = g_new0(struct mwConversation, 1); - c->service = srvc; - mwIdBlock_clone(&c->target, to); - c->state = mwConversation_CLOSED; - c->features = srvc->features; - - /* mark external users */ - /* c->ext_id = g_str_has_prefix(to->user, "@E "); */ - - srvc->convs = g_list_prepend(srvc->convs, c); - } - - return c; -} - - -static void convo_create_chan(struct mwConversation *c) { - struct mwSession *s; - struct mwChannelSet *cs; - struct mwChannel *chan; - struct mwLoginInfo *login; - struct mwPutBuffer *b; - - /* we only should be calling this if there isn't a channel already - associated with the conversation */ - g_return_if_fail(c != NULL); - g_return_if_fail(mwConversation_isPending(c)); - g_return_if_fail(c->channel == NULL); - - s = mwService_getSession(MW_SERVICE(c->service)); - cs = mwSession_getChannels(s); - - chan = mwChannel_newOutgoing(cs); - mwChannel_setService(chan, MW_SERVICE(c->service)); - mwChannel_setProtoType(chan, PROTOCOL_TYPE); - mwChannel_setProtoVer(chan, PROTOCOL_VER); - - /* offer all known ciphers */ - mwChannel_populateSupportedCipherInstances(chan); - - /* set the target */ - login = mwChannel_getUser(chan); - login->user_id = g_strdup(c->target.user); - login->community = g_strdup(c->target.community); - - /* compose the addtl create, with optional FANCY HTML! */ - b = mwPutBuffer_new(); - guint32_put(b, mwImAddtlA_NORMAL); - guint32_put(b, c->features); - mwPutBuffer_finalize(mwChannel_getAddtlCreate(chan), b); - - c->channel = mwChannel_create(chan)? NULL: chan; - if(c->channel) { - mwChannel_setServiceData(c->channel, c, NULL); - } -} - - -void mwConversation_open(struct mwConversation *conv) { - g_return_if_fail(conv != NULL); - g_return_if_fail(mwConversation_isClosed(conv)); - - convo_set_state(conv, mwConversation_PENDING); - convo_create_chan(conv); -} - - -static void convo_opened(struct mwConversation *conv) { - struct mwImHandler *h = NULL; - - g_return_if_fail(conv != NULL); - g_return_if_fail(conv->service != NULL); - - convo_set_state(conv, mwConversation_OPEN); - h = conv->service->handler; - - g_return_if_fail(h != NULL); - - if(h->conversation_opened) - h->conversation_opened(conv); -} - - -static void convo_free(struct mwConversation *conv) { - struct mwServiceIm *srvc; - - mwConversation_removeClientData(conv); - - srvc = conv->service; - srvc->convs = g_list_remove_all(srvc->convs, conv); - - mwIdBlock_clear(&conv->target); - g_free(conv); -} - - -static int send_accept(struct mwConversation *c) { - - struct mwChannel *chan = c->channel; - struct mwSession *s = mwChannel_getSession(chan); - struct mwUserStatus *stat = mwSession_getUserStatus(s); - - struct mwPutBuffer *b; - struct mwOpaque *o; - - b = mwPutBuffer_new(); - guint32_put(b, mwImAddtlA_NORMAL); - guint32_put(b, c->features); - guint32_put(b, mwImAddtlC_NORMAL); - mwUserStatus_put(b, stat); - - o = mwChannel_getAddtlAccept(chan); - mwOpaque_clear(o); - mwPutBuffer_finalize(o, b); - - return mwChannel_accept(chan); -} - - -static void recv_channelCreate(struct mwService *srvc, - struct mwChannel *chan, - struct mwMsgChannelCreate *msg) { - - /* - ensure it's the right service,proto,and proto ver - - check the opaque for the right opaque junk - - if not, close channel - - compose & send a channel accept - */ - - struct mwServiceIm *srvc_im = (struct mwServiceIm *) srvc; - struct mwImHandler *handler; - struct mwSession *s; - struct mwUserStatus *stat; - guint32 x, y, z; - struct mwGetBuffer *b; - struct mwConversation *c = NULL; - struct mwIdBlock idb; - - handler = srvc_im->handler; - s = mwChannel_getSession(chan); - stat = mwSession_getUserStatus(s); - - /* ensure the appropriate service/proto/ver */ - x = mwChannel_getServiceId(chan); - y = mwChannel_getProtoType(chan); - z = mwChannel_getProtoVer(chan); - - if( (x != mwService_IM) || (y != PROTOCOL_TYPE) || (z != PROTOCOL_VER) ) { - g_warning("unacceptable service, proto, ver:" - " 0x%08x, 0x%08x, 0x%08x", x, y, z); - mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL); - return; - } - - /* act upon the values in the addtl block */ - b = mwGetBuffer_wrap(&msg->addtl); - guint32_get(b, &x); - guint32_get(b, &y); - z = (guint) mwGetBuffer_error(b); - mwGetBuffer_free(b); - - if(z /* buffer error, BOOM! */ ) { - g_warning("bad/malformed addtl in IM service"); - mwChannel_destroy(chan, ERR_FAILURE, NULL); - return; - - } else if(x != mwImAddtlA_NORMAL) { - g_message("unknown params: 0x%08x, 0x%08x", x, y); - mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); - return; - - } else if(y == mwImAddtlB_PRECONF) { - if(! handler->place_invite) { - g_info("rejecting place-invite channel"); - mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); - return; - - } else { - g_info("accepting place-invite channel"); - } - - } else if(y != mwImClient_PLAIN && y != srvc_im->features) { - /** reject what we do not understand */ - mwChannel_destroy(chan, ERR_IM_NOT_REGISTERED, NULL); - return; - - } else if(stat->status == mwStatus_BUSY) { - g_info("rejecting IM channel due to DND status"); - mwChannel_destroy(chan, ERR_CLIENT_USER_DND, NULL); - return; - } - - login_into_id(&idb, mwChannel_getUser(chan)); - -#if 0 - c = convo_find_by_user(srvc_im, &idb); -#endif - - if(! c) { - c = g_new0(struct mwConversation, 1); - c->service = srvc_im; - srvc_im->convs = g_list_prepend(srvc_im->convs, c); - } - -#if 0 - /* we're going to re-associate any existing conversations with this - new channel. That means closing any existing ones */ - if(c->channel) { - g_info("closing existing IM channel 0x%08x", mwChannel_getId(c->channel)); - mwConversation_close(c, ERR_SUCCESS); - } -#endif - - /* set up the conversation with this channel, target, and be fancy - if the other side requested it */ - c->channel = chan; - mwIdBlock_clone(&c->target, &idb); - c->features = y; - convo_set_state(c, mwConversation_PENDING); - mwChannel_setServiceData(c->channel, c, NULL); - - if(send_accept(c)) { - g_warning("sending IM channel accept failed"); - mwConversation_free(c); - - } else { - convo_opened(c); - } -} - - -static void recv_channelAccept(struct mwService *srvc, struct mwChannel *chan, - struct mwMsgChannelAccept *msg) { - - struct mwConversation *conv; - - conv = mwChannel_getServiceData(chan); - if(! conv) { - g_warning("received channel accept for non-existant conversation"); - mwChannel_destroy(chan, ERR_FAILURE, NULL); - return; - } - - convo_opened(conv); -} - - -static void recv_channelDestroy(struct mwService *srvc, struct mwChannel *chan, - struct mwMsgChannelDestroy *msg) { - - struct mwConversation *c; - - c = mwChannel_getServiceData(chan); - g_return_if_fail(c != NULL); - - c->channel = NULL; - - if(mwChannel_isState(chan, mwChannel_ERROR)) { - - /* checking for failure on the receiving end to accept html - messages. Fail-over to a non-html format on a new channel for - the convo */ - if(c->features != mwImClient_PLAIN - && (msg->reason == ERR_IM_NOT_REGISTERED || - msg->reason == ERR_SERVICE_NO_SUPPORT)) { - - g_debug("falling back on a plaintext conversation"); - c->features = mwImClient_PLAIN; - convo_create_chan(c); - return; - } - } - - mwConversation_close(c, msg->reason); -} - - -static void convo_recv(struct mwConversation *conv, enum mwImSendType type, - gconstpointer msg) { - - struct mwServiceIm *srvc; - struct mwImHandler *handler; - - g_return_if_fail(conv != NULL); - - srvc = conv->service; - g_return_if_fail(srvc != NULL); - - handler = srvc->handler; - if(handler && handler->conversation_recv) - handler->conversation_recv(conv, type, msg); -} - - -static void convo_multi_start(struct mwConversation *conv) { - g_return_if_fail(conv->multi == NULL); - conv->multi = g_string_new(NULL); -} - - -static void convo_multi_stop(struct mwConversation *conv) { - - g_return_if_fail(conv->multi != NULL); - - /* actually send it */ - convo_recv(conv, conv->multi_type, conv->multi->str); - - /* clear up the multi buffer */ - g_string_free(conv->multi, TRUE); - conv->multi = NULL; -} - - -static void recv_text(struct mwServiceIm *srvc, struct mwChannel *chan, - struct mwGetBuffer *b) { - - struct mwConversation *c; - char *text = NULL; - - mwString_get(b, &text); - - if(! text) return; - - c = mwChannel_getServiceData(chan); - if(c) { - if(c->multi) { - g_string_append(c->multi, text); - - } else { - convo_recv(c, mwImSend_PLAIN, text); - } - } - - g_free(text); -} - - -static void convo_invite(struct mwConversation *conv, - struct mwOpaque *o) { - - struct mwServiceIm *srvc; - struct mwImHandler *handler; - - struct mwGetBuffer *b; - char *title, *name, *msg; - - g_info("convo_invite"); - - srvc = conv->service; - handler = srvc->handler; - - g_return_if_fail(handler != NULL); - g_return_if_fail(handler->place_invite != NULL); - - b = mwGetBuffer_wrap(o); - mwGetBuffer_advance(b, 4); - mwString_get(b, &title); - mwString_get(b, &msg); - mwGetBuffer_advance(b, 19); - mwString_get(b, &name); - - if(! mwGetBuffer_error(b)) { - handler->place_invite(conv, msg, title, name); - } - - mwGetBuffer_free(b); - g_free(msg); - g_free(title); - g_free(name); -} - - -static void recv_data(struct mwServiceIm *srvc, struct mwChannel *chan, - struct mwGetBuffer *b) { - - struct mwConversation *conv; - guint32 type, subtype; - struct mwOpaque o = { 0, 0 }; - char *x; - - guint32_get(b, &type); - guint32_get(b, &subtype); - mwOpaque_get(b, &o); - - if(mwGetBuffer_error(b)) { - mwOpaque_clear(&o); - return; - } - - conv = mwChannel_getServiceData(chan); - if(! conv) return; - - switch(type) { - case mwImData_TYPING: - convo_recv(conv, mwImSend_TYPING, GINT_TO_POINTER(! subtype)); - break; - - case mwImData_HTML: - if(o.len) { - if(conv->multi) { - g_string_append_len(conv->multi, (char *) o.data, o.len); - conv->multi_type = mwImSend_HTML; - - } else { - x = g_strndup((char *) o.data, o.len); - convo_recv(conv, mwImSend_HTML, x); - g_free(x); - } - } - break; - - case mwImData_SUBJECT: - x = g_strndup((char *) o.data, o.len); - convo_recv(conv, mwImSend_SUBJECT, x); - g_free(x); - break; - - case mwImData_MIME: - if(conv->multi) { - g_string_append_len(conv->multi, (char *) o.data, o.len); - conv->multi_type = mwImSend_MIME; - - } else { - x = g_strndup((char *) o.data, o.len); - convo_recv(conv, mwImSend_MIME, x); - g_free(x); - } - break; - - case mwImData_TIMESTAMP: - /* todo */ - break; - - case mwImData_INVITE: - convo_invite(conv, &o); - break; - - case mwImData_MULTI_START: - convo_multi_start(conv); - break; - - case mwImData_MULTI_STOP: - convo_multi_stop(conv); - break; - - default: - - mw_mailme_opaque(&o, "unknown data message type in IM service:" - " (0x%08x, 0x%08x)", type, subtype); - } - - mwOpaque_clear(&o); -} - - -static void recv(struct mwService *srvc, struct mwChannel *chan, - guint16 type, struct mwOpaque *data) { - - /* - ensure message type is something we want - - parse message type into either mwIMText or mwIMData - - handle - */ - - struct mwGetBuffer *b; - guint32 mt; - - g_return_if_fail(type == msg_MESSAGE); - - b = mwGetBuffer_wrap(data); - guint32_get(b, &mt); - - if(mwGetBuffer_error(b)) { - g_warning("failed to parse message for IM service"); - mwGetBuffer_free(b); - return; - } - - switch(mt) { - case mwIm_TEXT: - recv_text((struct mwServiceIm *) srvc, chan, b); - break; - - case mwIm_DATA: - recv_data((struct mwServiceIm *) srvc, chan, b); - break; - - default: - g_warning("unknown message type 0x%08x for IM service", mt); - } - - if(mwGetBuffer_error(b)) - g_warning("failed to parse message type 0x%08x for IM service", mt); - - mwGetBuffer_free(b); -} - - -static void clear(struct mwServiceIm *srvc) { - struct mwImHandler *h; - - while(srvc->convs) - convo_free(srvc->convs->data); - - h = srvc->handler; - if(h && h->clear) - h->clear(srvc); - srvc->handler = NULL; -} - - -static const char *name(struct mwService *srvc) { - return "Instant Messaging"; -} - - -static const char *desc(struct mwService *srvc) { - return "IM service with Standard and NotesBuddy features"; -} - - -static void start(struct mwService *srvc) { - mwService_started(srvc); -} - - -static void stop(struct mwServiceIm *srvc) { - - while(srvc->convs) - mwConversation_free(srvc->convs->data); - - mwService_stopped(MW_SERVICE(srvc)); -} - - -struct mwServiceIm *mwServiceIm_new(struct mwSession *session, - struct mwImHandler *hndl) { - - struct mwServiceIm *srvc_im; - struct mwService *srvc; - - g_return_val_if_fail(session != NULL, NULL); - g_return_val_if_fail(hndl != NULL, NULL); - - srvc_im = g_new0(struct mwServiceIm, 1); - srvc = MW_SERVICE(srvc_im); - - mwService_init(srvc, session, mwService_IM); - srvc->recv_create = recv_channelCreate; - srvc->recv_accept = recv_channelAccept; - srvc->recv_destroy = recv_channelDestroy; - srvc->recv = recv; - srvc->clear = (mwService_funcClear) clear; - srvc->get_name = name; - srvc->get_desc = desc; - srvc->start = start; - srvc->stop = (mwService_funcStop) stop; - - srvc_im->features = mwImClient_PLAIN; - srvc_im->handler = hndl; - - return srvc_im; -} - - -struct mwImHandler *mwServiceIm_getHandler(struct mwServiceIm *srvc) { - g_return_val_if_fail(srvc != NULL, NULL); - return srvc->handler; -} - - -gboolean mwServiceIm_supports(struct mwServiceIm *srvc, - enum mwImSendType type) { - - g_return_val_if_fail(srvc != NULL, FALSE); - - switch(type) { - case mwImSend_PLAIN: - case mwImSend_TYPING: - return TRUE; - - case mwImSend_SUBJECT: - case mwImSend_HTML: - case mwImSend_MIME: - case mwImSend_TIMESTAMP: - return srvc->features == mwImClient_NOTESBUDDY; - - default: - return FALSE; - } -} - - -void mwServiceIm_setClientType(struct mwServiceIm *srvc, - enum mwImClientType type) { - - g_return_if_fail(srvc != NULL); - srvc->features = type; -} - - -enum mwImClientType mwServiceIm_getClientType(struct mwServiceIm *srvc) { - g_return_val_if_fail(srvc != NULL, mwImClient_UNKNOWN); - return srvc->features; -} - - -static int convo_send_data(struct mwConversation *conv, - guint32 type, guint32 subtype, - struct mwOpaque *data) { - struct mwPutBuffer *b; - struct mwOpaque o; - struct mwChannel *chan; - int ret; - - chan = conv->channel; - g_return_val_if_fail(chan != NULL, -1); - - b = mwPutBuffer_new(); - - guint32_put(b, mwIm_DATA); - guint32_put(b, type); - guint32_put(b, subtype); - mwOpaque_put(b, data); - - mwPutBuffer_finalize(&o, b); - - ret = mwChannel_sendEncrypted(chan, msg_MESSAGE, &o, !conv->ext_id); - mwOpaque_clear(&o); - - return ret; -} - - -static int convo_send_multi_start(struct mwConversation *conv) { - return convo_send_data(conv, mwImData_MULTI_START, 0x00, NULL); -} - - -static int convo_send_multi_stop(struct mwConversation *conv) { - return convo_send_data(conv, mwImData_MULTI_STOP, 0x00, NULL); -} - - -/* breaks up a large message into segments, sends a start_segment - message, then sends each segment in turn, then sends a stop_segment - message */ -static int -convo_sendSegmented(struct mwConversation *conv, const char *message, - int (*send)(struct mwConversation *conv, - const char *msg)) { - char *buf = (char *) message; - gsize len; - int ret = 0; - - len = strlen(buf); - ret = convo_send_multi_start(conv); - - while(len && !ret) { - char tail; - gsize seg; - - seg = BREAKUP; - if(len < BREAKUP) - seg = len; - - /* temporarily NUL-terminate this segment */ - tail = buf[seg]; - buf[seg] = 0x00; - - ret = send(conv, buf); - - /* restore this segment */ - buf[seg] = tail; - - buf += seg; - len -= seg; - } - - if(! ret) - ret = convo_send_multi_stop(conv); - - return ret; -} - - -static int convo_sendText(struct mwConversation *conv, const char *text) { - struct mwPutBuffer *b; - struct mwOpaque o; - int ret; - - b = mwPutBuffer_new(); - - guint32_put(b, mwIm_TEXT); - mwString_put(b, text); - - mwPutBuffer_finalize(&o, b); - ret = mwChannel_sendEncrypted(conv->channel, msg_MESSAGE, &o, !conv->ext_id); - mwOpaque_clear(&o); - - return ret; -} - - -static int convo_sendTyping(struct mwConversation *conv, gboolean typing) { - return convo_send_data(conv, mwImData_TYPING, !typing, NULL); -} - - -static int convo_sendSubject(struct mwConversation *conv, - const char *subject) { - struct mwOpaque o; - - o.len = strlen(subject); - o.data = (guchar *) subject; - - return convo_send_data(conv, mwImData_SUBJECT, 0x00, &o); -} - - -static int convo_sendHtml(struct mwConversation *conv, const char *html) { - struct mwOpaque o; - - o.len = strlen(html); - o.data = (guchar *) html; - - if(o.len > BREAKUP) { - return convo_sendSegmented(conv, html, convo_sendHtml); - } else { - return convo_send_data(conv, mwImData_HTML, 0x00, &o); - } -} - - -static int convo_sendMime(struct mwConversation *conv, const char *mime) { - struct mwOpaque o; - - o.len = strlen(mime); - o.data = (guchar *) mime; - - if(o.len > BREAKUP) { - return convo_sendSegmented(conv, mime, convo_sendMime); - } else { - return convo_send_data(conv, mwImData_MIME, 0x00, &o); - } -} - - -int mwConversation_send(struct mwConversation *conv, enum mwImSendType type, - gconstpointer msg) { - - g_return_val_if_fail(conv != NULL, -1); - g_return_val_if_fail(mwConversation_isOpen(conv), -1); - g_return_val_if_fail(conv->channel != NULL, -1); - - switch(type) { - case mwImSend_PLAIN: - return convo_sendText(conv, msg); - case mwImSend_TYPING: - return convo_sendTyping(conv, GPOINTER_TO_INT(msg)); - case mwImSend_SUBJECT: - return convo_sendSubject(conv, msg); - case mwImSend_HTML: - return convo_sendHtml(conv, msg); - case mwImSend_MIME: - return convo_sendMime(conv, msg); - - default: - g_warning("unsupported IM Send Type, 0x%x", type); - return -1; - } -} - - -enum mwConversationState mwConversation_getState(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, mwConversation_UNKNOWN); - return conv->state; -} - - -struct mwServiceIm *mwConversation_getService(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, NULL); - return conv->service; -} - - -gboolean mwConversation_supports(struct mwConversation *conv, - enum mwImSendType type) { - g_return_val_if_fail(conv != NULL, FALSE); - - switch(type) { - case mwImSend_PLAIN: - case mwImSend_TYPING: - return TRUE; - - case mwImSend_SUBJECT: - case mwImSend_HTML: - case mwImSend_MIME: - return conv->features == mwImClient_NOTESBUDDY; - - default: - return FALSE; - } -} - - -enum mwImClientType -mwConversation_getClientType(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, mwImClient_UNKNOWN); - return conv->features; -} - - -struct mwIdBlock *mwConversation_getTarget(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, NULL); - return &conv->target; -} - - -struct mwLoginInfo *mwConversation_getTargetInfo(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, NULL); - g_return_val_if_fail(conv->channel != NULL, NULL); - return mwChannel_getUser(conv->channel); -} - - -void mwConversation_setClientData(struct mwConversation *conv, - gpointer data, GDestroyNotify clean) { - g_return_if_fail(conv != NULL); - mw_datum_set(&conv->client_data, data, clean); -} - - -gpointer mwConversation_getClientData(struct mwConversation *conv) { - g_return_val_if_fail(conv != NULL, NULL); - return mw_datum_get(&conv->client_data); -} - - -void mwConversation_removeClientData(struct mwConversation *conv) { - g_return_if_fail(conv != NULL); - mw_datum_clear(&conv->client_data); -} - - -void mwConversation_close(struct mwConversation *conv, guint32 reason) { - struct mwServiceIm *srvc; - struct mwImHandler *h; - - g_return_if_fail(conv != NULL); - - convo_set_state(conv, mwConversation_CLOSED); - - srvc = conv->service; - g_return_if_fail(srvc != NULL); - - h = srvc->handler; - if(h && h->conversation_closed) - h->conversation_closed(conv, reason); - - if(conv->channel) { - mwChannel_destroy(conv->channel, reason, NULL); - conv->channel = NULL; - } -} - - -void mwConversation_free(struct mwConversation *conv) { - g_return_if_fail(conv != NULL); - - if(! mwConversation_isClosed(conv)) - mwConversation_close(conv, ERR_SUCCESS); - - convo_free(conv); -} -