Blame src/session.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_cipher.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_util.h"
Packit 16808d
Packit 16808d
Packit 16808d
/** the hash table key for a service, for mwSession::services */
Packit 16808d
#define SERVICE_KEY(srvc) mwService_getType(srvc)
Packit 16808d
Packit 16808d
/** the hash table key for a cipher, for mwSession::ciphers */
Packit 16808d
#define CIPHER_KEY(ciph)  mwCipher_getType(ciph)
Packit 16808d
Packit 16808d
Packit 16808d
#define GPOINTER(val)  (GUINT_TO_POINTER((guint) (val)))
Packit 16808d
#define GUINT(val)     (GPOINTER_TO_UINT((val)))
Packit 16808d
Packit 16808d
Packit 16808d
struct mwSession {
Packit 16808d
Packit 16808d
  /** provides I/O and callback functions */
Packit 16808d
  struct mwSessionHandler *handler;
Packit 16808d
Packit 16808d
  enum mwSessionState state;  /**< session state */
Packit 16808d
  gpointer state_info;        /**< additional state info */
Packit 16808d
Packit 16808d
  /* input buffering for an incoming message */
Packit 16808d
  guchar *buf;  /**< buffer for incoming message data */
Packit 16808d
  gsize buf_len;       /**< length of buf */
Packit 16808d
  gsize buf_used;      /**< offset to last-used byte of buf */
Packit 16808d
  
Packit 16808d
  struct mwLoginInfo login;      /**< login information */
Packit 16808d
  struct mwUserStatus status;    /**< user status */
Packit 16808d
  struct mwPrivacyInfo privacy;  /**< privacy list */
Packit 16808d
Packit 16808d
  /** the collection of channels */
Packit 16808d
  struct mwChannelSet *channels;
Packit 16808d
Packit 16808d
  /** the collection of services, keyed to guint32 service id */
Packit 16808d
  GHashTable *services;
Packit 16808d
Packit 16808d
  /** the collection of ciphers, keyed to guint16 cipher type */
Packit 16808d
  GHashTable *ciphers;
Packit 16808d
Packit 16808d
  /** arbitrary key:value pairs */
Packit 16808d
  GHashTable *attributes;
Packit 16808d
Packit 16808d
  /** optional user data */
Packit 16808d
  struct mw_datum client_data;
Packit 16808d
};
Packit 16808d
Packit 16808d
Packit 16808d
static void property_set(struct mwSession *s, const char *key,
Packit 16808d
			 gpointer val, GDestroyNotify clean) {
Packit 16808d
Packit 16808d
  g_hash_table_insert(s->attributes, g_strdup(key),
Packit 16808d
		      mw_datum_new(val, clean));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static gpointer property_get(struct mwSession *s, const char *key) {
Packit 16808d
  struct mw_datum *p = g_hash_table_lookup(s->attributes, key);
Packit 16808d
  return p? p->data: NULL;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void property_del(struct mwSession *s, const char *key) {
Packit 16808d
  g_hash_table_remove(s->attributes, key);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/**
Packit 16808d
   set up the default properties for a newly created session
Packit 16808d
*/
Packit 16808d
static void session_defaults(struct mwSession *s) {
Packit 16808d
  property_set(s, mwSession_CLIENT_VER_MAJOR,
Packit 16808d
	       GPOINTER(MW_PROTOCOL_VERSION_MAJOR), NULL);
Packit 16808d
Packit 16808d
  property_set(s, mwSession_CLIENT_VER_MINOR, 
Packit 16808d
	       GPOINTER(MW_PROTOCOL_VERSION_MINOR), NULL);
Packit 16808d
Packit 16808d
  property_set(s, mwSession_CLIENT_TYPE_ID,
Packit 16808d
	       GPOINTER(mwLogin_MEANWHILE), NULL);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwSession *mwSession_new(struct mwSessionHandler *handler) {
Packit 16808d
  struct mwSession *s;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(handler != NULL, NULL);
Packit 16808d
Packit 16808d
  /* consider io_write and io_close to be absolute necessities */
Packit 16808d
  g_return_val_if_fail(handler->io_write != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(handler->io_close != NULL, NULL);
Packit 16808d
Packit 16808d
  s = g_new0(struct mwSession, 1);
Packit 16808d
Packit 16808d
  s->state = mwSession_STOPPED;
Packit 16808d
Packit 16808d
  s->handler = handler;
Packit 16808d
Packit 16808d
  s->channels = mwChannelSet_new(s);
Packit 16808d
  s->services = map_guint_new();
Packit 16808d
  s->ciphers = map_guint_new();
Packit 16808d
Packit 16808d
  s->attributes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
Packit 16808d
					(GDestroyNotify) mw_datum_free);
Packit 16808d
Packit 16808d
  session_defaults(s);
Packit 16808d
Packit 16808d
  return s;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** free and reset the session buffer */
Packit 16808d
static void session_buf_free(struct mwSession *s) {
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
Packit 16808d
  g_free(s->buf);
Packit 16808d
  s->buf = NULL;
Packit 16808d
  s->buf_len = 0;
Packit 16808d
  s->buf_used = 0;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** a polite string version of the session state enum */
Packit 16808d
static const char *state_str(enum mwSessionState state) {
Packit 16808d
  switch(state) {
Packit 16808d
  case mwSession_STARTING:      return "starting";
Packit 16808d
  case mwSession_HANDSHAKE:     return "handshake sent";
Packit 16808d
  case mwSession_HANDSHAKE_ACK: return "handshake acknowledged";
Packit 16808d
  case mwSession_LOGIN:         return "login sent";
Packit 16808d
  case mwSession_LOGIN_REDIR:   return "login redirected";
Packit 16808d
  case mwSession_LOGIN_CONT:    return "forcing login";
Packit 16808d
  case mwSession_LOGIN_ACK:     return "login acknowledged";
Packit 16808d
  case mwSession_STARTED:       return "started";
Packit 16808d
  case mwSession_STOPPING:      return "stopping";
Packit 16808d
  case mwSession_STOPPED:       return "stopped";
Packit 16808d
Packit 16808d
  case mwSession_UNKNOWN:       /* fall-through */
Packit 16808d
  default:                      return "UNKNOWN";
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_free(struct mwSession *s) {
Packit 16808d
  struct mwSessionHandler *h;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
Packit 16808d
  if(! mwSession_isStopped(s)) {
Packit 16808d
    g_debug("session is not stopped (state: %s), proceeding with free",
Packit 16808d
	    state_str(s->state));
Packit 16808d
  }
Packit 16808d
Packit 16808d
  h = s->handler;
Packit 16808d
  if(h && h->clear) h->clear(s);
Packit 16808d
  s->handler = NULL;
Packit 16808d
Packit 16808d
  session_buf_free(s);
Packit 16808d
Packit 16808d
  mwChannelSet_free(s->channels);
Packit 16808d
  g_hash_table_destroy(s->services);
Packit 16808d
  g_hash_table_destroy(s->ciphers);
Packit 16808d
  g_hash_table_destroy(s->attributes);
Packit 16808d
Packit 16808d
  mwLoginInfo_clear(&s->login);
Packit 16808d
  mwUserStatus_clear(&s->status);
Packit 16808d
  mwPrivacyInfo_clear(&s->privacy);
Packit 16808d
Packit 16808d
  g_free(s);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** write data to the session handler */
Packit 16808d
static int io_write(struct mwSession *s, const guchar *buf, gsize len) {
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  g_return_val_if_fail(s->handler != NULL, -1);
Packit 16808d
  g_return_val_if_fail(s->handler->io_write != NULL, -1);
Packit 16808d
Packit 16808d
  return s->handler->io_write(s, buf, len);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** close the session handler */
Packit 16808d
static void io_close(struct mwSession *s) {
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(s->handler != NULL);
Packit 16808d
  g_return_if_fail(s->handler->io_close != NULL);
Packit 16808d
Packit 16808d
  s->handler->io_close(s);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void state(struct mwSession *s, enum mwSessionState state,
Packit 16808d
		  gpointer info) {
Packit 16808d
Packit 16808d
  struct mwSessionHandler *sh;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(s->handler != NULL);
Packit 16808d
Packit 16808d
  if(mwSession_isState(s, state)) return;
Packit 16808d
Packit 16808d
  s->state = state;
Packit 16808d
  s->state_info = info;
Packit 16808d
Packit 16808d
  switch(state) {
Packit 16808d
  case mwSession_STOPPING:
Packit 16808d
  case mwSession_STOPPED:
Packit 16808d
    g_message("session state: %s (0x%08x)", state_str(state),
Packit 16808d
	      GPOINTER_TO_UINT(info));
Packit 16808d
    break;
Packit 16808d
Packit 16808d
  case mwSession_LOGIN_REDIR:
Packit 16808d
    g_message("session state: %s (%s)", state_str(state),
Packit 16808d
	      (char *)info);
Packit 16808d
    break;
Packit 16808d
Packit 16808d
  default:
Packit 16808d
    g_message("session state: %s", state_str(state));
Packit 16808d
  }
Packit 16808d
Packit 16808d
  sh = s->handler;
Packit 16808d
  if(sh && sh->on_stateChange)
Packit 16808d
    sh->on_stateChange(s, state, info);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_start(struct mwSession *s) {
Packit 16808d
  struct mwMsgHandshake *msg;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(mwSession_isStopped(s));
Packit 16808d
Packit 16808d
  if(mwSession_isStarted(s) || mwSession_isStarting(s)) {
Packit 16808d
    g_debug("attempted to start session that is already started/starting");
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
  
Packit 16808d
  state(s, mwSession_STARTING, 0);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgHandshake *) mwMessage_new(mwMessage_HANDSHAKE);
Packit 16808d
  msg->major = GUINT(property_get(s, mwSession_CLIENT_VER_MAJOR));
Packit 16808d
  msg->minor = GUINT(property_get(s, mwSession_CLIENT_VER_MINOR));
Packit 16808d
  msg->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
Packit 16808d
Packit 16808d
  msg->loclcalc_addr = GUINT(property_get(s, mwSession_CLIENT_IP));
Packit 16808d
Packit 16808d
  if(msg->major >= 0x001e && msg->minor >= 0x001d) {
Packit 16808d
    msg->unknown_a = 0x0100;
Packit 16808d
    msg->local_host = property_get(s, mwSession_CLIENT_HOST);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  if(ret) {
Packit 16808d
    mwSession_stop(s, CONNECTION_BROKEN);
Packit 16808d
  } else {
Packit 16808d
    state(s, mwSession_HANDSHAKE, 0);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_stop(struct mwSession *s, guint32 reason) {
Packit 16808d
  GList *list, *l = NULL;
Packit 16808d
  struct mwMsgChannelDestroy *msg;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  
Packit 16808d
  if(mwSession_isStopped(s) || mwSession_isStopping(s)) {
Packit 16808d
    g_debug("attempted to stop session that is already stopped/stopping");
Packit 16808d
    return;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  state(s, mwSession_STOPPING, GUINT_TO_POINTER(reason));
Packit 16808d
Packit 16808d
  for(list = l = mwSession_getServices(s); l; l = l->next)
Packit 16808d
    mwService_stop(MW_SERVICE(l->data));
Packit 16808d
  g_list_free(list);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgChannelDestroy *)
Packit 16808d
    mwMessage_new(mwMessage_CHANNEL_DESTROY);
Packit 16808d
Packit 16808d
  msg->head.channel = MW_MASTER_CHANNEL_ID;
Packit 16808d
  msg->reason = reason;
Packit 16808d
Packit 16808d
  /* don't care if this fails, we're closing the connection anyway */
Packit 16808d
  mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  session_buf_free(s);
Packit 16808d
Packit 16808d
  /* close the connection */
Packit 16808d
  io_close(s);
Packit 16808d
Packit 16808d
  state(s, mwSession_STOPPED, GUINT_TO_POINTER(reason));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** compose authentication information into an opaque based on the
Packit 16808d
    password, encrypted via RC2/40 */
Packit 16808d
static void compose_auth_rc2_40(struct mwOpaque *auth, const char *pass) {
Packit 16808d
  guchar iv[8], key[5];
Packit 16808d
  struct mwOpaque a, b, z;
Packit 16808d
  struct mwPutBuffer *p;
Packit 16808d
Packit 16808d
  /* get an IV and a random five-byte key */
Packit 16808d
  mwIV_init(iv);
Packit 16808d
  mwKeyRandom(key, 5);
Packit 16808d
Packit 16808d
  /* the opaque with the key */
Packit 16808d
  a.len = 5;
Packit 16808d
  a.data = key;
Packit 16808d
Packit 16808d
  /* the opaque to receive the encrypted pass */
Packit 16808d
  b.len = 0;
Packit 16808d
  b.data = NULL;
Packit 16808d
Packit 16808d
  /* the plain-text pass dressed up as an opaque */
Packit 16808d
  z.len = strlen(pass);
Packit 16808d
  z.data = (guchar *) pass;
Packit 16808d
Packit 16808d
  /* the opaque with the encrypted pass */
Packit 16808d
  mwEncrypt(a.data, a.len, iv, &z, &b);
Packit 16808d
Packit 16808d
  /* an opaque containing the other two opaques */
Packit 16808d
  p = mwPutBuffer_new();
Packit 16808d
  mwOpaque_put(p, &a);
Packit 16808d
  mwOpaque_put(p, &b);
Packit 16808d
  mwPutBuffer_finalize(auth, p);
Packit 16808d
Packit 16808d
  /* this is the only one to clear, as the other uses a static buffer */
Packit 16808d
  mwOpaque_clear(&b);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void compose_auth_rc2_128(struct mwOpaque *auth, const char *pass,
Packit 16808d
				 guint32 magic, struct mwOpaque *rkey) {
Packit 16808d
Packit 16808d
  guchar iv[8];
Packit 16808d
  struct mwOpaque a, b, c;
Packit 16808d
  struct mwPutBuffer *p;
Packit 16808d
Packit 16808d
  struct mwMpi *private, *public;
Packit 16808d
  struct mwMpi *remote;
Packit 16808d
  struct mwMpi *shared;
Packit 16808d
Packit 16808d
  private = mwMpi_new();
Packit 16808d
  public = mwMpi_new();
Packit 16808d
  remote = mwMpi_new();
Packit 16808d
  shared = mwMpi_new();
Packit 16808d
Packit 16808d
  mwIV_init(iv);
Packit 16808d
Packit 16808d
  mwMpi_randDHKeypair(private, public);
Packit 16808d
  mwMpi_import(remote, rkey);
Packit 16808d
  mwMpi_calculateDHShared(shared, remote, private);
Packit 16808d
Packit 16808d
  /* put the password in opaque a */
Packit 16808d
  p = mwPutBuffer_new();
Packit 16808d
  guint32_put(p, magic);
Packit 16808d
  mwString_put(p, pass);
Packit 16808d
  mwPutBuffer_finalize(&a, p);
Packit 16808d
Packit 16808d
  /* put the shared key in opaque b */
Packit 16808d
  mwMpi_export(shared, &b);
Packit 16808d
Packit 16808d
  /* encrypt the password (a) using the shared key (b), put the result
Packit 16808d
     in opaque c */
Packit 16808d
  mwEncrypt(b.data+(b.len-16), 16, iv, &a, &c);
Packit 16808d
Packit 16808d
  /* don't need the shared key anymore, re-use opaque (b) as the
Packit 16808d
     export of the public key */
Packit 16808d
  mwOpaque_clear(&b);
Packit 16808d
  mwMpi_export(public, &b);
Packit 16808d
Packit 16808d
  p = mwPutBuffer_new();
Packit 16808d
  guint16_put(p, 0x0001);  /* XXX: unknown */
Packit 16808d
  mwOpaque_put(p, &b);
Packit 16808d
  mwOpaque_put(p, &c);
Packit 16808d
  mwPutBuffer_finalize(auth, p);
Packit 16808d
Packit 16808d
  mwOpaque_clear(&a);
Packit 16808d
  mwOpaque_clear(&b);
Packit 16808d
  mwOpaque_clear(&c);
Packit 16808d
Packit 16808d
  mwMpi_free(private);
Packit 16808d
  mwMpi_free(public);
Packit 16808d
  mwMpi_free(remote);
Packit 16808d
  mwMpi_free(shared);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** handle the receipt of a handshake_ack message by sending the login
Packit 16808d
    message */
Packit 16808d
static void HANDSHAKE_ACK_recv(struct mwSession *s,
Packit 16808d
			       struct mwMsgHandshakeAck *msg) {
Packit 16808d
  struct mwMsgLogin *log;
Packit 16808d
  int ret;
Packit 16808d
			       
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
  g_return_if_fail(mwSession_isState(s, mwSession_HANDSHAKE) ||
Packit 16808d
		   mwSession_isState(s, mwSession_LOGIN_CONT));
Packit 16808d
Packit 16808d
  if(mwSession_isState(s, mwSession_LOGIN_CONT)) {
Packit 16808d
    /* this is a login continuation, don't re-send the login. We
Packit 16808d
       should receive a login ack in a moment */
Packit 16808d
Packit 16808d
    state(s, mwSession_HANDSHAKE_ACK, 0);
Packit 16808d
    state(s, mwSession_LOGIN, 0);
Packit 16808d
    return;
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    state(s, mwSession_HANDSHAKE_ACK, 0);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* record the major/minor versions from the server */
Packit 16808d
  property_set(s, mwSession_SERVER_VER_MAJOR, GPOINTER(msg->major), NULL);
Packit 16808d
  property_set(s, mwSession_SERVER_VER_MINOR, GPOINTER(msg->minor), NULL);
Packit 16808d
Packit 16808d
  /* compose the login message */
Packit 16808d
  log = (struct mwMsgLogin *) mwMessage_new(mwMessage_LOGIN);
Packit 16808d
  log->login_type = GUINT(property_get(s, mwSession_CLIENT_TYPE_ID));
Packit 16808d
  log->name = g_strdup(property_get(s, mwSession_AUTH_USER_ID));
Packit 16808d
Packit 16808d
  /** @todo default to password for now. later use token optionally */
Packit 16808d
  {
Packit 16808d
    const char *pw;
Packit 16808d
    pw = property_get(s, mwSession_AUTH_PASSWORD);
Packit 16808d
   
Packit 16808d
    if(msg->data.len >= 64) {
Packit 16808d
      /* good login encryption */
Packit 16808d
      log->auth_type = mwAuthType_RC2_128;
Packit 16808d
      compose_auth_rc2_128(&log->auth_data, pw, msg->magic, &msg->data);
Packit 16808d
Packit 16808d
    } else {
Packit 16808d
      /* BAD login encryption */
Packit 16808d
      log->auth_type = mwAuthType_RC2_40;
Packit 16808d
      compose_auth_rc2_40(&log->auth_data, pw);
Packit 16808d
    }
Packit 16808d
  }
Packit 16808d
  
Packit 16808d
  /* send the login message */
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(log));
Packit 16808d
  mwMessage_free(MW_MESSAGE(log));
Packit 16808d
Packit 16808d
  if(! ret) {
Packit 16808d
    /* sent login OK, set state appropriately */
Packit 16808d
    state(s, mwSession_LOGIN, 0);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/** handle the receipt of a login_ack message. This completes the
Packit 16808d
    startup sequence for the session */
Packit 16808d
static void LOGIN_ACK_recv(struct mwSession *s,
Packit 16808d
			   struct mwMsgLoginAck *msg) {
Packit 16808d
  GList *ll, *l;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
  g_return_if_fail(mwSession_isState(s, mwSession_LOGIN));
Packit 16808d
Packit 16808d
  /* store the login information in the session */
Packit 16808d
  mwLoginInfo_clear(&s->login);
Packit 16808d
  mwLoginInfo_clone(&s->login, &msg->login);
Packit 16808d
Packit 16808d
  state(s, mwSession_LOGIN_ACK, 0);
Packit 16808d
Packit 16808d
  /* start up our services */
Packit 16808d
  for(ll = l = mwSession_getServices(s); l; l = l->next) {
Packit 16808d
    mwService_start(l->data);
Packit 16808d
  }
Packit 16808d
  g_list_free(ll);
Packit 16808d
Packit 16808d
  /* @todo any further startup stuff? */
Packit 16808d
Packit 16808d
  state(s, mwSession_STARTED, 0);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void CHANNEL_CREATE_recv(struct mwSession *s,
Packit 16808d
				struct mwMsgChannelCreate *msg) {
Packit 16808d
  struct mwChannel *chan;
Packit 16808d
  chan = mwChannel_newIncoming(s->channels, msg->channel);
Packit 16808d
Packit 16808d
  /* hand off to channel */
Packit 16808d
  mwChannel_recvCreate(chan, msg);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void CHANNEL_ACCEPT_recv(struct mwSession *s,
Packit 16808d
				struct mwMsgChannelAccept *msg) {
Packit 16808d
  struct mwChannel *chan;
Packit 16808d
  chan = mwChannel_find(s->channels, msg->head.channel);
Packit 16808d
Packit 16808d
  g_return_if_fail(chan != NULL);
Packit 16808d
Packit 16808d
  /* hand off to channel */
Packit 16808d
  mwChannel_recvAccept(chan, msg);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void CHANNEL_DESTROY_recv(struct mwSession *s,
Packit 16808d
				 struct mwMsgChannelDestroy *msg) {
Packit 16808d
Packit 16808d
  /* the server can indicate that we should close the session by
Packit 16808d
     destroying the zero channel */
Packit 16808d
  if(msg->head.channel == MW_MASTER_CHANNEL_ID) {
Packit 16808d
    mwSession_stop(s, msg->reason);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    struct mwChannel *chan;
Packit 16808d
    chan = mwChannel_find(s->channels, msg->head.channel);
Packit 16808d
Packit 16808d
    /* we don't have any such channel... so I guess we destroyed it.
Packit 16808d
       This is to remove a warning from timing errors when two clients
Packit 16808d
       both try to close a channel at about the same time. */
Packit 16808d
    if(! chan) return;
Packit 16808d
    
Packit 16808d
    /* hand off to channel */
Packit 16808d
    mwChannel_recvDestroy(chan, msg);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void CHANNEL_SEND_recv(struct mwSession *s,
Packit 16808d
			      struct mwMsgChannelSend *msg) {
Packit 16808d
  struct mwChannel *chan;
Packit 16808d
  chan = mwChannel_find(s->channels, msg->head.channel);
Packit 16808d
Packit 16808d
  /* if we don't have any such channel, we're certainly not going to
Packit 16808d
     accept data from it */
Packit 16808d
  if(! chan) return;
Packit 16808d
Packit 16808d
  /* hand off to channel */
Packit 16808d
  mwChannel_recv(chan, msg);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void SET_PRIVACY_LIST_recv(struct mwSession *s,
Packit 16808d
				  struct mwMsgSetPrivacyList *msg) {
Packit 16808d
  struct mwSessionHandler *sh = s->handler;
Packit 16808d
Packit 16808d
  g_info("SET_PRIVACY_LIST");
Packit 16808d
Packit 16808d
  mwPrivacyInfo_clear(&s->privacy);
Packit 16808d
  mwPrivacyInfo_clone(&s->privacy, &msg->privacy);
Packit 16808d
Packit 16808d
  if(sh && sh->on_setPrivacyInfo)
Packit 16808d
    sh->on_setPrivacyInfo(s);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void SET_USER_STATUS_recv(struct mwSession *s,
Packit 16808d
				 struct mwMsgSetUserStatus *msg) {
Packit 16808d
  struct mwSessionHandler *sh = s->handler;
Packit 16808d
Packit 16808d
  mwUserStatus_clear(&s->status);
Packit 16808d
  mwUserStatus_clone(&s->status, &msg->status);
Packit 16808d
Packit 16808d
  if(sh && sh->on_setUserStatus)
Packit 16808d
    sh->on_setUserStatus(s);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void SENSE_SERVICE_recv(struct mwSession *s,
Packit 16808d
			       struct mwMsgSenseService *msg) {
Packit 16808d
  struct mwService *srvc;
Packit 16808d
Packit 16808d
  srvc = mwSession_getService(s, msg->service);
Packit 16808d
  if(srvc) mwService_start(srvc);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void ADMIN_recv(struct mwSession *s, struct mwMsgAdmin *msg) {
Packit 16808d
  struct mwSessionHandler *sh = s->handler;
Packit 16808d
Packit 16808d
  if(sh && sh->on_admin)
Packit 16808d
    sh->on_admin(s, msg->text);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void ANNOUNCE_recv(struct mwSession *s, struct mwMsgAnnounce *msg) {
Packit 16808d
  struct mwSessionHandler *sh = s->handler;
Packit 16808d
Packit 16808d
  if(sh && sh->on_announce)
Packit 16808d
    sh->on_announce(s, &msg->sender, msg->may_reply, msg->text);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static void LOGIN_REDIRECT_recv(struct mwSession *s,
Packit 16808d
				struct mwMsgLoginRedirect *msg) {
Packit 16808d
Packit 16808d
  state(s, mwSession_LOGIN_REDIR, msg->host);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
#define CASE(var, type) \
Packit 16808d
case mwMessage_ ## var: \
Packit 16808d
  var ## _recv(s, (struct type *) msg); \
Packit 16808d
  break;
Packit 16808d
Packit 16808d
Packit 16808d
static void session_process(struct mwSession *s,
Packit 16808d
			    const guchar *buf, gsize len) {
Packit 16808d
Packit 16808d
  struct mwOpaque o = { .len = len, .data = (guchar *) buf };
Packit 16808d
  struct mwGetBuffer *b;
Packit 16808d
  struct mwMessage *msg;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(buf != NULL);
Packit 16808d
Packit 16808d
  /* ignore zero-length messages */
Packit 16808d
  if(len == 0) return;
Packit 16808d
Packit 16808d
  /* wrap up buf */
Packit 16808d
  b = mwGetBuffer_wrap(&o);
Packit 16808d
Packit 16808d
  /* attempt to parse the message. */
Packit 16808d
  msg = mwMessage_get(b);
Packit 16808d
Packit 16808d
  if(mwGetBuffer_error(b)) {
Packit 16808d
    mw_mailme_opaque(&o, "parsing of message failed");
Packit 16808d
  }
Packit 16808d
Packit 16808d
  mwGetBuffer_free(b);
Packit 16808d
Packit 16808d
  g_return_if_fail(msg != NULL);
Packit 16808d
Packit 16808d
  /* handle each of the appropriate incoming types of mwMessage */
Packit 16808d
  switch(msg->type) {
Packit 16808d
    CASE(HANDSHAKE_ACK, mwMsgHandshakeAck);
Packit 16808d
    CASE(LOGIN_REDIRECT, mwMsgLoginRedirect);
Packit 16808d
    CASE(LOGIN_ACK, mwMsgLoginAck);
Packit 16808d
    CASE(CHANNEL_CREATE, mwMsgChannelCreate);
Packit 16808d
    CASE(CHANNEL_DESTROY, mwMsgChannelDestroy);
Packit 16808d
    CASE(CHANNEL_SEND, mwMsgChannelSend);
Packit 16808d
    CASE(CHANNEL_ACCEPT, mwMsgChannelAccept);
Packit 16808d
    CASE(SET_PRIVACY_LIST, mwMsgSetPrivacyList);
Packit 16808d
    CASE(SET_USER_STATUS, mwMsgSetUserStatus);
Packit 16808d
    CASE(SENSE_SERVICE, mwMsgSenseService);
Packit 16808d
    CASE(ADMIN, mwMsgAdmin);
Packit 16808d
    CASE(ANNOUNCE, mwMsgAnnounce);
Packit 16808d
    
Packit 16808d
  default:
Packit 16808d
    g_warning("unknown message type 0x%04x, no handler", msg->type);
Packit 16808d
  }
Packit 16808d
Packit 16808d
  mwMessage_free(msg);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
#undef CASE
Packit 16808d
Packit 16808d
Packit 16808d
#define ADVANCE(b, n, count) { b += count; n -= count; }
Packit 16808d
Packit 16808d
Packit 16808d
/* handle input to complete an existing buffer */
Packit 16808d
static gsize session_recv_cont(struct mwSession *s,
Packit 16808d
			       const guchar *b, gsize n) {
Packit 16808d
Packit 16808d
  /* determine how many bytes still required */
Packit 16808d
  gsize x = s->buf_len - s->buf_used;
Packit 16808d
Packit 16808d
  /* g_message(" session_recv_cont: session = %p, b = %p, n = %u",
Packit 16808d
	    s, b, n); */
Packit 16808d
  
Packit 16808d
  if(n < x) {
Packit 16808d
    /* not quite enough; still need some more */
Packit 16808d
    memcpy(s->buf+s->buf_used, b, n);
Packit 16808d
    s->buf_used += n;
Packit 16808d
    return 0;
Packit 16808d
    
Packit 16808d
  } else {
Packit 16808d
    /* enough to finish the buffer, at least */
Packit 16808d
    memcpy(s->buf+s->buf_used, b, x);
Packit 16808d
    ADVANCE(b, n, x);
Packit 16808d
    
Packit 16808d
    if(s->buf_len == 4) {
Packit 16808d
      /* if only the length bytes were being buffered, we'll now try
Packit 16808d
       to complete an actual message */
Packit 16808d
Packit 16808d
      struct mwOpaque o = { 4, s->buf };
Packit 16808d
      struct mwGetBuffer *gb = mwGetBuffer_wrap(&o);
Packit 16808d
      x = guint32_peek(gb);
Packit 16808d
      mwGetBuffer_free(gb);
Packit 16808d
Packit 16808d
      if(n < x) {
Packit 16808d
	/* there isn't enough to meet the demands of the length, so
Packit 16808d
	   we'll buffer it for next time */
Packit 16808d
Packit 16808d
	guchar *t;
Packit 16808d
	x += 4;
Packit 16808d
	t = (guchar *) g_malloc(x);
Packit 16808d
	memcpy(t, s->buf, 4);
Packit 16808d
	memcpy(t+4, b, n);
Packit 16808d
	
Packit 16808d
	session_buf_free(s);
Packit 16808d
	
Packit 16808d
	s->buf = t;
Packit 16808d
	s->buf_len = x;
Packit 16808d
	s->buf_used = n + 4;
Packit 16808d
	return 0;
Packit 16808d
	
Packit 16808d
      } else {
Packit 16808d
	/* there's enough (maybe more) for a full message. don't need
Packit 16808d
	   the old session buffer (which recall, was only the length
Packit 16808d
	   bytes) any more */
Packit 16808d
	
Packit 16808d
	session_buf_free(s);
Packit 16808d
	session_process(s, b, x);
Packit 16808d
	ADVANCE(b, n, x);
Packit 16808d
      }
Packit 16808d
      
Packit 16808d
    } else {
Packit 16808d
      /* process the now-complete buffer. remember to skip the first
Packit 16808d
	 four bytes, since they're just the size count */
Packit 16808d
      session_process(s, s->buf+4, s->buf_len-4);
Packit 16808d
      session_buf_free(s);
Packit 16808d
    }
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return n;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
/* handle input when there's nothing previously buffered */
Packit 16808d
static gsize session_recv_empty(struct mwSession *s,
Packit 16808d
				const guchar *b, gsize n) {
Packit 16808d
Packit 16808d
  struct mwOpaque o = { n, (guchar *) b };
Packit 16808d
  struct mwGetBuffer *gb;
Packit 16808d
  gsize x;
Packit 16808d
Packit 16808d
  if(n < 4) {
Packit 16808d
    /* uh oh. less than four bytes means we've got an incomplete
Packit 16808d
       length indicator. Have to buffer to get the rest of it. */
Packit 16808d
    s->buf = (guchar *) g_malloc0(4);
Packit 16808d
    memcpy(s->buf, b, n);
Packit 16808d
    s->buf_len = 4;
Packit 16808d
    s->buf_used = n;
Packit 16808d
    return 0;
Packit 16808d
  }
Packit 16808d
  
Packit 16808d
  /* peek at the length indicator. if it's a zero length message,
Packit 16808d
     don't process, just skip it */
Packit 16808d
  gb = mwGetBuffer_wrap(&o);
Packit 16808d
  x = guint32_peek(gb);
Packit 16808d
  mwGetBuffer_free(gb);
Packit 16808d
  if(! x) return n - 4;
Packit 16808d
Packit 16808d
  if(n < (x + 4)) {
Packit 16808d
    /* if the total amount of data isn't enough to cover the length
Packit 16808d
       bytes and the length indicated by those bytes, then we'll need
Packit 16808d
       to buffer. This is where the DOS mentioned below in
Packit 16808d
       session_recv takes place */
Packit 16808d
Packit 16808d
    x += 4;
Packit 16808d
    s->buf = (guchar *) g_malloc(x);
Packit 16808d
    memcpy(s->buf, b, n);
Packit 16808d
    s->buf_len = x;
Packit 16808d
    s->buf_used = n;
Packit 16808d
    return 0;
Packit 16808d
    
Packit 16808d
  } else {
Packit 16808d
    /* advance past length bytes */
Packit 16808d
    ADVANCE(b, n, 4);
Packit 16808d
    
Packit 16808d
    /* process and advance */
Packit 16808d
    session_process(s, b, x);
Packit 16808d
    ADVANCE(b, n, x);
Packit 16808d
Packit 16808d
    /* return left-over count */
Packit 16808d
    return n;
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
static gsize session_recv(struct mwSession *s,
Packit 16808d
			  const guchar *b, gsize n) {
Packit 16808d
Packit 16808d
  /* This is messy and kind of confusing. I'd like to simplify it at
Packit 16808d
     some point, but the constraints are as follows:
Packit 16808d
Packit 16808d
      - buffer up to a single full message on the session buffer
Packit 16808d
      - buffer must contain the four length bytes
Packit 16808d
      - the four length bytes indicate how much we'll need to buffer
Packit 16808d
      - the four length bytes might not arrive all at once, so it's
Packit 16808d
        possible that we'll need to buffer to get them.
Packit 16808d
      - since our buffering includes the length bytes, we know we
Packit 16808d
        still have an incomplete length if the buffer length is only
Packit 16808d
        four. */
Packit 16808d
  
Packit 16808d
  /** @todo we should allow a compiled-in upper limit to message
Packit 16808d
     sizes, and just drop messages over that size. However, to do that
Packit 16808d
     we'd need to keep track of the size of a message and keep
Packit 16808d
     dropping bytes until we'd fulfilled the entire length. eg: if we
Packit 16808d
     receive a message size of 10MB, we need to pass up exactly 10MB
Packit 16808d
     before it's safe to start processing the rest as a new
Packit 16808d
     message. As it stands, a malicious packet from the server can run
Packit 16808d
     us out of memory by indicating it's going to send us some
Packit 16808d
     obscenely long message (even if it never actually sends it) */
Packit 16808d
  
Packit 16808d
  /* g_message(" session_recv: session = %p, b = %p, n = %u",
Packit 16808d
	    s, b, n); */
Packit 16808d
  
Packit 16808d
  if(s->buf_len == 0) {
Packit 16808d
    while(n && (*b & 0x80)) {
Packit 16808d
      /* keep-alive and series bytes are ignored */
Packit 16808d
      ADVANCE(b, n, 1);
Packit 16808d
    }
Packit 16808d
  }
Packit 16808d
Packit 16808d
  if(n == 0) {
Packit 16808d
    return 0;
Packit 16808d
Packit 16808d
  } else if(s->buf_len > 0) {
Packit 16808d
    return session_recv_cont(s, b, n);
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    return session_recv_empty(s, b, n);
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
#undef ADVANCE
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_recv(struct mwSession *s, const guchar *buf, gsize n) {
Packit 16808d
  guchar *b = (guchar *) buf;
Packit 16808d
  gsize remain = 0;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
Packit 16808d
  while(n > 0) {
Packit 16808d
    remain = session_recv(s, b, n);
Packit 16808d
    b += (n - remain);
Packit 16808d
    n = remain;
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_send(struct mwSession *s, struct mwMessage *msg) {
Packit 16808d
  struct mwPutBuffer *b;
Packit 16808d
  struct mwOpaque o;
Packit 16808d
  int ret = 0;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
Packit 16808d
  /* writing nothing is easy */
Packit 16808d
  if(! msg) return 0;
Packit 16808d
Packit 16808d
  /* first we render the message into an opaque */
Packit 16808d
  b = mwPutBuffer_new();
Packit 16808d
  mwMessage_put(b, msg);
Packit 16808d
  mwPutBuffer_finalize(&o, b);
Packit 16808d
Packit 16808d
  /* then we render the opaque into... another opaque! */
Packit 16808d
  b = mwPutBuffer_new();
Packit 16808d
  mwOpaque_put(b, &o);
Packit 16808d
  mwOpaque_clear(&o);
Packit 16808d
  mwPutBuffer_finalize(&o, b);
Packit 16808d
Packit 16808d
  /* then we use that opaque's data and length to write to the socket */
Packit 16808d
  ret = io_write(s, o.data, o.len);
Packit 16808d
  mwOpaque_clear(&o);
Packit 16808d
Packit 16808d
  /* ensure we could actually write the message */
Packit 16808d
  if(! ret) {
Packit 16808d
Packit 16808d
    /* special case, as the server doesn't always respond to user
Packit 16808d
       status messages. Thus, we trigger the event when we send the
Packit 16808d
       messages as well as when we receive them */
Packit 16808d
    if(msg->type == mwMessage_SET_USER_STATUS) {
Packit 16808d
      SET_USER_STATUS_recv(s, (struct mwMsgSetUserStatus *) msg);
Packit 16808d
    }
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_sendKeepalive(struct mwSession *s) {
Packit 16808d
  const guchar b = 0x80;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  return io_write(s, &b, 1);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_forceLogin(struct mwSession *s) {
Packit 16808d
  struct mwMsgLoginContinue *msg;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  g_return_val_if_fail(mwSession_isState(s, mwSession_LOGIN_REDIR), -1);
Packit 16808d
  
Packit 16808d
  state(s, mwSession_LOGIN_CONT, 0x00);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgLoginContinue *)
Packit 16808d
    mwMessage_new(mwMessage_LOGIN_CONTINUE);
Packit 16808d
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
  
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_sendAnnounce(struct mwSession *s, gboolean may_reply,
Packit 16808d
			   const char *text, const GList *recipients) {
Packit 16808d
Packit 16808d
  struct mwMsgAnnounce *msg;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  g_return_val_if_fail(mwSession_isStarted(s), -1);
Packit 16808d
  
Packit 16808d
  msg = (struct mwMsgAnnounce *) mwMessage_new(mwMessage_ANNOUNCE);
Packit 16808d
Packit 16808d
  msg->recipients = (GList *) recipients;
Packit 16808d
  msg->may_reply = may_reply;
Packit 16808d
  msg->text = g_strdup(text);
Packit 16808d
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  msg->recipients = NULL;  /* don't kill our recipients param */
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwSessionHandler *mwSession_getHandler(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  return s->handler;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwLoginInfo *mwSession_getLoginInfo(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  return &s->login;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_setPrivacyInfo(struct mwSession *s,
Packit 16808d
			     struct mwPrivacyInfo *privacy) {
Packit 16808d
Packit 16808d
  struct mwMsgSetPrivacyList *msg;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  g_return_val_if_fail(privacy != NULL, -1);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgSetPrivacyList *)
Packit 16808d
    mwMessage_new(mwMessage_SET_PRIVACY_LIST);
Packit 16808d
Packit 16808d
  mwPrivacyInfo_clone(&msg->privacy, privacy);
Packit 16808d
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwPrivacyInfo *mwSession_getPrivacyInfo(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  return &s->privacy;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
int mwSession_setUserStatus(struct mwSession *s,
Packit 16808d
			    struct mwUserStatus *stat) {
Packit 16808d
Packit 16808d
  struct mwMsgSetUserStatus *msg;
Packit 16808d
  int ret;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, -1);
Packit 16808d
  g_return_val_if_fail(stat != NULL, -1);
Packit 16808d
Packit 16808d
  msg = (struct mwMsgSetUserStatus *)
Packit 16808d
    mwMessage_new(mwMessage_SET_USER_STATUS);
Packit 16808d
Packit 16808d
  mwUserStatus_clone(&msg->status, stat);
Packit 16808d
Packit 16808d
  ret = mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
Packit 16808d
  return ret;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwUserStatus *mwSession_getUserStatus(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  return &s->status;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
enum mwSessionState mwSession_getState(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, mwSession_UNKNOWN);
Packit 16808d
  return s->state;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gpointer mwSession_getStateInfo(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, 0);
Packit 16808d
  return s->state_info;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwChannelSet *mwSession_getChannels(struct mwSession *session) {
Packit 16808d
  g_return_val_if_fail(session != NULL, NULL);
Packit 16808d
  return session->channels;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gboolean mwSession_addService(struct mwSession *s, struct mwService *srv) {
Packit 16808d
  g_return_val_if_fail(s != NULL, FALSE);
Packit 16808d
  g_return_val_if_fail(srv != NULL, FALSE);
Packit 16808d
  g_return_val_if_fail(s->services != NULL, FALSE);
Packit 16808d
Packit 16808d
  if(map_guint_lookup(s->services, SERVICE_KEY(srv))) {
Packit 16808d
    return FALSE;
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    map_guint_insert(s->services, SERVICE_KEY(srv), srv);
Packit 16808d
    if(mwSession_isState(s, mwSession_STARTED))
Packit 16808d
      mwSession_senseService(s, mwService_getType(srv));
Packit 16808d
    return TRUE;
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwService *mwSession_getService(struct mwSession *s, guint32 srv) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->services != NULL, NULL);
Packit 16808d
Packit 16808d
  return map_guint_lookup(s->services, srv);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwService *mwSession_removeService(struct mwSession *s, guint32 srv) {
Packit 16808d
  struct mwService *svc;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->services != NULL, NULL);
Packit 16808d
Packit 16808d
  svc = map_guint_lookup(s->services, srv);
Packit 16808d
  if(svc) map_guint_remove(s->services, srv);
Packit 16808d
  return svc;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
GList *mwSession_getServices(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->services != NULL, NULL);
Packit 16808d
Packit 16808d
  return map_collect_values(s->services);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_senseService(struct mwSession *s, guint32 srvc) {
Packit 16808d
  struct mwMsgSenseService *msg;
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(srvc != 0x00);
Packit 16808d
  g_return_if_fail(mwSession_isStarted(s));
Packit 16808d
Packit 16808d
  msg = (struct mwMsgSenseService *)
Packit 16808d
    mwMessage_new(mwMessage_SENSE_SERVICE);
Packit 16808d
  msg->service = srvc;
Packit 16808d
Packit 16808d
  mwSession_send(s, MW_MESSAGE(msg));
Packit 16808d
  mwMessage_free(MW_MESSAGE(msg));
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gboolean mwSession_addCipher(struct mwSession *s, struct mwCipher *c) {
Packit 16808d
  g_return_val_if_fail(s != NULL, FALSE);
Packit 16808d
  g_return_val_if_fail(c != NULL, FALSE);
Packit 16808d
  g_return_val_if_fail(s->ciphers != NULL, FALSE);
Packit 16808d
Packit 16808d
  if(map_guint_lookup(s->ciphers, mwCipher_getType(c))) {
Packit 16808d
    g_message("cipher %s is already added, apparently",
Packit 16808d
	      NSTR(mwCipher_getName(c)));
Packit 16808d
    return FALSE;
Packit 16808d
Packit 16808d
  } else {
Packit 16808d
    g_message("adding cipher %s", NSTR(mwCipher_getName(c)));
Packit 16808d
    map_guint_insert(s->ciphers, mwCipher_getType(c), c);
Packit 16808d
    return TRUE;
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwCipher *mwSession_getCipher(struct mwSession *s, guint16 c) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->ciphers != NULL, NULL);
Packit 16808d
Packit 16808d
  return map_guint_lookup(s->ciphers, c);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
struct mwCipher *mwSession_removeCipher(struct mwSession *s, guint16 c) {
Packit 16808d
  struct mwCipher *ciph;
Packit 16808d
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->ciphers != NULL, NULL);
Packit 16808d
Packit 16808d
  ciph = map_guint_lookup(s->ciphers, c);
Packit 16808d
  if(ciph) map_guint_remove(s->ciphers, c);
Packit 16808d
  return ciph;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
GList *mwSession_getCiphers(struct mwSession *s) {
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->ciphers != NULL, NULL);
Packit 16808d
Packit 16808d
  return map_collect_values(s->ciphers);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_setProperty(struct mwSession *s, const char *key,
Packit 16808d
			   gpointer val, GDestroyNotify clean) {
Packit 16808d
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(s->attributes != NULL);
Packit 16808d
  g_return_if_fail(key != NULL);
Packit 16808d
Packit 16808d
  property_set(s, key, val, clean);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gpointer mwSession_getProperty(struct mwSession *s, const char *key) {
Packit 16808d
 
Packit 16808d
  g_return_val_if_fail(s != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(s->attributes != NULL, NULL);
Packit 16808d
  g_return_val_if_fail(key != NULL, NULL);
Packit 16808d
Packit 16808d
  return property_get(s, key);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_removeProperty(struct mwSession *s, const char *key) {
Packit 16808d
  g_return_if_fail(s != NULL);
Packit 16808d
  g_return_if_fail(s->attributes != NULL);
Packit 16808d
  g_return_if_fail(key != NULL);
Packit 16808d
Packit 16808d
  property_del(s, key);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_setClientData(struct mwSession *session,
Packit 16808d
			     gpointer data, GDestroyNotify clear) {
Packit 16808d
Packit 16808d
  g_return_if_fail(session != NULL);
Packit 16808d
  mw_datum_set(&session->client_data, data, clear);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
gpointer mwSession_getClientData(struct mwSession *session) {
Packit 16808d
  g_return_val_if_fail(session != NULL, NULL);
Packit 16808d
  return mw_datum_get(&session->client_data);
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
void mwSession_removeClientData(struct mwSession *session) {
Packit 16808d
  g_return_if_fail(session != NULL);
Packit 16808d
  mw_datum_clear(&session->client_data);
Packit 16808d
}
Packit 16808d