Blame src/srvc_im.c.fix-glib-headers

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