Blame src/session.c

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