Blame src/srvc_im.c

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