Blame src/srvc_im.c

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