Blame src/channel.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/ghash.h>
Packit ee6627
#include <glib/glist.h>
Packit ee6627
#include <string.h>
Packit ee6627
Packit ee6627
#include "mw_channel.h"
Packit ee6627
#include "mw_cipher.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_util.h"
Packit ee6627
Packit ee6627
Packit ee6627
/** @todo reorganize this file, stuff is just strewn about */
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannel {
Packit ee6627
Packit ee6627
  /** session this channel belongs to */
Packit ee6627
  struct mwSession *session;
Packit ee6627
Packit ee6627
  enum mwChannelState state;
Packit ee6627
Packit ee6627
  /** creator for incoming channel, target for outgoing channel */
Packit ee6627
  struct mwLoginInfo user;
Packit ee6627
Packit ee6627
  /* similar to data from the CreateCnl message in 8.4.1.7 */
Packit ee6627
  guint32 reserved;    /**< special, unknown meaning */
Packit ee6627
  guint32 id;          /**< channel ID */
Packit ee6627
  guint32 service;     /**< service ID */
Packit ee6627
  guint32 proto_type;  /**< service protocol type */
Packit ee6627
  guint32 proto_ver;   /**< service protocol version */
Packit ee6627
  guint32 options;     /**< channel options */
Packit ee6627
Packit ee6627
  struct mwOpaque addtl_create;
Packit ee6627
  struct mwOpaque addtl_accept;
Packit ee6627
Packit ee6627
  /** all those supported ciphers */
Packit ee6627
  GHashTable *supported;
Packit ee6627
  guint16 offered_policy;  /**< @see enum mwEncryptPolicy */
Packit ee6627
  guint16 policy;          /**< @see enum mwEncryptPolicy */
Packit ee6627
Packit ee6627
  /** cipher information determined at channel acceptance */
Packit ee6627
  struct mwCipherInstance *cipher;
Packit ee6627
Packit ee6627
  /** statistics table */
Packit ee6627
  GHashTable *stats;
Packit ee6627
Packit ee6627
  GSList *outgoing_queue;     /**< queued outgoing messages */
Packit ee6627
  GSList *incoming_queue;     /**< queued incoming messages */
Packit ee6627
Packit ee6627
  struct mw_datum srvc_data;  /**< service-specific data */
Packit ee6627
};
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannelSet {
Packit ee6627
  struct mwSession *session;  /**< owning session */
Packit ee6627
  GHashTable *map;            /**< map of all channels, by ID */
Packit ee6627
  guint32 counter;            /**< counter for outgoing ID */
Packit ee6627
};
Packit ee6627
Packit ee6627
Packit ee6627
static void flush_channel(struct mwChannel *);
Packit ee6627
Packit ee6627
Packit ee6627
static const char *state_str(enum mwChannelState state) {
Packit ee6627
  switch(state) {
Packit ee6627
  case mwChannel_NEW:      return "new";
Packit ee6627
  case mwChannel_INIT:     return "initializing";
Packit ee6627
  case mwChannel_WAIT:     return "waiting";
Packit ee6627
  case mwChannel_OPEN:     return "open";
Packit ee6627
  case mwChannel_DESTROY:  return "closing";
Packit ee6627
  case mwChannel_ERROR:    return "error";
Packit ee6627
Packit ee6627
  case mwChannel_UNKNOWN:  /* fall through */
Packit ee6627
  default:                 return "UNKNOWN";
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void state(struct mwChannel *chan, enum mwChannelState state,
Packit ee6627
		  guint32 err_code) {
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
Packit ee6627
  if(chan->state == state) return;
Packit ee6627
Packit ee6627
  chan->state = state;
Packit ee6627
Packit ee6627
  if(err_code) {
Packit ee6627
    g_message("channel 0x%08x state: %s (0x%08x)",
Packit ee6627
	      chan->id, state_str(state), err_code);
Packit ee6627
  } else {
Packit ee6627
    g_message("channel 0x%08x state: %s", chan->id, state_str(state));
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static gpointer get_stat(struct mwChannel *chan,
Packit ee6627
			 enum mwChannelStatField field) {
Packit ee6627
Packit ee6627
  return g_hash_table_lookup(chan->stats, (gpointer) field);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void set_stat(struct mwChannel *chan, enum mwChannelStatField field,
Packit ee6627
		     gpointer val) {
Packit ee6627
Packit ee6627
  g_hash_table_insert(chan->stats, (gpointer) field, val);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
#define incr_stat(chan, field, incr) \
Packit ee6627
  set_stat(chan, field, get_stat(chan, field) + incr)
Packit ee6627
Packit ee6627
Packit ee6627
#define timestamp_stat(chan, field) \
Packit ee6627
  set_stat(chan, field, (gpointer) time(NULL))
Packit ee6627
Packit ee6627
Packit ee6627
static void sup_free(gpointer a) {
Packit ee6627
  mwCipherInstance_free(a);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static struct mwCipherInstance *
Packit ee6627
get_supported(struct mwChannel *chan, guint16 id) {
Packit ee6627
Packit ee6627
  guint32 cid = (guint32) id;
Packit ee6627
  return g_hash_table_lookup(chan->supported, GUINT_TO_POINTER(cid));
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void put_supported(struct mwChannel *chan,
Packit ee6627
			  struct mwCipherInstance *ci) {
Packit ee6627
Packit ee6627
  struct mwCipher *cipher = mwCipherInstance_getCipher(ci);
Packit ee6627
  guint32 cid = (guint32) mwCipher_getType(cipher);
Packit ee6627
  g_hash_table_insert(chan->supported, GUINT_TO_POINTER(cid), ci);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannel *mwChannel_newIncoming(struct mwChannelSet *cs, guint32 id) {
Packit ee6627
  struct mwChannel *chan;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(cs != NULL, NULL);
Packit ee6627
  g_return_val_if_fail(cs->session != NULL, NULL);
Packit ee6627
Packit ee6627
  chan = g_new0(struct mwChannel, 1);
Packit ee6627
  chan->state = mwChannel_NEW;
Packit ee6627
  chan->session = cs->session;
Packit ee6627
  chan->id = id;
Packit ee6627
Packit ee6627
  chan->stats = g_hash_table_new(g_direct_hash, g_direct_equal);
Packit ee6627
Packit ee6627
  chan->supported = g_hash_table_new_full(g_direct_hash, g_direct_equal,
Packit ee6627
					  NULL, sup_free);
Packit ee6627
Packit ee6627
  g_hash_table_insert(cs->map, GUINT_TO_POINTER(id), chan);
Packit ee6627
Packit ee6627
  state(chan, mwChannel_WAIT, 0);
Packit ee6627
Packit ee6627
  return chan;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannel *mwChannel_newOutgoing(struct mwChannelSet *cs) {
Packit ee6627
  guint32 id;
Packit ee6627
  struct mwChannel *chan;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(cs != NULL, NULL);
Packit ee6627
  g_return_val_if_fail(cs->map != NULL, NULL);
Packit ee6627
Packit ee6627
  /* grab the next id, and try to make sure there isn't already a
Packit ee6627
     channel using it */
Packit ee6627
  do {
Packit ee6627
    id = ++cs->counter;
Packit ee6627
  } while(g_hash_table_lookup(cs->map, GUINT_TO_POINTER(id)));
Packit ee6627
  
Packit ee6627
  chan = mwChannel_newIncoming(cs, id);
Packit ee6627
  state(chan, mwChannel_INIT, 0);
Packit ee6627
Packit ee6627
  return chan;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint32 mwChannel_getId(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0);
Packit ee6627
  return chan->id;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwSession *mwChannel_getSession(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return chan->session;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint32 mwChannel_getServiceId(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0);
Packit ee6627
  return chan->service;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwService *mwChannel_getService(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return mwSession_getService(chan->session, chan->service);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_setService(struct mwChannel *chan, struct mwService *srvc) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(srvc != NULL);
Packit ee6627
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit ee6627
  chan->service = mwService_getType(srvc);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
gpointer mwChannel_getServiceData(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return mw_datum_get(&chan->srvc_data);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_setServiceData(struct mwChannel *chan,
Packit ee6627
			      gpointer data, GDestroyNotify clean) {
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  mw_datum_set(&chan->srvc_data, data, clean);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_removeServiceData(struct mwChannel *chan) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  mw_datum_clear(&chan->srvc_data);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint32 mwChannel_getProtoType(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0x00);
Packit ee6627
  return chan->proto_type;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_setProtoType(struct mwChannel *chan, guint32 proto_type) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit ee6627
  chan->proto_type = proto_type;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint32 mwChannel_getProtoVer(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0x00);
Packit ee6627
  return chan->proto_ver;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_setProtoVer(struct mwChannel *chan, guint32 proto_ver) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit ee6627
  chan->proto_ver = proto_ver;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint16 mwChannel_getEncryptPolicy(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0x00);
Packit ee6627
  return chan->policy;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
guint32 mwChannel_getOptions(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0x00);
Packit ee6627
  return chan->options;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_setOptions(struct mwChannel *chan, guint32 options) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(chan->state == mwChannel_INIT);
Packit ee6627
  chan->options = options;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwLoginInfo *mwChannel_getUser(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return &chan->user;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwOpaque *mwChannel_getAddtlCreate(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return &chan->addtl_create;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwOpaque *mwChannel_getAddtlAccept(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return &chan->addtl_accept;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwCipherInstance *
Packit ee6627
mwChannel_getCipherInstance(struct mwChannel *chan) {
Packit ee6627
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  return chan->cipher;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
enum mwChannelState mwChannel_getState(struct mwChannel *chan) {
Packit ee6627
  g_return_val_if_fail(chan != NULL, mwChannel_UNKNOWN);
Packit ee6627
  return chan->state;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
gpointer mwChannel_getStatistic(struct mwChannel *chan,
Packit ee6627
				enum mwChannelStatField stat) {
Packit ee6627
  
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0);
Packit ee6627
  g_return_val_if_fail(chan->stats != NULL, 0);
Packit ee6627
Packit ee6627
  return get_stat(chan, stat);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
/* send a channel create message */
Packit ee6627
int mwChannel_create(struct mwChannel *chan) {
Packit ee6627
  struct mwMsgChannelCreate *msg;
Packit ee6627
  GList *list, *l;
Packit ee6627
  int ret;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(chan != NULL, -1);
Packit ee6627
  g_return_val_if_fail(chan->state == mwChannel_INIT, -1);
Packit ee6627
  g_return_val_if_fail(mwChannel_isOutgoing(chan), -1);
Packit ee6627
Packit ee6627
  msg = (struct mwMsgChannelCreate *)
Packit ee6627
    mwMessage_new(mwMessage_CHANNEL_CREATE);
Packit ee6627
Packit ee6627
  msg->channel = chan->id;
Packit ee6627
  msg->target.user = g_strdup(chan->user.user_id);
Packit ee6627
  msg->target.community = g_strdup(chan->user.community);
Packit ee6627
  msg->service = chan->service;
Packit ee6627
  msg->proto_type = chan->proto_type;
Packit ee6627
  msg->proto_ver = chan->proto_ver;
Packit ee6627
  msg->options = chan->options;
Packit ee6627
  mwOpaque_clone(&msg->addtl, &chan->addtl_create);
Packit ee6627
Packit ee6627
  list = mwChannel_getSupportedCipherInstances(chan);
Packit ee6627
  if(list) {
Packit ee6627
    /* offer what we have */
Packit ee6627
    for(l = list; l; l = l->next) {
Packit ee6627
      struct mwEncryptItem *ei = mwCipherInstance_offer(l->data);
Packit ee6627
      msg->encrypt.items = g_list_append(msg->encrypt.items, ei);
Packit ee6627
    }
Packit ee6627
Packit ee6627
    /* we're easy to get along with */
Packit ee6627
    chan->offered_policy = mwEncrypt_WHATEVER;
Packit ee6627
    g_list_free(list);
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
    /* we apparently don't support anything */
Packit ee6627
    chan->offered_policy = mwEncrypt_NONE;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  msg->encrypt.mode = chan->offered_policy;
Packit ee6627
  msg->encrypt.extra = chan->offered_policy;
Packit ee6627
  
Packit ee6627
  ret = mwSession_send(chan->session, MW_MESSAGE(msg));
Packit ee6627
  mwMessage_free(MW_MESSAGE(msg));
Packit ee6627
Packit ee6627
  state(chan, (ret)? mwChannel_ERROR: mwChannel_WAIT, ret);
Packit ee6627
Packit ee6627
  return ret;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void channel_open(struct mwChannel *chan) {
Packit ee6627
  state(chan, mwChannel_OPEN, 0);
Packit ee6627
  timestamp_stat(chan, mwChannelStat_OPENED_AT);
Packit ee6627
  flush_channel(chan);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
int mwChannel_accept(struct mwChannel *chan) {
Packit ee6627
  struct mwSession *session;
Packit ee6627
  struct mwMsgChannelAccept *msg;
Packit ee6627
  struct mwCipherInstance *ci;
Packit ee6627
Packit ee6627
  int ret;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(chan != NULL, -1);
Packit ee6627
  g_return_val_if_fail(mwChannel_isIncoming(chan), -1);
Packit ee6627
  g_return_val_if_fail(chan->state == mwChannel_WAIT, -1);
Packit ee6627
Packit ee6627
  session = chan->session;
Packit ee6627
  g_return_val_if_fail(session != NULL, -1);
Packit ee6627
Packit ee6627
  msg = (struct mwMsgChannelAccept *)
Packit ee6627
    mwMessage_new(mwMessage_CHANNEL_ACCEPT);
Packit ee6627
Packit ee6627
  msg->head.channel = chan->id;
Packit ee6627
  msg->service = chan->service;
Packit ee6627
  msg->proto_type = chan->proto_type;
Packit ee6627
  msg->proto_ver = chan->proto_ver;
Packit ee6627
  mwOpaque_clone(&msg->addtl, &chan->addtl_accept);
Packit ee6627
Packit ee6627
  ci = chan->cipher;
Packit ee6627
Packit ee6627
  if(! ci) {
Packit ee6627
    /* automatically select a cipher if one hasn't been already */
Packit ee6627
Packit ee6627
    switch(chan->offered_policy) {
Packit ee6627
    case mwEncrypt_NONE:
Packit ee6627
      mwChannel_selectCipherInstance(chan, NULL);
Packit ee6627
      break;
Packit ee6627
      
Packit ee6627
    case mwEncrypt_RC2_40:
Packit ee6627
      ci = get_supported(chan, mwCipher_RC2_40);
Packit ee6627
      mwChannel_selectCipherInstance(chan, ci);
Packit ee6627
      break;
Packit ee6627
Packit ee6627
    case mwEncrypt_RC2_128:
Packit ee6627
      ci = get_supported(chan, mwCipher_RC2_128);
Packit ee6627
      mwChannel_selectCipherInstance(chan, ci);
Packit ee6627
      break;
Packit ee6627
      
Packit ee6627
    case mwEncrypt_WHATEVER:
Packit ee6627
    case mwEncrypt_ALL:
Packit ee6627
    default:
Packit ee6627
      {
Packit ee6627
	GList *l, *ll;
Packit ee6627
Packit ee6627
	l = mwChannel_getSupportedCipherInstances(chan);
Packit ee6627
	if(l) {
Packit ee6627
	  /* nobody selected a cipher, so we'll just pick the last in
Packit ee6627
	     the list of available ones */
Packit ee6627
	  for(ll = l; ll->next; ll = ll->next);
Packit ee6627
	  ci = ll->data;
Packit ee6627
	  g_list_free(l);
Packit ee6627
	  
Packit ee6627
	  mwChannel_selectCipherInstance(chan, ci);
Packit ee6627
	  
Packit ee6627
	} else {
Packit ee6627
	  /* this may cause breakage, but there's really nothing else
Packit ee6627
	     we can do. They want something we can't provide. If they
Packit ee6627
	     don't like it, then they'll error the channel out */
Packit ee6627
	  mwChannel_selectCipherInstance(chan, NULL);
Packit ee6627
	}
Packit ee6627
      }
Packit ee6627
    }
Packit ee6627
  }
Packit ee6627
Packit ee6627
  msg->encrypt.mode = chan->policy; /* set in selectCipherInstance */
Packit ee6627
  msg->encrypt.extra = chan->offered_policy;
Packit ee6627
Packit ee6627
  if(chan->cipher) {
Packit ee6627
    msg->encrypt.item = mwCipherInstance_accept(chan->cipher);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  ret = mwSession_send(session, MW_MESSAGE(msg));
Packit ee6627
  mwMessage_free(MW_MESSAGE(msg));
Packit ee6627
Packit ee6627
  if(ret) {
Packit ee6627
    state(chan, mwChannel_ERROR, ret);
Packit ee6627
  } else {
Packit ee6627
    channel_open(chan);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  return ret;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void channel_free(struct mwChannel *chan) {
Packit ee6627
  struct mwSession *s;
Packit ee6627
  struct mwMessage *msg;
Packit ee6627
  GSList *l;
Packit ee6627
Packit ee6627
  /* maybe no warning in the future */
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
Packit ee6627
  s = chan->session;
Packit ee6627
Packit ee6627
  mwLoginInfo_clear(&chan->user);
Packit ee6627
  mwOpaque_clear(&chan->addtl_create);
Packit ee6627
  mwOpaque_clear(&chan->addtl_accept);
Packit ee6627
Packit ee6627
  if(chan->supported) {
Packit ee6627
    g_hash_table_destroy(chan->supported);
Packit ee6627
    chan->supported = NULL;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  if(chan->stats) {
Packit ee6627
    g_hash_table_destroy(chan->stats);
Packit ee6627
    chan->stats = NULL;
Packit ee6627
  }
Packit ee6627
  
Packit ee6627
  mwCipherInstance_free(chan->cipher);
Packit ee6627
Packit ee6627
  /* clean up the outgoing queue */
Packit ee6627
  for(l = chan->outgoing_queue; l; l = l->next) {
Packit ee6627
    msg = (struct mwMessage *) l->data;
Packit ee6627
    l->data = NULL;
Packit ee6627
    mwMessage_free(msg);
Packit ee6627
  }
Packit ee6627
  g_slist_free(chan->outgoing_queue);
Packit ee6627
Packit ee6627
  /* clean up the incoming queue */
Packit ee6627
  for(l = chan->incoming_queue; l; l = l->next) {
Packit ee6627
    msg = (struct mwMessage *) l->data;
Packit ee6627
    l->data = NULL;
Packit ee6627
    mwMessage_free(msg);
Packit ee6627
  }
Packit ee6627
  g_slist_free(chan->incoming_queue);
Packit ee6627
Packit ee6627
  g_free(chan);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
int mwChannel_destroy(struct mwChannel *chan,
Packit ee6627
		      guint32 reason, struct mwOpaque *info) {
Packit ee6627
Packit ee6627
  struct mwMsgChannelDestroy *msg;
Packit ee6627
  struct mwSession *session;
Packit ee6627
  struct mwChannelSet *cs;
Packit ee6627
  int ret;
Packit ee6627
Packit ee6627
  /* may make this not a warning in the future */
Packit ee6627
  g_return_val_if_fail(chan != NULL, 0);
Packit ee6627
Packit ee6627
  state(chan, reason? mwChannel_ERROR: mwChannel_DESTROY, reason);
Packit ee6627
Packit ee6627
  session = chan->session;
Packit ee6627
  g_return_val_if_fail(session != NULL, -1);
Packit ee6627
Packit ee6627
  cs = mwSession_getChannels(session);
Packit ee6627
  g_return_val_if_fail(cs != NULL, -1);
Packit ee6627
Packit ee6627
  /* compose the message */
Packit ee6627
  msg = (struct mwMsgChannelDestroy *)
Packit ee6627
    mwMessage_new(mwMessage_CHANNEL_DESTROY);
Packit ee6627
  msg->head.channel = chan->id;
Packit ee6627
  msg->reason = reason;
Packit ee6627
  if(info) mwOpaque_clone(&msg->data, info);
Packit ee6627
Packit ee6627
  /* remove the channel from the channel set */
Packit ee6627
  g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id));
Packit ee6627
  
Packit ee6627
  /* send the message */
Packit ee6627
  ret = mwSession_send(session, (struct mwMessage *) msg);
Packit ee6627
  mwMessage_free(MW_MESSAGE(msg));
Packit ee6627
Packit ee6627
  return ret;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void queue_outgoing(struct mwChannel *chan,
Packit ee6627
			   struct mwMsgChannelSend *msg) {
Packit ee6627
Packit ee6627
  g_info("queue_outgoing, channel 0x%08x", chan->id);
Packit ee6627
  chan->outgoing_queue = g_slist_append(chan->outgoing_queue, msg);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static int channel_send(struct mwChannel *chan,
Packit ee6627
			struct mwMsgChannelSend *msg) {
Packit ee6627
Packit ee6627
  int ret = 0;
Packit ee6627
Packit ee6627
  /* if the channel is open, send and free the message. Otherwise,
Packit ee6627
     queue the message to be sent once the channel is finally
Packit ee6627
     opened */
Packit ee6627
Packit ee6627
  if(chan->state == mwChannel_OPEN) {
Packit ee6627
    ret = mwSession_send(chan->session, (struct mwMessage *) msg);
Packit ee6627
    mwMessage_free(MW_MESSAGE(msg));
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
    queue_outgoing(chan, msg);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  return ret;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
int mwChannel_sendEncrypted(struct mwChannel *chan,
Packit ee6627
			    guint32 type, struct mwOpaque *data,
Packit ee6627
			    gboolean encrypt) {
Packit ee6627
Packit ee6627
  struct mwMsgChannelSend *msg;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(chan != NULL, -1);
Packit ee6627
Packit ee6627
  msg = (struct mwMsgChannelSend *) mwMessage_new(mwMessage_CHANNEL_SEND);
Packit ee6627
  msg->head.channel = chan->id;
Packit ee6627
  msg->type = type;
Packit ee6627
Packit ee6627
  mwOpaque_clone(&msg->data, data);
Packit ee6627
Packit ee6627
  if(encrypt && chan->cipher) {
Packit ee6627
    msg->head.options = mwMessageOption_ENCRYPT;
Packit ee6627
    mwCipherInstance_encrypt(chan->cipher, &msg->data);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  return channel_send(chan, msg);  
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
int mwChannel_send(struct mwChannel *chan, guint32 type,
Packit ee6627
		   struct mwOpaque *data) {
Packit ee6627
Packit ee6627
  return mwChannel_sendEncrypted(chan, type, data, TRUE);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void queue_incoming(struct mwChannel *chan,
Packit ee6627
			   struct mwMsgChannelSend *msg) {
Packit ee6627
Packit ee6627
  /* we clone the message, because session_process will clear it once
Packit ee6627
     we return */
Packit ee6627
Packit ee6627
  struct mwMsgChannelSend *m = g_new0(struct mwMsgChannelSend, 1);
Packit ee6627
  m->head.type = msg->head.type;
Packit ee6627
  m->head.options = msg->head.options;
Packit ee6627
  m->head.channel = msg->head.channel;
Packit ee6627
  mwOpaque_clone(&m->head.attribs, &msg->head.attribs);
Packit ee6627
Packit ee6627
  m->type = msg->type;
Packit ee6627
  mwOpaque_clone(&m->data, &msg->data);
Packit ee6627
Packit ee6627
  g_info("queue_incoming, channel 0x%08x", chan->id);
Packit ee6627
  chan->incoming_queue = g_slist_append(chan->incoming_queue, m);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void channel_recv(struct mwChannel *chan,
Packit ee6627
			 struct mwMsgChannelSend *msg) {
Packit ee6627
Packit ee6627
  struct mwService *srvc;
Packit ee6627
  srvc = mwChannel_getService(chan);
Packit ee6627
Packit ee6627
  incr_stat(chan, mwChannelStat_MSG_RECV, 1);
Packit ee6627
Packit ee6627
  if(msg->head.options & mwMessageOption_ENCRYPT) {
Packit ee6627
    struct mwOpaque data = { 0, 0 };
Packit ee6627
    mwOpaque_clone(&data, &msg->data);
Packit ee6627
Packit ee6627
    mwCipherInstance_decrypt(chan->cipher, &data);
Packit ee6627
    mwService_recv(srvc, chan, msg->type, &data);
Packit ee6627
    mwOpaque_clear(&data);
Packit ee6627
    
Packit ee6627
  } else {
Packit ee6627
    mwService_recv(srvc, chan, msg->type, &msg->data);
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void flush_channel(struct mwChannel *chan) {
Packit ee6627
  GSList *l;
Packit ee6627
Packit ee6627
  for(l = chan->incoming_queue; l; l = l->next) {
Packit ee6627
    struct mwMsgChannelSend *msg = (struct mwMsgChannelSend *) l->data;
Packit ee6627
    l->data = NULL;
Packit ee6627
Packit ee6627
    channel_recv(chan, msg);
Packit ee6627
    mwMessage_free(MW_MESSAGE(msg));
Packit ee6627
  }
Packit ee6627
  g_slist_free(chan->incoming_queue);
Packit ee6627
  chan->incoming_queue = NULL;
Packit ee6627
Packit ee6627
  for(l = chan->outgoing_queue; l; l = l->next) {
Packit ee6627
    struct mwMessage *msg = (struct mwMessage *) l->data;
Packit ee6627
    l->data = NULL;
Packit ee6627
Packit ee6627
    mwSession_send(chan->session, msg);
Packit ee6627
    mwMessage_free(msg);
Packit ee6627
  }
Packit ee6627
  g_slist_free(chan->outgoing_queue);
Packit ee6627
  chan->outgoing_queue = NULL;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_recv(struct mwChannel *chan, struct mwMsgChannelSend *msg) {
Packit ee6627
  if(chan->state == mwChannel_OPEN) {
Packit ee6627
    channel_recv(chan, msg);
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
    queue_incoming(chan, msg);
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannel *mwChannel_find(struct mwChannelSet *cs, guint32 chan) {
Packit ee6627
  g_return_val_if_fail(cs != NULL, NULL);
Packit ee6627
  g_return_val_if_fail(cs->map != NULL, NULL);
Packit ee6627
  return g_hash_table_lookup(cs->map, GUINT_TO_POINTER(chan));
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannelSet_free(struct mwChannelSet *cs) {
Packit ee6627
  if(! cs) return;
Packit ee6627
  if(cs->map) g_hash_table_destroy(cs->map);
Packit ee6627
  g_free(cs);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
struct mwChannelSet *mwChannelSet_new(struct mwSession *s) {
Packit ee6627
  struct mwChannelSet *cs = g_new0(struct mwChannelSet, 1);
Packit ee6627
  cs->session = s;
Packit ee6627
Packit ee6627
  /* for some reason, g_int_hash/g_int_equal cause a SIGSEGV */
Packit ee6627
  cs->map = g_hash_table_new_full(g_direct_hash, g_direct_equal,
Packit ee6627
				  NULL, (GDestroyNotify) channel_free);
Packit ee6627
  return cs;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_recvCreate(struct mwChannel *chan,
Packit ee6627
			  struct mwMsgChannelCreate *msg) {
Packit ee6627
Packit ee6627
  struct mwSession *session;
Packit ee6627
  GList *list;
Packit ee6627
  struct mwService *srvc;
Packit ee6627
  
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(msg != NULL);
Packit ee6627
  g_return_if_fail(chan->id == msg->channel);
Packit ee6627
Packit ee6627
  session = chan->session;
Packit ee6627
  g_return_if_fail(session != NULL);
Packit ee6627
Packit ee6627
  if(mwChannel_isOutgoing(chan)) {
Packit ee6627
    g_warning("channel 0x%08x not an incoming channel", chan->id);
Packit ee6627
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit ee6627
    return;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  chan->offered_policy = msg->encrypt.mode;
Packit ee6627
  g_message("channel offered with encrypt policy 0x%04x", chan->policy);
Packit ee6627
Packit ee6627
  for(list = msg->encrypt.items; list; list = list->next) {
Packit ee6627
    struct mwEncryptItem *ei = list->data;
Packit ee6627
    struct mwCipher *cipher;
Packit ee6627
    struct mwCipherInstance *ci;
Packit ee6627
Packit ee6627
    g_message("channel offered cipher id 0x%04x", ei->id);
Packit ee6627
    cipher = mwSession_getCipher(session, ei->id);
Packit ee6627
    if(! cipher) {
Packit ee6627
      g_message("no such cipher found in session");
Packit ee6627
      continue;
Packit ee6627
    }
Packit ee6627
Packit ee6627
    ci = mwCipher_newInstance(cipher, chan);
Packit ee6627
    mwCipherInstance_offered(ci, ei);
Packit ee6627
    mwChannel_addSupportedCipherInstance(chan, ci);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  mwLoginInfo_clone(&chan->user, &msg->creator);
Packit ee6627
  chan->service = msg->service;
Packit ee6627
  chan->proto_type = msg->proto_type;
Packit ee6627
  chan->proto_ver = msg->proto_ver;
Packit ee6627
  
Packit ee6627
  srvc = mwSession_getService(session, msg->service);
Packit ee6627
  if(srvc) {
Packit ee6627
    mwService_recvCreate(srvc, chan, msg);
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
    mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
Packit ee6627
  }  
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_recvAccept(struct mwChannel *chan,
Packit ee6627
			  struct mwMsgChannelAccept *msg) {
Packit ee6627
Packit ee6627
  struct mwService *srvc;
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(msg != NULL);
Packit ee6627
  g_return_if_fail(chan->id == msg->head.channel);
Packit ee6627
Packit ee6627
  if(mwChannel_isIncoming(chan)) {
Packit ee6627
    g_warning("channel 0x%08x not an outgoing channel", chan->id);
Packit ee6627
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit ee6627
    return;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  if(chan->state != mwChannel_WAIT) {
Packit ee6627
    g_warning("channel 0x%08x state not WAIT: %s",
Packit ee6627
	      chan->id, state_str(chan->state));
Packit ee6627
    mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit ee6627
    return;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  mwLoginInfo_clone(&chan->user, &msg->acceptor);
Packit ee6627
Packit ee6627
  srvc = mwSession_getService(chan->session, chan->service);
Packit ee6627
  if(! srvc) {
Packit ee6627
    g_warning("no service: 0x%08x", chan->service);
Packit ee6627
    mwChannel_destroy(chan, ERR_SERVICE_NO_SUPPORT, NULL);
Packit ee6627
    return;
Packit ee6627
  }
Packit ee6627
Packit ee6627
  chan->policy = msg->encrypt.mode;
Packit ee6627
  g_message("channel accepted with encrypt policy 0x%04x", chan->policy);
Packit ee6627
Packit ee6627
  if(! msg->encrypt.mode || ! msg->encrypt.item) {
Packit ee6627
    /* no mode or no item means no encryption */
Packit ee6627
    mwChannel_selectCipherInstance(chan, NULL);
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
    guint16 cid = msg->encrypt.item->id;
Packit ee6627
    struct mwCipherInstance *ci = get_supported(chan, cid);
Packit ee6627
Packit ee6627
    if(! ci) {
Packit ee6627
      g_warning("not an offered cipher: 0x%04x", cid);
Packit ee6627
      mwChannel_destroy(chan, ERR_REQUEST_INVALID, NULL);
Packit ee6627
      return;
Packit ee6627
    }
Packit ee6627
Packit ee6627
    mwCipherInstance_accepted(ci, msg->encrypt.item);
Packit ee6627
    mwChannel_selectCipherInstance(chan, ci);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  /* mark it as open for the service */
Packit ee6627
  state(chan, mwChannel_OPEN, 0);
Packit ee6627
Packit ee6627
  /* let the service know */
Packit ee6627
  mwService_recvAccept(srvc, chan, msg);
Packit ee6627
Packit ee6627
  /* flush it if the service didn't just immediately close it */
Packit ee6627
  if(mwChannel_isState(chan, mwChannel_OPEN)) {
Packit ee6627
    channel_open(chan);
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_recvDestroy(struct mwChannel *chan,
Packit ee6627
			   struct mwMsgChannelDestroy *msg) {
Packit ee6627
Packit ee6627
  struct mwChannelSet *cs;
Packit ee6627
  struct mwService *srvc;
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(msg != NULL);
Packit ee6627
  g_return_if_fail(chan->id == msg->head.channel);
Packit ee6627
Packit ee6627
  state(chan, msg->reason? mwChannel_ERROR: mwChannel_DESTROY, msg->reason);
Packit ee6627
Packit ee6627
  srvc = mwChannel_getService(chan);
Packit ee6627
  if(srvc) mwService_recvDestroy(srvc, chan, msg);
Packit ee6627
Packit ee6627
  cs = mwSession_getChannels(chan->session);
Packit ee6627
  g_return_if_fail(cs != NULL);
Packit ee6627
  g_return_if_fail(cs->map != NULL);
Packit ee6627
Packit ee6627
  g_hash_table_remove(cs->map, GUINT_TO_POINTER(chan->id));
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_populateSupportedCipherInstances(struct mwChannel *chan) {
Packit ee6627
  struct mwSession *session;
Packit ee6627
  GList *list;
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
Packit ee6627
  session = chan->session;
Packit ee6627
  g_return_if_fail(session != NULL);
Packit ee6627
Packit ee6627
  for(list = mwSession_getCiphers(session); list; list = list->next) {
Packit ee6627
    struct mwCipherInstance *ci = mwCipher_newInstance(list->data, chan);
Packit ee6627
    if(! ci) continue;
Packit ee6627
    put_supported(chan, ci);
Packit ee6627
  }
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_addSupportedCipherInstance(struct mwChannel *chan,
Packit ee6627
					  struct mwCipherInstance *ci) {
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_message("channel 0x%08x added cipher %s", chan->id,
Packit ee6627
	    NSTR(mwCipher_getName(mwCipherInstance_getCipher(ci))));
Packit ee6627
  put_supported(chan, ci);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
static void collect(gpointer a, gpointer b, gpointer c) {
Packit ee6627
  GList **list = c;
Packit ee6627
  *list = g_list_append(*list, b);
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
GList *mwChannel_getSupportedCipherInstances(struct mwChannel *chan) {
Packit ee6627
  GList *list = NULL;
Packit ee6627
Packit ee6627
  g_return_val_if_fail(chan != NULL, NULL);
Packit ee6627
  g_hash_table_foreach(chan->supported, collect, &list);
Packit ee6627
Packit ee6627
  return list;
Packit ee6627
}
Packit ee6627
Packit ee6627
Packit ee6627
void mwChannel_selectCipherInstance(struct mwChannel *chan,
Packit ee6627
				    struct mwCipherInstance *ci) {
Packit ee6627
  struct mwCipher *c;
Packit ee6627
Packit ee6627
  g_return_if_fail(chan != NULL);
Packit ee6627
  g_return_if_fail(chan->supported != NULL);
Packit ee6627
Packit ee6627
  chan->cipher = ci;
Packit ee6627
  if(ci) {
Packit ee6627
    guint cid;
Packit ee6627
Packit ee6627
    c = mwCipherInstance_getCipher(ci);
Packit ee6627
    cid = mwCipher_getType(c);
Packit ee6627
Packit ee6627
    g_hash_table_steal(chan->supported, GUINT_TO_POINTER(cid));
Packit ee6627
Packit ee6627
    switch(mwCipher_getType(c)) {
Packit ee6627
    case mwCipher_RC2_40:
Packit ee6627
      chan->policy = mwEncrypt_RC2_40;
Packit ee6627
      break;
Packit ee6627
Packit ee6627
    case mwCipher_RC2_128:
Packit ee6627
      chan->policy = mwEncrypt_RC2_128;
Packit ee6627
      break;
Packit ee6627
Packit ee6627
    default:
Packit ee6627
      /* unsure if this is bad */
Packit ee6627
      chan->policy = mwEncrypt_WHATEVER;
Packit ee6627
    }
Packit ee6627
Packit ee6627
    g_message("channel 0x%08x selected cipher %s",
Packit ee6627
	      chan->id, NSTR(mwCipher_getName(c)));
Packit ee6627
Packit ee6627
  } else {
Packit ee6627
Packit ee6627
    chan->policy = mwEncrypt_NONE;
Packit ee6627
    g_message("channel 0x%08x selected no cipher", chan->id);
Packit ee6627
  }
Packit ee6627
Packit ee6627
  g_hash_table_destroy(chan->supported);
Packit ee6627
  chan->supported = NULL;
Packit ee6627
}
Packit ee6627
Packit ee6627