Index: protocols/Makefile
===================================================================
--- protocols/Makefile	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,11 +8,7 @@
 
 -include ../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/
-endif
 
 # [SH] Program variables
-objects = account.o bee.o bee_chat.o bee_ft.o bee_user.o nogaim.o
-
+objects = md5.o nogaim.o proxy.o sha.o util.o $(SSL_CLIENT)
 
 # [SH] The next two lines should contain the directory name (in $(subdirs))
@@ -25,12 +21,9 @@
 # Expansion of variables
 subdirobjs := $(join $(subdirs),$(addprefix /,$(subdirobjs)))
+CFLAGS += -Wall
 LFLAGS += -r
 
 # [SH] Phony targets
 all: protocols.o
-check: all
-lcov: check
-gcov:
-	gcov *.c
 
 .PHONY: all clean distclean $(subdirs)
@@ -40,8 +33,7 @@
 
 distclean: clean $(subdirs)
-	rm -rf .depend
 
 $(subdirs):
-	$(MAKE) -C $@ $(MAKECMDGOALS)
+	@$(MAKE) -C $@ $(MAKECMDGOALS)
 
 ### MAIN PROGRAM
@@ -49,11 +41,9 @@
 protocols.o: $(objects) $(subdirs)
 	@echo '*' Linking protocols.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o
+	@$(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o
 
 $(objects): ../Makefile.settings Makefile
 
-$(objects): %.o: $(_SRCDIR_)%.c
+$(objects): %.o: %.c
 	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
--include .depend/*.d
+	@$(CC) -c $(CFLAGS) $< -o $@
Index: protocols/account.c
===================================================================
--- protocols/account.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,476 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Account management functions                                         */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "account.h"
-
-static const char* account_protocols_local[] = {
-	"gg", "whatsapp", NULL
-};
-
-static char *set_eval_nick_source(set_t *set, char *value);
-
-account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass)
-{
-	account_t *a;
-	set_t *s;
-	char tag[strlen(prpl->name) + 10];
-
-	if (bee->accounts) {
-		for (a = bee->accounts; a->next; a = a->next) {
-			;
-		}
-		a = a->next = g_new0(account_t, 1);
-	} else {
-		bee->accounts = a = g_new0(account_t, 1);
-	}
-
-	a->prpl = prpl;
-	a->user = g_strdup(user);
-	a->pass = g_strdup(pass);
-	a->auto_connect = 1;
-	a->bee = bee;
-
-	s = set_add(&a->set, "auto_connect", "true", set_eval_account, a);
-	s->flags |= SET_NOSAVE;
-
-	s = set_add(&a->set, "auto_reconnect", "true", set_eval_bool, a);
-
-	s = set_add(&a->set, "handle_unknown", NULL, NULL, a);
-	s->flags |= SET_NULL_OK;
-
-	s = set_add(&a->set, "nick_format", NULL, NULL, a);
-	s->flags |= SET_NULL_OK;
-
-	s = set_add(&a->set, "nick_source", "handle", set_eval_nick_source, a);
-	s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
-
-	s = set_add(&a->set, "password", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
-
-	s = set_add(&a->set, "tag", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE;
-
-	s = set_add(&a->set, "username", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
-	set_setstr(&a->set, "username", user);
-
-	if (prpl == &protocol_missing) {
-		s = set_add(&a->set, "server", NULL, set_eval_account, a);
-		s->flags |= SET_NOSAVE | SET_HIDDEN | ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
-	}
-
-	/* Hardcode some more clever tag guesses. */
-	strcpy(tag, prpl->name);
-	if (strcmp(prpl->name, "jabber") == 0) {
-		if (strstr(a->user, "@gmail.com") ||
-		    strstr(a->user, "@googlemail.com")) {
-			strcpy(tag, "gtalk");
-		}
-	}
-
-	if (account_by_tag(bee, tag)) {
-		char *numpos = tag + strlen(tag);
-		int i;
-
-		for (i = 2; i < 10000; i++) {
-			sprintf(numpos, "%d", i);
-			if (!account_by_tag(bee, tag)) {
-				break;
-			}
-		}
-	}
-	set_setstr(&a->set, "tag", tag);
-
-	a->nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	/* This function adds some more settings (and might want to do more
-	   things that have to be done now, although I can't think of anything. */
-	if (prpl->init) {
-		prpl->init(a);
-	}
-
-	s = set_add(&a->set, "away", NULL, set_eval_account, a);
-	s->flags |= SET_NULL_OK;
-
-	if (a->flags & ACC_FLAG_STATUS_MESSAGE) {
-		s = set_add(&a->set, "status", NULL, set_eval_account, a);
-		s->flags |= SET_NULL_OK;
-	}
-
-	return a;
-}
-
-char *set_eval_account(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-
-	/* Double-check: We refuse to edit on-line accounts. */
-	if (set->flags & ACC_SET_OFFLINE_ONLY && acc->ic) {
-		return SET_INVALID;
-	}
-
-	if (strcmp(set->key, "server") == 0) {
-		g_free(acc->server);
-		if (value && *value) {
-			acc->server = g_strdup(value);
-			return value;
-		} else {
-			acc->server = g_strdup(set->def);
-			return g_strdup(set->def);
-		}
-	} else if (strcmp(set->key, "username") == 0) {
-		g_free(acc->user);
-		acc->user = g_strdup(value);
-		return value;
-	} else if (strcmp(set->key, "password") == 0) {
-		/* set -del allows /oper to be used to change the password or,
-		   iff oauth is enabled, reset the oauth credential magic.
-		*/
-		if (!value) {
-			if (set_getbool(&(acc->set), "oauth")) {
-				value = "";
-			} else {
-				value = PASSWORD_PENDING;
-				((irc_t *) acc->bee->ui_data)->status |= OPER_HACK_ACCOUNT_PASSWORD;
-				irc_rootmsg((irc_t *) acc->bee->ui_data, "You may now use /OPER to set the password");
-			}
-		}
-
-		g_free(acc->pass);
-		acc->pass = g_strdup(value);
-		return NULL;    /* password shouldn't be visible in plaintext! */
-	} else if (strcmp(set->key, "tag") == 0) {
-		account_t *oa;
-
-		/* Enforce uniqueness. */
-		if ((oa = account_by_tag(acc->bee, value)) && oa != acc) {
-			return SET_INVALID;
-		}
-
-		g_free(acc->tag);
-		acc->tag = g_strdup(value);
-		return value;
-	} else if (strcmp(set->key, "auto_connect") == 0) {
-		if (!is_bool(value)) {
-			return SET_INVALID;
-		}
-
-		acc->auto_connect = bool2int(value);
-		return value;
-	} else if (strcmp(set->key, "away") == 0 ||
-	           strcmp(set->key, "status") == 0) {
-		if (acc->ic && acc->ic->flags & OPT_LOGGED_IN) {
-			/* If we're currently on-line, set the var now already
-			   (bit of a hack) and send an update. */
-			g_free(set->value);
-			set->value = g_strdup(value);
-
-			imc_away_send_update(acc->ic);
-		}
-
-		return value;
-	}
-
-	return SET_INVALID;
-}
-
-/* For bw compatibility, have this write-only setting. */
-static char *set_eval_nick_source(set_t *set, char *value)
-{
-	account_t *a = set->data;
-
-	if (strcmp(value, "full_name") == 0) {
-		set_setstr(&a->set, "nick_format", "%full_name");
-	} else if (strcmp(value, "first_name") == 0) {
-		set_setstr(&a->set, "nick_format", "%first_name");
-	} else {
-		set_setstr(&a->set, "nick_format", "%-@nick");
-	}
-
-	return value;
-}
-
-account_t *account_get(bee_t *bee, const char *id)
-{
-	account_t *a, *ret = NULL;
-	char *handle, *s;
-	int nr;
-
-	/* Tags get priority above anything else. */
-	if ((a = account_by_tag(bee, id))) {
-		return a;
-	}
-
-	/* This checks if the id string ends with (...) */
-	if ((handle = strchr(id, '(')) && (s = strchr(handle, ')')) && s[1] == 0) {
-		struct prpl *proto;
-
-		*s = *handle = 0;
-		handle++;
-
-		if ((proto = find_protocol(id))) {
-			for (a = bee->accounts; a; a = a->next) {
-				if (a->prpl == proto &&
-				    a->prpl->handle_cmp(handle, a->user) == 0) {
-					ret = a;
-				}
-			}
-		}
-
-		/* Restore the string. */
-		handle--;
-		*handle = '(';
-		*s = ')';
-
-		if (ret) {
-			return ret;
-		}
-	}
-
-	if (sscanf(id, "%d", &nr) == 1 && nr < 1000) {
-		for (a = bee->accounts; a; a = a->next) {
-			if ((nr--) == 0) {
-				return(a);
-			}
-		}
-
-		return(NULL);
-	}
-
-	for (a = bee->accounts; a; a = a->next) {
-		if (g_strcasecmp(id, a->prpl->name) == 0) {
-			if (!ret) {
-				ret = a;
-			} else {
-				return(NULL);   /* We don't want to match more than one... */
-			}
-		} else if (strstr(a->user, id)) {
-			if (!ret) {
-				ret = a;
-			} else {
-				return(NULL);
-			}
-		}
-	}
-
-	return(ret);
-}
-
-account_t *account_by_tag(bee_t *bee, const char *tag)
-{
-	account_t *a;
-
-	for (a = bee->accounts; a; a = a->next) {
-		if (a->tag && g_strcasecmp(tag, a->tag) == 0) {
-			return a;
-		}
-	}
-
-	return NULL;
-}
-
-void account_del(bee_t *bee, account_t *acc)
-{
-	account_t *a, *l = NULL;
-
-	if (acc->ic) {
-		/* Caller should have checked, accounts still in use can't be deleted. */
-		return;
-	}
-
-	for (a = bee->accounts; a; a = (l = a)->next) {
-		if (a == acc) {
-			if (l) {
-				l->next = a->next;
-			} else {
-				bee->accounts = a->next;
-			}
-
-			/** FIXME
-			for( c = bee->chatrooms; c; c = nc )
-			{
-			        nc = c->next;
-			        if( acc == c->acc )
-			                chat_del( bee, c );
-			}
-			*/
-
-			while (a->set) {
-				set_del(&a->set, a->set->key);
-			}
-
-			g_hash_table_destroy(a->nicks);
-
-			g_free(a->tag);
-			g_free(a->user);
-			g_free(a->pass);
-			g_free(a->server);
-			if (a->reconnect) {     /* This prevents any reconnect still queued to happen */
-				cancel_auto_reconnect(a);
-			}
-			g_free(a);
-
-			break;
-		}
-	}
-}
-
-static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond);
-
-void account_on(bee_t *bee, account_t *a)
-{
-	if (a->ic) {
-		/* Trying to enable an already-enabled account */
-		return;
-	}
-
-	cancel_auto_reconnect(a);
-
-	a->reconnect = 0;
-	a->prpl->login(a);
-
-	if (a->ic && !(a->ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
-		a->ic->keepalive = b_timeout_add(120000, account_on_timeout, a->ic);
-	}
-}
-
-void account_off(bee_t *bee, account_t *a)
-{
-	imc_logout(a->ic, FALSE);
-	a->ic = NULL;
-	if (a->reconnect) {
-		/* Shouldn't happen */
-		cancel_auto_reconnect(a);
-	}
-}
-
-static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = d;
-
-	if (!(ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
-		imcb_error(ic, "Connection timeout");
-		imc_logout(ic, TRUE);
-	}
-
-	return FALSE;
-}
-
-struct account_reconnect_delay {
-	int start;
-	char op;
-	int step;
-	int max;
-};
-
-int account_reconnect_delay_parse(char *value, struct account_reconnect_delay *p)
-{
-	memset(p, 0, sizeof(*p));
-	/* A whole day seems like a sane "maximum maximum". */
-	p->max = 86400;
-
-	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
-	while (*value && g_ascii_isdigit(*value)) {
-		p->start = p->start * 10 + *value++ - '0';
-	}
-
-	/* Sure, call me evil for implementing my own fscanf here, but it's
-	   dead simple and I immediately know where to continue parsing. */
-
-	if (*value == 0) {
-		/* If the string ends now, the delay is constant. */
-		return 1;
-	} else if (*value != '+' && *value != '*') {
-		/* Otherwise allow either a + or a * */
-		return 0;
-	}
-
-	p->op = *value++;
-
-	/* + or * the delay by this number every time. */
-	while (*value && g_ascii_isdigit(*value)) {
-		p->step = p->step * 10 + *value++ - '0';
-	}
-
-	if (*value == 0) {
-		/* Use the default maximum (one day). */
-		return 1;
-	} else if (*value != '<') {
-		return 0;
-	}
-
-	p->max = 0;
-	value++;
-	while (*value && g_ascii_isdigit(*value)) {
-		p->max = p->max * 10 + *value++ - '0';
-	}
-
-	return p->max > 0;
-}
-
-char *set_eval_account_reconnect_delay(set_t *set, char *value)
-{
-	struct account_reconnect_delay p;
-
-	return account_reconnect_delay_parse(value, &p) ? value : SET_INVALID;
-}
-
-int account_reconnect_delay(account_t *a)
-{
-	char *setting = set_getstr(&a->bee->set, "auto_reconnect_delay");
-	struct account_reconnect_delay p;
-
-	if (account_reconnect_delay_parse(setting, &p)) {
-		if (a->auto_reconnect_delay == 0) {
-			a->auto_reconnect_delay = p.start;
-		} else if (p.op == '+') {
-			a->auto_reconnect_delay += p.step;
-		} else if (p.op == '*') {
-			a->auto_reconnect_delay *= p.step;
-		}
-
-		if (a->auto_reconnect_delay > p.max) {
-			a->auto_reconnect_delay = p.max;
-		}
-	} else {
-		a->auto_reconnect_delay = 0;
-	}
-
-	return a->auto_reconnect_delay;
-}
-
-int protocol_account_islocal(const char* protocol)
-{
-	const char** p = account_protocols_local;
-
-	do {
-		if (strcmp(*p, protocol) == 0) {
-			return 1;
-		}
-	} while (*(++p));
-	return 0;
-}
Index: protocols/account.h
===================================================================
--- protocols/account.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,76 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Account management functions                                         */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _ACCOUNT_H
-#define _ACCOUNT_H
-
-typedef struct account {
-	struct prpl *prpl;
-	char *user;
-	char *pass;
-	char *server;
-	char *tag;
-
-	int auto_connect;
-	int auto_reconnect_delay;
-	int reconnect;
-	int flags;
-
-	set_t *set;
-	GHashTable *nicks;
-
-	struct bee *bee;
-	struct im_connection *ic;
-	struct account *next;
-} account_t;
-
-account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass);
-account_t *account_get(bee_t *bee, const char *id);
-account_t *account_by_tag(bee_t *bee, const char *tag);
-void account_del(bee_t *bee, account_t *acc);
-void account_on(bee_t *bee, account_t *a);
-void account_off(bee_t *bee, account_t *a);
-
-char *set_eval_account(set_t *set, char *value);
-char *set_eval_account_reconnect_delay(set_t *set, char *value);
-int account_reconnect_delay(account_t *a);
-
-int protocol_account_islocal(const char* protocol);
-
-typedef enum {
-	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
-	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
-	ACC_SET_LOCKABLE = 0x08         /* Setting cannot be changed if the account is locked down */
-} account_set_flag_t;
-
-typedef enum {
-	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */
-	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
-	ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
-	ACC_FLAG_LOCAL = 0x08,          /* Contact list is local. */
-	ACC_FLAG_LOCKED = 0x10,         /* Account is locked (cannot be deleted, certain settings can't changed) */
-} account_flag_t;
-
-#endif
Index: protocols/bee.c
===================================================================
--- protocols/bee.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Some IM-core stuff                                                   */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-static char *set_eval_away_status(set_t *set, char *value);
-
-bee_t *bee_new()
-{
-	bee_t *b = g_new0(bee_t, 1);
-	set_t *s;
-
-	s = set_add(&b->set, "auto_connect", "true", set_eval_bool, b);
-	s = set_add(&b->set, "auto_reconnect", "true", set_eval_bool, b);
-	s = set_add(&b->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, b);
-	s = set_add(&b->set, "away", NULL, set_eval_away_status, b);
-	s->flags |= SET_NULL_OK | SET_HIDDEN;
-	s = set_add(&b->set, "debug", "false", set_eval_bool, b);
-	s = set_add(&b->set, "mobile_is_away", "false", set_eval_bool, b);
-	s = set_add(&b->set, "save_on_quit", "true", set_eval_bool, b);
-	s = set_add(&b->set, "status", NULL, set_eval_away_status, b);
-	s->flags |= SET_NULL_OK;
-	s = set_add(&b->set, "strip_html", "true", NULL, b);
-
-	b->user = g_malloc(1);
-
-	return b;
-}
-
-void bee_free(bee_t *b)
-{
-	while (b->accounts) {
-		if (b->accounts->ic) {
-			imc_logout(b->accounts->ic, FALSE);
-		} else if (b->accounts->reconnect) {
-			cancel_auto_reconnect(b->accounts);
-		}
-
-		if (b->accounts->ic == NULL) {
-			account_del(b, b->accounts);
-		} else {
-			/* Nasty hack, but account_del() doesn't work in this
-			   case and we don't want infinite loops, do we? ;-) */
-			b->accounts = b->accounts->next;
-		}
-	}
-
-	while (b->set) {
-		set_del(&b->set, b->set->key);
-	}
-
-	bee_group_free(b);
-
-	g_free(b->user);
-	g_free(b);
-}
-
-static char *set_eval_away_status(set_t *set, char *value)
-{
-	bee_t *bee = set->data;
-	account_t *a;
-
-	g_free(set->value);
-	set->value = g_strdup(value);
-
-	for (a = bee->accounts; a; a = a->next) {
-		struct im_connection *ic = a->ic;
-
-		if (ic && ic->flags & OPT_LOGGED_IN) {
-			imc_away_send_update(ic);
-		}
-	}
-
-	return value;
-}
Index: protocols/bee.h
===================================================================
--- protocols/bee.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,196 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle, save and search buddies                             */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef __BEE_H__
-#define __BEE_H__
-
-struct bee_ui_funcs;
-struct groupchat;
-
-typedef struct bee {
-	/* Settings. See set.h for how these work. The UI can add its
-	   own settings here. */
-	struct set *set;
-
-	GSList *users;  /* struct bee_user */
-	GSList *groups; /* struct bee_group */
-	struct account *accounts; /* TODO(wilmer): Use GSList here too? */
-
-	/* Symbolic, to refer to the local user (who has no real bee_user
-	   object). Not to be used by anything except so far imcb_chat_add/
-	   remove_buddy(). */
-	struct bee_user *user;
-
-	/* Fill in the callbacks for events you care about. */
-	const struct bee_ui_funcs *ui;
-
-	/* And this one will be passed to every callback for any state the
-	   UI may want to keep. */
-	void *ui_data;
-} bee_t;
-
-bee_t *bee_new();
-void bee_free(bee_t *b);
-
-/* TODO(wilmer): Kill at least the OPT_ flags that have an equivalent here. */
-typedef enum {
-	BEE_USER_ONLINE = 1,    /* Compatibility with old OPT_LOGGED_IN flag */
-	BEE_USER_AWAY = 4,      /* Compatibility with old OPT_AWAY flag */
-	BEE_USER_MOBILE = 8,    /* Compatibility with old OPT_MOBILE flag */
-	BEE_USER_LOCAL = 256,   /* Locally-added contacts (not in real contact list) */
-	BEE_USER_SPECIAL = 512, /* Denotes a user as being special */
-	BEE_USER_NOOTR = 4096,  /* Per-user version of OPT_NOOTR */
-} bee_user_flags_t;
-
-typedef struct bee_user {
-	struct im_connection *ic;
-	char *handle;
-	char *fullname;
-	char *nick;
-	struct bee_group *group;
-
-	bee_user_flags_t flags;
-	char *status;     /* NULL means available, anything else is an away state. */
-	char *status_msg; /* Status and/or away message. */
-
-	/* Set using imcb_buddy_times(). */
-	time_t login_time, idle_time;
-
-	bee_t *bee;
-	void *ui_data;
-	void *data; /* Can be used by the IM module. */
-} bee_user_t;
-
-typedef struct bee_chat_info {
-	char *title;
-	char *topic;
-} bee_chat_info_t;
-
-/* This one's mostly used so save space and make it easier (cheaper) to
-   compare groups of contacts, etc. */
-typedef struct bee_group {
-	char *key;  /* Lower case version of the name. */
-	char *name;
-} bee_group_t;
-
-typedef struct bee_ui_funcs {
-	void (*imc_connected)(struct im_connection *ic);
-	void (*imc_disconnected)(struct im_connection *ic);
-
-	gboolean (*user_new)(bee_t *bee, struct bee_user *bu);
-	gboolean (*user_free)(bee_t *bee, struct bee_user *bu);
-	/* Set the fullname first, then call this one to notify the UI. */
-	gboolean (*user_fullname)(bee_t *bee, bee_user_t *bu);
-	gboolean (*user_nick_hint)(bee_t *bee, bee_user_t *bu, const char *hint);
-	/* Notify the UI when an existing user is moved between groups. */
-	gboolean (*user_group)(bee_t *bee, bee_user_t *bu);
-	/* State info is already updated, old is provided in case the UI needs a diff. */
-	gboolean (*user_status)(bee_t *bee, struct bee_user *bu, struct bee_user *old);
-	/* On every incoming message. sent_at = 0 means unknown. */
-	gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
-	/* Flags currently defined (OPT_TYPING/THINKING) in nogaim.h. */
-	gboolean (*user_typing)(bee_t *bee, bee_user_t *bu, guint32 flags);
-	/* CTCP-like stuff (buddy action) response */
-	gboolean (*user_action_response)(bee_t *bee, bee_user_t *bu, const char *action, char * const args[],
-	                                 void *data);
-
-	/* Called at creation time. Don't show to the user until s/he is
-	   added using chat_add_user().  UI state can be stored via c->data. */
-	gboolean (*chat_new)(bee_t *bee, struct groupchat *c);
-	gboolean (*chat_free)(bee_t *bee, struct groupchat *c);
-	/* System messages of any kind. */
-	gboolean (*chat_log)(bee_t *bee, struct groupchat *c, const char *text);
-	gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
-	gboolean (*chat_add_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu);
-	gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason);
-	gboolean (*chat_topic)(bee_t *bee, struct groupchat *c, const char *new_topic, bee_user_t *bu);
-	gboolean (*chat_name_hint)(bee_t *bee, struct groupchat *c, const char *name);
-	gboolean (*chat_invite)(bee_t *bee, bee_user_t *bu, const char *name, const char *msg);
-
-	struct file_transfer* (*ft_in_start)(bee_t * bee, bee_user_t * bu, const char *file_name, size_t file_size);
-	gboolean (*ft_out_start)(struct im_connection *ic, struct file_transfer *ft);
-	void (*ft_close)(struct im_connection *ic, struct file_transfer *ft);
-	void (*ft_finished)(struct im_connection *ic, struct file_transfer *ft);
-
-	void (*log)(bee_t *bee, const char *tag, const char *msg);
-	gboolean (*user_nick_change)(bee_t *bee, bee_user_t *bu, const char *hint);
-} bee_ui_funcs_t;
-
-
-/* bee.c */
-bee_t *bee_new();
-void bee_free(bee_t *b);
-
-/* bee_user.c */
-bee_user_t *bee_user_new(bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags);
-int bee_user_free(bee_t *bee, bee_user_t *bu);
-bee_user_t *bee_user_by_handle(bee_t *bee, struct im_connection *ic, const char *handle);
-int bee_user_msg(bee_t *bee, bee_user_t *bu, const char *msg, int flags);
-bee_group_t *bee_group_by_name(bee_t *bee, const char *name, gboolean creat);
-void bee_group_free(bee_t *bee);
-
-/* Callbacks from IM modules to core: */
-/* Buddy activity */
-/* To manipulate the status of a handle.
- * - flags can be |='d with OPT_* constants. You will need at least:
- *   OPT_LOGGED_IN and OPT_AWAY.
- * - 'state' and 'message' can be NULL */
-G_MODULE_EXPORT void imcb_buddy_status(struct im_connection *ic, const char *handle, int flags, const char *state,
-                                       const char *message);
-G_MODULE_EXPORT void imcb_buddy_status_msg(struct im_connection *ic, const char *handle, const char *message);
-G_MODULE_EXPORT void imcb_buddy_times(struct im_connection *ic, const char *handle, time_t login, time_t idle);
-/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
-G_MODULE_EXPORT void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags,
-                                    time_t sent_at);
-G_MODULE_EXPORT void imcb_notify_email(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-
-/* bee_chat.c */
-/* These two functions are to create a group chat.
- * - imcb_chat_new(): the 'handle' parameter identifies the chat, like the
- *   channel name on IRC.
- * - After you have a groupchat pointer, you should add the handles, finally
- *   the user her/himself. At that point the group chat will be visible to the
- *   user, too. */
-G_MODULE_EXPORT struct groupchat *imcb_chat_new(struct im_connection *ic, const char *handle);
-G_MODULE_EXPORT void imcb_chat_name_hint(struct groupchat *c, const char *name);
-G_MODULE_EXPORT void imcb_chat_free(struct groupchat *c);
-/* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
-G_MODULE_EXPORT void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at);
-/* System messages specific to a groupchat, so they can be displayed in the right context. */
-G_MODULE_EXPORT void imcb_chat_log(struct groupchat *c, char *format, ...);
-/* To tell BitlBee 'who' changed the topic of 'c' to 'topic'. */
-G_MODULE_EXPORT void imcb_chat_topic(struct groupchat *c, char *who, char *topic, time_t set_at);
-G_MODULE_EXPORT void imcb_chat_add_buddy(struct groupchat *c, const char *handle);
-/* To remove a handle from a group chat. Reason can be NULL. */
-G_MODULE_EXPORT void imcb_chat_remove_buddy(struct groupchat *c, const char *handle, const char *reason);
-G_MODULE_EXPORT int bee_chat_msg(bee_t *bee, struct groupchat *c, const char *msg, int flags);
-G_MODULE_EXPORT struct groupchat *bee_chat_by_title(bee_t *bee, struct im_connection *ic, const char *title);
-G_MODULE_EXPORT void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg);
-
-G_GNUC_DEPRECATED G_MODULE_EXPORT void bee_chat_list_finish(struct im_connection *ic);
-G_MODULE_EXPORT void imcb_chat_list_finish(struct im_connection *ic);
-G_MODULE_EXPORT void imcb_chat_list_free(struct im_connection *ic);
-
-#endif /* __BEE_H__ */
Index: protocols/bee_chat.c
===================================================================
--- protocols/bee_chat.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,303 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle rooms                                                */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-struct groupchat *imcb_chat_new(struct im_connection *ic, const char *handle)
-{
-	struct groupchat *c = g_new0(struct groupchat, 1);
-	bee_t *bee = ic->bee;
-
-	/* This one just creates the conversation structure, user won't see
-	   anything yet until s/he is joined to the conversation. (This
-	   allows you to add other already present participants first.) */
-
-	ic->groupchats = g_slist_prepend(ic->groupchats, c);
-	c->ic = ic;
-	c->title = g_strdup(handle);
-	c->topic = g_strdup_printf(
-	        "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
-	        c->title);
-
-	if (set_getbool(&ic->bee->set, "debug")) {
-		imcb_log(ic, "Creating new conversation: (id=%p,handle=%s)", c, handle);
-	}
-
-	if (bee->ui->chat_new) {
-		bee->ui->chat_new(bee, c);
-	}
-
-	return c;
-}
-
-void imcb_chat_name_hint(struct groupchat *c, const char *name)
-{
-	bee_t *bee = c->ic->bee;
-
-	if (bee->ui->chat_name_hint) {
-		bee->ui->chat_name_hint(bee, c, name);
-	}
-}
-
-void imcb_chat_free(struct groupchat *c)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	GList *ir;
-
-	if (bee->ui->chat_free) {
-		bee->ui->chat_free(bee, c);
-	}
-
-	if (set_getbool(&ic->bee->set, "debug")) {
-		imcb_log(ic, "You were removed from conversation %p", c);
-	}
-
-	ic->groupchats = g_slist_remove(ic->groupchats, c);
-
-	for (ir = c->in_room; ir; ir = ir->next) {
-		g_free(ir->data);
-	}
-	g_list_free(c->in_room);
-	g_free(c->title);
-	g_free(c->topic);
-	g_free(c);
-}
-
-static gboolean handle_is_self(struct im_connection *ic, const char *handle)
-{
-	return (ic->acc->prpl->handle_is_self) ?
-	       ic->acc->prpl->handle_is_self(ic, handle) :
-	       (ic->acc->prpl->handle_cmp(ic->acc->user, handle) == 0);
-}
-
-void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-	gboolean temp = FALSE;
-	char *s;
-
-	if (handle_is_self(ic, who) && !(flags & OPT_SELFMESSAGE)) {
-		return;
-	}
-
-	bu = bee_user_by_handle(bee, ic, who);
-	temp = (bu == NULL);
-
-	if (temp) {
-		bu = bee_user_new(bee, ic, who, BEE_USER_ONLINE);
-	}
-
-	s = set_getstr(&ic->bee->set, "strip_html");
-	if ((g_strcasecmp(s, "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && s)) {
-		strip_html(msg);
-	}
-
-	if (bee->ui->chat_msg) {
-		bee->ui->chat_msg(bee, c, bu, msg, flags, sent_at);
-	}
-
-	if (temp) {
-		bee_user_free(bee, bu);
-	}
-}
-
-void imcb_chat_log(struct groupchat *c, char *format, ...)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	va_list params;
-	char *text;
-
-	if (!bee->ui->chat_log) {
-		return;
-	}
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	bee->ui->chat_log(bee, c, text);
-	g_free(text);
-}
-
-void imcb_chat_topic(struct groupchat *c, char *who, char *topic, time_t set_at)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	if (!bee->ui->chat_topic) {
-		return;
-	}
-
-	if (who == NULL) {
-		bu = NULL;
-	} else if (handle_is_self(ic, who)) {
-		bu = bee->user;
-	} else {
-		bu = bee_user_by_handle(bee, ic, who);
-	}
-
-	if ((g_strcasecmp(set_getstr(&ic->bee->set, "strip_html"), "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html"))) {
-		strip_html(topic);
-	}
-
-	bee->ui->chat_topic(bee, c, topic, bu);
-}
-
-void imcb_chat_add_buddy(struct groupchat *c, const char *handle)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-	gboolean me;
-
-	if (set_getbool(&c->ic->bee->set, "debug")) {
-		imcb_log(c->ic, "User %s added to conversation %p", handle, c);
-	}
-
-	me = handle_is_self(ic, handle);
-
-	/* Most protocols allow people to join, even when they're not in
-	   your contact list. Try to handle that here */
-	if (!me && !bu) {
-		bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-	}
-
-	/* Add the handle to the room userlist */
-	/* TODO: Use bu instead of a string */
-	c->in_room = g_list_append(c->in_room, g_strdup(handle));
-
-	if (bee->ui->chat_add_user) {
-		bee->ui->chat_add_user(bee, c, me ? bee->user : bu);
-	}
-
-	if (me) {
-		c->joined = 1;
-	}
-}
-
-void imcb_chat_remove_buddy(struct groupchat *c, const char *handle, const char *reason)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = NULL;
-
-	if (set_getbool(&bee->set, "debug")) {
-		imcb_log(ic, "User %s removed from conversation %p (%s)", handle, c, reason ? reason : "");
-	}
-
-	/* It might be yourself! */
-	if (handle_is_self(ic, handle)) {
-		if (c->joined == 0) {
-			return;
-		}
-
-		bu = bee->user;
-		c->joined = 0;
-	} else {
-		bu = bee_user_by_handle(bee, ic, handle);
-	}
-
-	if (bee->ui->chat_remove_user && bu) {
-		bee->ui->chat_remove_user(bee, c, bu, reason);
-	}
-}
-
-int bee_chat_msg(bee_t *bee, struct groupchat *c, const char *msg, int flags)
-{
-	struct im_connection *ic = c->ic;
-	char *buf = NULL;
-
-	if ((ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	} else {
-		buf = g_strdup(msg);
-	}
-
-	ic->acc->prpl->chat_msg(c, buf, flags);
-	g_free(buf);
-
-	return 1;
-}
-
-struct groupchat *bee_chat_by_title(bee_t *bee, struct im_connection *ic, const char *title)
-{
-	struct groupchat *c;
-	GSList *l;
-
-	for (l = ic->groupchats; l; l = l->next) {
-		c = l->data;
-		if (strcmp(c->title, title) == 0) {
-			return c;
-		}
-	}
-
-	return NULL;
-}
-
-void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg)
-{
-	bee_user_t *bu = bee_user_by_handle(ic->bee, ic, who);
-
-	if (bu && ic->bee->ui->chat_invite) {
-		ic->bee->ui->chat_invite(ic->bee, bu, name, msg);
-	}
-}
-
-void imcb_chat_list_finish(struct im_connection *ic)
-{
-	cmd_chat_list_finish(ic);
-}
-
-void bee_chat_list_finish(struct im_connection *ic)
-{
-	imcb_log(ic, "Warning: using deprecated bee_chat_list_finish. This will be removed in the stable release.");
-	imcb_chat_list_finish(ic);
-}
-
-void imcb_chat_list_free(struct im_connection *ic)
-{
-	bee_chat_info_t *ci;
-	GSList *l = ic->chatlist;
-
-	while (l) {
-		ci = l->data;
-		l = g_slist_delete_link(l, l);
-
-		g_free(ci->title);
-		g_free(ci->topic);
-		g_free(ci);
-	}
-
-	ic->chatlist = NULL;
-}
Index: protocols/bee_ft.c
===================================================================
--- protocols/bee_ft.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>             *
-\********************************************************************/
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "ft.h"
-
-file_transfer_t *imcb_file_send_start(struct im_connection *ic, char *handle, char *file_name, size_t file_size)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (bee->ui->ft_in_start && bu) {
-		return bee->ui->ft_in_start(bee, bu, file_name, file_size);
-	} else {
-		return NULL;
-	}
-}
-
-gboolean imcb_file_recv_start(struct im_connection *ic, file_transfer_t *ft)
-{
-	bee_t *bee = ic->bee;
-
-	if (bee->ui->ft_out_start) {
-		return bee->ui->ft_out_start(ic, ft);
-	} else {
-		return FALSE;
-	}
-}
-
-void imcb_file_canceled(struct im_connection *ic, file_transfer_t *file, char *reason)
-{
-	bee_t *bee = ic->bee;
-
-	if (file->canceled) {
-		file->canceled(file, reason);
-	}
-
-	if (bee->ui->ft_close) {
-		bee->ui->ft_close(ic, file);
-	}
-}
-
-void imcb_file_finished(struct im_connection *ic, file_transfer_t *file)
-{
-	bee_t *bee = ic->bee;
-
-	if (bee->ui->ft_finished) {
-		bee->ui->ft_finished(ic, file);
-	}
-}
Index: protocols/bee_user.c
===================================================================
--- protocols/bee_user.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,332 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle, save and search buddies                             */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-bee_user_t *bee_user_new(bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags)
-{
-	bee_user_t *bu;
-
-	if (bee_user_by_handle(bee, ic, handle) != NULL) {
-		return NULL;
-	}
-
-	bu = g_new0(bee_user_t, 1);
-	bu->bee = bee;
-	bu->ic = ic;
-	bu->flags = flags;
-	bu->handle = g_strdup(handle);
-	bee->users = g_slist_prepend(bee->users, bu);
-
-	if (ic->bee_users) {
-		g_hash_table_insert(ic->bee_users, bu->handle, bu);
-	}
-	if (bee->ui->user_new) {
-		bee->ui->user_new(bee, bu);
-	}
-	if (ic->acc->prpl->buddy_data_add) {
-		ic->acc->prpl->buddy_data_add(bu);
-	}
-
-	/* Offline by default. This will set the right flags. */
-	imcb_buddy_status(ic, handle, 0, NULL, NULL);
-
-	return bu;
-}
-
-int bee_user_free(bee_t *bee, bee_user_t *bu)
-{
-	if (!bu) {
-		return 0;
-	}
-
-	if (bee->ui->user_free) {
-		bee->ui->user_free(bee, bu);
-	}
-	if (bu->ic->acc->prpl->buddy_data_free) {
-		bu->ic->acc->prpl->buddy_data_free(bu);
-	}
-	if (bu->ic->bee_users) {
-		g_hash_table_remove(bu->ic->bee_users, bu->handle);
-	}
-	bee->users = g_slist_remove(bee->users, bu);
-
-	g_free(bu->handle);
-	g_free(bu->fullname);
-	g_free(bu->nick);
-	g_free(bu->status);
-	g_free(bu->status_msg);
-	g_free(bu);
-
-	return 1;
-}
-
-bee_user_t *bee_user_by_handle_slow(bee_t *bee, struct im_connection *ic, const char *handle)
-{
-	GSList *l;
-
-	for (l = bee->users; l; l = l->next) {
-		bee_user_t *bu = l->data;
-
-		if (bu->ic == ic && ic->acc->prpl->handle_cmp(bu->handle, handle) == 0) {
-			return bu;
-		}
-	}
-
-	return NULL;
-}
-
-bee_user_t *bee_user_by_handle(bee_t *bee, struct im_connection *ic, const char *handle)
-{
-	if (!ic->bee_users) {
-		return bee_user_by_handle_slow(bee, ic, handle);
-	}
-
-	return g_hash_table_lookup(ic->bee_users, handle);
-}
-
-int bee_user_msg(bee_t *bee, bee_user_t *bu, const char *msg, int flags)
-{
-	char *buf = NULL;
-	int st;
-
-	if ((bu->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	} else {
-		buf = g_strdup(msg);
-	}
-
-	st = bu->ic->acc->prpl->buddy_msg(bu->ic, bu->handle, buf, flags);
-	g_free(buf);
-
-	return st;
-}
-
-
-/* Groups */
-static bee_group_t *bee_group_new(bee_t *bee, const char *name)
-{
-	bee_group_t *bg = g_new0(bee_group_t, 1);
-
-	bg->name = g_strdup(name);
-	bg->key = g_utf8_casefold(name, -1);
-	bee->groups = g_slist_prepend(bee->groups, bg);
-
-	return bg;
-}
-
-bee_group_t *bee_group_by_name(bee_t *bee, const char *name, gboolean creat)
-{
-	GSList *l;
-	char *key;
-
-	if (name == NULL) {
-		return NULL;
-	}
-
-	key = g_utf8_casefold(name, -1);
-	for (l = bee->groups; l; l = l->next) {
-		bee_group_t *bg = l->data;
-		if (strcmp(bg->key, key) == 0) {
-			break;
-		}
-	}
-	g_free(key);
-
-	if (!l) {
-		return creat ? bee_group_new(bee, name) : NULL;
-	} else {
-		return l->data;
-	}
-}
-
-void bee_group_free(bee_t *bee)
-{
-	while (bee->groups) {
-		bee_group_t *bg = bee->groups->data;
-		g_free(bg->name);
-		g_free(bg->key);
-		g_free(bg);
-		bee->groups = g_slist_remove(bee->groups, bee->groups->data);
-	}
-}
-
-
-/* IM->UI callbacks */
-void imcb_buddy_status(struct im_connection *ic, const char *handle, int flags, const char *state, const char *message)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu, *old;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		char *h = set_getstr(&ic->acc->set, "handle_unknown") ? :
-		          set_getstr(&ic->bee->set, "handle_unknown");
-
-		if (g_strncasecmp(h, "add", 3) == 0) {
-			bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-		} else {
-			if (g_strcasecmp(h, "ignore") != 0) {
-				imcb_log(ic, "imcb_buddy_status() for unknown handle %s:\n"
-				         "flags = %d, state = %s, message = %s", handle, flags,
-				         state ? state : "NULL", message ? message : "NULL");
-			}
-
-			return;
-		}
-	}
-
-	/* May be nice to give the UI something to compare against. */
-	old = g_memdup2(bu, sizeof(bee_user_t));
-
-	/* TODO(wilmer): OPT_AWAY, or just state == NULL ? */
-	bu->flags = flags;
-	bu->status_msg = g_strdup(message);
-	if (state && *state) {
-		bu->status = g_strdup(state);
-	} else if (flags & OPT_AWAY) {
-		bu->status = g_strdup("Away");
-	} else {
-		bu->status = NULL;
-	}
-
-	if (bu->status == NULL && (flags & OPT_MOBILE) &&
-	    set_getbool(&bee->set, "mobile_is_away")) {
-		bu->flags |= BEE_USER_AWAY;
-		bu->status = g_strdup("Mobile");
-	}
-
-	if (bee->ui->user_status) {
-		bee->ui->user_status(bee, bu, old);
-	}
-
-	g_free(old->status_msg);
-	g_free(old->status);
-	g_free(old);
-}
-
-/* Same, but only change the away/status message, not any away/online state info. */
-void imcb_buddy_status_msg(struct im_connection *ic, const char *handle, const char *message)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu, *old;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		return;
-	}
-
-	old = g_memdup2(bu, sizeof(bee_user_t));
-
-	bu->status_msg = message && *message ? g_strdup(message) : NULL;
-
-	if (bee->ui->user_status) {
-		bee->ui->user_status(bee, bu, old);
-	}
-
-	g_free(old->status_msg);
-	g_free(old);
-}
-
-void imcb_buddy_times(struct im_connection *ic, const char *handle, time_t login, time_t idle)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		return;
-	}
-
-	bu->login_time = login;
-	bu->idle_time = idle;
-}
-
-void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags, time_t sent_at)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu && !(ic->flags & OPT_LOGGING_OUT)) {
-		char *h = set_getstr(&ic->acc->set, "handle_unknown") ? :
-		          set_getstr(&ic->bee->set, "handle_unknown");
-
-		if (g_strcasecmp(h, "ignore") == 0) {
-			return;
-		} else if (g_strncasecmp(h, "add", 3) == 0) {
-			bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-		}
-	}
-
-	if (bee->ui->user_msg && bu) {
-		bee->ui->user_msg(bee, bu, msg, flags, sent_at);
-	} else {
-		imcb_log(ic, "Message from unknown handle %s:\n%s", handle, msg);
-	}
-}
-
-void imcb_notify_email(struct im_connection *ic, char *format, ...)
-{
-	const char *handle;
-	va_list params;
-	char *msg;
-
-	if (!set_getbool(&ic->acc->set, "mail_notifications")) {
-		return;
-	}
-
-	va_start(params, format);
-	msg = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	/* up to the protocol to set_add this if they want to use this */
-	handle = set_getstr(&ic->acc->set, "mail_notifications_handle");
-
-	if (handle != NULL) {
-		imcb_buddy_msg(ic, handle, msg, 0, 0);
-	} else {
-		imcb_log(ic, "%s", msg);
-	}
-
-	g_free(msg);
-}
-
-void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags)
-{
-	bee_user_t *bu;
-
-	if (ic->bee->ui->user_typing &&
-	    (bu = bee_user_by_handle(ic->bee, ic, handle))) {
-		ic->bee->ui->user_typing(ic->bee, bu, flags);
-	}
-}
-
-void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data)
-{
-	if (bu->bee->ui->user_action_response) {
-		bu->bee->ui->user_action_response(bu->bee, bu, action, args, data);
-	}
-}
Index: protocols/ft.h
===================================================================
--- protocols/ft.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,176 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2006 Marijn Kruisselbrink and others                     *
-\********************************************************************/
-
-/* Generic file transfer header                                     */
-
-/*
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _FT_H
-#define _FT_H
-
-/*
- * One buffer is needed for each transfer. The receiver stores a message
- * in it and gives it to the sender. The sender will stall the receiver
- * till the buffer has been sent out.
- */
-#define FT_BUFFER_SIZE 2048
-
-typedef enum {
-	FT_STATUS_LISTENING     = 1,
-	FT_STATUS_TRANSFERRING  = 2,
-	FT_STATUS_FINISHED      = 4,
-	FT_STATUS_CANCELED      = 8,
-	FT_STATUS_CONNECTING    = 16
-} file_status_t;
-
-/*
- * This structure holds all irc specific information regarding an incoming (from the point of view of
- * the irc client) file transfer. New instances of this struct should only be created by calling the
- * imcb_file_send_start() method, which will initialize most of the fields. The data field and the various
- * methods are zero-initialized. Instances will automatically be deleted once the transfer is completed,
- * canceled, or the connection to the irc client has been lost (note that also if only the irc connection
- * and not the file transfer connection is lost, the file transfer will still be canceled and freed).
- *
- * The following (poor ascii-art) diagram illustrates what methods are called for which status-changes:
- *
- *	                        /-----------\                    /----------\
- *	               -------> | LISTENING | -----------------> | CANCELED |
- *	                        \-----------/  [canceled,]free   \----------/
- *	                              |
- *	                              | accept
- *	                              V
- *	               /------ /-------------\                    /--------------------------\
- *	   out_of_data |       | TRANSFERRING | -----------------> | TRANSFERRING | CANCELED |
- *	               \-----> \-------------/  [canceled,]free   \--------------------------/
- *	                              |
- *	                              | finished,free
- *	                              V
- *	                 /-------------------------\
- *	                 | TRANSFERRING | FINISHED |
- *	                 \-------------------------/
- */
-typedef struct file_transfer {
-
-	/* Are we sending something? */
-	int sending;
-
-	/*
-	 * The current status of this file transfer.
-	 */
-	file_status_t status;
-
-	/*
-	 * file size
-	 */
-	size_t file_size;
-
-	/*
-	 * Number of bytes that have been successfully transferred.
-	 */
-	size_t bytes_transferred;
-
-	/*
-	 * Time started. Used to calculate kb/s.
-	 */
-	time_t started;
-
-	/*
-	 * file name
-	 */
-	char *file_name;
-
-	/*
-	 * A unique local ID for this file transfer.
-	 */
-	unsigned int local_id;
-
-	/*
-	 * IM-protocol specific data associated with this file transfer.
-	 */
-	gpointer data;
-	struct im_connection *ic;
-
-	/*
-	 * Private data.
-	 */
-	gpointer priv;
-
-	/*
-	 * If set, called after successful connection setup.
-	 */
-	void (*accept)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is canceled or finished.
-	 * Subsequently, this structure will be freed.
-	 *
-	 */
-	void (*free)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is finished and successful.
-	 */
-	void (*finished)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is canceled.
-	 * ( canceled either by the transfer implementation or by
-	 *  a call to imcb_file_canceled )
-	 */
-	void (*canceled)(struct file_transfer *file, char *reason);
-
-	/*
-	 * called by the sending side to indicate that it is writable.
-	 * The callee should check if data is available and call the
-	 * function(as seen below) if that is the case.
-	 */
-	gboolean (*write_request)(struct file_transfer *file);
-
-	/*
-	 * When sending files, protocols register this function to receive data.
-	 * This should only be called once after write_request is called. The caller
-	 * should not read more data until write_request is called again. This technique
-	 * avoids buffering.
-	 */
-	gboolean (*write)(struct file_transfer *file, char *buffer, unsigned int len);
-
-	/* The send buffer associated with this transfer.
-	 * Since receivers always wait for a write_request call one is enough.
-	 */
-	char buffer[FT_BUFFER_SIZE];
-
-} file_transfer_t;
-
-/*
- * This starts a file transfer from bitlbee to the user.
- */
-file_transfer_t *imcb_file_send_start(struct im_connection *ic, char *user_nick, char *file_name, size_t file_size);
-
-/*
- * This should be called by a protocol when the transfer is canceled. Note that
- * the canceled() and free() callbacks given in file will be called by this function.
- */
-void imcb_file_canceled(struct im_connection *ic, file_transfer_t *file, char *reason);
-
-gboolean imcb_file_recv_start(struct im_connection *ic, file_transfer_t *ft);
-
-void imcb_file_finished(struct im_connection *ic, file_transfer_t *file);
-#endif
Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/jabber/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,19 +8,13 @@
 
 -include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/jabber/
-endif
 
 # [SH] Program variables
-objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o hipchat.o
+objects = expat.o hashtable.o jid.o jpacket.o jutil.o log.o pool.o str.o xmlnode.o xmlparse.o xmlrole.o xmltok.o jabber.o
 
+CFLAGS += -Wall
 LFLAGS += -r
 
 # [SH] Phony targets
-all: jabber_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
+all: jabberr.o
 
 .PHONY: all clean distclean
@@ -30,5 +24,4 @@
 
 distclean: clean
-	rm -rf .depend
 
 ### MAIN PROGRAM
@@ -36,11 +29,9 @@
 $(objects): ../../Makefile.settings Makefile
 
-$(objects): %.o: $(_SRCDIR_)%.c
+$(objects): %.o: %.c
 	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
+	@$(CC) -c $(CFLAGS) $< -o $@
 
-jabber_mod.o: $(objects)
-	@echo '*' Linking jabber_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o jabber_mod.o
-
--include .depend/*.d
+jabberr.o: $(objects)
+	@echo '*' Linking jabberr.o
+	@$(LD) $(LFLAGS) $(objects) -o jabberr.o
Index: protocols/jabber/asciitab.h
===================================================================
--- protocols/jabber/asciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/asciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,62 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
Index: protocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,529 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Conference rooms                                         *
-*                                                                           *
-*  Copyright 2007-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "sha1.h"
-
-static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   gboolean always_use_nicks)
-{
-	struct jabber_chat *jc;
-	struct xt_node *node;
-	struct groupchat *c;
-	char *roomjid;
-
-	roomjid = g_strdup_printf("%s/%s", room, nick);
-	node = xt_new_node("x", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_MUC);
-	if (password) {
-		xt_add_child(node, xt_new_node("password", password, NULL));
-	}
-	node = jabber_make_packet("presence", NULL, roomjid, node);
-	jabber_cache_add(ic, node, jabber_chat_join_failed);
-
-	if (!jabber_write_packet(ic, node)) {
-		g_free(roomjid);
-		return NULL;
-	}
-
-	jc = g_new0(struct jabber_chat, 1);
-	jc->name = jabber_normalize(room);
-
-	if ((jc->me = jabber_buddy_add(ic, roomjid)) == NULL) {
-		g_free(roomjid);
-		g_free(jc->name);
-		g_free(jc);
-		return NULL;
-	}
-
-	if (always_use_nicks) {
-		jc->flags = JCFLAG_ALWAYS_USE_NICKS;
-	}
-
-	/* roomjid isn't normalized yet, and we need an original version
-	   of the nick to send a proper presence update. */
-	jc->my_full_jid = roomjid;
-
-	c = imcb_chat_new(ic, room);
-	c->data = jc;
-
-	return c;
-}
-
-struct groupchat *jabber_chat_with(struct im_connection *ic, char *who)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_chat *jc;
-	struct groupchat *c;
-	GChecksum *sum;
-	double now = gettime();
-	char *uuid, *rjid, *cserv;
-
-	sum = g_checksum_new(G_CHECKSUM_SHA1);
-	g_checksum_update(sum, (uint8_t *) ic->acc->user, strlen(ic->acc->user));
-	g_checksum_update(sum, (uint8_t *) &now, sizeof(now));
-	g_checksum_update(sum, (uint8_t *) who, strlen(who));
-	uuid = sha1_random_uuid(&sum);
-
-	if (jd->flags & JFLAG_GTALK) {
-		cserv = g_strdup("groupchat.google.com");
-	} else {
-		/* Guess... */
-		cserv = g_strdup_printf("conference.%s", jd->server);
-	}
-
-	rjid = g_strdup_printf("private-chat-%s@%s", uuid, cserv);
-	g_free(uuid);
-	g_free(cserv);
-
-	c = jabber_chat_join(ic, rjid, jd->username, NULL, FALSE);
-	g_free(rjid);
-	if (c == NULL) {
-		return NULL;
-	}
-
-	jc = c->data;
-	jc->invite = g_strdup(who);
-
-	return c;
-}
-
-static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_error *err;
-	struct jabber_buddy *bud;
-	char *room;
-
-	room = xt_find_attr(orig, "to");
-	bud = jabber_buddy_by_jid(ic, room, 0);
-	err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
-	if (err) {
-		imcb_error(ic, "Error joining groupchat %s: %s%s%s", room, err->code,
-		           err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-	}
-	if (bud) {
-		struct groupchat *c = jabber_chat_by_jid(ic, bud->bare_jid);
-		if (c) {
-			jabber_chat_free(c);
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	/* This is a self message sent by this bitlbee - just drop it */
-	return XT_ABORT;
-}
-
-struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name)
-{
-	char *normalized = jabber_normalize(name);
-	GSList *l;
-	struct groupchat *ret;
-	struct jabber_chat *jc;
-
-	for (l = ic->groupchats; l; l = l->next) {
-		ret = l->data;
-		jc = ret->data;
-		if (strcmp(normalized, jc->name) == 0) {
-			break;
-		}
-	}
-	g_free(normalized);
-
-	return l ? ret : NULL;
-}
-
-void jabber_chat_free(struct groupchat *c)
-{
-	struct jabber_chat *jc = c->data;
-
-	jabber_buddy_remove_bare(c->ic, jc->name);
-
-	g_free(jc->last_sent_message);
-	g_free(jc->my_full_jid);
-	g_free(jc->name);
-	g_free(jc->invite);
-	g_free(jc);
-
-	imcb_chat_free(c);
-}
-
-int jabber_chat_msg(struct groupchat *c, char *message, int flags)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	jc->flags |= JCFLAG_MESSAGE_SENT;
-
-	node = xt_new_node("body", message, NULL);
-	node = jabber_make_packet("message", "groupchat", jc->name, node);
-
-	jabber_cache_add(ic, node, jabber_chat_self_message);
-
-	g_free(jc->last_sent_message);
-	jc->last_sent_message = g_strdup(message);
-
-	return !jabber_write_packet(ic, node);
-}
-
-int jabber_chat_topic(struct groupchat *c, char *topic)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	node = xt_new_node("subject", topic, NULL);
-	node = jabber_make_packet("message", "groupchat", jc->name, node);
-
-	if (!jabber_write_packet(ic, node)) {
-		xt_free_node(node);
-		return 0;
-	}
-	xt_free_node(node);
-
-	return 1;
-}
-
-int jabber_chat_leave(struct groupchat *c, const char *reason)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	node = xt_new_node("x", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_MUC);
-	node = jabber_make_packet("presence", "unavailable", jc->my_full_jid, node);
-
-	if (!jabber_write_packet(ic, node)) {
-		xt_free_node(node);
-		return 0;
-	}
-	xt_free_node(node);
-
-	return 1;
-}
-
-void jabber_chat_invite(struct groupchat *c, char *who, char *message)
-{
-	struct xt_node *node;
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-
-	node = xt_new_node("reason", message, NULL);
-
-	node = xt_new_node("invite", NULL, node);
-	xt_add_attr(node, "to", who);
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_MUC_USER);
-
-	node = jabber_make_packet("message", NULL, jc->name, node);
-
-	jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-}
-
-static int jabber_chat_has_other_resources(struct im_connection *ic, struct jabber_buddy *bud)
-{
-	struct jabber_buddy *cur;
-
-	for (cur = jabber_buddy_by_jid(ic, bud->bare_jid, GET_BUDDY_FIRST); cur; cur = cur->next) {
-		if (cur != bud && jabber_compare_jid(cur->ext_jid, bud->ext_jid)) {
-			return TRUE;
-		}
-	}
-	
-	return FALSE;
-}
-
-/* Not really the same syntax as the normal pkt_ functions, but this isn't
-   called by the xmltree parser directly and this way I can add some extra
-   parameters so we won't have to repeat too many things done by the caller
-   already. */
-void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node)
-{
-	struct groupchat *chat;
-	struct xt_node *c;
-	char *type = xt_find_attr(node, "type");
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_chat *jc;
-	char *s;
-
-	if ((chat = jabber_chat_by_jid(ic, bud->bare_jid)) == NULL) {
-		/* How could this happen?? We could do kill( self, 11 )
-		   now or just wait for the OS to do it. :-) */
-		return;
-	}
-
-	jc = chat->data;
-
-	if (type == NULL && !(bud->flags & JBFLAG_IS_CHATROOM)) {
-		bud->flags |= JBFLAG_IS_CHATROOM;
-		/* If this one wasn't set yet, this buddy just joined the chat.
-		   Slightly hackish way of finding out eh? ;-) */
-
-		/* This is pretty messy... Here it sets ext_jid to the real
-		   JID of the participant. Works for non-anonymized channels.
-		   Might break if someone joins a chat twice, though. */
-		for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) {
-			if ((s = xt_find_attr(c, "xmlns")) &&
-			    (strcmp(s, XMLNS_MUC_USER) == 0)) {
-				struct xt_node *item;
-
-				item = xt_find_node(c->children, "item");
-				if ((s = xt_find_attr(item, "jid"))) {
-					/* Yay, found what we need. :-) */
-					bud->ext_jid = jabber_normalize(s);
-					break;
-				}
-			}
-		}
-
-		/* Make up some other handle, if necessary. */
-		if (bud->ext_jid == NULL) {
-			if (bud == jc->me) {
-				bud->ext_jid = g_strdup(jd->me);
-			} else {
-				int i;
-
-				/* Don't want the nick to be at the end, so let's
-				   think of some slightly different notation to use
-				   for anonymous groupchat participants in BitlBee. */
-				bud->ext_jid = g_strdup_printf("%s=%s", bud->resource, bud->bare_jid);
-
-				/* And strip any unwanted characters. */
-				for (i = 0; bud->resource[i]; i++) {
-					if (bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@') {
-						bud->ext_jid[i] = '_';
-					}
-				}
-			}
-			bud->flags |= JBFLAG_IS_ANONYMOUS;
-		} else if (bud == jc->me) {
-			g_free(bud->ext_jid);
-			bud->ext_jid = g_strdup(jd->me);
-		}
-
-		if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) {
-			/* If JIDs are anonymized, add them to the local
-			   list for the duration of this chat. */
-			imcb_add_buddy(ic, bud->ext_jid, NULL);
-			imcb_buddy_nick_hint(ic, bud->ext_jid, bud->resource);
-		}
-
-		if (bud == jc->me && jc->invite != NULL) {
-			char *msg = g_strdup_printf("Please join me in room %s", jc->name);
-			jabber_chat_invite(chat, jc->invite, msg);
-			g_free(jc->invite);
-			g_free(msg);
-			jc->invite = NULL;
-		}
-
-		s = strchr(bud->ext_jid, '/');
-		if (s) {
-			*s = 0; /* Should NEVER be NULL, but who knows... */
-		}
-
-		imcb_chat_add_buddy(chat, bud->ext_jid);
-
-		if (bud != jc->me && (jc->flags & JCFLAG_ALWAYS_USE_NICKS) && !(bud->flags & JBFLAG_IS_ANONYMOUS)) {
-			imcb_buddy_nick_change(ic, bud->ext_jid, bud->resource);
-		}
-
-		if (s) {
-			*s = '/';
-		}
-	} else if (type) { /* type can only be NULL or "unavailable" in this function */
-		if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid && !jabber_chat_has_other_resources(ic, bud)) {
-			char *reason = NULL;
-			char *status = NULL;
-			char *status_text = NULL;
-			
-			if ((c = xt_find_node_by_attr(node->children, "x", "xmlns", XMLNS_MUC_USER))) {
-				struct xt_node *c2 = c->children;
-
-				while ((c2 = xt_find_node(c2, "status"))) {
-					char *code = xt_find_attr(c2, "code");
-					if (g_strcmp0(code, "301") == 0) {
-						status = "Banned";
-						break;
-					} else if (g_strcmp0(code, "303") == 0) {
-						/* This could be handled in a cleverer way,
-						 * but let's just show a literal part/join for now */
-						status = "Changing nicks";
-						break;
-					} else if (g_strcmp0(code, "307") == 0) {
-						status = "Kicked";
-						break;
-					}
-					c2 = c2->next;
-				}
-
-				/* Sometimes the status message is in presence/x/item/reason */
-				if ((c2 = xt_find_path(c, "item/reason")) && c2->text && c2->text_len) {
-					status_text = c2->text;
-				}
-			}
-
-			/* Sometimes the status message is right inside <presence> */
-			if ((c = xt_find_node(node->children, "status")) && c->text && c->text_len) {
-				status_text = c->text;
-			}
-
-			if (status_text && status) {
-				reason = g_strdup_printf("%s: %s", status, status_text);
-			} else {
-				reason = g_strdup(status_text ? : status);
-			}
-
-			s = strchr(bud->ext_jid, '/');
-			if (s) {
-				*s = 0;
-			}
-			imcb_chat_remove_buddy(chat, bud->ext_jid, reason);
-			if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) {
-				imcb_remove_buddy(ic, bud->ext_jid, reason);
-			}
-			if (s) {
-				*s = '/';
-			}
-
-			g_free(reason);
-		}
-
-		if (bud == jc->me) {
-			jabber_chat_free(chat);
-		}
-	}
-}
-
-void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node)
-{
-	struct xt_node *subject = xt_find_node(node->children, "subject");
-	struct xt_node *body = xt_find_node(node->children, "body");
-	struct groupchat *chat = NULL;
-	struct jabber_chat *jc = NULL;
-	char *from = NULL;
-	char *nick = NULL;
-	char *final_from = NULL;
-	char *bare_jid = NULL;
-	guint32 flags = 0;
-
-	from = (bud) ? bud->full_jid : xt_find_attr(node, "from");
-
-	if (from) {
-		nick = strchr(from, '/');
-		if (nick) {
-			*nick = 0;
-		}
-		chat = jabber_chat_by_jid(ic, from);
-		if (nick) {
-			*nick = '/';
-			nick++;
-		}
-	}
-
-	jc = (chat) ? chat->data : NULL;
-
-	if (!bud) {
-		struct xt_node *c;
-		char *s;
-
-		/* Try some clever stuff to find out the real JID here */
-		c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY);
-
-		if (c && ((s = xt_find_attr(c, "from")) ||
-		          (s = xt_find_attr(c, "from_jid")))) {
-			/* This won't be useful if it's the MUC JID */
-			if (!(jc && jabber_compare_jid(s, jc->name))) {
-				/* Hopefully this one makes more sense! */
-				bud = jabber_buddy_by_jid(ic, s, GET_BUDDY_FIRST | GET_BUDDY_CREAT);
-			}
-		}
-
-	}
-
-	if (subject && chat) {
-		char empty[1] = "";
-		char *subject_text = subject->text_len > 0 ? subject->text : empty;
-		if (g_strcmp0(chat->topic, subject_text) != 0) {
-			bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL;
-			imcb_chat_topic(chat, bare_jid, subject_text,
-			                jabber_get_timestamp(node));
-			g_free(bare_jid);
-			bare_jid = NULL;
-		}
-	}
-
-	if (body == NULL || body->text_len == 0) {
-		/* Meh. Empty messages aren't very interesting, no matter
-		   how much some servers love to send them. */
-		return;
-	}
-
-	if (chat == NULL) {
-		if (nick == NULL) {
-			imcb_log(ic, "System message from unknown groupchat %s: %s", from, body->text);
-		} else {
-			imcb_log(ic, "Groupchat message from unknown JID %s: %s", from, body->text);
-		}
-
-		return;
-	} else if (chat != NULL && bud == NULL && nick == NULL) {
-		imcb_chat_log(chat, "From conference server: %s", body->text);
-		return;
-	} else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me) {
-		if (jabber_cache_handle_packet(ic, node) == XT_ABORT) {
-			/* Self message marked by this bitlbee, don't show it */
-			return;
-		} else if (xt_find_attr(node, "id") == NULL &&
-		           g_strcmp0(body->text, jc->last_sent_message) == 0) {
-			/* Some misbehaving servers (like slack) eat the ids and echo anyway.
-			 * Try to detect those cases by comparing against the last sent message. */
-			return;
-		}
-	}
-
-	if (bud) {
-		bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid);
-		final_from = bare_jid;
-		if (bud == jc->me || (g_strcasecmp(final_from, ic->acc->user) == 0)) {
-			flags = OPT_SELFMESSAGE;
-		}
-	} else {
-		final_from = nick;
-	}
-
-	imcb_chat_msg(chat, final_from, body->text, flags, jabber_get_timestamp(node));
-
-	g_free(bare_jid);
-}
Index: protocols/jabber/expat.c
===================================================================
--- protocols/jabber/expat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/expat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,54 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "lib.h"
+#include <glib.h>
+
+void xmlnode_put_expat_attribs(xmlnode owner, const char** atts)
+{
+    int i = 0;
+    if (atts == NULL) return;
+    while (atts[i] != '\0')
+    {
+        xmlnode_put_attrib(owner, atts[i], atts[i+1]);
+        i += 2;
+    }
+}
Index: protocols/jabber/hashtable.c
===================================================================
--- protocols/jabber/hashtable.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/hashtable.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,142 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+csompliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+
+#ifdef XML_UNICODE_WCHAR_T
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+#endif
+
+#include "hashtable.h"
+
+#define INIT_SIZE 64
+
+static
+int keyeq(KEY s1, KEY s2)
+{
+    for (; *s1 == *s2; s1++, s2++)
+        if (*s1 == 0)
+            return 1;
+    return 0;
+}
+
+static
+unsigned long hash(KEY s)
+{
+    unsigned long h = 0;
+    while (*s)
+        h = (h << 5) + h + (unsigned char)*s++;
+    return h;
+}
+
+NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize)
+{
+    size_t i;
+    if (table->size == 0) {
+        if (!createSize)
+            return 0;
+        table->v = calloc(INIT_SIZE, sizeof(NAMED *));
+        if (!table->v)
+            return 0;
+        table->size = INIT_SIZE;
+        table->usedLim = INIT_SIZE / 2;
+        i = hash(name) & (table->size - 1);
+    }
+    else {
+        unsigned long h = hash(name);
+        for (i = h & (table->size - 1);
+                table->v[i];
+                i == 0 ? i = table->size - 1 : --i) {
+            if (keyeq(name, table->v[i]->name))
+                return table->v[i];
+        }
+        if (!createSize)
+            return 0;
+        if (table->used == table->usedLim) {
+            /* check for overflow */
+            size_t newSize = table->size * 2;
+            NAMED **newV = calloc(newSize, sizeof(NAMED *));
+            if (!newV)
+                return 0;
+            for (i = 0; i < table->size; i++)
+                if (table->v[i]) {
+                    size_t j;
+                    for (j = hash(table->v[i]->name) & (newSize - 1);
+                            newV[j];
+                            j == 0 ? j = newSize - 1 : --j)
+                        ;
+                    newV[j] = table->v[i];
+                }
+            g_free(table->v);
+            table->v = newV;
+            table->size = newSize;
+            table->usedLim = newSize/2;
+            for (i = h & (table->size - 1);
+                    table->v[i];
+                    i == 0 ? i = table->size - 1 : --i)
+                ;
+        }
+    }
+    table->v[i] = calloc(1, createSize);
+    if (!table->v[i])
+        return 0;
+    table->v[i]->name = name;
+    (table->used)++;
+    return table->v[i];
+}
+
+void hashTableDestroy(HASH_TABLE *table)
+{
+    size_t i;
+    for (i = 0; i < table->size; i++) {
+        NAMED *p = table->v[i];
+        if (p)
+            g_free(p);
+    }
+    g_free(table->v);
+}
+
+void hashTableInit(HASH_TABLE *p)
+{
+    p->size = 0;
+    p->usedLim = 0;
+    p->used = 0;
+    p->v = 0;
+}
+
+void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table)
+{
+    iter->p = table->v;
+    iter->end = iter->p + table->size;
+}
+
+NAMED *hashTableIterNext(HASH_TABLE_ITER *iter)
+{
+    while (iter->p != iter->end) {
+        NAMED *tem = *(iter->p)++;
+        if (tem)
+            return tem;
+    }
+    return 0;
+}
+
Index: protocols/jabber/hashtable.h
===================================================================
--- protocols/jabber/hashtable.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/hashtable.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,69 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+
+#include <stddef.h>
+
+#ifdef XML_UNICODE
+
+#ifdef XML_UNICODE_WCHAR_T
+typedef const wchar_t *KEY;
+#else /* not XML_UNICODE_WCHAR_T */
+typedef const unsigned short *KEY;
+#endif /* not XML_UNICODE_WCHAR_T */
+
+#else /* not XML_UNICODE */
+
+typedef const char *KEY;
+
+#endif /* not XML_UNICODE */
+
+typedef struct {
+  KEY name;
+} NAMED;
+
+typedef struct {
+  NAMED **v;
+  size_t size;
+  size_t used;
+  size_t usedLim;
+} HASH_TABLE;
+
+NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize);
+void hashTableInit(HASH_TABLE *);
+void hashTableDestroy(HASH_TABLE *);
+
+typedef struct {
+  NAMED **p;
+  NAMED **end;
+} HASH_TABLE_ITER;
+
+void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *);
+NAMED *hashTableIterNext(HASH_TABLE_ITER *);
Index: protocols/jabber/hipchat.c
===================================================================
--- protocols/jabber/hipchat.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,143 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - HipChat specific functions                               *
-*                                                                           *
-*  Copyright 2015 Xamarin Inc                                               *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *sep, *jid;
-
-	jid = xt_find_attr(node, "jid");
-
-	sep = strchr(jid, '/');
-	if (sep) {
-		*sep = '\0';
-	}
-
-	jabber_set_me(ic, jid);
-	imcb_log(ic, "Setting Hipchat JID to %s", jid);
-
-	if (sep) {
-		*sep = '/';
-	}
-
-	jd->muc_host = g_strdup(xt_find_attr(node, "muc_host"));
-
-	/* Hipchat's auth doesn't expect a restart here */
-	jd->flags &= ~JFLAG_STREAM_RESTART;
-
-	if (!jabber_get_roster(ic) ||
-	    !jabber_iq_disco_server(ic) ||
-	    !jabber_get_hipchat_profile(ic)) {
-		return XT_ABORT;
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_hipchat_profile(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node;
-	int st;
-
-	imcb_log(ic, "Fetching hipchat profile for %s", jd->me);
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_HIPCHAT_PROFILE);
-	node = jabber_make_packet("iq", "get", jd->me, node);
-
-	jabber_cache_add(ic, node, jabber_parse_hipchat_profile);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query, *name_node;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received NULL profile packet");
-		return XT_ABORT;
-	}
-
-	name_node = xt_find_node(query->children, "name");
-	if (!name_node) {
-		imcb_log(ic, "Warning: Can't find real name in profile. Joining groupchats will not be possible.");
-		return XT_ABORT;
-	}
-
-	set_setstr(&ic->acc->set, "display_name", name_node->text);
-	return XT_HANDLED;
-
-}
-
-/* Returns a newly allocated string that tries to match the "slug" part of the JID using an
- * approximation of the method used by the server. This might fail in some rare conditions
- * (old JIDs generated a different way, locale settings unicode, etc) */
-char *hipchat_make_channel_slug(const char *name)
-{
-	char *lower;
-	char *new = g_malloc(strlen(name) + 1);
-	int i = 0;
-
-	do {
-		if (*name == ' ') {
-			new[i++] = '_';
-		} else if (*name && !strchr("\"&'/:<>@", *name)) {
-			new[i++] = *name;
-		}
-	} while (*(name++));
-
-	new[i] = '\0';
-
-	lower = g_utf8_strdown(new, -1);
-	g_free(new);
-
-	return lower;
-}
-
-char *hipchat_guess_channel_name(struct im_connection *ic, const char *name)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *slug, *retval, *underscore;
-	
-	if (!(underscore = strchr(jd->username, '_')) || !jd->muc_host) {
-		return NULL;
-	}
-
-	slug = hipchat_make_channel_slug(name);
-
-	/* Get the organization ID from the username, before the underscore */
-	*underscore = '\0';
-
-	retval = g_strdup_printf("%s_%s@%s", jd->username, slug, jd->muc_host);
-
-	*underscore = '_';
-
-	g_free(slug);
-
-	return retval;
-}
Index: protocols/jabber/iasciitab.h
===================================================================
--- protocols/jabber/iasciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/iasciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,63 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */
+/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML,
+/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML,
+/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM,
+/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS,
+/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS,
+/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL,
+/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT,
+/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI,
+/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST,
+/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB,
+/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT,
+/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX,
+/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT,
+/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER,
Index: protocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,595 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - I/O stuff (plain, SSL), queues, etc                      *
-*                                                                           *
-*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "ssl_client.h"
-
-static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond);
-static gboolean jabber_write_queue(struct im_connection *ic);
-
-int jabber_write_packet(struct im_connection *ic, struct xt_node *node)
-{
-	char *buf;
-	int st;
-
-	buf = xt_to_string(node);
-	st = jabber_write(ic, buf, strlen(buf));
-	g_free(buf);
-
-	return st;
-}
-
-int jabber_write(struct im_connection *ic, char *buf, int len)
-{
-	struct jabber_data *jd = ic->proto_data;
-	gboolean ret;
-
-	if (jd->flags & JFLAG_XMLCONSOLE && !(ic->flags & OPT_LOGGING_OUT)) {
-		char *msg, *s;
-
-		msg = g_strdup_printf("TX: %s", buf);
-		/* Don't include auth info in XML logs. */
-		if (strncmp(msg, "TX: <auth ", 10) == 0 && (s = strchr(msg, '>'))) {
-			s++;
-			while (*s && *s != '<') {
-				*(s++) = '*';
-			}
-		}
-		imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
-		g_free(msg);
-	}
-
-	if (jd->tx_len == 0) {
-		/* If the queue is empty, allocate a new buffer. */
-		jd->tx_len = len;
-		jd->txq = g_memdup2(buf, len);
-
-		/* Try if we can write it immediately so we don't have to do
-		   it via the event handler. If not, add the handler. (In
-		   most cases it probably won't be necessary.) */
-		if ((ret = jabber_write_queue(ic)) && jd->tx_len > 0) {
-			jd->w_inpa = b_input_add(jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic);
-		}
-	} else {
-		/* Just add it to the buffer if it's already filled. The
-		   event handler is already set. */
-		jd->txq = g_renew(char, jd->txq, jd->tx_len + len);
-		memcpy(jd->txq + jd->tx_len, buf, len);
-		jd->tx_len += len;
-
-		/* The return value for write() doesn't necessarily mean
-		   that everything got sent, it mainly means that the
-		   connection (officially) still exists and can still
-		   be accessed without hitting SIGSEGV. IOW: */
-		ret = TRUE;
-	}
-
-	return ret;
-}
-
-/* Splitting up in two separate functions: One to use as a callback and one
-   to use in the function above to escape from having to wait for the event
-   handler to call us, if possible.
-
-   Two different functions are necessary because of the return values: The
-   callback should only return TRUE if the write was successful AND if the
-   buffer is not empty yet (ie. if the handler has to be called again when
-   the socket is ready for more data). */
-static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond)
-{
-	struct jabber_data *jd = ((struct im_connection *) data)->proto_data;
-
-	return jd->fd != -1 &&
-	       jabber_write_queue(data) &&
-	       jd->tx_len > 0;
-}
-
-static gboolean jabber_write_queue(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	int st;
-
-	if (jd->ssl) {
-		st = ssl_write(jd->ssl, jd->txq, jd->tx_len);
-	} else {
-		st = write(jd->fd, jd->txq, jd->tx_len);
-	}
-
-	if (st == jd->tx_len) {
-		/* We wrote everything, clear the buffer. */
-		g_free(jd->txq);
-		jd->txq = NULL;
-		jd->tx_len = 0;
-
-		return TRUE;
-	} else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
-		/* Set fd to -1 to make sure we won't write to it anymore. */
-		closesocket(jd->fd);    /* Shouldn't be necessary after errors? */
-		jd->fd = -1;
-
-		imcb_error(ic, "Short write() to server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	} else if (st > 0) {
-		char *s;
-
-		s = g_memdup2(jd->txq + st, jd->tx_len - st);
-		jd->tx_len -= st;
-		g_free(jd->txq);
-		jd->txq = s;
-
-		return TRUE;
-	} else {
-		/* Just in case we had EINPROGRESS/EAGAIN: */
-
-		return TRUE;
-	}
-}
-
-static gboolean jabber_feed_input(struct im_connection *ic, char *buf, int size)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* Allow not passing a size for debugging purposes.
-	 * This never happens when reading from the socket */
-	if (size == -1) {
-		size = strlen(buf);
-	}
-
-	/* Parse. */
-	if (xt_feed(jd->xt, buf, size) < 0) {
-		imcb_error(ic, "XML stream error");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	/* Execute all handlers. */
-	if (!xt_handle(jd->xt, NULL, 1)) {
-		/* Don't do anything, the handlers should have
-		   aborted the connection already. */
-		return FALSE;
-	}
-
-	if (jd->flags & JFLAG_STREAM_RESTART) {
-		jd->flags &= ~JFLAG_STREAM_RESTART;
-		jabber_start_stream(ic);
-	}
-
-	/* Garbage collection. */
-	xt_cleanup(jd->xt, NULL, 1);
-
-	/* This is a bit hackish, unfortunately. Although xmltree
-	   has nifty event handler stuff, it only calls handlers
-	   when nodes are complete. Since the server should only
-	   send an opening <stream:stream> tag, we have to check
-	   this by hand. :-( */
-	if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) {
-		if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) {
-			jd->flags |= JFLAG_STREAM_STARTED;
-
-			/* If there's no version attribute, assume
-			   this is an old server that can't do SASL
-			   authentication. */
-			if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) {
-				/* If there's no version= tag, we suppose
-				   this server does NOT implement: XMPP 1.0,
-				   SASL and TLS. */
-				if (set_getbool(&ic->acc->set, "tls")) {
-					imcb_error(ic, "TLS is turned on for this "
-						   "account, but is not supported by this server");
-					imc_logout(ic, FALSE);
-					return FALSE;
-				} else {
-					if (!jabber_init_iq_auth(ic)) {
-						return FALSE;
-					}
-				}
-			}
-		} else {
-			imcb_error(ic, "XML stream error");
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-
-static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char buf[512];
-	int st;
-
-	if (jd->fd == -1) {
-		return FALSE;
-	}
-
-	if (jd->ssl) {
-		st = ssl_read(jd->ssl, buf, sizeof(buf));
-	} else {
-		st = read(jd->fd, buf, sizeof(buf));
-	}
-
-	if (st > 0) {
-		if (!jabber_feed_input(ic, buf, st)) {
-			return FALSE;
-		}
-	} else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
-		closesocket(jd->fd);
-		jd->fd = -1;
-
-		imcb_error(ic, "Error while reading from server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	if (ssl_pending(jd->ssl)) {
-		/* OpenSSL empties the TCP buffers completely but may keep some
-		   data in its internap buffers. select() won't see that, but
-		   ssl_pending() does. */
-		return jabber_read_callback(data, fd, cond);
-	} else {
-		return TRUE;
-	}
-}
-
-gboolean jabber_connected_plain(gpointer data, gint source, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return FALSE;
-	}
-
-	if (source == -1) {
-		imcb_error(ic, "Could not connect to server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	imcb_log(ic, "Connected to server, logging in");
-
-	return jabber_start_stream(ic);
-}
-
-gboolean jabber_connected_ssl(gpointer data, int returncode, void *source, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return FALSE;
-	}
-
-	jd = ic->proto_data;
-
-	if (source == NULL) {
-		/* The SSL connection will be cleaned up by the SSL lib
-		   already, set it to NULL here to prevent a double cleanup: */
-		jd->ssl = NULL;
-
-		if (returncode != 0) {
-			char *err = ssl_verify_strerror(returncode);
-			imcb_error(ic, "Certificate verification problem 0x%x: %s",
-			           returncode, err ? err : "Unknown");
-			g_free(err);
-			imc_logout(ic, FALSE);
-		} else {
-			imcb_error(ic, "Could not connect to server");
-			imc_logout(ic, TRUE);
-		}
-
-		return FALSE;
-	}
-
-	imcb_log(ic, "Connected to server, logging in");
-
-	return jabber_start_stream(ic);
-}
-
-static xt_status jabber_end_of_stream(struct xt_node *node, gpointer data)
-{
-	imc_logout(data, TRUE);
-	return XT_ABORT;
-}
-
-static xt_status jabber_pkt_features(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply;
-	int trytls;
-
-	trytls = g_strcasecmp(set_getstr(&ic->acc->set, "tls"), "try") == 0;
-	c = xt_find_node(node->children, "starttls");
-	if (c && !jd->ssl) {
-		/* If the server advertises the STARTTLS feature and if we're
-		   not in a secure connection already: */
-
-		c = xt_find_node(c->children, "required");
-
-		if (c && (!trytls && !set_getbool(&ic->acc->set, "tls"))) {
-			imcb_error(ic, "Server requires TLS connections, but TLS is turned off for this account");
-			imc_logout(ic, FALSE);
-
-			return XT_ABORT;
-		}
-
-		/* Only run this if the tls setting is set to true or try: */
-		if ((trytls || set_getbool(&ic->acc->set, "tls"))) {
-			reply = xt_new_node("starttls", NULL, NULL);
-			xt_add_attr(reply, "xmlns", XMLNS_TLS);
-			if (!jabber_write_packet(ic, reply)) {
-				xt_free_node(reply);
-				return XT_ABORT;
-			}
-			xt_free_node(reply);
-
-			return XT_HANDLED;
-		}
-	} else if (!c && !jd->ssl) {
-		/* If the server does not advertise the STARTTLS feature and
-		   we're not in a secure connection already: (Servers have a
-		   habit of not advertising <starttls/> anymore when already
-		   using SSL/TLS. */
-
-		if (!trytls && set_getbool(&ic->acc->set, "tls")) {
-			imcb_error(ic, "TLS is turned on for this account, but is not supported by this server");
-			imc_logout(ic, FALSE);
-
-			return XT_ABORT;
-		}
-	}
-
-	/* This one used to be in jabber_handlers[], but it has to be done
-	   from here to make sure the TLS session will be initialized
-	   properly before we attempt SASL authentication. */
-	if ((c = xt_find_node(node->children, "mechanisms"))) {
-		if (sasl_pkt_mechanisms(c, data) == XT_ABORT) {
-			return XT_ABORT;
-		}
-	}
-	/* If the server *SEEMS* to support SASL authentication but doesn't
-	   support it after all, we should try to do authentication the
-	   other way. jabber.com doesn't seem to do SASL while it pretends
-	   to be XMPP 1.0 compliant! */
-	else if (!(jd->flags & JFLAG_AUTHENTICATED) && set_getbool(&ic->acc->set, "sasl") && sasl_supported(ic)) {
-		if (!jabber_init_iq_auth(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	if ((c = xt_find_node(node->children, "bind"))) {
-		jd->flags |= JFLAG_WANT_BIND;
-	}
-
-	if ((c = xt_find_node(node->children, "session"))) {
-		jd->flags |= JFLAG_WANT_SESSION;
-	}
-
-	if (jd->flags & JFLAG_AUTHENTICATED) {
-		return jabber_pkt_bind_sess(ic, NULL, NULL);
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_pkt_proceed_tls(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *tlsname;
-
-	xmlns = xt_find_attr(node, "xmlns");
-
-	/* Just ignore it when it doesn't seem to be TLS-related (is that at
-	   all possible??). */
-	if (!xmlns || strcmp(xmlns, XMLNS_TLS) != 0) {
-		return XT_HANDLED;
-	}
-
-	/* We don't want event handlers to touch our TLS session while it's
-	   still initializing! */
-	b_event_remove(jd->r_inpa);
-	if (jd->tx_len > 0) {
-		/* Actually the write queue should be empty here, but just
-		   to be sure... */
-		b_event_remove(jd->w_inpa);
-		g_free(jd->txq);
-		jd->txq = NULL;
-		jd->tx_len = 0;
-	}
-	jd->w_inpa = jd->r_inpa = 0;
-
-	imcb_log(ic, "Converting stream to TLS");
-
-	jd->flags |= JFLAG_STARTTLS_DONE;
-
-	/* If the user specified a server for the account, use this server as the
-	 * hostname in the certificate verification. Else we use the domain from
-	 * the username. */
-	if (ic->acc->server && *ic->acc->server) {
-		tlsname = ic->acc->server;
-	} else {
-		tlsname = jd->server;
-	}
-
-	jd->ssl = ssl_starttls(jd->fd, tlsname, set_getbool(&ic->acc->set, "tls_verify"),
-	                       jabber_connected_ssl, ic);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_pkt_stream_error(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	int allow_reconnect = TRUE;
-	struct jabber_error *err;
-	struct xt_node *host;
-
-	if (!(ic->flags & OPT_LOGGED_IN) &&
-	    (host = xt_find_node(node->children, "see-other-host")) &&
-	    host->text) {
-		char *s;
-		int port = set_getint(&ic->acc->set, "port");
-
-		/* Let's try to obey this request, if we're not logged
-		   in yet (i.e. not have too much state yet). */
-		if (jd->ssl) {
-			ssl_disconnect(jd->ssl);
-		}
-		closesocket(jd->fd);
-		b_event_remove(jd->r_inpa);
-		b_event_remove(jd->w_inpa);
-
-		jd->ssl = NULL;
-		jd->r_inpa = jd->w_inpa = 0;
-		jd->flags &= JFLAG_XMLCONSOLE;
-
-		s = strchr(host->text, ':');
-		if (s != NULL) {
-			sscanf(s + 1, "%d", &port);
-		}
-
-		imcb_log(ic, "Redirected to %s", host->text);
-		jd->fd = proxy_connect(host->text, port, jabber_connected_plain, ic);
-
-		return XT_ABORT;
-	}
-
-	err = jabber_error_parse(node, XMLNS_STREAM_ERROR);
-
-	/* Tssk... */
-	if (err->code == NULL) {
-		imcb_error(ic, "Unknown stream error reported by server");
-		imc_logout(ic, allow_reconnect);
-		jabber_error_free(err);
-		return XT_ABORT;
-	}
-
-	/* We know that this is a fatal error. If it's a "conflict" error, we
-	   should turn off auto-reconnect to make sure we won't get some nasty
-	   infinite loop! */
-	if (strcmp(err->code, "conflict") == 0) {
-		imcb_error(ic, "Account and resource used from a different location");
-		allow_reconnect = FALSE;
-	} else if (strcmp(err->code, "not-authorized") == 0) {
-		imcb_error(ic, "Not authorized");
-		allow_reconnect = FALSE;
-	} else {
-		imcb_error(ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
-		           err->text ? err->text : "");
-	}
-
-	jabber_error_free(err);
-	imc_logout(ic, allow_reconnect);
-
-	return XT_ABORT;
-}
-
-static xt_status jabber_xmlconsole(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-
-	if (jd->flags & JFLAG_XMLCONSOLE) {
-		char *msg, *pkt;
-
-		pkt = xt_to_string(node);
-		msg = g_strdup_printf("RX: %s", pkt);
-		imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
-		g_free(msg);
-		g_free(pkt);
-	}
-
-	return XT_NEXT;
-}
-
-static const struct xt_handler_entry jabber_handlers[] = {
-	{ NULL,                 "stream:stream",        jabber_xmlconsole },
-	{ "stream:stream",      "<root>",               jabber_end_of_stream },
-	{ "message",            "stream:stream",        jabber_pkt_message },
-	{ "presence",           "stream:stream",        jabber_pkt_presence },
-	{ "iq",                 "stream:stream",        jabber_pkt_iq },
-	{ "stream:features",    "stream:stream",        jabber_pkt_features },
-	{ "stream:error",       "stream:stream",        jabber_pkt_stream_error },
-	{ "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
-	{ "challenge",          "stream:stream",        sasl_pkt_challenge },
-	{ "success",            "stream:stream",        sasl_pkt_result },
-	{ "failure",            "stream:stream",        sasl_pkt_result },
-	{ NULL,                 NULL,                   NULL }
-};
-
-gboolean jabber_start_stream(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	int st;
-	char *greet;
-
-	/* We'll start our stream now, so prepare everything to receive one
-	   from the server too. */
-	xt_free(jd->xt);        /* In case we're RE-starting. */
-	jd->xt = xt_new(jabber_handlers, ic);
-
-	if (jd->r_inpa <= 0) {
-		jd->r_inpa = b_input_add(jd->fd, B_EV_IO_READ, jabber_read_callback, ic);
-	}
-
-	greet = g_strdup_printf("%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
-	                        "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",
-	                        (jd->flags & JFLAG_STARTTLS_DONE) ? "" : "<?xml version='1.0' ?>",
-	                        jd->server);
-
-	st = jabber_write(ic, greet, strlen(greet));
-
-	g_free(greet);
-
-	return st;
-}
-
-void jabber_end_stream(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* Let's only do this if the queue is currently empty, otherwise it'd
-	   take too long anyway. */
-	if (jd->tx_len == 0) {
-		char eos[] = "</stream:stream>";
-		struct xt_node *node;
-		int st = 1;
-
-		if (ic->flags & OPT_LOGGED_IN) {
-			node = jabber_make_packet("presence", "unavailable", NULL, NULL);
-			st = jabber_write_packet(ic, node);
-			xt_free_node(node);
-		}
-
-		if (st) {
-			jabber_write(ic, eos, strlen(eos));
-		}
-	}
-}
Index: protocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,1120 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - IQ packets                                               *
-*                                                                           *
-*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
-static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply = NULL;
-	char *type, *s;
-	int st, pack = 1;
-
-	type = xt_find_attr(node, "type");
-
-	if (!type) {
-		imcb_error(ic, "Received IQ packet without type.");
-		imc_logout(ic, TRUE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(type, "result") == 0 || strcmp(type, "error") == 0) {
-		return jabber_cache_handle_packet(ic, node);
-	} else if (strcmp(type, "get") == 0) {
-		if (!((c = xt_find_node(node->children, "query")) ||
-		      (c = xt_find_node(node->children, "ping")) ||
-		      (c = xt_find_node(node->children, "time"))) ||
-		    !(s = xt_find_attr(c, "xmlns"))) {
-
-			reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
-			st = jabber_write_packet(ic, reply);
-			xt_free_node(reply);
-			return st;
-		}
-
-		reply = xt_new_node("query", NULL, NULL);
-		xt_add_attr(reply, "xmlns", s);
-
-		/* Of course this is a very essential query to support. ;-) */
-		if (strcmp(s, XMLNS_VERSION) == 0) {
-			xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
-			xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, NULL));
-		} else if (strcmp(s, XMLNS_TIME_OLD) == 0) {
-			time_t time_ep;
-			char buf[1024];
-
-			buf[sizeof(buf) - 1] = 0;
-			time_ep = time(NULL);
-
-			strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%S", gmtime(&time_ep));
-			xt_add_child(reply, xt_new_node("utc", buf, NULL));
-
-			strftime(buf, sizeof(buf) - 1, "%Z", localtime(&time_ep));
-			xt_add_child(reply, xt_new_node("tz", buf, NULL));
-		} else if (strcmp(s, XMLNS_TIME) == 0) {
-			time_t time_ep;
-			char buf[1024];
-
-			buf[sizeof(buf) - 1] = 0;
-			time_ep = time(NULL);
-
-			xt_free_node(reply);
-			reply = xt_new_node("time", NULL, NULL);
-			xt_add_attr(reply, "xmlns", XMLNS_TIME);
-
-			strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%SZ", gmtime(&time_ep));
-			xt_add_child(reply, xt_new_node("utc", buf, NULL));
-
-			strftime(buf, sizeof(buf) - 1, "%z", localtime(&time_ep));
-			if (strlen(buf) >= 5) {
-				buf[6] = '\0';
-				buf[5] = buf[4];
-				buf[4] = buf[3];
-				buf[3] = ':';
-			}
-			xt_add_child(reply, xt_new_node("tzo", buf, NULL));
-		} else if (strcmp(s, XMLNS_PING) == 0) {
-			xt_free_node(reply);
-			reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), NULL);
-			if ((s = xt_find_attr(node, "id"))) {
-				xt_add_attr(reply, "id", s);
-			}
-			pack = 0;
-		} else if (strcmp(s, XMLNS_DISCO_INFO) == 0) {
-			const char *features[] = { XMLNS_DISCO_INFO,
-				                   XMLNS_VERSION,
-				                   XMLNS_TIME_OLD,
-				                   XMLNS_TIME,
-				                   XMLNS_CHATSTATES,
-				                   XMLNS_MUC,
-				                   XMLNS_PING,
-				                   XMLNS_RECEIPTS,
-				                   XMLNS_SI,
-				                   XMLNS_BYTESTREAMS,
-				                   XMLNS_FILETRANSFER,
-				                   XMLNS_CARBONS,
-				                   NULL };
-			const char **f;
-
-			c = xt_new_node("identity", NULL, NULL);
-			xt_add_attr(c, "category", "client");
-			xt_add_attr(c, "type", "pc");
-			xt_add_attr(c, "name", set_getstr(&ic->acc->set, "user_agent"));
-			xt_add_child(reply, c);
-
-			for (f = features; *f; f++) {
-				c = xt_new_node("feature", NULL, NULL);
-				xt_add_attr(c, "var", *f);
-				xt_add_child(reply, c);
-			}
-		} else {
-			xt_free_node(reply);
-			reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
-			pack = 0;
-		}
-	} else if (strcmp(type, "set") == 0) {
-		if ((c = xt_find_node(node->children, "si")) &&
-		    (s = xt_find_attr(c, "xmlns")) &&
-		    (strcmp(s, XMLNS_SI) == 0)) {
-			return jabber_si_handle_request(ic, node, c);
-		} else if ((c = xt_find_node(node->children, "new-mail")) &&
-		           (s = xt_find_attr(c, "xmlns")) &&
-		           (strcmp(s, XMLNS_GMAILNOTIFY) == 0)) {
-			return jabber_gmail_handle_new(ic, node);
-		} else if (!(c = xt_find_node(node->children, "query")) ||
-		           !(s = xt_find_attr(c, "xmlns"))) {
-			return XT_HANDLED;
-		} else if (strcmp(s, XMLNS_ROSTER) == 0) {
-			/* This is a roster push. XMPP servers send this when someone
-			   was added to (or removed from) the buddy list. AFAIK they're
-			   sent even if we added this buddy in our own session. */
-			int bare_len = strlen(jd->me);
-
-			if ((s = xt_find_attr(node, "from")) == NULL ||
-			    (strncmp(s, jd->me, bare_len) == 0 &&
-			     (s[bare_len] == 0 || s[bare_len] == '/'))) {
-				jabber_parse_roster(ic, node, NULL);
-
-				/* Should we generate a reply here? Don't think it's
-				   very important... */
-			} else {
-				imcb_log(ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)");
-
-				xt_free_node(reply);
-				reply = jabber_make_error_packet(node, "not-allowed", "cancel", NULL);
-				pack = 0;
-			}
-		} else if (strcmp(s, XMLNS_BYTESTREAMS) == 0) {
-			/* Bytestream Request (stage 2 of file transfer) */
-			return jabber_bs_recv_request(ic, node, c);
-		} else {
-			xt_free_node(reply);
-			reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL);
-			pack = 0;
-		}
-	}
-
-	/* If we recognized the xmlns and managed to generate a reply,
-	   finish and send it. */
-	if (reply) {
-		/* Normally we still have to pack it into an iq-result
-		   packet, but for errors, for example, we don't. */
-		if (pack) {
-			reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), reply);
-			if ((s = xt_find_attr(node, "id"))) {
-				xt_add_attr(reply, "id", s);
-			}
-		}
-
-		st = jabber_write_packet(ic, reply);
-		xt_free_node(reply);
-		if (!st) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_init_iq_auth(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node;
-	int st;
-
-	node = xt_new_node("query", NULL, xt_new_node("username", jd->username, NULL));
-	xt_add_attr(node, "xmlns", XMLNS_AUTH);
-	node = jabber_make_packet("iq", "get", NULL, node);
-
-	jabber_cache_add(ic, node, jabber_do_iq_auth);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *reply, *query;
-	xt_status st;
-	char *s;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	/* Time to authenticate ourselves! */
-	reply = xt_new_node("query", NULL, NULL);
-	xt_add_attr(reply, "xmlns", XMLNS_AUTH);
-	xt_add_child(reply, xt_new_node("username", jd->username, NULL));
-	xt_add_child(reply, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
-
-	if (xt_find_node(query->children, "digest") && (s = xt_find_attr(jd->xt->root, "id"))) {
-		/* We can do digest authentication, it seems, and of
-		   course we prefer that. */
-		GChecksum *sha;
-		char hash_hex[41];
-		unsigned char hash[20];
-		int i;
-		gsize digest_len = 20;
-
-		sha = g_checksum_new(G_CHECKSUM_SHA1);
-		g_checksum_update(sha, (unsigned char *) s, strlen(s));
-		g_checksum_update(sha, (unsigned char *) ic->acc->pass, strlen(ic->acc->pass));
-		g_checksum_get_digest(sha, hash, &digest_len);
-		g_checksum_free(sha);
-
-		for (i = 0; i < 20; i++) {
-			sprintf(hash_hex + i * 2, "%02x", hash[i]);
-		}
-
-		xt_add_child(reply, xt_new_node("digest", hash_hex, NULL));
-	} else if (xt_find_node(query->children, "password")) {
-		/* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
-		xt_add_child(reply, xt_new_node("password", ic->acc->pass, NULL));
-	} else {
-		xt_free_node(reply);
-
-		imcb_error(ic, "Can't find suitable authentication method");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	reply = jabber_make_packet("iq", "set", NULL, reply);
-	jabber_cache_add(ic, reply, jabber_finish_iq_auth);
-	st = jabber_write_packet(ic, reply);
-
-	return st ? XT_HANDLED : XT_ABORT;
-}
-
-static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *type;
-
-	if (!(type = xt_find_attr(node, "type"))) {
-		imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(type, "error") == 0) {
-		imcb_error(ic, "Authentication failure");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	} else if (strcmp(type, "result") == 0) {
-		/* This happens when we just successfully authenticated the
-		   old (non-SASL) way. */
-		jd->flags |= JFLAG_AUTHENTICATED;
-		if (!jabber_get_roster(ic)) {
-			return XT_ABORT;
-		}
-		if (!jabber_iq_disco_server(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply = NULL;
-	char *s;
-
-	if (node && (c = xt_find_node(node->children, "bind"))) {
-		c = xt_find_node(c->children, "jid");
-		if (!c || !c->text) {
-			/* Server is crap, but this is no disaster. */
-		} else if (jabber_compare_jid(jd->me, c->text) == 0) {
-			s = strchr(c->text, '/');
-			if (s) {
-				*s = '\0';
-			}
-			jabber_set_me(ic, c->text);
-			if (s) {
-				*s = '/';
-			}
-		} else if (c && c->text_len && (s = strchr(c->text, '/')) &&
-		           strcmp(s + 1, set_getstr(&ic->acc->set, "resource")) != 0) {
-			imcb_log(ic, "Server changed session resource string to `%s'", s + 1);
-		}
-	}
-
-	if (jd->flags & JFLAG_WANT_BIND) {
-		reply = xt_new_node("bind", NULL, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
-		xt_add_attr(reply, "xmlns", XMLNS_BIND);
-		jd->flags &= ~JFLAG_WANT_BIND;
-	} else if (jd->flags & JFLAG_WANT_SESSION) {
-		reply = xt_new_node("session", NULL, NULL);
-		xt_add_attr(reply, "xmlns", XMLNS_SESSION);
-		jd->flags &= ~JFLAG_WANT_SESSION;
-	}
-
-	if (reply != NULL) {
-		reply = jabber_make_packet("iq", "set", NULL, reply);
-		jabber_cache_add(ic, reply, jabber_pkt_bind_sess);
-
-		if (!jabber_write_packet(ic, reply)) {
-			return XT_ABORT;
-		}
-		if (jd->flags & JFLAG_GMAILNOTIFY && node == NULL) {
-			jabber_iq_query_server(ic, jd->server, XMLNS_DISCO_INFO);
-		}
-	} else if ((jd->flags & (JFLAG_WANT_BIND | JFLAG_WANT_SESSION)) == 0) {
-		if (!jabber_get_roster(ic)) {
-			return XT_ABORT;
-		}
-		if (!jabber_iq_disco_server(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_roster(struct im_connection *ic)
-{
-	struct xt_node *node;
-	int st;
-
-	imcb_log(ic, "Authenticated, requesting buddy list");
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "get", NULL, node);
-
-	jabber_cache_add(ic, node, jabber_parse_roster);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_iq_query_gmail(struct im_connection *ic);
-
-static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node)
-{
-	struct xt_node *response;
-	struct jabber_data *jd = ic->proto_data;
-	
-	response = jabber_make_packet("iq", "result", jd->me, NULL);
-
-	jabber_cache_add(ic, response, NULL);
-	if (!jabber_write_packet(ic, response)) {
-		return XT_ABORT;
-	}
-
-	jabber_iq_query_gmail(ic);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *query, *c;
-	int initial = (orig != NULL);
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received NULL roster packet");
-		return XT_HANDLED;
-	}
-
-	c = query->children;
-	while ((c = xt_find_node(c, "item"))) {
-		struct xt_node *group = xt_find_node(c->children, "group");
-		char *jid = xt_find_attr(c, "jid");
-		char *name = xt_find_attr(c, "name");
-		char *sub = xt_find_attr(c, "subscription");
-		char *mention_name = xt_find_attr(c, "mention_name");
-
-		if (jid && sub) {
-			if ((strcmp(sub, "both") == 0 || strcmp(sub, "to") == 0)) {
-				imcb_add_buddy(ic, jid, (group && group->text_len) ?
-				               group->text : NULL);
-
-				if (name) {
-					imcb_rename_buddy(ic, jid, name);
-				}
-
-				/* This could also be used to set the full name as nick for fb/gtalk,
-				 * but i'm keeping the old (ugly?) default behavior just to be safe */
-				if (mention_name && (jd->flags & JFLAG_HIPCHAT)) {
-					imcb_buddy_nick_hint(ic, jid, mention_name);
-				}
-			} else if (strcmp(sub, "remove") == 0) {
-				jabber_buddy_remove_bare(ic, jid);
-				imcb_remove_buddy(ic, jid, NULL);
-			}
-		}
-
-		c = c->next;
-	}
-
-	if (initial) {
-		imcb_connected(ic);
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
-{
-	struct xt_node *node;
-
-	if (strchr(bare_jid, '/')) {
-		return 1;       /* This was an error, but return 0 should only be done if the connection died... */
-
-	}
-	node = xt_new_node("vCard", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_VCARD);
-	node = jabber_make_packet("iq", "get", bare_jid, node);
-
-	jabber_cache_add(ic, node, jabber_iq_display_vcard);
-	return jabber_write_packet(ic, node);
-}
-
-static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
-	GString *reply;
-	char *s;
-
-	if ((s = xt_find_attr(node, "type")) == NULL ||
-	    strcmp(s, "result") != 0 ||
-	    (vc = xt_find_node(node->children, "vCard")) == NULL) {
-		s = xt_find_attr(orig, "to");   /* If this returns NULL something's wrong.. */
-		imcb_log(ic, "Could not retrieve vCard of %s", s ? s : "(NULL)");
-		return XT_HANDLED;
-	}
-
-	s = xt_find_attr(orig, "to");
-	reply = g_string_new("vCard information for ");
-	reply = g_string_append(reply, s ? s : "(NULL)");
-	reply = g_string_append(reply, ":\n");
-
-	/* I hate this format, I really do... */
-
-	if ((c = xt_find_node(vc->children, "FN")) && c->text_len) {
-		g_string_append_printf(reply, "Name: %s\n", c->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "N")) && c->children) {
-		reply = g_string_append(reply, "Full name:");
-
-		if ((sc = xt_find_node(c->children, "PREFIX")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "GIVEN")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "MIDDLE")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "FAMILY")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "SUFFIX")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-
-		reply = g_string_append_c(reply, '\n');
-	}
-
-	if ((c = xt_find_node(vc->children, "NICKNAME")) && c->text_len) {
-		g_string_append_printf(reply, "Nickname: %s\n", c->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "BDAY")) && c->text_len) {
-		g_string_append_printf(reply, "Date of birth: %s\n", c->text);
-	}
-
-	/* Slightly alternative use of for... ;-) */
-	for (c = vc->children; (c = xt_find_node(c, "EMAIL")); c = c->next) {
-		if ((sc = xt_find_node(c->children, "USERID")) == NULL || sc->text_len == 0) {
-			continue;
-		}
-
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "URL")) && c->text_len) {
-		g_string_append_printf(reply, "Homepage: %s\n", c->text);
-	}
-
-	/* Slightly alternative use of for... ;-) */
-	for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s address: ", s);
-
-		if ((sc = xt_find_node(c->children, "STREET")) && sc->text_len) {
-			g_string_append_printf(reply, "%s ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "EXTADR")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "PCODE")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "LOCALITY")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "REGION")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "CTRY")) && sc->text_len) {
-			g_string_append_printf(reply, "%s", sc->text);
-		}
-
-		if (reply->str[reply->len - 2] == ',') {
-			reply = g_string_truncate(reply, reply->len - 2);
-		}
-
-		reply = g_string_append_c(reply, '\n');
-	}
-
-	for (c = vc->children; (c = xt_find_node(c, "TEL")); c = c->next) {
-		if ((sc = xt_find_node(c->children, "NUMBER")) == NULL || sc->text_len == 0) {
-			continue;
-		}
-
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s phone number: %s\n", s, sc->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "DESC")) && c->text_len) {
-		g_string_append_printf(reply, "Other information:\n%s", c->text);
-	}
-
-	/* *sigh* */
-
-	imcb_log(ic, "%s", reply->str);
-	g_string_free(reply, TRUE);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
-{
-	struct xt_node *node;
-	int st;
-
-	/* Build the item entry */
-	node = xt_new_node("item", NULL, NULL);
-	xt_add_attr(node, "jid", handle);
-	if (name) {
-		xt_add_attr(node, "name", name);
-	}
-	if (group) {
-		xt_add_child(node, xt_new_node("group", group, NULL));
-	}
-
-	/* And pack it into a roster-add packet */
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "set", NULL, node);
-	jabber_cache_add(ic, node, jabber_add_to_roster_callback);
-
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	char *s, *jid = NULL;
-	struct xt_node *c;
-
-	if ((c = xt_find_node(orig->children, "query")) &&
-	    (c = xt_find_node(c->children, "item")) &&
-	    (jid = xt_find_attr(c, "jid")) &&
-	    (s = xt_find_attr(node, "type")) &&
-	    strcmp(s, "result") == 0) {
-		if (bee_user_by_handle(ic->bee, ic, jid) == NULL) {
-			imcb_add_buddy(ic, jid, NULL);
-		}
-	} else {
-		imcb_log(ic, "Error while adding `%s' to your contact list.",
-		         jid ? jid : "(unknown handle)");
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_remove_from_roster(struct im_connection *ic, char *handle)
-{
-	struct xt_node *node;
-	int st;
-
-	/* Build the item entry */
-	node = xt_new_node("item", NULL, NULL);
-	xt_add_attr(node, "jid", handle);
-	xt_add_attr(node, "subscription", "remove");
-
-	/* And pack it into a roster-add packet */
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "set", NULL, node);
-
-	st = jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-	return st;
-}
-
-xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
-{
-	struct xt_node *node, *query;
-	struct jabber_buddy *bud;
-
-	if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
-		/* Who cares about the unknown... */
-		imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
-		return XT_HANDLED;
-	}
-
-	if (bud->features) { /* been here already */
-		return XT_HANDLED;
-	}
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
-
-	if (!(query = jabber_make_packet("iq", "get", bare_jid, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate feature query");
-		xt_free_node(node);
-		return XT_HANDLED;
-	}
-
-	jabber_cache_add(ic, query, jabber_iq_parse_features);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_buddy *bud;
-	char *feature, *xmlns, *from;
-
-	if (!(from = xt_find_attr(node, "from")) ||
-	    !(c = xt_find_node(node->children, "query")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns")) ||
-	    !(strcmp(xmlns, XMLNS_DISCO_INFO) == 0)) {
-		imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
-		return XT_HANDLED;
-	}
-	if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-		/* Who cares about the unknown... */
-		imcb_log(ic, "Couldn't find buddy: %s", from);
-		return XT_HANDLED;
-	}
-
-	c = c->children;
-	while ((c = xt_find_node(c, "feature"))) {
-		feature = xt_find_attr(c, "var");
-		if (feature) {
-			bud->features = g_slist_append(bud->features, g_strdup(feature));
-		}
-		c = c->next;
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_gmail(struct im_connection *ic)
-{
-	struct xt_node *node, *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_GMAILNOTIFY);
-	if (jd->gmail_time) {
-		char *formatted = g_strdup_printf("%" G_GUINT64_FORMAT, (jd->gmail_time + 1));
-		xt_add_attr(node, "newer-than-time", formatted);
-		g_free(formatted);
-	}
-	if (jd->gmail_tid) {
-		xt_add_attr(node, "newer-than-tid", jd->gmail_tid);
-	}
-
-	if (!(query = jabber_make_packet("iq", "get", jd->me, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate server query");
-		xt_free_node(node);
-	}
-
-	jabber_cache_add(ic, query, jabber_iq_parse_gmail);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
-{
-	struct xt_node *node, *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", xmlns);
-
-	if (!(query = jabber_make_packet("iq", "get", jid, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate server query");
-		xt_free_node(node);
-	}
-
-	jd->have_streamhosts--;
-	jabber_cache_add(ic, query, jabber_iq_parse_server_features);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *from;
-	guint64 l_time = 0;
-	char *tid = NULL;
-	int max = 0;
-
-	if (!(c = xt_find_node(node->children, "mailbox")) ||
-	    !(from = xt_find_attr(node, "from")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns")) ||
-	    (g_strcmp0(xmlns, XMLNS_GMAILNOTIFY) != 0)) {
-		imcb_log(ic, "WARNING: Received incomplete mailbox packet for gmail notify");
-		return XT_HANDLED;
-	}
-
-	max = set_getint(&ic->acc->set, "mail_notifications_limit");
-	c = c->children;
-
-	while ((max-- > 0) && (c = xt_find_node(c, "mail-thread-info"))) {
-		struct xt_node *s;
-		char *subject = "<no subject>";
-		char *sender = "<no sender>";
-		guint64 t_time;
-
-		t_time = g_ascii_strtoull(xt_find_attr(c, "date"), NULL, 10);
-		if (t_time && t_time > l_time) {
-			l_time = t_time;
-			tid = xt_find_attr(c, "tid");
-		}
-
-		if ((s = xt_find_node(c->children, "senders")) &&
-		    (s = xt_find_node_by_attr(s->children, "sender", "unread", "1"))) {
-			sender = xt_find_attr(s, "name");
-		}
-
-		if ((s = xt_find_node(c->children, "subject")) && s->text) {
-			subject = s->text;
-		}
-
-		imcb_notify_email(ic, "New mail from %s: %s", sender, subject);
-
-		c = c->next;
-	}
-
-	if (l_time && (!jd->gmail_time || l_time > jd->gmail_time)) {
-		jd->gmail_time = l_time;
-		if (tid) {
-			g_free(jd->gmail_tid);
-			jd->gmail_tid = g_strdup(tid);
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-/*
- * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
- */
-xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *from;
-
-	if (!(c = xt_find_node(node->children, "query")) ||
-	    !(from = xt_find_attr(node, "from")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns"))) {
-		imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
-		return XT_HANDLED;
-	}
-
-	jd->have_streamhosts++;
-
-	if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
-		char *itemjid;
-
-		/* answer from server */
-
-		c = c->children;
-		while ((c = xt_find_node(c, "item"))) {
-			itemjid = xt_find_attr(c, "jid");
-
-			if (itemjid) {
-				jabber_iq_query_server(ic, itemjid, XMLNS_DISCO_INFO);
-			}
-
-			c = c->next;
-		}
-	} else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
-		char *category, *type;
-
-		/* answer from potential proxy */
-
-		c = c->children;
-		while ((c = xt_find_node(c, "identity"))) {
-			category = xt_find_attr(c, "category");
-			type = xt_find_attr(c, "type");
-
-			if (type && (strcmp(type, "bytestreams") == 0) &&
-			    category && (strcmp(category, "proxy") == 0)) {
-				jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
-			}
-
-			c = c->next;
-		}
-
-		if (jd->flags & JFLAG_GMAILNOTIFY) {
-			/* search for gmail notification feature */
-			c = xt_find_node(node->children, "query");
-			c = c->children;
-			while ((c = xt_find_node(c, "feature"))) {
-				if (strcmp(xt_find_attr(c, "var"), XMLNS_GMAILNOTIFY) == 0) {
-					jabber_iq_query_gmail(ic);
-				}
-				c = c->next;
-			}
-		}
-
-	} else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
-		char *host, *jid, *port_s;
-		int port;
-
-		/* answer from proxy */
-
-		if ((c = xt_find_node(c->children, "streamhost")) &&
-		    (host = xt_find_attr(c, "host")) &&
-		    (port_s = xt_find_attr(c, "port")) &&
-		    (sscanf(port_s, "%d", &port) == 1) &&
-		    (jid = xt_find_attr(c, "jid"))) {
-			jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
-
-			sh->jid = g_strdup(jid);
-			sh->host = g_strdup(host);
-			g_snprintf(sh->port, sizeof(sh->port), "%u", port);
-
-			imcb_log(ic, "Proxy found: jid %s host %s port %u", jid, host, port);
-			jd->streamhosts = g_slist_append(jd->streamhosts, sh);
-		}
-	}
-
-	if (jd->have_streamhosts == 0) {
-		jd->have_streamhosts++;
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_version_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig);
-
-void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
-{
-	struct xt_node *node, *query;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_VERSION);
-	query = jabber_make_packet("iq", "get", bud->full_jid, node);
-	jabber_cache_add(ic, query, jabber_iq_version_response);
-
-	jabber_write_packet(ic, query);
-}
-
-static xt_status jabber_iq_version_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query;
-	GString *rets;
-	char *s;
-	char *ret[2] = {};
-	bee_user_t *bu;
-	struct jabber_buddy *bud = NULL;
-
-	if ((s = xt_find_attr(node, "from")) &&
-	    (bud = jabber_buddy_by_jid(ic, s, 0)) &&
-	    (query = xt_find_node(node->children, "query")) &&
-	    (bu = bee_user_by_handle(ic->bee, ic, bud->bare_jid))) {
-		rets = g_string_new("Resource ");
-		g_string_append(rets, bud->resource);
-	} else {
-		return XT_HANDLED;
-	}
-
-	for (query = query->children; query; query = query->next) {
-		if (query->text_len > 0) {
-			g_string_append_printf(rets, " %s: %s,", query->name, query->text);
-		}
-	}
-
-	g_string_truncate(rets, rets->len - 1);
-	ret[0] = rets->str;
-	imcb_buddy_action_response(bu, "VERSION", ret, NULL);
-	g_string_free(rets, TRUE);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
-                                                 struct xt_node *node, struct xt_node *orig);
-
-int jabber_iq_disco_server(struct im_connection *ic)
-{
-	struct xt_node *node, *iq;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
-	iq = jabber_make_packet("iq", "get", jd->server, node);
-
-	jabber_cache_add(ic, iq, jabber_iq_disco_server_response);
-	return jabber_write_packet(ic, iq);
-}
-
-static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
-                                                 struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *query, *id;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		return XT_HANDLED;
-	}
-
-	if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
-	    set_getbool(&ic->acc->set, "carbons")) {
-
-		struct xt_node *enable, *iq;
-
-		enable = xt_new_node("enable", NULL, NULL);
-		xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
-		iq = jabber_make_packet("iq", "set", NULL, enable);
-
-		jabber_cache_add(ic, iq, jabber_iq_carbons_response);
-		jabber_write_packet(ic, iq);
-	}
-
-	if ((id = xt_find_node(query->children, "identity"))) {
-		char *cat, *type, *name;
-
-		if (!(cat = xt_find_attr(id, "category")) ||
-		    !(type = xt_find_attr(id, "type")) ||
-		    !(name = xt_find_attr(id, "name"))) {
-			return XT_HANDLED;
-		}
-
-		if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
-		    strstr(name, "Google") != NULL) {
-			jd->flags |= JFLAG_GTALK;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_carbons_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_error *err;
-
-	if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
-		imcb_error(ic, "Error enabling carbons: %s%s%s",
-		           err->code, err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-	} else {
-		imcb_log(ic, "Carbons enabled");
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server)
-{
-	struct xt_node *node;
-	int st;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS);
-	node = jabber_make_packet("iq", "get", (char *) muc_server, node);
-
-	jabber_cache_add(ic, node, jabber_iq_disco_muc_response);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query, *c;
-	struct jabber_error *err;
-	GSList *rooms =  NULL;
-
-	if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
-		imcb_error(ic, "The server replied with an error: %s%s%s",
-		           err->code, err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-		return XT_HANDLED;
-	}
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_error(ic, "Received incomplete MUC list reply");
-		return XT_HANDLED;
-	}
-
-	c = query->children;
-	while ((c = xt_find_node(c, "item"))) {
-		char *jid = xt_find_attr(c, "jid");
-
-		if (!jid || !strchr(jid, '@')) {
-			c = c->next;
-			continue;
-		}
-
-		bee_chat_info_t *ci = g_new(bee_chat_info_t, 1);
-		ci->title = g_strdup(xt_find_attr(c, "jid"));
-		ci->topic = g_strdup(xt_find_attr(c, "name"));
-		rooms = g_slist_prepend(rooms, ci);
-
-		c = c->next;
-	}
-
-	imcb_chat_list_free(ic);
-	ic->chatlist = g_slist_reverse(rooms);
-	imcb_chat_list_finish(ic);
-
-	return XT_HANDLED;
-}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/jabber/jabber.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,808 +1,2445 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Main file                                                *
-*                                                                           *
-*  Copyright 2006-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <glib.h>
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef _WIN32
+#include <sys/utsname.h>
+#endif
+#include <errno.h>
 #include <string.h>
-#include <unistd.h>
-#include <ctype.h>
+#include <stdlib.h>
 #include <stdio.h>
-
+#include <time.h>
+#include <sys/stat.h>
+#include "jabber.h"
+#include "nogaim.h"
+#include "bitlbee.h"
+#include "proxy.h"
 #include "ssl_client.h"
-#include "xmltree.h"
-#include "bitlbee.h"
-#include "jabber.h"
-#include "oauth.h"
-
-GSList *jabber_connections;
-
-/* First enty is the default */
-static const int jabber_port_list[] = {
-	5222,
-	5223,
-	5220,
-	5221,
-	5224,
-	5225,
-	5226,
-	5227,
-	5228,
-	5229,
-	80,
-	443,
-	0
+
+/* The priv member of gjconn's is a gaim_connection for now. */
+#define GJ_GC(x) ((struct gaim_connection *)(x)->priv)
+
+#define IQID_AUTH "__AUTH__"
+
+#define IQ_NONE -1
+#define IQ_AUTH 0
+#define IQ_ROSTER 1
+
+#define UC_AWAY (0x02 | UC_UNAVAILABLE)
+#define UC_CHAT  0x04
+#define UC_XA   (0x08 | UC_UNAVAILABLE)
+#define UC_DND  (0x10 | UC_UNAVAILABLE)
+
+#define DEFAULT_SERVER "jabber.org"
+#define DEFAULT_GROUPCHAT "conference.jabber.org"
+#define DEFAULT_PORT 5222
+#define DEFAULT_PORT_SSL 5223
+
+#define JABBER_GROUP "Friends"
+
+/* i18n disabled - Bitlbee */
+#define N_(String) String
+
+/*
+ * Note: "was_connected" may seem redundant, but it was needed and I
+ * didn't want to touch the Jabber state stuff not specific to Gaim.
+ */
+typedef struct gjconn_struct {
+	/* Core structure */
+	pool p;			/* Memory allocation pool */
+	int state;		/* Connection state flag */
+	int was_connected;	/* We were once connected */
+	int fd;			/* Connection file descriptor */
+	void *ssl;		/* SSL connection */
+	jid user;		/* User info */
+	char *pass;		/* User passwd */
+
+	/* Stream stuff */
+	int id;			/* id counter for jab_getid() function */
+	char idbuf[9];		/* temporary storage for jab_getid() */
+	char *sid;		/* stream id from server, for digest auth */
+	XML_Parser parser;	/* Parser instance */
+	xmlnode current;	/* Current node in parsing instance.. */
+
+	/* Event callback ptrs */
+	void (*on_state)(struct gjconn_struct *gjc, int state);
+	void (*on_packet)(struct gjconn_struct *gjc, jpacket p);
+
+	GHashTable *queries;	/* query tracker */
+
+	void *priv;
+} *gjconn, gjconn_struct;
+
+typedef void (*gjconn_state_h)(gjconn gjc, int state);
+typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p);
+
+static gjconn gjab_new(char *user, char *pass, void *priv);
+static void gjab_delete(gjconn gjc);
+static void gjab_state_handler(gjconn gjc, gjconn_state_h h);
+static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h);
+static void gjab_start(gjconn gjc);
+static void gjab_stop(gjconn gjc);
+/*
+static int gjab_getfd(gjconn gjc);
+static jid gjab_getjid(gjconn gjc);
+static char *gjab_getsid(gjconn gjc);
+*/
+static char *gjab_getid(gjconn gjc);
+static void gjab_send(gjconn gjc, xmlnode x);
+static void gjab_send_raw(gjconn gjc, const char *str);
+static void gjab_recv(gjconn gjc);
+static void gjab_auth(gjconn gjc);
+
+/*
+ * It is *this* to which we point the gaim_connection proto_data
+ */
+struct jabber_data {
+	gjconn gjc;
+	gboolean did_import;
+	GSList *chats;
+	GHashTable *hash;
+	time_t idle;
+	gboolean die;
 };
 
-static void jabber_init(account_t *acc)
-{
-	set_t *s;
-	char str[16];
-
-	s = set_add(&acc->set, "activity_timeout", "600", set_eval_int, acc);
-
-	s = set_add(&acc->set, "display_name", NULL, NULL, acc);
-
-	g_snprintf(str, sizeof(str), "%d", jabber_port_list[0]);
-	s = set_add(&acc->set, "port", str, set_eval_int, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "priority", "0", set_eval_priority, acc);
-
-	s = set_add(&acc->set, "proxy", "<local>;<auto>", NULL, acc);
-
-	s = set_add(&acc->set, "resource", "BitlBee", NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "resource_select", "activity", NULL, acc);
-
-	s = set_add(&acc->set, "sasl", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT;
-
-	s = set_add(&acc->set, "server", NULL, set_eval_account, acc);
-	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-
-	set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
-
-	if (strcmp(acc->prpl->name, "hipchat") == 0) {
-		set_setstr(&acc->set, "server", "chat.hipchat.com");
+/*
+ * Jabber "chat group" info.  Pointers to these go in jabber_data
+ * pending and existing chats lists.
+ */
+struct jabber_chat {
+	jid Jid;
+	struct gaim_connection *gc;
+	struct conversation *b;
+	int id;
+	int state;
+};
+
+/*
+ * Jabber chat states...
+ *
+ * Note: due to a bug in one version of the Jabber server, subscriptions
+ * to chat groups aren't (always?) properly removed at the server.  The
+ * result is clients receive Jabber "presence" notifications for JIDs
+ * they no longer care about.  The problem with such vestigial notifies is
+ * that we really have no way of telling if it's vestigial or if it's a
+ * valid "buddy" presence notification.  So we keep jabber_chat structs
+ * around after leaving a chat group and simply mark them "closed."  That
+ * way we can test for such errant presence notifications.  I.e.: if we
+ * get a presence notfication from a JID that matches a chat group JID,
+ * we disregard it.
+ */
+#define JCS_PENDING 1	/* pending */
+#define JCS_ACTIVE  2	/* active */
+#define JCS_CLOSED  3	/* closed */
+
+
+static char *jabber_name()
+{
+	return "Jabber";
+}
+
+#define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); }
+
+static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group);
+static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from);
+
+static char *create_valid_jid(const char *given, char *server, char *resource)
+{
+	char *valid;
+
+	if (!strchr(given, '@'))
+		valid = g_strdup_printf("%s@%s/%s", given, server, resource);
+	else if (!strchr(strchr(given, '@'), '/'))
+		valid = g_strdup_printf("%s/%s", given, resource);
+	else
+		valid = g_strdup(given);
+
+	return valid;
+}
+
+static gjconn gjab_new(char *user, char *pass, void *priv)
+{
+	pool p;
+	gjconn gjc;
+
+	if (!user)
+		return (NULL);
+
+	p = pool_new();
+	if (!p)
+		return (NULL);
+	gjc = pmalloc_x(p, sizeof(gjconn_struct), 0);
+	if (!gjc) {
+		pool_free(p);	/* no need for this anymore! */
+		return (NULL);
+	}
+	gjc->p = p;
+
+	if((gjc->user = jid_new(p, user)) == NULL) {
+		pool_free(p);	/* no need for this anymore! */
+		return (NULL);
+	}
+	gjc->pass = pstrdup(p, pass);
+
+	gjc->state = JCONN_STATE_OFF;
+	gjc->was_connected = 0;
+	gjc->id = 1;
+	gjc->fd = -1;
+
+	gjc->priv = priv;
+
+	return gjc;
+}
+
+static void gjab_delete(gjconn gjc)
+{
+	if (!gjc)
+		return;
+
+	gjab_stop(gjc);
+	pool_free(gjc->p);
+}
+
+static void gjab_state_handler(gjconn gjc, gjconn_state_h h)
+{
+	if (!gjc)
+		return;
+
+	gjc->on_state = h;
+}
+
+static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h)
+{
+	if (!gjc)
+		return;
+
+	gjc->on_packet = h;
+}
+
+static void gjab_stop(gjconn gjc)
+{
+	if (!gjc || gjc->state == JCONN_STATE_OFF)
+		return;
+
+	gjab_send_raw(gjc, "</stream:stream>");
+	gjc->state = JCONN_STATE_OFF;
+	gjc->was_connected = 0;
+	if (gjc->ssl) {
+		ssl_disconnect(gjc->ssl);
+		gjc->ssl = NULL;
 	} else {
-		/* this reuses set_eval_oauth, which clears the password */
-		set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
-	}
-
-	s = set_add(&acc->set, "ssl", "false", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "tls", "true", set_eval_tls, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "tls_verify", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "user_agent", "BitlBee", NULL, acc);
-
-	s = set_add(&acc->set, "xmlconsole", "false", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	/* changing this is rarely needed so keeping it secret */
-	s = set_add(&acc->set, "mail_notifications_limit", "5", set_eval_int, acc);
-	s->flags |= SET_HIDDEN_DEFAULT;
-
-	s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-
-	s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
-	              ACC_FLAG_HANDLE_DOMAINS;
-}
-
-static void jabber_generate_id_hash(struct jabber_data *jd);
-
-static void jabber_login(account_t *acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct jabber_data *jd = g_new0(struct jabber_data, 1);
-	char *s;
-
-	/* For now this is needed in the _connected() handlers if using
-	   GLib event handling, to make sure we're not handling events
-	   on dead connections. */
-	jabber_connections = g_slist_prepend(jabber_connections, ic);
-
-	jd->ic = ic;
-	ic->proto_data = jd;
-
-	jabber_set_me(ic, acc->user);
-
-	jd->fd = jd->r_inpa = jd->w_inpa = -1;
-
-	if (strcmp(acc->prpl->name, "hipchat") == 0) {
-		jd->flags |= JFLAG_HIPCHAT;
-	}
-
-	if (jd->server == NULL) {
-		imcb_error(ic, "Incomplete account name (format it like <username@jabberserver.name>)");
-		imc_logout(ic, FALSE);
+		closesocket(gjc->fd);
+	}
+	gjc->fd = -1;
+	XML_ParserFree(gjc->parser);
+	gjc->parser = NULL;
+}
+
+/*
+static int gjab_getfd(gjconn gjc)
+{
+	if (gjc)
+		return gjc->fd;
+	else
+		return -1;
+}
+
+static jid gjab_getjid(gjconn gjc)
+{
+	if (gjc)
+		return (gjc->user);
+	else
+		return NULL;
+}
+
+static char *gjab_getsid(gjconn gjc)
+{
+	if (gjc)
+		return (gjc->sid);
+	else
+		return NULL;
+}
+*/
+
+static char *gjab_getid(gjconn gjc)
+{
+	g_snprintf(gjc->idbuf, 8, "%d", gjc->id++);
+	return &gjc->idbuf[0];
+}
+
+static void gjab_send(gjconn gjc, xmlnode x)
+{
+	if (gjc && gjc->state != JCONN_STATE_OFF) {
+		char *buf = xmlnode2str(x);
+		if (!buf)
+			return;
+		else if (gjc->ssl)
+			ssl_write(gjc->ssl, buf, strlen(buf));
+		else
+			write(gjc->fd, buf, strlen(buf));
+	}
+}
+
+static void gjab_send_raw(gjconn gjc, const char *str)
+{
+	if (gjc && gjc->state != JCONN_STATE_OFF) {
+		int len;
+		
+		/*
+		 * JFIXME: No error detection?!?!
+		 */
+		if (gjc->ssl)
+			len = ssl_write(gjc->ssl, str, strlen(str));
+		else
+			len = write(gjc->fd, str, strlen(str));
+			
+		if(len < 0) {
+			/* Do NOT write to stdout/stderr directly, IRC clients
+			   might get confused, and we don't want that...
+			fprintf(stderr, "DBG: Problem sending.  Error: %d\n", errno);
+			fflush(stderr); */
+		}
+	}
+}
+
+static void gjab_reqroster(gjconn gjc)
+{
+	xmlnode x;
+
+	x = jutil_iqnew(JPACKET__GET, NS_ROSTER);
+	xmlnode_put_attrib(x, "id", gjab_getid(gjc));
+
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+}
+
+static void gjab_reqauth(gjconn gjc)
+{
+	xmlnode x, y, z;
+	char *user;
+
+	if (!gjc)
 		return;
-	}
-
-	if (strstr(jd->server, ".facebook.com")) {
-		imcb_error(ic, "Facebook's XMPP service is gone. Try this instead: https://wiki.bitlbee.org/HowtoFacebookMQTT");
-		imc_logout(ic, FALSE);
+
+	x = jutil_iqnew(JPACKET__GET, NS_AUTH);
+	xmlnode_put_attrib(x, "id", IQID_AUTH);
+	y = xmlnode_get_tag(x, "query");
+
+	user = gjc->user->user;
+
+	if (user) {
+		z = xmlnode_insert_tag(y, "username");
+		xmlnode_insert_cdata(z, user, -1);
+	}
+
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+}
+
+static void gjab_auth(gjconn gjc)
+{
+	xmlnode x, y, z;
+	char *hash, *user;
+
+	if (!gjc)
 		return;
-	}
-
-	if ((s = strchr(jd->server, '/'))) {
-		*s = 0;
-		set_setstr(&acc->set, "resource", s + 1);
-
-		/* Also remove the /resource from the original variable so we
-		   won't have to do this again every time. */
-		s = strchr(acc->user, '/');
-		*s = 0;
-	}
-
-	jd->node_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, jabber_cache_entry_free);
-	jd->buddies = g_hash_table_new(g_str_hash, g_str_equal);
-
-	if (set_getbool(&acc->set, "oauth")) {
-		GSList *p_in = NULL;
-		const char *tok;
-
-		jd->fd = jd->r_inpa = jd->w_inpa = -1;
-
-		/* There are no other options atm, so assume google for everything 
-		   Facebook and MSN XMPP used to be here. RIP. */
-		jd->oauth2_service = &oauth2_service_google;
-
-		oauth_params_parse(&p_in, ic->acc->pass);
-
-		/* First see if we have a refresh token, in which case any
-		   access token we *might* have has probably expired already
-		   anyway. */
-		if ((tok = oauth_params_get(&p_in, "refresh_token"))) {
-			sasl_oauth2_refresh(ic, tok);
-		}
-		/* If we don't have a refresh token, let's hope the access
-		   token is still usable. */
-		else if ((tok = oauth_params_get(&p_in, "access_token"))) {
-			jd->oauth2_access_token = g_strdup(tok);
-			jabber_connect(ic);
-		}
-		/* If we don't have any, start the OAuth process now. Don't
-		   even open an XMPP connection yet. */
+
+	x = jutil_iqnew(JPACKET__SET, NS_AUTH);
+	xmlnode_put_attrib(x, "id", IQID_AUTH);
+	y = xmlnode_get_tag(x, "query");
+
+	user = gjc->user->user;
+
+	if (user) {
+		z = xmlnode_insert_tag(y, "username");
+		xmlnode_insert_cdata(z, user, -1);
+	}
+
+	z = xmlnode_insert_tag(y, "resource");
+	xmlnode_insert_cdata(z, gjc->user->resource, -1);
+
+	if (gjc->sid) {
+		z = xmlnode_insert_tag(y, "digest");
+		hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1);
+		strcpy(hash, gjc->sid);
+		strcat(hash, gjc->pass);
+		hash = shahash(hash);
+		xmlnode_insert_cdata(z, hash, 40);
+	} else {
+		z = xmlnode_insert_tag(y, "password");
+		xmlnode_insert_cdata(z, gjc->pass, -1);
+	}
+
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+
+	return;
+}
+
+static void gjab_recv(gjconn gjc)
+{
+	static char buf[4096];
+	int len;
+
+	if (!gjc || gjc->state == JCONN_STATE_OFF)
+		return;
+	
+	if (gjc->ssl)
+		len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1);
+	else
+		len = read(gjc->fd, buf, sizeof(buf) - 1);
+	
+	if (len > 0) {
+		struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+		buf[len] = '\0';
+		XML_Parse(gjc->parser, buf, len, 0);
+		if (jd->die)
+			signoff(GJ_GC(gjc));
+	} else if (len < 0 || errno != EAGAIN) {
+		STATE_EVT(JCONN_STATE_OFF)
+	}
+}
+
+static void startElement(void *userdata, const char *name, const char **attribs)
+{
+	xmlnode x;
+	gjconn gjc = (gjconn) userdata;
+
+	if (gjc->current) {
+		/* Append the node to the current one */
+		x = xmlnode_insert_tag(gjc->current, name);
+		xmlnode_put_expat_attribs(x, attribs);
+
+		gjc->current = x;
+	} else {
+		x = xmlnode_new_tag(name);
+		xmlnode_put_expat_attribs(x, attribs);
+		if (strcmp(name, "stream:stream") == 0) {
+			/* special case: name == stream:stream */
+			/* id attrib of stream is stored for digest auth */
+			gjc->sid = g_strdup(xmlnode_get_attrib(x, "id"));
+			/* STATE_EVT(JCONN_STATE_AUTH) */
+			xmlnode_free(x);
+		} else {
+			gjc->current = x;
+		}
+	}
+}
+
+static void endElement(void *userdata, const char *name)
+{
+	gjconn gjc = (gjconn) userdata;
+	xmlnode x;
+	jpacket p;
+
+	if (gjc->current == NULL) {
+		/* we got </stream:stream> */
+		STATE_EVT(JCONN_STATE_OFF)
+		    return;
+	}
+
+	x = xmlnode_get_parent(gjc->current);
+
+	if (!x) {
+		/* it is time to fire the event */
+		p = jpacket_new(gjc->current);
+
+		if (gjc->on_packet)
+			(gjc->on_packet) (gjc, p);
+		else
+			xmlnode_free(gjc->current);
+	}
+
+	gjc->current = x;
+}
+
+static void jabber_callback(gpointer data, gint source, GaimInputCondition condition)
+{
+	struct gaim_connection *gc = (struct gaim_connection *)data;
+	struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+
+	gjab_recv(jd->gjc);
+}
+
+static void charData(void *userdata, const char *s, int slen)
+{
+	gjconn gjc = (gjconn) userdata;
+
+	if (gjc->current)
+		xmlnode_insert_cdata(gjc->current, s, slen);
+}
+
+static void gjab_connected(gpointer data, gint source, GaimInputCondition cond)
+{
+	xmlnode x;
+	char *t, *t2;
+	struct gaim_connection *gc = data;
+	struct jabber_data *jd;
+	gjconn gjc;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		return;
+	}
+
+	jd = gc->proto_data;
+	gjc = jd->gjc;
+
+	if (gjc->fd != source)
+		gjc->fd = source;
+
+	if (source == -1) {
+		STATE_EVT(JCONN_STATE_OFF)
+		return;
+	}
+
+	gjc->state = JCONN_STATE_CONNECTED;
+	STATE_EVT(JCONN_STATE_CONNECTED)
+
+	/* start stream */
+	x = jutil_header(NS_CLIENT, gjc->user->server);
+	t = xmlnode2str(x);
+	/* this is ugly, we can create the string here instead of jutil_header */
+	/* what do you think about it? -madcat */
+	t2 = strstr(t, "/>");
+	*t2++ = '>';
+	*t2 = '\0';
+	gjab_send_raw(gjc, "<?xml version='1.0'?>");
+	gjab_send_raw(gjc, t);
+	xmlnode_free(x);
+
+	gjc->state = JCONN_STATE_ON;
+	STATE_EVT(JCONN_STATE_ON);
+
+	gc = GJ_GC(gjc);
+	gc->inpa = gaim_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc);
+}
+
+static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition cond)
+{
+	struct gaim_connection *gc = data;
+	struct jabber_data *jd;
+	gjconn gjc;
+	
+	if (!g_slist_find(get_connections(), gc)) {
+		ssl_disconnect(source);
+		return;
+	}
+	
+	jd = gc->proto_data;
+	gjc = jd->gjc;
+	
+	if (source == NULL) {
+		STATE_EVT(JCONN_STATE_OFF)
+		return;
+	}
+	
+	gjab_connected(data, gjc->fd, cond);
+}
+
+static void gjab_start(gjconn gjc)
+{
+	struct aim_user *user;
+	int port = -1, ssl = 0;
+	char *server = NULL, *s;
+
+	if (!gjc || gjc->state != JCONN_STATE_OFF)
+		return;
+
+	user = GJ_GC(gjc)->user;
+	if (*user->proto_opt[0]) {
+		/* If there's a dot, assume there's a hostname in the beginning */
+		if (strchr(user->proto_opt[0], '.')) {
+			server = g_strdup(user->proto_opt[0]);
+			if ((s = strchr(server, ':')))
+				*s = 0;
+		}
+		
+		/* After the hostname, there can be a port number */
+		s = strchr(user->proto_opt[0], ':');
+		if (s && isdigit(s[1]))
+			sscanf(s + 1, "%d", &port);
+		
+		/* And if there's the string ssl, the user wants an SSL-connection */
+		if (strstr(user->proto_opt[0], ":ssl") || g_strcasecmp(user->proto_opt[0], "ssl") == 0)
+			ssl = 1;
+	}
+	
+	if (port == -1 && !ssl)
+		port = DEFAULT_PORT;
+	else if (port == -1 && ssl)
+		port = DEFAULT_PORT_SSL;
+	
+	if (server == NULL)
+		server = g_strdup(gjc->user->server);
+
+	gjc->parser = XML_ParserCreate(NULL);
+	XML_SetUserData(gjc->parser, (void *)gjc);
+	XML_SetElementHandler(gjc->parser, startElement, endElement);
+	XML_SetCharacterDataHandler(gjc->parser, charData);
+	
+	if (ssl) {
+		if ((gjc->ssl = ssl_connect(server, port, gjab_connected_ssl, GJ_GC(gjc))))
+			gjc->fd = ssl_getfd(gjc->ssl);
+		else
+			gjc->fd = -1;
+	} else {
+		gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc));
+	}
+	
+	g_free(server);
+	
+	if (!user->gc || (gjc->fd < 0)) {
+		STATE_EVT(JCONN_STATE_OFF)
+		return;
+	}
+}
+
+/*
+ * Find existing/active Jabber chat
+ */
+static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat)
+{
+	GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats;
+	struct jabber_chat *jc = NULL;
+
+	while (jcs) {
+		jc = jcs->data;
+		if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER))
+			break;
+		jc = NULL;
+		jcs = jcs->next;
+	}
+
+	return jc;
+}
+
+/*
+ * Find pending chat
+ */
+static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat)
+{
+	GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats;
+	struct jabber_chat *jc = NULL;
+
+	while (jcs) {
+		jc = jcs->data;
+		if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER))
+			break;
+		jc = NULL;
+		jcs = jcs->next;
+	}
+
+	return jc;
+}
+
+static gboolean find_chat_buddy(struct conversation *b, char *name)
+{
+	GList *m = b->in_room;
+
+	while (m) {
+		if (!strcmp(m->data, name))
+			return TRUE;
+		m = m->next;
+	}
+
+	return FALSE;
+}
+
+/*
+ * Remove a buddy from the (gaim) buddylist (if he's on it)
+ */
+static void jabber_remove_gaim_buddy(struct gaim_connection *gc, char *buddyname)
+{
+	struct buddy *b;
+
+	if ((b = find_buddy(gc, buddyname)) != NULL) {
+		/* struct group *group;
+
+		group = find_group_by_buddy(gc, buddyname);
+		remove_buddy(gc, group, b); */
+		jabber_remove_buddy(gc, b->name, JABBER_GROUP);
+	}
+}
+
+/*
+ * keep track of away msg same as yahoo plugin
+ */
+static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type)
+{
+	struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+	gpointer val = g_hash_table_lookup(jd->hash, name);
+	char *show;
+	char *vshow = NULL;
+	char *status = NULL;
+	char *msg = NULL;
+
+	if (type && (g_strcasecmp(type, "unavailable") == 0)) {
+		vshow = _("Unavailable");
+	} else {
+		if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) {
+			if (!g_strcasecmp(show, "away")) {
+				vshow = _("Away");
+			} else if (!g_strcasecmp(show, "chat")) {
+				vshow = _("Online");
+			} else if (!g_strcasecmp(show, "xa")) {
+				vshow = _("Extended Away");
+			} else if (!g_strcasecmp(show, "dnd")) {
+				vshow = _("Do Not Disturb");
+			}
+		}
+	}
+
+	status = xmlnode_get_tag_data(p->x, "status");
+
+	if(vshow != NULL || status != NULL ) {
+		/* kinda hokey, but it works :-) */
+		msg = g_strdup_printf("%s%s%s",
+			(vshow == NULL? "" : vshow),
+			(vshow == NULL || status == NULL? "" : ": "),
+			(status == NULL? "" : status));
+	} else {
+		msg = g_strdup(_("Online"));
+	}
+
+	if (val) {
+		g_free(val);
+		g_hash_table_insert(jd->hash, name, msg);
+	} else {
+		g_hash_table_insert(jd->hash, g_strdup(name), msg);
+	}
+}
+
+static time_t iso8601_to_time(char *timestamp)
+{
+	struct tm t;
+	time_t retval = 0;
+
+	if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d",
+		&t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec))
+	{
+		t.tm_year -= 1900;
+		t.tm_mon -= 1;
+		t.tm_isdst = 0;
+		retval = mktime(&t);
+#		ifdef HAVE_TM_GMTOFF
+			retval += t.tm_gmtoff;
+#		else
+#		        ifdef HAVE_TIMEZONE
+				tzset();	/* making sure */
+				retval -= timezone;
+#		        endif
+#		endif
+	}
+
+	return retval;
+}
+
+static void jabber_handlemessage(gjconn gjc, jpacket p)
+{
+	xmlnode y, xmlns, subj, z;
+	time_t time_sent = time(NULL);
+
+	char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL;
+	char m[BUF_LONG * 2];
+
+	type = xmlnode_get_attrib(p->x, "type");
+
+	z = xmlnode_get_firstchild(p->x);
+
+	while(z)
+	{
+	   if(NSCHECK(z,NS_DELAY))
+	   {
+	      char *timestamp = xmlnode_get_attrib(z,"stamp");
+	      time_sent = iso8601_to_time(timestamp);
+	   }
+	   z = xmlnode_get_nextsibling(z);
+	}
+
+	if (!type || !g_strcasecmp(type, "normal") || !g_strcasecmp(type, "chat")) {
+
+		/* XXX namespaces could be handled better. (mid) */
+		if ((xmlns = xmlnode_get_tag(p->x, "x")))
+			type = xmlnode_get_attrib(xmlns, "xmlns");
+
+		from = jid_full(p->from);
+		/*
+		if ((y = xmlnode_get_tag(p->x, "html"))) {
+			msg = xmlnode_get_data(y);
+		} else
+		*/
+		if ((y = xmlnode_get_tag(p->x, "body"))) {
+			msg = xmlnode_get_data(y);
+		}
+
+
+		if (!from)
+			return;
+
+		if (type && !g_strcasecmp(type, "jabber:x:conference")) {
+			char *room;
+			GList *m = NULL;
+			char **data;
+
+			room = xmlnode_get_attrib(xmlns, "jid");
+			data = g_strsplit(room, "@", 2);
+			m = g_list_append(m, g_strdup(data[0]));
+			m = g_list_append(m, g_strdup(data[1]));
+			m = g_list_append(m, g_strdup(gjc->user->user));
+			g_strfreev(data);
+
+			/* ** Bitlbee ** serv_got_chat_invite(GJ_GC(gjc), room, from, msg, m); */
+		} else if (msg) { /* whisper */
+			struct jabber_chat *jc;
+			g_snprintf(m, sizeof(m), "%s", msg);
+			if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b)
+				serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent);
+			else {
+				int flags = 0;
+				/* ** Bitlbee **
+				if (xmlnode_get_tag(p->x, "gaim"))
+					flags = IM_FLAG_GAIMUSER;
+				if (find_conversation(jid_full(p->from)))
+					serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, time_sent, -1);
+				else {
+				** End - Bitlbee ** */
+					if(p->from->user) {
+					    from = g_strdup_printf("%s@%s", p->from->user, p->from->server);
+					} else {
+					    /* server message? */
+					    from = g_strdup(p->from->server);
+					}
+					serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1);
+					g_free(from);
+				/* ** Bitlbee ** } ** End - Bitlbee ** */
+			}
+		}
+
+	} else if (!g_strcasecmp(type, "error")) {
+		if ((y = xmlnode_get_tag(p->x, "error"))) {
+			type = xmlnode_get_attrib(y, "code");
+			msg = xmlnode_get_data(y);
+		}
+
+		if (msg) {
+			from = g_strdup_printf("Error %s", type ? type : "");
+			do_error_dialog(GJ_GC(gjc), msg, from);
+			g_free(from);
+		}
+	} else if (!g_strcasecmp(type, "groupchat")) {
+		struct jabber_chat *jc;
+		static int i = 0;
+
+		/*
+		if ((y = xmlnode_get_tag(p->x, "html"))) {
+			msg = xmlnode_get_data(y);
+		} else
+		*/
+		if ((y = xmlnode_get_tag(p->x, "body"))) {
+			msg = xmlnode_get_data(y);
+		}
+
+		msg = utf8_to_str(msg);
+		
+		if ((subj = xmlnode_get_tag(p->x, "subject"))) {
+		   	topic = xmlnode_get_data(subj);
+		} 
+		topic = utf8_to_str(topic);
+
+		jc = find_existing_chat(GJ_GC(gjc), p->from);
+		if (!jc) {
+			/* we're not in this chat. are we supposed to be? */
+			if ((jc = find_pending_chat(GJ_GC(gjc), p->from)) != NULL) {
+				/* yes, we're supposed to be. so now we are. */
+				jc->b = serv_got_joined_chat(GJ_GC(gjc), i++, p->from->user);
+				jc->id = jc->b->id;
+				jc->state = JCS_ACTIVE;
+			} else {
+				/* no, we're not supposed to be. */
+				g_free(msg);
+				return;
+			}
+		}
+		if (p->from->resource) {
+			if (!y) {
+				if (!find_chat_buddy(jc->b, p->from->resource)) {
+					add_chat_buddy(jc->b, p->from->resource);
+				} else if ((y = xmlnode_get_tag(p->x, "status"))) {
+					char *buf;
+
+					buf = g_strdup_printf("%s@%s/%s",
+						p->from->user, p->from->server, p->from->resource);
+					jabber_track_away(gjc, p, buf, NULL);
+					g_free(buf);
+
+				}
+			} else if (jc->b && msg) {
+				char buf[8192];
+
+				if (topic) {
+					char tbuf[8192];
+					g_snprintf(tbuf, sizeof(tbuf), "%s", topic);
+				}
+				
+
+				g_snprintf(buf, sizeof(buf), "%s", msg);
+				serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, time_sent);
+			}
+		} else { /* message from the server */
+		   	if(jc->b && topic) {
+			   	char tbuf[8192];
+				g_snprintf(tbuf, sizeof(tbuf), "%s", topic);
+			}
+		}
+
+		g_free(msg);
+		g_free(topic);
+
+	}
+}
+	   
+static void jabber_handlepresence(gjconn gjc, jpacket p)
+{
+	char *to, *from, *type;
+	struct buddy *b = NULL;
+	jid who;
+	char *buddy;
+	xmlnode y;
+	char *show;
+	int state = 0;
+	GSList *resources;
+	char *res;
+	struct conversation *cnv = NULL;
+	struct jabber_chat *jc = NULL;
+
+	to = xmlnode_get_attrib(p->x, "to");
+	from = xmlnode_get_attrib(p->x, "from");
+	type = xmlnode_get_attrib(p->x, "type");
+	
+	if (type && g_strcasecmp(type, "error") == 0) {
+		return;
+	}
+	else if ((y = xmlnode_get_tag(p->x, "show"))) {
+		show = xmlnode_get_data(y);
+		if (!show) {
+			state = 0;
+		} else if (!g_strcasecmp(show, "away")) {
+			state = UC_AWAY;
+		} else if (!g_strcasecmp(show, "chat")) {
+			state = UC_CHAT;
+		} else if (!g_strcasecmp(show, "xa")) {
+			state = UC_XA;
+		} else if (!g_strcasecmp(show, "dnd")) {
+			state = UC_DND;
+		}
+	} else {
+		state = 0;
+	}
+
+	who = jid_new(gjc->p, from);
+	if (who->user == NULL) {
+		/* FIXME: transport */
+		return;
+	}
+
+	buddy = g_strdup_printf("%s@%s", who->user, who->server);
+
+	/* um. we're going to check if it's a chat. if it isn't, and there are pending
+	 * chats, create the chat. if there aren't pending chats and we don't have the
+	 * buddy on our list, simply bail out. */
+	if ((cnv = NULL) == NULL) {
+		static int i = 0x70;
+		if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) {
+			jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user);
+			jc->id = jc->b->id;
+			jc->state = JCS_ACTIVE;
+		} else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) {
+			g_free(buddy);
+			return;
+		}
+	}
+
+	if (!cnv) {
+		resources = b->proto_data;
+		res = who->resource;
+		if (res)
+			while (resources) {
+				if (!strcmp(res, resources->data))
+					break;
+				resources = resources->next;
+			}
+
+		/* keep track of away msg same as yahoo plugin */
+		jabber_track_away(gjc, p, normalize(b->name), type);
+
+		if (type && (g_strcasecmp(type, "unavailable") == 0)) {
+			if (resources) {
+				g_free(resources->data);
+				b->proto_data = g_slist_remove(b->proto_data, resources->data);
+			}
+			if (!b->proto_data) {
+				serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0);
+			}
+		} else {
+			if (!resources) {
+				b->proto_data = g_slist_append(b->proto_data, g_strdup(res));
+			}
+
+			serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0);
+
+		}
+	} else {
+		if (who->resource) {
+			char *buf;
+
+			buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);
+			jabber_track_away(gjc, p, buf, type);
+			g_free(buf);
+
+			if (type && !g_strcasecmp(type, "unavailable")) {
+				struct jabber_data *jd;
+				if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) {
+					g_free(buddy);
+					return;
+				}
+				jd = jc->gc->proto_data;
+				/* if it's not ourselves...*/
+				if (strcmp(who->resource, jc->Jid->resource) && jc->b) {
+					remove_chat_buddy(jc->b, who->resource, NULL);
+					g_free(buddy);
+					return;
+				}
+
+				jc->state = JCS_CLOSED;
+				serv_got_chat_left(GJ_GC(gjc), jc->id);
+				/*
+				 * TBD: put back some day?
+				jd->chats = g_slist_remove(jd->chats, jc);
+				g_free(jc);
+				 */
+			} else {
+				if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) {
+					g_free(buddy);
+					return;
+				}
+				if (!find_chat_buddy(jc->b, who->resource)) {
+					add_chat_buddy(jc->b, who->resource);
+				}
+			}
+		}
+	}
+
+	g_free(buddy);
+
+	return;
+}
+
+/*
+ * Used only by Jabber accept/deny add stuff just below
+ */
+struct jabber_add_permit {
+	gjconn gjc;
+	gchar *user;
+};
+
+/*
+ * Common part for Jabber accept/deny adds
+ *
+ * "type" says whether we'll permit/deny the subscribe request
+ */
+static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type)
+{
+	xmlnode g = xmlnode_new_tag("presence");
+
+	xmlnode_put_attrib(g, "to", jap->user);
+	xmlnode_put_attrib(g, "type", type);
+	gjab_send(jap->gjc, g);
+
+	xmlnode_free(g);
+}
+
+/*
+ * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n()
+ */
+static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap)
+{
+	jabber_accept_deny_add(jap, "subscribed");
+	/*
+	 * If we don't already have the buddy on *our* buddylist,
+	 * ask if we want him or her added.
+	 */
+	if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) {
+		show_got_added(GJ_GC(jap->gjc), NULL, jap->user, NULL, NULL);
+	}
+	g_free(jap->user);
+	g_free(jap);
+}
+
+/*
+ * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n()
+ */
+static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap)
+{
+	jabber_accept_deny_add(jap, "unsubscribed");
+	g_free(jap->user);
+	g_free(jap);
+}
+
+/*
+ * Handle subscription requests
+ */
+static void jabber_handles10n(gjconn gjc, jpacket p)
+{
+	xmlnode g;
+	char *Jid = xmlnode_get_attrib(p->x, "from");
+	char *type = xmlnode_get_attrib(p->x, "type");
+
+	g = xmlnode_new_tag("presence");
+	xmlnode_put_attrib(g, "to", Jid);
+
+	if (!strcmp(type, "subscribe")) {
+		/*
+		 * A "subscribe to us" request was received - put up the approval dialog
+		 */
+		struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1);
+		gchar *msg = g_strdup_printf(_("The user %s wants to add you to their buddy list."),
+				Jid);
+
+		jap->gjc = gjc;
+		jap->user = g_strdup(Jid);
+		do_ask_dialog(GJ_GC(gjc), msg, jap, jabber_accept_add, jabber_deny_add);
+
+		g_free(msg);
+		xmlnode_free(g);	/* Never needed it here anyway */
+		return;
+
+	} else if (!strcmp(type, "unsubscribe")) {
+		/*
+		 * An "unsubscribe to us" was received - simply "approve" it
+		 */
+		xmlnode_put_attrib(g, "type", "unsubscribed");
+	} else {
+		/*
+		 * Did we attempt to subscribe to somebody and they do not exist?
+		 */
+		if (!strcmp(type, "unsubscribed")) {
+			xmlnode y;
+			char *status;
+			if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) &&
+					!strcmp(status, "Not Found")) {
+				char *msg = g_strdup_printf("%s: \"%s\"", _("No such user"), 
+					xmlnode_get_attrib(p->x, "from"));
+				do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error"));
+				g_free(msg);
+			}
+		}
+
+		xmlnode_free(g);
+		return;
+	}
+
+	gjab_send(gjc, g);
+	xmlnode_free(g);
+}
+
+/*
+ * Pending subscription to a buddy?
+ */
+#define BUD_SUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \
+					(ask) != NULL && !g_strcasecmp((ask), "subscribe")) 
+
+/*
+ * Subscribed to a buddy?
+ */
+#define BUD_SUBD_TO(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \
+					((ask) == NULL || !g_strcasecmp((ask), "subscribe")))
+
+/*
+ * Pending unsubscription to a buddy?
+ */
+#define BUD_USUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \
+					(ask) != NULL && !g_strcasecmp((ask), "unsubscribe")) 
+
+/*
+ * Unsubscribed to a buddy?
+ */
+#define BUD_USUBD_TO(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \
+					((ask) == NULL || !g_strcasecmp((ask), "unsubscribe")))
+
+/*
+ * If a buddy is added or removed from the roster on another resource
+ * jabber_handlebuddy is called
+ *
+ * Called with roster item node.
+ */
+static void jabber_handlebuddy(gjconn gjc, xmlnode x)
+{
+	xmlnode g;
+	char *Jid, *name, *sub, *ask;
+	jid who;
+	struct buddy *b = NULL;
+	char *buddyname, *groupname = NULL;
+
+	Jid = xmlnode_get_attrib(x, "jid");
+	name = xmlnode_get_attrib(x, "name");
+	sub = xmlnode_get_attrib(x, "subscription");
+	ask = xmlnode_get_attrib(x, "ask");
+	who = jid_new(gjc->p, Jid);
+
+	/* JFIXME: jabber_handleroster() had a "FIXME: transport" at this
+	 * equivilent point.  So...
+	 *
+	 * We haven't allocated any memory or done anything interesting to
+	 * this point, so we'll violate Good Coding Structure here by
+	 * simply bailing out.
+	 */
+	if (!who || !who->user) {
+		return;
+	}
+
+	buddyname = g_strdup_printf("%s@%s", who->user, who->server);
+
+	if((g = xmlnode_get_tag(x, "group")) != NULL) {
+		groupname = xmlnode_get_data(g);
+	}
+
+	/*
+	 * Add or remove a buddy?  Change buddy's alias or group?
+	 */
+	if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) {
+		if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) {
+			add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname,
+				name ? name : buddyname);
+		} else {
+			/* struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); */
+
+			/* 
+			 * If the buddy's in a new group or his/her alias is changed...
+			 */
+			if(groupname) {
+				int present = b->present;	/* save presence state */
+				int uc = b->uc;			/* and away state (?) */
+				int idle = b->idle;
+				int signon = b->signon;
+
+				/*
+				 * seems rude, but it seems to be the only way...
+				 */
+				/* remove_buddy(GJ_GC(gjc), c_grp, b); */
+				jabber_remove_buddy(GJ_GC(gjc), b->name, JABBER_GROUP);
+				
+				add_buddy(GJ_GC(gjc), groupname, buddyname,
+					name ? name : buddyname);
+				if(present) {
+					serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0);
+				}
+			} else if(name != NULL && strcmp(b->show, name)) {
+				strncpy(b->show, name, BUDDY_ALIAS_MAXLEN);
+				b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0';	/* cheap safety feature */
+				serv_buddy_rename(GJ_GC(gjc), buddyname, b->show);
+			}
+		}
+	}  else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !g_strcasecmp(sub, "remove")) {
+		jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname);
+	}
+	g_free(buddyname);
+
+}
+
+static void jabber_handleroster(gjconn gjc, xmlnode querynode)
+{
+	xmlnode x;
+
+	x = xmlnode_get_firstchild(querynode);
+	while (x) {
+		jabber_handlebuddy(gjc, x);
+		x = xmlnode_get_nextsibling(x);
+	}
+
+	x = jutil_presnew(0, NULL, "Online");
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+}
+
+static void jabber_handleauthresp(gjconn gjc, jpacket p)
+{
+	if (jpacket_subtype(p) == JPACKET__RESULT) {
+		if (xmlnode_has_children(p->x)) {
+			xmlnode query = xmlnode_get_tag(p->x, "query");
+			set_login_progress(GJ_GC(gjc), 4, _("Authenticating"));
+			if (!xmlnode_get_tag(query, "digest")) {
+				g_free(gjc->sid);
+				gjc->sid = NULL;
+			}
+			gjab_auth(gjc);
+		} else {
+			account_online(GJ_GC(gjc));
+
+			if (bud_list_cache_exists(GJ_GC(gjc)))
+				do_import(GJ_GC(gjc), NULL);
+
+			((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE;
+
+			gjab_reqroster(gjc);
+		}
+	} else {
+		xmlnode xerr;
+		char *errmsg = NULL;
+		int errcode = 0;
+		struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+
+		xerr = xmlnode_get_tag(p->x, "error");
+		if (xerr) {
+			char msg[BUF_LONG];
+			errmsg = xmlnode_get_data(xerr);
+			if (xmlnode_get_attrib(xerr, "code")) {
+				errcode = atoi(xmlnode_get_attrib(xerr, "code"));
+				g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg ? errmsg : "Unknown error");
+			} else
+				g_snprintf(msg, sizeof(msg), "%s", errmsg);
+			hide_login_progress(GJ_GC(gjc), msg);
+		} else {
+			hide_login_progress(GJ_GC(gjc), _("Unknown login error"));
+		}
+
+		jd->die = TRUE;
+	}
+}
+
+static void jabber_handleversion(gjconn gjc, xmlnode iqnode) {
+	xmlnode querynode, x;
+	char *id, *from;
+	char os[1024];
+#ifndef _WIN32
+	struct utsname osinfo;
+
+	uname(&osinfo);
+	g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine);
+#else
+	g_snprintf(os, sizeof os, "Windows %d %d", _winmajor, _winminor);
+#endif
+
+
+	id = xmlnode_get_attrib(iqnode, "id");
+	from = xmlnode_get_attrib(iqnode, "from");
+
+	x = jutil_iqnew(JPACKET__RESULT, NS_VERSION);
+
+	xmlnode_put_attrib(x, "to", from);
+	xmlnode_put_attrib(x, "id", id);
+	querynode = xmlnode_get_tag(x, "query");
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1);
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), BITLBEE_VERSION, -1);
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1);
+
+	gjab_send(gjc, x);
+
+	xmlnode_free(x);
+}
+
+static void jabber_handletime(gjconn gjc, xmlnode iqnode) {
+	xmlnode querynode, x;
+	char *id, *from;
+	time_t now_t; 
+	struct tm *now;
+	char buf[1024];
+
+	time(&now_t);
+	now = localtime(&now_t);
+
+	id = xmlnode_get_attrib(iqnode, "id");
+	from = xmlnode_get_attrib(iqnode, "from");
+
+	x = jutil_iqnew(JPACKET__RESULT, NS_TIME);
+
+	xmlnode_put_attrib(x, "to", from);
+	xmlnode_put_attrib(x, "id", id);
+	querynode = xmlnode_get_tag(x, "query");
+
+	strftime(buf, 1024, "%Y%m%dT%T", now);
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1);
+	strftime(buf, 1024, "%Z", now);
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1);
+	strftime(buf, 1024, "%d %b %Y %T", now);
+	xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1);
+	
+	gjab_send(gjc, x);
+
+	xmlnode_free(x);
+}
+
+static void jabber_handlelast(gjconn gjc, xmlnode iqnode) {
+   	xmlnode x, querytag;
+	char *id, *from;
+	struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+	char idle_time[32];
+	
+	id = xmlnode_get_attrib(iqnode, "id");
+	from = xmlnode_get_attrib(iqnode, "from");
+
+	x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last");
+
+	xmlnode_put_attrib(x, "to", from);
+	xmlnode_put_attrib(x, "id", id);
+	querytag = xmlnode_get_tag(x, "query");
+	g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0);
+	xmlnode_put_attrib(querytag, "seconds", idle_time);
+
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+}
+
+/*
+ * delete == TRUE: delete found entry
+ *
+ * returns pointer to (local) copy of value if found, NULL otherwise
+ *
+ * Note: non-reentrant!  Local static storage re-used on subsequent calls.
+ * If you're going to need to keep the returned value, make a copy!
+ */
+static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete)
+{
+	gpointer my_key, my_val;
+	static gchar *ret_val = NULL;
+
+	if(ret_val != NULL) {
+		g_free(ret_val);
+		ret_val = NULL;
+	}
+
+	/* self-protection */
+	if(queries != NULL && key != NULL) {
+		if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) {
+			ret_val = g_strdup((gchar *) my_val);
+			if(delete) {
+				g_hash_table_remove(queries, key);
+				g_free(my_key);
+				g_free(my_val);
+			}
+		}
+	}
+
+	return(ret_val);
+}
+
+static void jabber_handlepacket(gjconn gjc, jpacket p)
+{
+	char *id;
+	switch (p->type) {
+	case JPACKET_MESSAGE:
+		jabber_handlemessage(gjc, p);
+		break;
+	case JPACKET_PRESENCE:
+		jabber_handlepresence(gjc, p);
+		break;
+	case JPACKET_IQ:
+		id = xmlnode_get_attrib(p->x, "id");
+		if (id != NULL && !strcmp(id, IQID_AUTH)) {
+			jabber_handleauthresp(gjc, p);
+			break;
+		}
+
+		if (jpacket_subtype(p) == JPACKET__SET) {
+			xmlnode querynode;
+			querynode = xmlnode_get_tag(p->x, "query");
+			if (NSCHECK(querynode, "jabber:iq:roster")) {
+				jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode));
+			}
+		} else if (jpacket_subtype(p) == JPACKET__GET) {
+		   	xmlnode querynode;
+			querynode = xmlnode_get_tag(p->x, "query");
+		   	if (NSCHECK(querynode, NS_VERSION)) {
+			   	jabber_handleversion(gjc, p->x);
+			} else if (NSCHECK(querynode, NS_TIME)) {
+			   	jabber_handletime(gjc, p->x);
+			} else if (NSCHECK(querynode, "jabber:iq:last")) {
+			   	jabber_handlelast(gjc, p->x);
+			}
+		} else if (jpacket_subtype(p) == JPACKET__RESULT) {
+			xmlnode querynode, vcard;
+			/* char *xmlns; */
+			char *from;
+
+			/*
+			 * TBD: ISTM maybe this part could use a serious re-work?
+			 */
+			from = xmlnode_get_attrib(p->x, "from");
+			querynode = xmlnode_get_tag(p->x, "query");
+			vcard = xmlnode_get_tag(p->x, "vCard");
+			if (!vcard)
+				vcard = xmlnode_get_tag(p->x, "VCARD");
+
+			if (NSCHECK(querynode, NS_ROSTER)) {
+				jabber_handleroster(gjc, querynode);
+			} else if (NSCHECK(querynode, NS_VCARD)) {
+				jabber_track_queries(gjc->queries, id, TRUE);	/* delete query track */
+                                jabber_handlevcard(gjc, querynode, from);
+			} else if (vcard) {
+				jabber_track_queries(gjc->queries, id, TRUE);	/* delete query track */
+                                jabber_handlevcard(gjc, vcard, from);
+			} else {
+				char *val;
+
+				/* handle "null" query results */
+				if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) {
+					if (!g_strncasecmp(val, "vcard", 5)) {
+						jabber_handlevcard(gjc, NULL, from);
+					}
+
+					/* No-op */
+				}
+			}
+
+		} else if (jpacket_subtype(p) == JPACKET__ERROR) {
+			xmlnode xerr;
+			char *from, *errmsg = NULL;
+			int errcode = 0;
+
+			from = xmlnode_get_attrib(p->x, "from");
+			xerr = xmlnode_get_tag(p->x, "error");
+			if (xerr) {
+				errmsg = xmlnode_get_data(xerr);
+				if (xmlnode_get_attrib(xerr, "code"))
+					errcode = atoi(xmlnode_get_attrib(xerr, "code"));
+			}
+
+			from = g_strdup_printf("Error %d (%s)", errcode, from);
+			do_error_dialog(GJ_GC(gjc), errmsg, from);
+			g_free(from);
+
+		}
+
+		break;
+	case JPACKET_S10N:
+		jabber_handles10n(gjc, p);
+		break;
+	}
+
+	xmlnode_free(p->x);
+
+	return;
+}
+
+static void jabber_handlestate(gjconn gjc, int state)
+{
+	switch (state) {
+	case JCONN_STATE_OFF:
+		if(gjc->was_connected) {
+			hide_login_progress_error(GJ_GC(gjc), _("Connection lost"));
+		} else {
+			hide_login_progress(GJ_GC(gjc), _("Unable to connect"));
+		}
+		signoff(GJ_GC(gjc));
+		break;
+	case JCONN_STATE_CONNECTED:
+		gjc->was_connected = 1;
+		set_login_progress(GJ_GC(gjc), 2, _("Connected"));
+		break;
+	case JCONN_STATE_ON:
+		set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method"));
+		gjab_reqauth(gjc);
+		break;
+	}
+	return;
+}
+
+static void jabber_login(struct aim_user *user)
+{
+	struct gaim_connection *gc = new_gaim_conn(user);
+	struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1);
+	char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "BitlBee");
+
+	jd->hash = g_hash_table_new(g_str_hash, g_str_equal);
+	jd->chats = NULL;	/* we have no chats yet */
+
+	set_login_progress(gc, 1, _("Connecting"));
+
+	if (!(jd->gjc = gjab_new(loginname, user->password, gc))) {
+		g_free(loginname);
+		hide_login_progress(gc, _("Unable to connect"));
+		signoff(gc);
+		return;
+	}
+
+	g_free(loginname);
+	gjab_state_handler(jd->gjc, jabber_handlestate);
+	gjab_packet_handler(jd->gjc, jabber_handlepacket);
+	jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal);
+	gjab_start(jd->gjc);
+}
+
+static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) {
+   	g_free(key);
+	g_free(val);
+	return TRUE;
+}
+
+static gboolean jabber_free(gpointer data)
+{
+	struct jabber_data *jd = data;
+
+	if(jd->gjc != NULL) {
+		gjab_delete(jd->gjc);
+		g_free(jd->gjc->sid);
+		jd->gjc = NULL;
+	}
+	g_free(jd);
+
+	return FALSE;
+}
+
+static void jabber_close(struct gaim_connection *gc)
+{
+	struct jabber_data *jd = gc->proto_data;
+
+	if(jd) {
+		GSList *jcs = jd->chats;
+
+		/* Free-up the jabber_chat struct allocs and the list */
+		while (jcs) {
+			g_free(jcs->data);
+			jcs = jcs->next;
+		}
+		g_slist_free(jd->chats);
+
+		/* Free-up the away status memories and the list */
+		if(jd->hash != NULL) {
+			g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL);
+			g_hash_table_destroy(jd->hash);
+			jd->hash = NULL;
+		}
+
+		/* Free-up the pending queries memories and the list */
+		if(jd->gjc != NULL && jd->gjc->queries != NULL) {
+			g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL);
+			g_hash_table_destroy(jd->gjc->queries);
+			jd->gjc->queries = NULL;
+		}
+	}
+	if (gc->inpa)
+		gaim_input_remove(gc->inpa);
+
+	if(jd) {
+		g_timeout_add(50, jabber_free, jd);
+		if(jd->gjc != NULL)
+			xmlnode_free(jd->gjc->current);
+	}
+	gc->proto_data = NULL;
+}
+
+static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags)
+{
+	xmlnode x, y;
+	char *realwho;
+	gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+	if (!who || !message)
+		return 0;
+
+	x = xmlnode_new_tag("message");
+	/* Bare username and "username" not the server itself? */
+	if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0)
+		realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
+	else
+		realwho = g_strdup(who);
+	xmlnode_put_attrib(x, "to", realwho);
+	g_free(realwho);
+
+	xmlnode_insert_tag(x, "bitlbee");
+	xmlnode_put_attrib(x, "type", "chat");
+
+	if (message && strlen(message)) {
+		y = xmlnode_insert_tag(x, "body");
+		xmlnode_insert_cdata(y, message, -1);
+	}
+
+	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+	xmlnode_free(x);
+	return 1;
+}
+
+/*
+ * Add/update buddy's roster entry on server
+ */
+static void jabber_roster_update(struct gaim_connection *gc, char *name)
+{
+	xmlnode x, y;
+	char *realwho;
+	gjconn gjc;
+	struct buddy *buddy = NULL;
+	/* struct group *buddy_group = NULL; */
+	
+	if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) {
+		gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+		if (!strchr(name, '@'))
+			realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
 		else {
-			sasl_oauth2_init(ic);
-			ic->flags |= OPT_SLOW_LOGIN;
-		}
-
-		oauth_params_free(&p_in);
+			jid who = jid_new(gjc->p, name);
+			if (who->user == NULL) {
+				/* FIXME: transport */
+				return;
+			}
+			realwho = g_strdup_printf("%s@%s", who->user, who->server);
+		}
+
+
+		x = jutil_iqnew(JPACKET__SET, NS_ROSTER);
+		y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item");
+		xmlnode_put_attrib(y, "jid", realwho);
+
+
+		/* If we can find the buddy, there's an alias for him, it's not 0-length
+		 * and it doesn't match his JID, add the "name" attribute.
+		 */
+		if((buddy = find_buddy(gc, realwho)) != NULL &&
+			buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) {
+
+			xmlnode_put_attrib(y, "name", buddy->show);
+		}
+
+		/*
+		 * Find out what group the buddy's in and send that along
+		 * with the roster item.
+		 */
+		/* ** Bitlbee disabled **
+		if((buddy_group = NULL) != NULL) {
+			xmlnode z;
+			z = xmlnode_insert_tag(y, "group");
+			xmlnode_insert_cdata(z, buddy_group->name, -1);
+		}
+		** End - Bitlbee ** */
+
+		gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+
+		xmlnode_free(x);
+		g_free(realwho);
+	}
+}
+
+/*
+ * Change buddy's group on server roster
+ */
+static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group)
+{
+	if(strcmp(old_group, new_group)) {
+		jabber_roster_update(gc, name);
+	}
+}
+
+static void jabber_add_buddy(struct gaim_connection *gc, char *name)
+{
+	xmlnode x;
+	char *realwho;
+	gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+	if (!((struct jabber_data *)gc->proto_data)->did_import)
+		return;
+
+	if (!name)
+		return;
+
+	if (!strcmp(gc->username, name))
+		return;
+
+	if (!strchr(name, '@'))
+		realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
+	else {
+		jid who;
+		
+		if((who = jid_new(gjc->p, name)) == NULL) {
+			char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name);
+			do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error"));
+			g_free(msg);
+			jabber_remove_gaim_buddy(gc, name);
+			return;
+		}
+		if (who->user == NULL) {
+			/* FIXME: transport */
+			return;
+		}
+		realwho = g_strdup_printf("%s@%s", who->user, who->server);
+	}
+
+	x = xmlnode_new_tag("presence");
+	xmlnode_put_attrib(x, "to", realwho);
+	xmlnode_put_attrib(x, "type", "subscribe");
+	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+	xmlnode_free(x);
+
+	jabber_roster_update(gc, realwho);
+
+	g_free(realwho);
+}
+
+static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group)
+{
+	xmlnode x;
+	char *realwho;
+	gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+
+	if (!name)
+		return;
+
+	if (!strchr(name, '@'))
+		realwho = g_strdup_printf("%s@%s", name, gjc->user->server);
+	else
+		realwho = g_strdup(name);
+
+	x = xmlnode_new_tag("presence");
+	xmlnode_put_attrib(x, "to", realwho);
+	xmlnode_put_attrib(x, "type", "unsubscribe");
+	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
+	g_free(realwho);
+	xmlnode_free(x);
+}
+
+static void jabber_get_info(struct gaim_connection *gc, char *who) {
+	xmlnode x;
+	char *id;
+	char *realwho;
+	struct jabber_data *jd = gc->proto_data;
+	gjconn gjc = jd->gjc;
+
+	x = jutil_iqnew(JPACKET__GET, NS_VCARD);
+	/* Bare username? */
+	if (!strchr(who, '@')) {
+		realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
 	} else {
-		jabber_connect(ic);
-	}
-}
-
-static void jabber_xmlconsole_enable(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	const char *handle = JABBER_XMLCONSOLE_HANDLE;
-	bee_user_t *bu;
+		realwho = g_strdup(who);
+	}
+	xmlnode_put_attrib(x, "to", realwho);
+	g_free(realwho);
+
+	id = gjab_getid(gjc);
+	xmlnode_put_attrib(x, "id", id);
+
+	g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard"));
+
+	gjab_send(gjc, x);
+
+	xmlnode_free(x);
 	
-	jd->flags |= JFLAG_XMLCONSOLE;
-
-	if (!(bu = bee_user_by_handle(ic->bee, ic, handle))) {
-		bu = bee_user_new(ic->bee, ic, handle, 0);
-		bu->flags |= BEE_USER_NOOTR;
-	}
-}
-
-/* Separate this from jabber_login() so we can do OAuth first if necessary.
-   Putting this in io.c would probably be more correct. */
-void jabber_connect(struct im_connection *ic)
-{
-	account_t *acc = ic->acc;
-	struct jabber_data *jd = ic->proto_data;
-	int i;
-	char *connect_to;
-	struct ns_srv_reply **srvl = NULL, *srv = NULL;
-
-	/* Figure out the hostname to connect to. */
-	if (acc->server && *acc->server) {
-		connect_to = acc->server;
-	} else if ((srvl = srv_lookup("xmpp-client", "tcp", jd->server)) ||
-	           (srvl = srv_lookup("jabber-client", "tcp", jd->server))) {
-		/* Find the lowest-priority one. These usually come
-		   back in random/shuffled order. Not looking at
-		   weights etc for now. */
-		srv = *srvl;
-		for (i = 1; srvl[i]; i++) {
-			if (srvl[i]->prio < srv->prio) {
-				srv = srvl[i];
-			}
-		}
-
-		connect_to = srv->name;
+}
+
+static void jabber_get_away_msg(struct gaim_connection *gc, char *who) {
+	struct jabber_data *jd = gc->proto_data;
+	gjconn gjc = jd->gjc;
+	char *status;
+
+	/* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */
+	gchar **str_arr = (gchar **) g_new(gpointer, 3);
+	gchar **ap = str_arr;
+	gchar *realwho, *final;
+
+	/* Bare username? */
+	if (!strchr(who, '@')) {
+		realwho = g_strdup_printf("%s@%s", who, gjc->user->server);
 	} else {
-		connect_to = jd->server;
-	}
-
-	imcb_log(ic, "Connecting");
-
-	for (i = 0; jabber_port_list[i] > 0; i++) {
-		if (set_getint(&acc->set, "port") == jabber_port_list[i]) {
-			break;
-		}
-	}
-
-	if (jabber_port_list[i] == 0) {
-		imcb_log(ic, "Illegal port number");
-		imc_logout(ic, FALSE);
+		realwho = g_strdup(who);
+	}
+	*ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho);
+
+	if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) {
+		status = _("Unknown");
+	}
+	*ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status);
+
+	*ap = NULL;
+
+	final= g_strjoinv(NULL, str_arr);
+	g_strfreev(str_arr);
+
+	g_free(realwho);
+	g_free(final);
+	
+}
+
+static GList *jabber_away_states(struct gaim_connection *gc) {
+	GList *m = NULL;
+
+	m = g_list_append(m, "Online");
+	m = g_list_append(m, "Chatty");
+	m = g_list_append(m, "Away");
+	m = g_list_append(m, "Extended Away");
+	m = g_list_append(m, "Do Not Disturb");
+
+	return m;
+}
+
+static void jabber_set_away(struct gaim_connection *gc, char *state, char *message)
+{
+	xmlnode x, y;
+	struct jabber_data *jd = gc->proto_data;
+	gjconn gjc = jd->gjc;
+
+	gc->away = NULL; /* never send an auto-response */
+
+	x = xmlnode_new_tag("presence");
+
+	if (!strcmp(state, GAIM_AWAY_CUSTOM)) {
+		/* oh goody. Gaim is telling us what to do. */
+		if (message) {
+			/* Gaim wants us to be away */
+			y = xmlnode_insert_tag(x, "show");
+			xmlnode_insert_cdata(y, "away", -1);
+			y = xmlnode_insert_tag(x, "status");
+			{
+				char *utf8 = str_to_utf8(message);
+				xmlnode_insert_cdata(y, utf8, -1);
+				g_free(utf8);
+			}
+			gc->away = "";
+		} else {
+			/* Gaim wants us to not be away */
+			/* but for Jabber, we can just send presence with no other information. */
+		}
+	} else {
+		/* state is one of our own strings. it won't be NULL. */
+		if (!g_strcasecmp(state, "Online")) {
+			/* once again, we don't have to put anything here */
+		} else if (!g_strcasecmp(state, "Chatty")) {
+			y = xmlnode_insert_tag(x, "show");
+			xmlnode_insert_cdata(y, "chat", -1);
+		} else if (!g_strcasecmp(state, "Away")) {
+			y = xmlnode_insert_tag(x, "show");
+			xmlnode_insert_cdata(y, "away", -1);
+			gc->away = "";
+		} else if (!g_strcasecmp(state, "Extended Away")) {
+			y = xmlnode_insert_tag(x, "show");
+			xmlnode_insert_cdata(y, "xa", -1);
+			gc->away = "";
+		} else if (!g_strcasecmp(state, "Do Not Disturb")) {
+			y = xmlnode_insert_tag(x, "show");
+			xmlnode_insert_cdata(y, "dnd", -1);
+			gc->away = "";
+		}
+	}
+
+	gjab_send(gjc, x);
+	xmlnode_free(x);
+}
+
+static void jabber_set_idle(struct gaim_connection *gc, int idle) {
+	struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+   	jd->idle = idle ? time(NULL) - idle : idle;
+}
+
+static void jabber_keepalive(struct gaim_connection *gc) {
+	struct jabber_data *jd = (struct jabber_data *)gc->proto_data;
+	gjab_send_raw(jd->gjc, "  \t  ");
+}
+
+static void jabber_buddy_free(struct buddy *b)
+{
+	while (b->proto_data) {
+		g_free(((GSList *)b->proto_data)->data);
+		b->proto_data = g_slist_remove(b->proto_data, ((GSList *)b->proto_data)->data);
+	}
+}
+
+/*---------------------------------------*/
+/* Jabber "set info" (vCard) support     */
+/*---------------------------------------*/
+
+/*
+ * V-Card format:
+ *
+ *  <vCard prodid='' version='' xmlns=''>
+ *    <FN></FN>
+ *    <N>
+ *	<FAMILY/>
+ *	<GIVEN/>
+ *    </N>
+ *    <NICKNAME/>
+ *    <URL/>
+ *    <ADR>
+ *	<STREET/>
+ *	<EXTADD/>
+ *	<LOCALITY/>
+ *	<REGION/>
+ *	<PCODE/>
+ *	<COUNTRY/>
+ *    </ADR>
+ *    <TEL/>
+ *    <EMAIL/>
+ *    <ORG>
+ *	<ORGNAME/>
+ *	<ORGUNIT/>
+ *    </ORG>
+ *    <TITLE/>
+ *    <ROLE/>
+ *    <DESC/>
+ *    <BDAY/>
+ *  </vCard>
+ *
+ * See also:
+ *
+ *	http://docs.jabber.org/proto/html/vcard-temp.html
+ *	http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd
+ */
+
+/*
+ * Cross-reference user-friendly V-Card entry labels to vCard XML tags
+ * and attributes.
+ *
+ * Order is (or should be) unimportant.  For example: we have no way of
+ * knowing in what order real data will arrive.
+ *
+ * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag
+ *         name, XML tag's parent tag "path" (relative to vCard node).
+ *
+ *         List is terminated by a NULL label pointer.
+ *
+ *	   Entries with no label text, but with XML tag and parent tag
+ *	   entries, are used by V-Card XML construction routines to
+ *	   "automagically" construct the appropriate XML node tree.
+ *
+ * Thoughts on future direction/expansion
+ *
+ *	This is a "simple" vCard.
+ *
+ *	It is possible for nodes other than the "vCard" node to have
+ *      attributes.  Should that prove necessary/desirable, add an
+ *      "attributes" pointer to the vcard_template struct, create the
+ *      necessary tag_attr structs, and add 'em to the vcard_dflt_data
+ *      array.
+ *
+ *	The above changes will (obviously) require changes to the vCard
+ *      construction routines.
+ */
+
+static struct vcard_template {
+	char *label;			/* label text pointer */
+	char *text;			/* entry text pointer */
+	int  visible;			/* should entry field be "visible?" */
+	int  editable;			/* should entry field be editable? */
+	char *tag;			/* tag text */
+	char *ptag;			/* parent tag "path" text */
+	char *url;			/* vCard display format if URL */
+} vcard_template_data[] = {
+	{N_("Full Name"),          NULL, TRUE, TRUE, "FN",        NULL,  NULL},
+	{N_("Family Name"),        NULL, TRUE, TRUE, "FAMILY",    "N",   NULL},
+	{N_("Given Name"),         NULL, TRUE, TRUE, "GIVEN",     "N",   NULL},
+	{N_("Nickname"),           NULL, TRUE, TRUE, "NICKNAME",  NULL,  NULL},
+	{N_("URL"),                NULL, TRUE, TRUE, "URL",       NULL,  "<A HREF=\"%s\">%s</A>"},
+	{N_("Street Address"),     NULL, TRUE, TRUE, "STREET",    "ADR", NULL},
+	{N_("Extended Address"),   NULL, TRUE, TRUE, "EXTADD",    "ADR", NULL},
+	{N_("Locality"),           NULL, TRUE, TRUE, "LOCALITY",  "ADR", NULL},
+	{N_("Region"),             NULL, TRUE, TRUE, "REGION",    "ADR", NULL},
+	{N_("Postal Code"),        NULL, TRUE, TRUE, "PCODE",     "ADR", NULL},
+	{N_("Country"),            NULL, TRUE, TRUE, "COUNTRY",   "ADR", NULL},
+	{N_("Telephone"),          NULL, TRUE, TRUE, "TELEPHONE", NULL,  NULL},
+	{N_("Email"),              NULL, TRUE, TRUE, "EMAIL",     NULL,  "<A HREF=\"mailto:%s\">%s</A>"},
+	{N_("Organization Name"),  NULL, TRUE, TRUE, "ORGNAME",   "ORG", NULL},
+	{N_("Organization Unit"),  NULL, TRUE, TRUE, "ORGUNIT",   "ORG", NULL},
+	{N_("Title"),              NULL, TRUE, TRUE, "TITLE",     NULL,  NULL},
+	{N_("Role"),               NULL, TRUE, TRUE, "ROLE",      NULL,  NULL},
+	{N_("Birthday"),           NULL, TRUE, TRUE, "BDAY",      NULL,  NULL},
+	{N_("Description"),        NULL, TRUE, TRUE, "DESC",      NULL,  NULL},
+	{"", NULL, TRUE, TRUE, "N",     NULL, NULL},
+	{"", NULL, TRUE, TRUE, "ADR",   NULL, NULL},
+	{"", NULL, TRUE, TRUE, "ORG",   NULL, NULL},
+	{NULL, NULL, 0, 0, NULL, NULL, NULL}
+};
+
+/*
+ * Used by routines to parse an XML-encoded string into an xmlnode tree
+ */
+typedef struct {
+	XML_Parser parser;
+	xmlnode current;
+} *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct;
+
+
+/*
+ * Used by XML_Parse on parsing CDATA
+ */
+static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen)
+{
+	xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+
+	if (xmlp->current)
+		xmlnode_insert_cdata(xmlp->current, s, slen);
+}
+
+/*
+ * Used by XML_Parse to start or append to an xmlnode
+ */
+static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs)
+{
+	xmlnode x;
+	xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+
+	if (xmlp->current) {
+		/* Append the node to the current one */
+		x = xmlnode_insert_tag(xmlp->current, name);
+		xmlnode_put_expat_attribs(x, attribs);
+
+		xmlp->current = x;
+	} else {
+		x = xmlnode_new_tag(name);
+		xmlnode_put_expat_attribs(x, attribs);
+		xmlp->current = x;
+	}
+}
+
+/*
+ * Used by XML_Parse to end an xmlnode
+ */
+static void xmlstr2xmlnode_endElement(void *userdata, const char *name)
+{
+	xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata;
+	xmlnode x;
+
+	if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) {
+		xmlp->current = x;
+	}
+}
+
+/*
+ * Parse an XML-encoded string into an xmlnode tree
+ *
+ * Caller is responsible for freeing the returned xmlnode
+ */
+static xmlnode xmlstr2xmlnode(char *xmlstring)
+{
+	xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1);
+	xmlnode x = NULL;
+
+	my_parser->parser = XML_ParserCreate(NULL);
+	my_parser->current = NULL;
+
+	XML_SetUserData(my_parser->parser, (void *)my_parser);
+	XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement);
+	XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData);
+	XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0);
+
+	x = my_parser->current;
+
+	XML_ParserFree(my_parser->parser);
+	g_free(my_parser);
+
+	return(x);
+}
+
+/*
+ * Insert a tag node into an xmlnode tree, recursively inserting parent tag
+ * nodes as necessary
+ *
+ * Returns pointer to inserted node
+ *
+ * Note to hackers: this code is designed to be re-entrant (it's recursive--it
+ * calls itself), so don't put any "static"s in here!
+ */
+static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag)
+{
+	xmlnode x = NULL;
+
+	/*
+	 * If the parent tag wasn't specified, see if we can get it
+	 * from the vCard template struct.
+	 */
+	if(parent_tag == NULL) {
+		struct vcard_template *vc_tp = vcard_template_data;
+
+		while(vc_tp->label != NULL) {
+			if(strcmp(vc_tp->tag, new_tag) == 0) {
+				parent_tag = vc_tp->ptag;
+				break;
+			}
+			++vc_tp;
+		}
+	}
+
+	/*
+	 * If we have a parent tag...
+	 */
+	if(parent_tag != NULL ) {
+		/*
+		 * Try to get the parent node for a tag
+		 */
+		if((x = xmlnode_get_tag(start, parent_tag)) == NULL) {
+			/*
+			 * Descend?
+			 */
+			char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag);
+			char *parent;
+
+			if((parent = strrchr(grand_parent, '/')) != NULL) {
+				*(parent++) = '\0';
+				x = insert_tag_to_parent_tag(start, grand_parent, parent);
+			} else {
+				x = xmlnode_insert_tag(start, grand_parent);
+			}
+			g_free(grand_parent);
+		} else {
+			/*
+			 * We found *something* to be the parent node.
+			 * Note: may be the "root" node!
+			 */
+			xmlnode y;
+			if((y = xmlnode_get_tag(x, new_tag)) != NULL) {
+				return(y);
+			}
+		}
+	}
+
+	/*
+	 * insert the new tag into its parent node
+	 */
+	return(xmlnode_insert_tag((x == NULL? start : x), new_tag));
+}
+
+/*
+ * Send vCard info to Jabber server
+ */
+static void jabber_set_info(struct gaim_connection *gc, char *info)
+{
+	xmlnode x, vc_node;
+	char *id;
+	struct jabber_data *jd = gc->proto_data;
+	gjconn gjc = jd->gjc;
+
+	x = xmlnode_new_tag("iq");
+	xmlnode_put_attrib(x,"type","set");
+
+	id = gjab_getid(gjc);
+	
+	xmlnode_put_attrib(x, "id", id);
+
+	/*
+	 * Send only if there's actually any *information* to send
+	 */
+	if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL &&
+			g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) {
+		xmlnode_insert_tag_node(x, vc_node);
+		gjab_send(gjc, x);
+	}
+
+	xmlnode_free(x);
+}
+
+/*
+ * displays a Jabber vCard
+ */
+static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from)
+{
+	struct jabber_data *jd = GJ_GC(gjc)->proto_data;
+	jid who = jid_new(gjc->p, from);
+	char *status = NULL, *text = NULL;
+	GString *str = g_string_sized_new(100);
+	xmlnode child;
+
+	gchar *buddy = NULL;
+	
+	if(querynode == NULL) {
+		serv_got_crap(GJ_GC(gjc), "%s - Received empty info reply from %s", _("User Info"), from);
 		return;
 	}
 
-	/* For non-SSL connections we can try to use the port # from the SRV
-	   reply, but let's not do that when using SSL, SSL usually runs on
-	   non-standard ports... */
-	if (set_getbool(&acc->set, "ssl")) {
-		jd->ssl = ssl_connect(connect_to, set_getint(&acc->set, "port"), set_getbool(&acc->set,
-		                                                                             "tls_verify"), jabber_connected_ssl,
-		                      ic);
-		jd->fd = jd->ssl ? ssl_getfd(jd->ssl) : -1;
+	if(who->resource != NULL && (who->resource)[0] != '\0') {
+		buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);
 	} else {
-		jd->fd = proxy_connect(connect_to, srv ? srv->port : set_getint(&acc->set,
-		                                                                "port"), jabber_connected_plain, ic);
-	}
-	srv_free(srvl);
-
-	if (jd->fd == -1) {
-		imcb_error(ic, "Could not connect to server");
-		imc_logout(ic, TRUE);
-
-		return;
-	}
-
-	if (set_getbool(&acc->set, "xmlconsole")) {
-		jabber_xmlconsole_enable(ic);
-	}
-
-	if (set_getbool(&acc->set, "mail_notifications")) {
-		/* It's gmail specific, but it checks for server support before enabling it */
-		jd->flags |= JFLAG_GMAILNOTIFY;
-		if (set_getstr(&acc->set, "mail_notifications_handle")) {
-			imcb_add_buddy(ic, set_getstr(&acc->set, "mail_notifications_handle"), NULL);
-		}
-	}
-
-	jabber_generate_id_hash(jd);
-}
-
-/* This generates an unfinished g_checksum variable. Every time we generate
-   an ID, we finish the state by adding a sequence number and take the hash. */
-static void jabber_generate_id_hash(struct jabber_data *jd)
-{
-	guint8 binbuf[4];
-	char *s;
-
-	jd->cached_id_prefix = g_checksum_new(G_CHECKSUM_MD5);
-	g_checksum_update(jd->cached_id_prefix, jd->username, strlen(jd->username));
-	g_checksum_update(jd->cached_id_prefix, jd->server, strlen(jd->server));
-	s = set_getstr(&jd->ic->acc->set, "resource");
-	g_checksum_update(jd->cached_id_prefix, s, strlen(s));
-	random_bytes(binbuf, 4);
-	g_checksum_update(jd->cached_id_prefix, binbuf, 4);
-}
-
-static void jabber_logout(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	imcb_chat_list_free(ic);
-
-	while (jd->filetransfers) {
-		imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
-	}
-
-	while (jd->streamhosts) {
-		jabber_streamhost_t *sh = jd->streamhosts->data;
-		jd->streamhosts = g_slist_remove(jd->streamhosts, sh);
-		g_free(sh->jid);
-		g_free(sh->host);
-		g_free(sh);
-	}
-
-	if (jd->fd >= 0) {
-		jabber_end_stream(ic);
-	}
-
-	while (ic->groupchats) {
-		jabber_chat_free(ic->groupchats->data);
-	}
-
-	if (jd->r_inpa >= 0) {
-		b_event_remove(jd->r_inpa);
-	}
-	if (jd->w_inpa >= 0) {
-		b_event_remove(jd->w_inpa);
-	}
-
-	if (jd->ssl) {
-		ssl_disconnect(jd->ssl);
-	}
-	if (jd->fd >= 0) {
-		proxy_disconnect(jd->fd);
-	}
-
-	if (jd->tx_len) {
-		g_free(jd->txq);
-	}
-
-	if (jd->node_cache) {
-		g_hash_table_destroy(jd->node_cache);
-	}
-
-	if (jd->buddies) {
-		jabber_buddy_remove_all(ic);
-	}
-
-	xt_free(jd->xt);
-
-	g_checksum_free(jd->cached_id_prefix);
-
-	g_free(jd->oauth2_access_token);
-	g_free(jd->away_message);
-	g_free(jd->internal_jid);
-	g_free(jd->gmail_tid);
-	g_free(jd->muc_host);
-	g_free(jd->username);
-	g_free(jd->me);
-	g_free(jd);
-
-	jabber_connections = g_slist_remove(jabber_connections, ic);
-}
-
-static int jabber_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud;
-	struct xt_node *node;
-	char *s;
-	int st;
-
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		return jabber_write(ic, message, strlen(message));
-	}
-
-	if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
-	    !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
-
-		if (jd->flags & JFLAG_HIPCHAT) {
-			sasl_oauth2_got_token(ic, message, NULL, NULL);
-			return 1;
-		} else if (sasl_oauth2_get_refresh_token(ic, message)) {
-			return 1;
-		} else {
-			imcb_error(ic, "OAuth failure");
-			imc_logout(ic, TRUE);
-			return 0;
-		}
-	}
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE_OK);
-	}
-
-	node = xt_new_node("body", message, NULL);
-	node = jabber_make_packet("message", "chat", bud ? bud->full_jid : who, node);
-
-	if (bud && (jd->flags & JFLAG_WANT_TYPING) &&
-	    ((bud->flags & JBFLAG_DOES_XEP85) ||
-	     !(bud->flags & JBFLAG_PROBED_XEP85))) {
-		struct xt_node *act;
-
-		/* If the user likes typing notification and if we don't know
-		   (and didn't probe before) if this resource supports XEP85,
-		   include a probe in this packet now. Also, if we know this
-		   buddy does support XEP85, we have to send this <active/>
-		   tag to tell that the user stopped typing (well, that's what
-		   we guess when s/he pressed Enter...). */
-		act = xt_new_node("active", NULL, NULL);
-		xt_add_attr(act, "xmlns", XMLNS_CHATSTATES);
-		xt_add_child(node, act);
-
-		/* Just make sure we do this only once. */
-		bud->flags |= JBFLAG_PROBED_XEP85;
-	}
-
-	/* XEP-0364 suggests we add message processing hints (XEP-0334) to OTR messages,
-	   mostly to avoid carbons (XEP-0280) and server-side message archiving.
-	   OTR messages are roughly like this: /^\?OTR(.*\?| Error:|:)/
-	   But I'm going to simplify it to messages starting with "?OTR". */
-	if (g_str_has_prefix(message, "?OTR")) {
-		int i;
-		char *hints[] = {
-			"no-copy", XMLNS_HINTS,
-			"no-permanent-store", XMLNS_HINTS,
-			"private", XMLNS_CARBONS,
-			NULL
-		};
-			
-		for (i = 0; hints[i]; i += 2) {
-			struct xt_node *hint;
-			hint = xt_new_node(hints[i], NULL, NULL);
-			xt_add_attr(hint, "xmlns", hints[i + 1]);
-			xt_add_child(node, hint);
-		}
-	}
-
-	st = jabber_write_packet(ic, node);
-	xt_free_node(node);
-
-	return st;
-}
-
-static GList *jabber_away_states(struct im_connection *ic)
-{
-	static GList *l = NULL;
-	int i;
-
-	if (l == NULL) {
-		for (i = 0; jabber_away_state_list[i].full_name; i++) {
-			l = g_list_append(l, (void *) jabber_away_state_list[i].full_name);
-		}
-	}
-
-	return l;
-}
-
-static void jabber_get_info(struct im_connection *ic, char *who)
-{
-	struct jabber_buddy *bud;
-
-	bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_FIRST);
-
-	while (bud) {
-		imcb_log(ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority);
-		if (bud->away_state) {
-			imcb_log(ic, "Away state: %s", bud->away_state->full_name);
-		}
-		imcb_log(ic, "Status message: %s", bud->away_message ? bud->away_message : "(none)");
-
-		bud = bud->next;
-	}
-
-	jabber_get_vcard(ic, who);
-}
-
-static void jabber_set_away(struct im_connection *ic, char *state_txt, char *message)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* state_txt == NULL -> Not away.
-	   Unknown state -> fall back to the first defined away state. */
-	if (state_txt == NULL) {
-		jd->away_state = NULL;
-	} else if ((jd->away_state = jabber_away_state_by_name(state_txt)) == NULL) {
-		jd->away_state = jabber_away_state_list;
-	}
-
-	g_free(jd->away_message);
-	jd->away_message = (message && *message) ? g_strdup(message) : NULL;
-
-	presence_send_update(ic);
-}
-
-static void jabber_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		jabber_xmlconsole_enable(ic);
-		return;
-	}
-
-	if (jabber_add_to_roster(ic, who, NULL, group)) {
-		presence_send_request(ic, who, "subscribe");
-	}
-}
-
-static void jabber_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		jd->flags &= ~JFLAG_XMLCONSOLE;
-		/* Not necessary for now. And for now the code isn't too
-		   happy if the buddy is completely gone right after calling
-		   this function already.
-		imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
-		*/
-		return;
-	}
-
-	/* We should always do this part. Clean up our administration a little bit. */
-	jabber_buddy_remove_bare(ic, who);
-
-	if (jabber_remove_from_roster(ic, who)) {
-		presence_send_request(ic, who, "unsubscribe");
-	}
-}
-
-static struct groupchat *jabber_chat_join_(struct im_connection *ic, const char *room, const char *nick,
-                                           const char *password, set_t **sets)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *final_nick;
-
-	/* Ignore the passed nick parameter if we have our own default */
-	if (!(final_nick = set_getstr(sets, "nick")) &&
-	    !(final_nick = set_getstr(&ic->acc->set, "display_name"))) {
-		/* Well, whatever, actually use the provided default, then */
-		final_nick = (char *) nick;
-	}
-
-	if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) {
-		char *guessed_name = hipchat_guess_channel_name(ic, room);
-		if (guessed_name) {
-			set_setstr(sets, "room", guessed_name);
-			g_free(guessed_name);
-
-			/* call this same function again with the fixed name */
-			return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets);
-		}
-	}
-
-	if (strchr(room, '@') == NULL) {
-		imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
-		           room, room, jd->server);
-	} else {
-		struct groupchat *old;
-
-		if ((old = jabber_chat_by_jid(ic, room))) {
-			imcb_log(ic, "Warning: Already present in chat `%s' - trying to join anyway", room);
-			jabber_chat_free(old);
-		}
-
-		/* jabber_chat_join without the underscore is the conference.c one */
-		return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"),
-		                        set_getbool(sets, "always_use_nicks"));
-	}
-
-	return NULL;
-}
-
-static struct groupchat *jabber_chat_with_(struct im_connection *ic, char *who)
-{
-	return jabber_chat_with(ic, who);
-}
-
-static void jabber_chat_list_(struct im_connection *ic, const char *server)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (server && *server) {
-		jabber_iq_disco_muc(ic, server);
-	} else if (jd->muc_host && *jd->muc_host) {
-		jabber_iq_disco_muc(ic, jd->muc_host);
-	} else {
-		/* throw an error here, don't query conference.[server] directly.
-		 * for things like jabber.org it gets you 18000 results of garbage */
-		imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server);
-	}
-}
-
-static void jabber_chat_msg_(struct groupchat *c, char *message, int flags)
-{
-	if (c && message) {
-		jabber_chat_msg(c, message, flags);
-	}
-}
-
-static void jabber_chat_topic_(struct groupchat *c, char *topic)
-{
-	if (c && topic) {
-		jabber_chat_topic(c, topic);
-	}
-}
-
-static void jabber_chat_leave_(struct groupchat *c)
-{
-	if (c) {
-		jabber_chat_leave(c, NULL);
-	}
-}
-
-static void jabber_chat_invite_(struct groupchat *c, char *who, char *msg)
-{
-	struct jabber_data *jd = c->ic->proto_data;
-	struct jabber_chat *jc = c->data;
-	gchar *msg_alt = NULL;
-
-	if (msg == NULL) {
-		msg_alt = g_strdup_printf("%s invited you to %s", jd->me, jc->name);
-	}
-
-	if (c && who) {
-		jabber_chat_invite(c, who, msg ? msg : msg_alt);
-	}
-
-	g_free(msg_alt);
-}
-
-static void jabber_keepalive(struct im_connection *ic)
-{
-	/* Just any whitespace character is enough as a keepalive for XMPP sessions. */
-	if (!jabber_write(ic, "\n", 1)) {
-		return;
-	}
-
-	/* This runs the garbage collection every minute, which means every packet
-	   is in the cache for about a minute (which should be enough AFAIK). */
-	jabber_cache_clean(ic);
-}
-
-static int jabber_send_typing(struct im_connection *ic, char *who, int typing)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *bare;
-
-	/* Enable typing notification related code from now. */
-	jd->flags |= JFLAG_WANT_TYPING;
-
-	if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
-	    (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
-		/* Sending typing notifications to unknown buddies is
-		   unsupported for now. Shouldn't be a problem, I think. */
-		return 0;
-	}
-
-
-	if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
-		/* We're only allowed to send this stuff if we know the other
-		   side supports it. If the bare JID has the flag, all other
-		   resources get it, too (That is the case in gtalk) */
-
-		struct xt_node *node;
-		char *type;
-		int st;
-
-		if (typing & OPT_TYPING) {
-			type = "composing";
-		} else if (typing & OPT_THINKING) {
-			type = "paused";
-		} else {
-			type = "active";
-		}
-
-		node = xt_new_node(type, NULL, NULL);
-		xt_add_attr(node, "xmlns", XMLNS_CHATSTATES);
-		node = jabber_make_packet("message", "chat", bud->full_jid, node);
-
-		st = jabber_write_packet(ic, node);
-		xt_free_node(node);
-
-		return st;
-	}
-
-	return 1;
-}
-
-void jabber_chat_add_settings(account_t *acc, set_t **head)
-{
-	set_add(head, "always_use_nicks", "false", set_eval_bool, NULL);
-
-	/* Meh. Stupid room passwords. Not trying to obfuscate/hide
-	   them from the user for now. */
-	set_add(head, "password", NULL, NULL, NULL);
-}
-
-void jabber_chat_free_settings(account_t *acc, set_t **head)
-{
-	set_del(head, "always_use_nicks");
-
-	set_del(head, "password");
-}
-
-GList *jabber_buddy_action_list(bee_user_t *bu)
-{
-	static GList *ret = NULL;
-
-	if (ret == NULL) {
-		static const struct buddy_action ba[2] = {
-			{ "VERSION", "Get client (version) information" },
-		};
-
-		ret = g_list_prepend(ret, (void *) ba + 0);
-	}
-
-	return ret;
-}
-
-void *jabber_buddy_action(struct bee_user *bu, const char *action, char * const args[], void *data)
-{
-	if (g_strcasecmp(action, "VERSION") == 0) {
-		struct jabber_buddy *bud;
-
-		if ((bud = jabber_buddy_by_ext_jid(bu->ic, bu->handle, 0)) == NULL) {
-			bud = jabber_buddy_by_jid(bu->ic, bu->handle, GET_BUDDY_FIRST);
-		}
-		for (; bud; bud = bud->next) {
-			jabber_iq_version_send(bu->ic, bud, data);
-		}
-	}
-
-	return NULL;
-}
-
-gboolean jabber_handle_is_self(struct im_connection *ic, const char *who)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return ((g_strcasecmp(who, ic->acc->user) == 0) ||
-	        (jd->internal_jid &&
-	         g_strcasecmp(who, jd->internal_jid) == 0));
-}
-
-void jabber_initmodule()
-{
-	struct prpl *ret = g_new0(struct prpl, 1);
-	struct prpl *hipchat = NULL;
-
-	ret->name = "jabber";
-	ret->mms = 0;                        /* no limit */
+		buddy = g_strdup_printf("%s@%s", who->user, who->server);
+	}
+
+	if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) {
+		status = _("Unknown");
+	}
+
+	g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"),
+	                       status);
+
+	for(child = querynode->firstchild; child; child = child->next)
+	{
+		xmlnode child2;
+
+		if(child->type != NTYPE_TAG)
+			continue;
+
+		text = xmlnode_get_data(child);
+		if(text && !strcmp(child->name, "FN")) {
+			info_string_append(str, "\n", _("Full Name"), text);
+		} else if (!strcmp(child->name, "N")) {
+			for (child2 = child->firstchild; child2; child2 = child2->next) {
+				char *text2 = NULL;
+
+				if (child2->type != NTYPE_TAG)
+					continue;
+
+				text2 = xmlnode_get_data(child2);
+				if (text2 && !strcmp(child2->name, "FAMILY")) {
+					info_string_append(str, "\n", _("Family Name"), text2);
+				} else if (text2 && !strcmp(child2->name, "GIVEN")) {
+					info_string_append(str, "\n", _("Given Name"), text2);
+				} else if (text2 && !strcmp(child2->name, "MIDDLE")) {
+					info_string_append(str, "\n", _("Middle Name"), text2);
+				}
+			}
+		} else if (text && !strcmp(child->name, "NICKNAME")) {
+			info_string_append(str, "\n", _("Nickname"), text);
+		} else if (text && !strcmp(child->name, "BDAY")) {
+			info_string_append(str, "\n", _("Birthday"), text);
+		} else if (!strcmp(child->name, "ADR")) {
+			/* show wich address it is */
+			/* Just for the beauty of bitlbee 
+			if (child->firstchild)
+				g_string_sprintfa(str, "%s:\n", _("Address"));
+			*/
+			for(child2 = child->firstchild; child2; child2 = child2->next) {
+				char *text2 = NULL;
+
+				if(child2->type != NTYPE_TAG)
+					continue;
+
+				text2 = xmlnode_get_data(child2);
+				if(text2 && !strcmp(child2->name, "POBOX")) {
+					info_string_append(str, "\n",
+							_("P.O. Box"), text2);
+				} else if(text2 && !strcmp(child2->name, "EXTADR")) {
+					info_string_append(str, "\n",
+							_("Extended Address"), text2);
+				} else if(text2 && !strcmp(child2->name, "STREET")) {
+					info_string_append(str, "\n",
+							_("Street Address"), text2);
+				} else if(text2 && !strcmp(child2->name, "LOCALITY")) {
+					info_string_append(str, "\n",
+							_("Locality"), text2);
+				} else if(text2 && !strcmp(child2->name, "REGION")) {
+					info_string_append(str, "\n",
+							_("Region"), text2);
+				} else if(text2 && !strcmp(child2->name, "PCODE")) {
+					info_string_append(str, "\n",
+							_("Postal Code"), text2);
+				} else if(text2 && (!strcmp(child2->name, "CTRY")
+							|| !strcmp(child2->name, "COUNTRY"))) {
+					info_string_append(str, "\n", _("Country"), text2);
+				}
+			}
+		} else if(!strcmp(child->name, "TEL")) {
+			char *number = NULL;
+			if ((child2 = xmlnode_get_tag(child, "NUMBER"))) {
+				/* show what kind of number it is */
+				number = xmlnode_get_data(child2);
+				if(number) {
+					info_string_append(str, "\n", _("Telephone"), number);
+				}
+			} else if((number = xmlnode_get_data(child))) {
+				/* lots of clients (including gaim) do this,
+				 * but it's out of spec */
+				info_string_append(str, "\n", _("Telephone"), number);
+			}
+		} else if(!strcmp(child->name, "EMAIL")) {
+			char *userid = NULL;
+			if((child2 = xmlnode_get_tag(child, "USERID"))) {
+				/* show what kind of email it is */
+				userid = xmlnode_get_data(child2);
+				if(userid) {
+					info_string_append(str, "\n", _("Email"), userid);
+				}
+			} else if((userid = xmlnode_get_data(child))) {
+				/* lots of clients (including gaim) do this,
+				 * but it's out of spec */
+				info_string_append(str, "\n", _("Email"), userid);
+			}
+		} else if(!strcmp(child->name, "ORG")) {
+			for(child2 = child->firstchild; child2; child2 = child2->next) {
+				char *text2 = NULL;
+
+				if(child2->type != NTYPE_TAG)
+					continue;
+
+				text2 = xmlnode_get_data(child2);
+				if(text2 && !strcmp(child2->name, "ORGNAME")) {
+					info_string_append(str, "\n", _("Organization Name"), text2);
+				} else if(text2 && !strcmp(child2->name, "ORGUNIT")) {
+					info_string_append(str, "\n", _("Organization Unit"), text2);
+				}
+			}
+		} else if(text && !strcmp(child->name, "TITLE")) {
+			info_string_append(str, "\n", _("Title"), text);
+		} else if(text && !strcmp(child->name, "ROLE")) {
+			info_string_append(str, "\n", _("Role"), text);
+		} else if(text && !strcmp(child->name, "DESC")) {
+			g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Description"), 
+					text, _("End of Description"));
+		}
+	}
+
+	serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str);
+
+	g_free(buddy);
+	g_string_free(str, TRUE);
+}
+
+
+static GList *jabber_actions()
+{
+	GList *m = NULL;
+
+	m = g_list_append(m, _("Set User Info"));
+	/*
+	m = g_list_append(m, _("Set Dir Info"));
+	m = g_list_append(m, _("Change Password"));
+	 */
+
+	return m;
+}
+
+static struct prpl *my_protocol = NULL;
+
+void jabber_init(struct prpl *ret)
+{
+	/* the NULL's aren't required but they're nice to have */
+	ret->protocol = PROTO_JABBER;
+	ret->name = jabber_name;
+	ret->away_states = jabber_away_states;
+	ret->actions = jabber_actions;
 	ret->login = jabber_login;
-	ret->init = jabber_init;
-	ret->logout = jabber_logout;
-	ret->buddy_msg = jabber_buddy_msg;
-	ret->away_states = jabber_away_states;
+	ret->close = jabber_close;
+	ret->send_im = jabber_send_im;
+	ret->set_info = jabber_set_info;
+	ret->get_info = jabber_get_info;
 	ret->set_away = jabber_set_away;
-//	ret->set_info = jabber_set_info;
-	ret->get_info = jabber_get_info;
+	ret->get_away = jabber_get_away_msg;
+	ret->set_idle = jabber_set_idle;
 	ret->add_buddy = jabber_add_buddy;
 	ret->remove_buddy = jabber_remove_buddy;
-	ret->chat_msg = jabber_chat_msg_;
-	ret->chat_topic = jabber_chat_topic_;
-	ret->chat_invite = jabber_chat_invite_;
-	ret->chat_leave = jabber_chat_leave_;
-	ret->chat_join = jabber_chat_join_;
-	ret->chat_with = jabber_chat_with_;
-	ret->chat_list = jabber_chat_list_;
-	ret->chat_add_settings = jabber_chat_add_settings;
-	ret->chat_free_settings = jabber_chat_free_settings;
+	ret->add_permit = NULL;
+	ret->add_deny = NULL;
+	ret->rem_permit = NULL;
+	ret->rem_deny = NULL;
+	ret->set_permit_deny = NULL;
 	ret->keepalive = jabber_keepalive;
-	ret->send_typing = jabber_send_typing;
-	ret->handle_cmp = g_strcasecmp;
-	ret->handle_is_self = jabber_handle_is_self;
-	ret->transfer_request = jabber_si_transfer_request;
-	ret->buddy_action_list = jabber_buddy_action_list;
-	ret->buddy_action = jabber_buddy_action;
-
-	register_protocol(ret);
-
-	/* Another one for hipchat, which has completely different logins */
-	hipchat = g_memdup2(ret, sizeof(struct prpl));
-	hipchat->name = "hipchat";
-	register_protocol(hipchat);
-}
+	ret->buddy_free = jabber_buddy_free;
+	ret->alias_buddy = jabber_roster_update;
+	ret->group_buddy = jabber_group_change;
+
+	my_protocol = ret;
+}
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/jabber/jabber.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,367 +1,315 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Main file                                                *
-*                                                                           *
-*  Copyright 2006-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#ifndef _JABBER_H
-#define _JABBER_H
-
-#include <glib.h>
-
-#include "bitlbee.h"
-#include "xmltree.h"
-
-extern GSList *jabber_connections;
-
-typedef enum {
-	JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
-	                                   and want to do auth. */
-	JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
-	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
-	                                   SASL or TLS). */
-	JFLAG_WANT_SESSION = 8,         /* Set if the server wants a <session/> tag
-	                                   before we continue. */
-	JFLAG_WANT_BIND = 16,           /* ... for <bind> tag. */
-	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
-	                                   activates all XEP-85 related code. */
-	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
-	JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */
-	JFLAG_GMAILNOTIFY = 256,        /* If gmail notification is enabled */
-
-	JFLAG_GTALK =  0x100000,        /* Is Google Talk, as confirmed by iq discovery */
-	JFLAG_HIPCHAT = 0x200000,       /* Is hipchat, because prpl->name says so */
-} jabber_flags_t;
-
-typedef enum {
-	JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make
-	                                   sure it gets sent only once. */
-	JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
-	                                   XEP85 (typing notification shite). */
-	JBFLAG_IS_CHATROOM = 4,         /* It's convenient to use this JID thingy for
-	                                   groupchat state info too. */
-	JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have
-	                                   have a real JID. */
-	JBFLAG_HIDE_SUBJECT = 16,       /* Hide the subject field since we probably
-	                                   showed it already. */
-} jabber_buddy_flags_t;
-
-/* Stores a streamhost's (a.k.a. proxy) data */
-typedef struct {
-	char *jid;
-	char *host;
-	char port[6];
-} jabber_streamhost_t;
-
-typedef enum {
-	JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
-	                                   we can detect echoes/backlogs. */
-	JCFLAG_ALWAYS_USE_NICKS = 2,
-} jabber_chat_flags_t;
-
-struct jabber_data {
-	struct im_connection *ic;
-
-	int fd;
-	void *ssl;
-	char *txq;
-	int tx_len;
-	int r_inpa, w_inpa;
-
-	struct xt_parser *xt;
-	jabber_flags_t flags;
-
-	char *username;         /* USERNAME@server */
-	char *server;           /* username@SERVER -=> server/domain, not hostname */
-	char *me;               /* bare jid */
-	char *internal_jid;
-
-	const struct oauth2_service *oauth2_service;
-	char *oauth2_access_token;
-
-	/* After changing one of these two (or the priority setting), call
-	   presence_send_update() to inform the server about the changes. */
-	const struct jabber_away_state *away_state;
-	char *away_message;
-	guint64 gmail_time;
-	char *gmail_tid;
-
-	GChecksum *cached_id_prefix;
-	GHashTable *node_cache;
-	GHashTable *buddies;
-
-	GSList *filetransfers;
-	GSList *streamhosts;
-	int have_streamhosts;
-
-	char *muc_host;
-};
-
-struct jabber_away_state {
-	char code[5];
-	char *full_name;
-};
-
-typedef xt_status (*jabber_cache_event) (struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-struct jabber_cache_entry {
-	time_t saved_at;
-	struct xt_node *node;
-	jabber_cache_event func;
-};
-
-/* Somewhat messy data structure: We have a hash table with the bare JID as
-   the key and the head of a struct jabber_buddy list as the value. The head
-   is always a bare JID. If the JID has other resources (often the case,
-   except for some transports that don't support multiple resources), those
-   follow. In that case, the bare JID at the beginning doesn't actually
-   refer to a real session and should only be used for operations that
-   support incomplete JIDs. */
-struct jabber_buddy {
-	char *bare_jid;
-	char *full_jid;
-	char *resource;
-
-	char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */
-	               /* otherwise something similar to the conference JID. */
-
-	int priority;
-	struct jabber_away_state *away_state;
-	char *away_message;
-	GSList *features;
-
-	time_t last_msg;
-	jabber_buddy_flags_t flags;
-
-	struct jabber_buddy *next;
-};
-
-struct jabber_chat {
-	int flags;
-	char *name;
-	char *my_full_jid; /* Separate copy because of case sensitivity. */
-	struct jabber_buddy *me;
-	char *invite;
-	char *last_sent_message;
-};
-
-struct jabber_transfer {
-	/* bitlbee's handle for this transfer */
-	file_transfer_t *ft;
-
-	/* the stream's private handle */
-	gpointer streamhandle;
-
-	/* timeout for discover queries */
-	gint disco_timeout;
-	gint disco_timeout_fired;
-
-	struct im_connection *ic;
-
-	struct jabber_buddy *bud;
-
-	int watch_in;
-	int watch_out;
-
-	char *ini_jid;
-	char *tgt_jid;
-	char *iq_id;
-	char *sid;
-	int accepted;
-
-	size_t bytesread, byteswritten;
-	int fd;
-	struct sockaddr_storage saddr;
-};
-
-#define JABBER_XMLCONSOLE_HANDLE "_xmlconsole"
-#define JABBER_OAUTH_HANDLE "jabber_oauth"
-
-/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
-   first one should be used, but when storing a packet in the cache, a
-   "special" kind of ID is assigned to make it easier later to figure out
-   if we have to do call an event handler for the response packet. Also
-   we'll append a hash to make sure we won't trigger on cached packets from
-   other BitlBee users. :-) */
-#define JABBER_PACKET_ID "BeeP"
-#define JABBER_CACHED_ID "BeeC"
-
-/* The number of seconds to keep cached packets before garbage collecting
-   them. This gc is done on every keepalive (every minute). */
-#define JABBER_CACHE_MAX_AGE 600
-
-/* RFC 392[01] stuff */
-#define XMLNS_TLS          "urn:ietf:params:xml:ns:xmpp-tls"
-#define XMLNS_SASL         "urn:ietf:params:xml:ns:xmpp-sasl"
-#define XMLNS_BIND         "urn:ietf:params:xml:ns:xmpp-bind"
-#define XMLNS_SESSION      "urn:ietf:params:xml:ns:xmpp-session"
-#define XMLNS_STANZA_ERROR "urn:ietf:params:xml:ns:xmpp-stanzas"
-#define XMLNS_STREAM_ERROR "urn:ietf:params:xml:ns:xmpp-streams"
-#define XMLNS_ROSTER       "jabber:iq:roster"
-
-/* Some supported extensions/legacy stuff */
-#define XMLNS_AUTH         "jabber:iq:auth"                                      /* XEP-0078 */
-#define XMLNS_VERSION      "jabber:iq:version"                                   /* XEP-0092 */
-#define XMLNS_TIME_OLD     "jabber:iq:time"                                      /* XEP-0090 */
-#define XMLNS_TIME         "urn:xmpp:time"                                       /* XEP-0202 */
-#define XMLNS_PING         "urn:xmpp:ping"                                       /* XEP-0199 */
-#define XMLNS_RECEIPTS     "urn:xmpp:receipts"                                   /* XEP-0184 */
-#define XMLNS_VCARD        "vcard-temp"                                          /* XEP-0054 */
-#define XMLNS_DELAY_OLD    "jabber:x:delay"                                      /* XEP-0091 */
-#define XMLNS_DELAY        "urn:xmpp:delay"                                      /* XEP-0203 */
-#define XMLNS_XDATA        "jabber:x:data"                                       /* XEP-0004 */
-#define XMLNS_GMAILNOTIFY  "google:mail:notify"                                  /* Not a XEP */
-#define XMLNS_CARBONS      "urn:xmpp:carbons:2"                                  /* XEP-0280 */
-#define XMLNS_FORWARDING   "urn:xmpp:forward:0"                                  /* XEP-0297 */
-#define XMLNS_HINTS        "urn:xmpp:hints"                                      /* XEP-0334 */
-#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"               /* XEP-0085 */
-#define XMLNS_DISCO_INFO   "http://jabber.org/protocol/disco#info"               /* XEP-0030 */
-#define XMLNS_DISCO_ITEMS  "http://jabber.org/protocol/disco#items"              /* XEP-0030 */
-#define XMLNS_MUC          "http://jabber.org/protocol/muc"                      /* XEP-0045 */
-#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"                 /* XEP-0045 */
-#define XMLNS_CAPS         "http://jabber.org/protocol/caps"                     /* XEP-0115 */
-#define XMLNS_FEATURE      "http://jabber.org/protocol/feature-neg"              /* XEP-0020 */
-#define XMLNS_SI           "http://jabber.org/protocol/si"                       /* XEP-0095 */
-#define XMLNS_FILETRANSFER "http://jabber.org/protocol/si/profile/file-transfer" /* XEP-0096 */
-#define XMLNS_BYTESTREAMS  "http://jabber.org/protocol/bytestreams"              /* XEP-0065 */
-#define XMLNS_IBB          "http://jabber.org/protocol/ibb"                      /* XEP-0047 */
-
-/* Hipchat protocol extensions*/
-#define XMLNS_HIPCHAT         "http://hipchat.com"
-#define XMLNS_HIPCHAT_PROFILE "http://hipchat.com/protocol/profile"
-
-/* jabber.c */
-void jabber_connect(struct im_connection *ic);
-
-/* iq.c */
-xt_status jabber_pkt_iq(struct xt_node *node, gpointer data);
-int jabber_init_iq_auth(struct im_connection *ic);
-xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-int jabber_get_roster(struct im_connection *ic);
-int jabber_get_vcard(struct im_connection *ic, char *bare_jid);
-int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group);
-int jabber_remove_from_roster(struct im_connection *ic, char *handle);
-xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid);
-xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns);
-void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data);
-int jabber_iq_disco_server(struct im_connection *ic);
-int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server);
-
-/* si.c */
-int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode);
-void jabber_si_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *who);
-void jabber_si_free_transfer(file_transfer_t *ft);
-
-/* s5bytestream.c */
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode);
-gboolean jabber_bs_send_start(struct jabber_transfer *tf);
-gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len);
-
-/* message.c */
-xt_status jabber_pkt_message(struct xt_node *node, gpointer data);
-
-/* presence.c */
-xt_status jabber_pkt_presence(struct xt_node *node, gpointer data);
-int presence_send_update(struct im_connection *ic);
-int presence_send_request(struct im_connection *ic, char *handle, char *request);
-
-/* jabber_util.c */
-char *set_eval_priority(set_t *set, char *value);
-char *set_eval_tls(set_t *set, char *value);
-struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children);
-struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code);
-void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func);
-struct xt_node *jabber_cache_get(struct im_connection *ic, char *id);
-void jabber_cache_entry_free(gpointer entry);
-void jabber_cache_clean(struct im_connection *ic);
-xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node);
-const struct jabber_away_state *jabber_away_state_by_code(char *code);
-const struct jabber_away_state *jabber_away_state_by_name(char *name);
-void jabber_buddy_ask(struct im_connection *ic, char *handle);
-int jabber_compare_jid(const char *jid1, const char *jid2);
-char *jabber_normalize(const char *orig);
-
-typedef enum {
-	GET_BUDDY_CREAT = 1,    /* Try to create it, if necessary. */
-	GET_BUDDY_EXACT = 2,    /* Get an exact match (only makes sense with bare JIDs). */
-	GET_BUDDY_FIRST = 4,    /* No selection, simply get the first resource for this JID. */
-	GET_BUDDY_BARE = 8,     /* Get the bare version of the JID (possibly inexistent). */
-	GET_BUDDY_BARE_OK = 16, /* Allow returning a bare JID if that seems better. */
-} get_buddy_flags_t;
-
-struct jabber_error {
-	char *code, *text, *type;
-};
-
-struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid);
-struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid, get_buddy_flags_t flags);
-struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid, get_buddy_flags_t flags);
-int jabber_buddy_remove(struct im_connection *ic, char *full_jid);
-int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid);
-void jabber_buddy_remove_all(struct im_connection *ic);
-time_t jabber_get_timestamp(struct xt_node *xt);
-struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns);
-void jabber_error_free(struct jabber_error *err);
-gboolean jabber_set_me(struct im_connection *ic, const char *me);
-char *jabber_get_bare_jid(char *jid);
-
-extern const struct jabber_away_state jabber_away_state_list[];
-
-/* io.c */
-int jabber_write_packet(struct im_connection *ic, struct xt_node *node);
-int jabber_write(struct im_connection *ic, char *buf, int len);
-gboolean jabber_connected_plain(gpointer data, gint source, b_input_condition cond);
-gboolean jabber_connected_ssl(gpointer data, int returncode, void *source, b_input_condition cond);
-gboolean jabber_start_stream(struct im_connection *ic);
-void jabber_end_stream(struct im_connection *ic);
-
-/* sasl.c */
-xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data);
-xt_status sasl_pkt_challenge(struct xt_node *node, gpointer data);
-xt_status sasl_pkt_result(struct xt_node *node, gpointer data);
-gboolean sasl_supported(struct im_connection *ic);
-void sasl_oauth2_init(struct im_connection *ic);
-int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg);
-int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token);
-void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error);
-
-extern const struct oauth2_service oauth2_service_google;
-
-/* conference.c */
-struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   gboolean always_use_nicks);
-struct groupchat *jabber_chat_with(struct im_connection *ic, char *who);
-struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name);
-void jabber_chat_free(struct groupchat *c);
-int jabber_chat_msg(struct groupchat *ic, char *message, int flags);
-int jabber_chat_topic(struct groupchat *c, char *topic);
-int jabber_chat_leave(struct groupchat *c, const char *reason);
-void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node);
-void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node);
-void jabber_chat_invite(struct groupchat *c, char *who, char *message);
-
-/* hipchat.c */
-int jabber_get_hipchat_profile(struct im_connection *ic);
-xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node);
-char *hipchat_make_channel_slug(const char *name);
-char *hipchat_guess_channel_name(struct im_connection *ic, const char *name);
-
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Jabber
+ *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+#include <ctype.h>
+#ifdef _WIN32
+#undef DATADIR
+#include "sock.h"
 #endif
+
+#include "lib.h"
+
+
+#ifndef INCL_JABBER_H
+#define INCL_JABBER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* JID structures & constants                                */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define JID_RESOURCE 1
+#define JID_USER     2
+#define JID_SERVER   4
+
+typedef struct jid_struct
+{ 
+    pool               p;
+    char*              resource;
+    char*              user;
+    char*              server;
+    char*              full;
+    struct jid_struct *next; /* for lists of jids */
+} *jid;
+  
+jid     jid_new(pool p, char *idstr);	       /* Creates a jabber id from the idstr */
+void    jid_set(jid id, char *str, int item);  /* Individually sets jid components */
+char*   jid_full(jid id);		       /* Builds a string type=user/resource@server from the jid data */
+int     jid_cmp(jid a, jid b);		       /* Compares two jid's, returns 0 for perfect match */
+int     jid_cmpx(jid a, jid b, int parts);     /* Compares just the parts specified as JID_|JID_ */
+jid     jid_append(jid a, jid b);	       /* Appending b to a (list), no dups */
+xmlnode jid_xres(jid id);		       /* Returns xmlnode representation of the resource?query=string */
+xmlnode jid_nodescan(jid id, xmlnode x);       /* Scans the children of the node for a matching jid attribute */
+jid     jid_user(jid a);                       /* returns the same jid but just of the user@host part */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* JPacket structures & constants                            */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define JPACKET_UNKNOWN   0x00
+#define JPACKET_MESSAGE   0x01
+#define JPACKET_PRESENCE  0x02
+#define JPACKET_IQ        0x04
+#define JPACKET_S10N      0x08
+
+#define JPACKET__UNKNOWN      0
+#define JPACKET__NONE         1
+#define JPACKET__ERROR        2
+#define JPACKET__CHAT         3
+#define JPACKET__GROUPCHAT    4
+#define JPACKET__GET          5
+#define JPACKET__SET          6
+#define JPACKET__RESULT       7
+#define JPACKET__SUBSCRIBE    8
+#define JPACKET__SUBSCRIBED   9
+#define JPACKET__UNSUBSCRIBE  10
+#define JPACKET__UNSUBSCRIBED 11
+#define JPACKET__AVAILABLE    12
+#define JPACKET__UNAVAILABLE  13
+#define JPACKET__PROBE        14
+#define JPACKET__HEADLINE     15
+#define JPACKET__INVISIBLE    16
+
+typedef struct jpacket_struct
+{
+    unsigned char type;
+    int           subtype;
+    int           flag;
+    void*         aux1;
+    xmlnode       x;
+    jid           to;
+    jid           from;
+    char*         iqns;
+    xmlnode       iq;
+    pool          p;
+} *jpacket, _jpacket;
+ 
+jpacket jpacket_new(xmlnode x);	    /* Creates a jabber packet from the xmlnode */
+int     jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Presence Proxy DB structures & constants                  */
+/*                                                           */
+/* --------------------------------------------------------- */
+typedef struct ppdb_struct
+{			      
+    jid     id;		       /* entry data */
+    int     pri;
+    xmlnode x;
+    struct ppdb_struct* user;  /* linked list for user@server */
+    pool                p;     /* db-level data */
+    struct ppdb_struct* next;
+} _ppdb, *ppdb;
+
+ppdb    ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */
+xmlnode ppdb_primary(ppdb db, jid id);		 /* Fetches the matching primary presence for the id */
+void    ppdb_free(ppdb db);			 /* Frees the db and all entries */
+xmlnode ppdb_get(ppdb db, jid id);		 /* Called successively to return each presence xmlnode */
+						 /*   for the id and children, returns NULL at the end */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Simple Jabber Rate limit functions                        */
+/*                                                           */
+/* --------------------------------------------------------- */
+typedef struct jlimit_struct
+{
+    char *key;
+    int start;
+    int points;
+    int maxt, maxp;
+    pool p;
+} *jlimit, _jlimit;
+ 
+jlimit jlimit_new(int maxt, int maxp);
+void jlimit_free(jlimit r);
+int jlimit_check(jlimit r, char *key, int points);
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Error structures & constants                              */
+/*                                                           */
+/* --------------------------------------------------------- */
+typedef struct terror_struct
+{
+    int  code;
+    char msg[64];
+} terror;
+
+#define TERROR_BAD           (terror){400,"Bad Request"}
+#define TERROR_AUTH          (terror){401,"Unauthorized"}
+#define TERROR_PAY           (terror){402,"Payment Required"}
+#define TERROR_FORBIDDEN     (terror){403,"Forbidden"}
+#define TERROR_NOTFOUND      (terror){404,"Not Found"}
+#define TERROR_NOTALLOWED    (terror){405,"Not Allowed"}
+#define TERROR_NOTACCEPTABLE (terror){406,"Not Acceptable"}
+#define TERROR_REGISTER      (terror){407,"Registration Required"}
+#define TERROR_REQTIMEOUT    (terror){408,"Request Timeout"}
+#define TERROR_CONFLICT      (terror){409,"Conflict"}
+
+#define TERROR_INTERNAL   (terror){500,"Internal Server Error"}
+#define TERROR_NOTIMPL    (terror){501,"Not Implemented"}
+#define TERROR_EXTERNAL   (terror){502,"Remote Server Error"}
+#define TERROR_UNAVAIL    (terror){503,"Service Unavailable"}
+#define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"}
+#define TERROR_DISCONNECTED (terror){510,"Disconnected"}
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Namespace constants                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)
+
+#define NS_CLIENT    "jabber:client"
+#define NS_SERVER    "jabber:server"
+#define NS_AUTH      "jabber:iq:auth"
+#define NS_REGISTER  "jabber:iq:register"
+#define NS_ROSTER    "jabber:iq:roster"
+#define NS_OFFLINE   "jabber:x:offline"
+#define NS_AGENT     "jabber:iq:agent"
+#define NS_AGENTS    "jabber:iq:agents"
+#define NS_DELAY     "jabber:x:delay"
+#define NS_VERSION   "jabber:iq:version"
+#define NS_TIME      "jabber:iq:time"
+#define NS_VCARD     "vcard-temp"
+#define NS_PRIVATE   "jabber:iq:private"
+#define NS_SEARCH    "jabber:iq:search"
+#define NS_OOB       "jabber:iq:oob"
+#define NS_XOOB      "jabber:x:oob"
+#define NS_ADMIN     "jabber:iq:admin"
+#define NS_FILTER    "jabber:iq:filter"
+#define NS_AUTH_0K   "jabber:iq:auth:0k"
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Message Types                                             */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define TMSG_NORMAL	"normal"
+#define TMSG_ERROR	"error"
+#define TMSG_CHAT	"chat"
+#define TMSG_GROUPCHAT	"groupchat"
+#define TMSG_HEADLINE	"headline"
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* JUtil functions                                           */
+/*                                                           */
+/* --------------------------------------------------------- */
+xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */
+xmlnode jutil_iqnew(int type, char *ns);		 /* Create a skeleton iq packet */
+xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body);
+							 /* Create a skeleton message packet */
+xmlnode jutil_header(char* xmlns, char* server);	 /* Create a skeleton stream packet */
+int     jutil_priority(xmlnode x);			 /* Determine priority of this packet */
+void    jutil_tofrom(xmlnode x);			 /* Swaps to/from fields on a packet */
+xmlnode jutil_iqresult(xmlnode x);			 /* Generate a skeleton iq/result, given a iq/query */
+char*   jutil_timestamp(void);				 /* Get stringified timestamp */
+void    jutil_error(xmlnode x, terror E);		 /* Append an <error> node to x */
+void    jutil_delay(xmlnode msg, char *reason);		 /* Append a delay packet to msg */
+char*   jutil_regkey(char *key, char *seed);		 /* pass a seed to generate a key, pass the key again to validate (returns it) */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* JConn structures & functions                              */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define JCONN_STATE_OFF       0
+#define JCONN_STATE_CONNECTED 1
+#define JCONN_STATE_ON        2
+#define JCONN_STATE_AUTH      3
+
+typedef struct jconn_struct
+{
+    /* Core structure */
+    pool        p;	       /* Memory allocation pool */
+    int         state;	   /* Connection state flag */
+    int         fd;	       /* Connection file descriptor */
+    jid         user;      /* User info */
+    char        *pass;     /* User passwd */
+
+    /* Stream stuff */
+    int         id;        /* id counter for jab_getid() function */
+    char        idbuf[9];  /* temporary storage for jab_getid() */
+    char        *sid;      /* stream id from server, for digest auth */
+    XML_Parser  parser;    /* Parser instance */
+    xmlnode     current;   /* Current node in parsing instance.. */
+
+    /* Event callback ptrs */
+    void (*on_state)(struct jconn_struct *j, int state);
+    void (*on_packet)(struct jconn_struct *j, jpacket p);
+
+} *jconn, jconn_struct;
+
+typedef void (*jconn_state_h)(jconn j, int state);
+typedef void (*jconn_packet_h)(jconn j, jpacket p);
+
+
+jconn jab_new(char *user, char *pass);
+void jab_delete(jconn j);
+void jab_state_handler(jconn j, jconn_state_h h);
+void jab_packet_handler(jconn j, jconn_packet_h h);
+void jab_start(jconn j);
+void jab_stop(jconn j);
+
+int jab_getfd(jconn j);
+jid jab_getjid(jconn j);
+char *jab_getsid(jconn j);
+char *jab_getid(jconn j);
+
+void jab_send(jconn j, xmlnode x);
+void jab_send_raw(jconn j, const char *str);
+void jab_recv(jconn j);
+void jab_poll(jconn j, int timeout);
+
+char *jab_auth(jconn j);
+char *jab_reg(jconn j);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* INCL_JABBER_H */
Index: protocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,849 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Misc. stuff                                              *
-*                                                                           *
-*  Copyright 2006-2010 Wilmer van der Gaast <wilmer@gaast.net>
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "base64.h"
-
-static unsigned int next_id = 1;
-
-char *set_eval_priority(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-	int i;
-
-	if (sscanf(value, "%d", &i) == 1) {
-		/* Priority is a signed 8-bit integer, according to RFC 3921. */
-		if (i < -128 || i > 127) {
-			return SET_INVALID;
-		}
-	} else {
-		return SET_INVALID;
-	}
-
-	/* Only run this stuff if the account is online ATM,
-	   and if the setting seems to be acceptable. */
-	if (acc->ic) {
-		/* Although set_eval functions usually are very nice and
-		   convenient, they have one disadvantage: If I would just
-		   call p_s_u() now to send the new prio setting, it would
-		   send the old setting because the set->value gets changed
-		   after the (this) eval returns a non-NULL value.
-
-		   So now I can choose between implementing post-set
-		   functions next to evals, or just do this little hack: */
-
-		g_free(set->value);
-		set->value = g_strdup(value);
-
-		/* (Yes, sorry, I prefer the hack. :-P) */
-
-		presence_send_update(acc->ic);
-	}
-
-	return value;
-}
-
-char *set_eval_tls(set_t *set, char *value)
-{
-	if (g_strcasecmp(value, "try") == 0) {
-		return value;
-	} else {
-		return set_eval_bool(set, value);
-	}
-}
-
-struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children)
-{
-	struct xt_node *node;
-
-	node = xt_new_node(name, NULL, children);
-
-	if (type) {
-		xt_add_attr(node, "type", type);
-	}
-	if (to) {
-		xt_add_attr(node, "to", to);
-	}
-
-	/* IQ packets should always have an ID, so let's generate one. It
-	   might get overwritten by jabber_cache_add() if this packet has
-	   to be saved until we receive a response. Cached packets get
-	   slightly different IDs so we can recognize them. */
-	if (strcmp(name, "iq") == 0) {
-		char *id = g_strdup_printf("%s%05x", JABBER_PACKET_ID, (next_id++) & 0xfffff);
-		xt_add_attr(node, "id", id);
-		g_free(id);
-	}
-
-	return node;
-}
-
-struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code)
-{
-	struct xt_node *node, *c;
-	char *to;
-
-	/* Create the "defined-condition" tag. */
-	c = xt_new_node(err_cond, NULL, NULL);
-	xt_add_attr(c, "xmlns", XMLNS_STANZA_ERROR);
-
-	/* Put it in an <error> tag. */
-	c = xt_new_node("error", NULL, c);
-	xt_add_attr(c, "type", err_type);
-
-	/* Add the error code, if present */
-	if (err_code) {
-		xt_add_attr(c, "code", err_code);
-	}
-
-	/* To make the actual error packet, we copy the original packet and
-	   add our <error>/type="error" tag. Including the original packet
-	   is recommended, so let's just do it. */
-	node = xt_dup(orig);
-	xt_add_child(node, c);
-	xt_add_attr(node, "type", "error");
-
-	/* Return to sender. */
-	if ((to = xt_find_attr(node, "from"))) {
-		xt_add_attr(node, "to", to);
-		xt_remove_attr(node, "from");
-	}
-
-	return node;
-}
-
-/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
-   them when you receive the response. Use this BEFORE sending the packet so
-   it'll get a new id= tag, and do NOT free() the packet after sending it! */
-void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_cache_entry *entry = g_new0(struct jabber_cache_entry, 1);
-	GChecksum *id_hash, *copy;
-	gsize digest_len = MD5_HASH_SIZE;
-	guint8 id_sum[MD5_HASH_SIZE];
-	char *id, *asc_hash;
-
-	next_id++;
-
-	id_hash = jd->cached_id_prefix;
-	g_checksum_update(id_hash, (guint8 *) &next_id, sizeof(next_id));
-
-	copy = g_checksum_copy(id_hash);
-	g_checksum_get_digest(copy, id_sum, &digest_len);
-	g_checksum_free(copy);
-
-	asc_hash = base64_encode(id_sum, 12);
-
-	id = g_strdup_printf("%s%s", JABBER_CACHED_ID, asc_hash);
-	xt_add_attr(node, "id", id);
-	g_free(id);
-	g_free(asc_hash);
-
-	entry->node = node;
-	entry->func = func;
-	entry->saved_at = time(NULL);
-	g_hash_table_insert(jd->node_cache, xt_find_attr(node, "id"), entry);
-}
-
-void jabber_cache_entry_free(gpointer data)
-{
-	struct jabber_cache_entry *entry = data;
-
-	xt_free_node(entry->node);
-	g_free(entry);
-}
-
-gboolean jabber_cache_clean_entry(gpointer key, gpointer entry, gpointer nullpointer);
-
-/* This one should be called from time to time (from keepalive, in this case)
-   to make sure things don't stay in the node cache forever. By marking nodes
-   during the first run and deleting marked nodes during a next run, every
-   node should be available in the cache for at least a minute (assuming the
-   function is indeed called every minute). */
-void jabber_cache_clean(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	time_t threshold = time(NULL) - JABBER_CACHE_MAX_AGE;
-
-	g_hash_table_foreach_remove(jd->node_cache, jabber_cache_clean_entry, &threshold);
-}
-
-gboolean jabber_cache_clean_entry(gpointer key, gpointer entry_, gpointer threshold_)
-{
-	struct jabber_cache_entry *entry = entry_;
-	time_t *threshold = threshold_;
-
-	return entry->saved_at < *threshold;
-}
-
-xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_cache_entry *entry;
-	char *s;
-
-	if ((s = xt_find_attr(node, "id")) == NULL ||
-	    strncmp(s, JABBER_CACHED_ID, strlen(JABBER_CACHED_ID)) != 0) {
-		/* Silently ignore it, without an ID (or a non-cache
-		   ID) we don't know how to handle the packet and we
-		   probably don't have to. */
-		return XT_HANDLED;
-	}
-
-	entry = g_hash_table_lookup(jd->node_cache, s);
-
-	if (entry == NULL) {
-		/*
-		There's no longer an easy way to see if we generated this
-		one or someone else, and there's a ten-minute timeout anyway,
-		so meh.
-
-		imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
-		              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
-		*/
-	} else if (entry->func) {
-		return entry->func(ic, node, entry->node);
-	}
-
-	return XT_HANDLED;
-}
-
-const struct jabber_away_state jabber_away_state_list[] =
-{
-	{ "away",  "Away" },
-	{ "chat",  "Free for Chat" },   /* WTF actually uses this? */
-	{ "dnd",   "Do not Disturb" },
-	{ "xa",    "Extended Away" },
-	{ "",      NULL }
-};
-
-const struct jabber_away_state *jabber_away_state_by_code(char *code)
-{
-	int i;
-
-	if (code == NULL) {
-		return NULL;
-	}
-
-	for (i = 0; jabber_away_state_list[i].full_name; i++) {
-		if (g_strcasecmp(jabber_away_state_list[i].code, code) == 0) {
-			return jabber_away_state_list + i;
-		}
-	}
-
-	return NULL;
-}
-
-const struct jabber_away_state *jabber_away_state_by_name(char *name)
-{
-	int i;
-
-	if (name == NULL) {
-		return NULL;
-	}
-
-	for (i = 0; jabber_away_state_list[i].full_name; i++) {
-		if (g_strcasecmp(jabber_away_state_list[i].full_name, name) == 0) {
-			return jabber_away_state_list + i;
-		}
-	}
-
-	return NULL;
-}
-
-struct jabber_buddy_ask_data {
-	struct im_connection *ic;
-	char *handle;
-	char *realname;
-};
-
-static void jabber_buddy_ask_yes(void *data)
-{
-	struct jabber_buddy_ask_data *bla = data;
-
-	presence_send_request(bla->ic, bla->handle, "subscribed");
-
-	imcb_ask_add(bla->ic, bla->handle, NULL);
-
-	g_free(bla->handle);
-	g_free(bla);
-}
-
-static void jabber_buddy_ask_no(void *data)
-{
-	struct jabber_buddy_ask_data *bla = data;
-
-	presence_send_request(bla->ic, bla->handle, "unsubscribed");
-
-	g_free(bla->handle);
-	g_free(bla);
-}
-
-void jabber_buddy_ask(struct im_connection *ic, char *handle)
-{
-	struct jabber_buddy_ask_data *bla = g_new0(struct jabber_buddy_ask_data, 1);
-	char *buf;
-
-	bla->ic = ic;
-	bla->handle = g_strdup(handle);
-
-	buf = g_strdup_printf("The user %s wants to add you to his/her buddy list.", handle);
-	imcb_ask(ic, buf, bla, jabber_buddy_ask_yes, jabber_buddy_ask_no);
-	g_free(buf);
-}
-
-/* Compares just the bare portions of two Jabber IDs. */
-int jabber_compare_jid(const char *jid1, const char *jid2)
-{
-	int i;
-
-	if (!jid1 || !jid2) {
-		return FALSE;
-	}
-
-	for (i = 0;; i++) {
-		if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
-			if ((jid1[i] == '\0' || jid1[i] == '/') && (jid2[i] == '\0' || jid2[i] == '/')) {
-				break;
-			}
-			return FALSE;
-		}
-		if (g_ascii_tolower(jid1[i]) != g_ascii_tolower(jid2[i])) {
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-/* The /resource part is case sensitive. This stops once we see a slash.
-   Returns a new string. Don't leak it! */
-char *jabber_normalize(const char *orig)
-{
-	char *lower, *new, *s;
-
-	if (!(s = strchr(orig, '/'))) {
-		return g_utf8_strdown(orig, -1);
-	}
-
-	lower = g_utf8_strdown(orig, (s - orig));    /* stop in s */
-	new = g_strconcat(lower, s, NULL);
-	g_free(lower);
-	return new;
-}
-
-/* Similar to jabber_normalize, but works with addresses in the form
- * resource=chatroom@example.com */
-char *jabber_normalize_ext(const char *orig)
-{
-	char *lower, *new, *s;
-
-	if (!(s = strchr(orig, '='))) {
-		return g_utf8_strdown(orig, -1);
-	}
-
-	lower = g_utf8_strdown(s, -1);   /* start in s */
-
-	*s = 0;
-	new = g_strconcat(orig, lower, NULL);
-	*s = '=';
-
-	g_free(lower);
-	return new;
-}
-
-/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
-   FULL jid or if we already have this buddy/resource. XXX: No, great, actually
-   buddies from transports don't (usually) have resources. So we'll really have
-   to deal with that properly. Set their ->resource property to NULL. Do *NOT*
-   allow to mix this stuff, though... */
-struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid_)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *new, *bi;
-	char *s, *full_jid;
-
-	full_jid = jabber_normalize(full_jid_);
-
-	if ((s = strchr(full_jid, '/'))) {
-		*s = 0;
-	}
-
-	new = g_new0(struct jabber_buddy, 1);
-
-	if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
-		/* The first entry is always a bare JID. If there are more, we
-		   should ignore the first one here. */
-		if (bud->next) {
-			bud = bud->next;
-		}
-
-		/* If this is a transport buddy or whatever, it can't have more
-		   than one instance, so this is always wrong: */
-		if (s == NULL || bud->resource == NULL) {
-			if (s) {
-				*s = '/';
-			}
-			g_free(new);
-			g_free(full_jid);
-			return NULL;
-		}
-
-		new->bare_jid = bud->bare_jid;
-
-		/* We already have another resource for this buddy, add the
-		   new one to the list. */
-		for (bi = bud; bi; bi = bi->next) {
-			/* Check for dupes. */
-			if (strcmp(bi->resource, s + 1) == 0) {
-				*s = '/';
-				g_free(new);
-				g_free(full_jid);
-				return NULL;
-			}
-			/* Append the new item to the list. */
-			else if (bi->next == NULL) {
-				bi->next = new;
-				break;
-			}
-		}
-	} else {
-		new->full_jid = new->bare_jid = g_strdup(full_jid);
-		g_hash_table_insert(jd->buddies, new->bare_jid, new);
-
-		if (s) {
-			new->next = g_new0(struct jabber_buddy, 1);
-			new->next->bare_jid = new->bare_jid;
-			new = new->next;
-		}
-	}
-
-	if (s) {
-		*s = '/';
-		new->full_jid = full_jid;
-		new->resource = strchr(new->full_jid, '/') + 1;
-	} else {
-		/* Let's waste some more bytes of RAM instead of to make
-		   memory management a total disaster here. And it saves
-		   me one g_free() call in this function. :-P */
-		new->full_jid = full_jid;
-	}
-
-	return new;
-}
-
-/* Finds a buddy from our structures. Can find both full- and bare JIDs. When
-   asked for a bare JID, it uses the "resource_select" setting to see which
-   resource to pick. */
-struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *head;
-	char *s, *jid;
-
-	jid = jabber_normalize(jid_);
-
-	if ((s = strchr(jid, '/'))) {
-		int bare_exists = 0;
-
-		*s = 0;
-		if ((bud = g_hash_table_lookup(jd->buddies, jid))) {
-			bare_exists = 1;
-
-			if (bud->next) {
-				bud = bud->next;
-			}
-
-			/* Just return the first one for this bare JID. */
-			if (flags & GET_BUDDY_FIRST) {
-				*s = '/';
-				g_free(jid);
-				return bud;
-			}
-
-			/* Is this one of those no-resource buddies? */
-			if (bud->resource == NULL) {
-				*s = '/';
-				g_free(jid);
-				return NULL;
-			}
-
-			/* See if there's an exact match. */
-			for (; bud; bud = bud->next) {
-				if (strcmp(bud->resource, s + 1) == 0) {
-					break;
-				}
-			}
-		}
-
-		if (bud == NULL && (flags & GET_BUDDY_CREAT) &&
-		    (bare_exists || bee_user_by_handle(ic->bee, ic, jid))) {
-			*s = '/';
-			bud = jabber_buddy_add(ic, jid);
-		}
-
-		g_free(jid);
-		return bud;
-	} else {
-		struct jabber_buddy *best_prio, *best_time;
-		char *set;
-
-		head = g_hash_table_lookup(jd->buddies, jid);
-		bud = (head && head->next) ? head->next : head;
-
-		g_free(jid);
-
-		if (bud == NULL) {
-			/* No match. Create it now? */
-			return ((flags & GET_BUDDY_CREAT) &&
-			        bee_user_by_handle(ic->bee, ic, jid_)) ?
-			       jabber_buddy_add(ic, jid_) : NULL;
-		} else if (bud->resource && (flags & GET_BUDDY_EXACT)) {
-			/* We want an exact match, so in thise case there shouldn't be a /resource. */
-			if (head != bud && head->resource == NULL) {
-				return head;
-			} else {
-				return NULL;
-			}
-		} else if (bud->resource == NULL || bud->next == NULL) {
-			/* No need for selection if there's only one option. */
-			return bud;
-		} else if (flags & GET_BUDDY_FIRST) {
-			/* Looks like the caller doesn't care about details. */
-			return bud;
-		} else if (flags & GET_BUDDY_BARE) {
-			return head;
-		}
-
-		best_prio = best_time = bud;
-		for (; bud; bud = bud->next) {
-			if (bud->priority > best_prio->priority) {
-				best_prio = bud;
-			}
-			if (bud->last_msg > best_time->last_msg) {
-				best_time = bud;
-			}
-		}
-
-		if ((set = set_getstr(&ic->acc->set, "resource_select")) == NULL) {
-			return NULL;
-		} else if (strcmp(set, "priority") == 0) {
-			return best_prio;
-		} else if (flags & GET_BUDDY_BARE_OK) { /* && strcmp( set, "activity" ) == 0 */
-			if (best_time->last_msg + set_getint(&ic->acc->set, "activity_timeout") >= time(NULL)) {
-				return best_time;
-			} else {
-				return head;
-			}
-		} else {
-			return best_time;
-		}
-	}
-}
-
-/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
-   to export to BitlBee. This is mainly for groupchats right now. It's
-   a bit of a hack, but I just think having the user nickname in the hostname
-   part of the hostmask doesn't look nice on IRC. Normally you can convert
-   a normal JID to ext_jid by swapping the part before and after the / and
-   replacing the / with a =. But there should be some stripping (@s are
-   allowed in Jabber nicks...). */
-struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
-{
-	struct jabber_buddy *bud;
-	char *s, *jid;
-
-	jid = jabber_normalize_ext(jid_);
-
-	if ((s = strchr(jid, '=')) == NULL) {
-		g_free(jid);
-		return NULL;
-	}
-
-	for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
-		/* Hmmm, could happen if not all people in the chat are anonymized? */
-		if (bud->ext_jid == NULL) {
-			continue;
-		}
-
-		if (strcmp(bud->ext_jid, jid) == 0) {
-			break;
-		}
-	}
-
-	g_free(jid);
-
-	return bud;
-}
-
-/* Remove one specific full JID from our list. Use this when a buddy goes
-   off-line (because (s)he can still be online from a different location.
-   XXX: See above, we should accept bare JIDs too... */
-int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *prev = NULL, *bi;
-	char *s, *full_jid;
-
-	full_jid = jabber_normalize(full_jid_);
-
-	if ((s = strchr(full_jid, '/'))) {
-		*s = 0;
-	}
-
-	if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
-		if (bud->next) {
-			bud = (prev = bud)->next;
-		}
-
-		/* If there's only one item in the list (and if the resource
-		   matches), removing it is simple. (And the hash reference
-		   should be removed too!) */
-		if (bud->next == NULL &&
-		    ((s == NULL && bud->resource == NULL) ||
-		     (bud->resource && s && strcmp(bud->resource, s + 1) == 0))) {
-			int st = jabber_buddy_remove_bare(ic, full_jid);
-			g_free(full_jid);
-			return st;
-		} else if (s == NULL || bud->resource == NULL) {
-			/* Tried to remove a bare JID while this JID does seem
-			   to have resources... (Or the opposite.) *sigh* */
-			g_free(full_jid);
-			return 0;
-		} else {
-			for (bi = bud; bi; bi = (prev = bi)->next) {
-				if (strcmp(bi->resource, s + 1) == 0) {
-					break;
-				}
-			}
-
-			g_free(full_jid);
-
-			if (bi) {
-				if (prev) {
-					prev->next = bi->next;
-				} else {
-					/* Don't think this should ever happen anymore. */
-					g_hash_table_replace(jd->buddies, bi->bare_jid, bi->next);
-				}
-
-				g_free(bi->ext_jid);
-				g_free(bi->full_jid);
-				g_free(bi->away_message);
-				g_free(bi);
-
-				return 1;
-			} else {
-				return 0;
-			}
-		}
-	} else {
-		g_free(full_jid);
-		return 0;
-	}
-}
-
-/* Remove a buddy completely; removes all resources that belong to the
-   specified bare JID. Use this when removing someone from the contact
-   list, for example. */
-int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *next;
-
-	if (strchr(bare_jid, '/')) {
-		return 0;
-	}
-
-	if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
-		/* Most important: Remove the hash reference. We don't know
-		   this buddy anymore. */
-		g_hash_table_remove(jd->buddies, bud->bare_jid);
-		g_free(bud->bare_jid);
-
-		/* Deallocate the linked list of resources. */
-		while (bud) {
-			/* ext_jid && anonymous means that this buddy is
-			   specific to one groupchat (the one we're
-			   currently cleaning up) so it can be deleted
-			   completely. */
-			if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
-				imcb_remove_buddy(ic, bud->ext_jid, NULL);
-			}
-
-			next = bud->next;
-			g_free(bud->ext_jid);
-			g_free(bud->full_jid);
-			g_free(bud->away_message);
-			g_free(bud);
-			bud = next;
-		}
-
-		return 1;
-	} else {
-		return 0;
-	}
-}
-
-static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
-{
-	struct jabber_buddy *bud, *next;
-
-	bud = value;
-	if (bud->bare_jid != bud->full_jid) {
-		g_free(bud->bare_jid);
-	}
-	while (bud) {
-		next = bud->next;
-		g_free(bud->ext_jid);
-		g_free(bud->full_jid);
-		g_free(bud->away_message);
-		g_free(bud);
-		bud = next;
-	}
-
-	return TRUE;
-}
-
-void jabber_buddy_remove_all(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
-	g_hash_table_destroy(jd->buddies);
-}
-
-time_t jabber_get_timestamp(struct xt_node *xt)
-{
-	struct xt_node *c;
-	char *s = NULL;
-	struct tm tp;
-	gboolean is_old = TRUE;
-	const char *format;
-
-	/* XEP-0091 has <x> */
-	c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
-
-	if (!c || !(s = xt_find_attr(c, "stamp"))) {
-		is_old = FALSE;
-
-		/* XEP-0203 has <delay> */
-		c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
-		if (!c || !(s = xt_find_attr(c, "stamp"))) {
-			return 0;
-		}
-	}
-
-	memset(&tp, 0, sizeof(tp));
-
-	/* The other main difference between XEPs is the timestamp format */
-	format = (is_old) ? "%4d%2d%2dT%2d:%2d:%2d" : "%4d-%2d-%2dT%2d:%2d:%2dZ";
-
-	if (sscanf(s, format, &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
-	           &tp.tm_hour, &tp.tm_min, &tp.tm_sec) != 6) {
-		return 0;
-	}
-
-	tp.tm_year -= 1900;
-	tp.tm_mon--;
-
-	return mktime_utc(&tp);
-}
-
-struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
-{
-	struct jabber_error *err;
-	struct xt_node *c;
-	char *s;
-
-	if (node == NULL) {
-		return NULL;
-	}
-
-	err = g_new0(struct jabber_error, 1);
-	err->type = xt_find_attr(node, "type");
-
-	for (c = node->children; c; c = c->next) {
-		if (!(s = xt_find_attr(c, "xmlns")) ||
-		    strcmp(s, xmlns) != 0) {
-			continue;
-		}
-
-		if (strcmp(c->name, "text") != 0) {
-			err->code = c->name;
-		}
-		/* Only use the text if it doesn't have an xml:lang attribute,
-		   if it's empty or if it's set to something English. */
-		else if (!(s = xt_find_attr(c, "xml:lang")) ||
-		         !*s || strncmp(s, "en", 2) == 0) {
-			err->text = c->text;
-		}
-	}
-
-	return err;
-}
-
-void jabber_error_free(struct jabber_error *err)
-{
-	g_free(err);
-}
-
-gboolean jabber_set_me(struct im_connection *ic, const char *me)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (strchr(me, '@') == NULL) {
-		return FALSE;
-	}
-
-	g_free(jd->username);
-	g_free(jd->me);
-
-	jd->me = jabber_normalize(me);
-	jd->server = strchr(jd->me, '@');
-	jd->username = g_strndup(jd->me, jd->server - jd->me);
-	jd->server++;
-
-	/* Set the "internal" account username, for groupchats */
-	g_free(jd->internal_jid);
-	jd->internal_jid = g_strdup(jd->me);
-
-	return TRUE;
-}
-
-/* Returns new reference! g_free() afterwards. */
-char *jabber_get_bare_jid(char *jid)
-{
-	char *s = NULL;
-
-	if (jid == NULL) {
-		return NULL;
-	}
-
-	if ((s = strchr(jid, '/'))) {
-		return g_strndup(jid, s - jid);
-	} else {
-		return g_strdup(jid);
-	}
-}
Index: protocols/jabber/jid.c
===================================================================
--- protocols/jabber/jid.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jid.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,170 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static jid jid_safe(jid id)
+{
+    char *str;
+
+    if(strlen(id->server) == 0 || strlen(id->server) > 255)
+        return NULL;
+
+    /* lowercase the hostname, make sure it's valid characters */
+    for(str = id->server; *str != '\0'; str++)
+    {
+        *str = tolower(*str);
+        if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return NULL;
+    }
+
+    /* cut off the user */
+    if(id->user != NULL && strlen(id->user) > 64)
+        id->user[64] = '\0';
+
+    /* check for low and invalid ascii characters in the username */
+    if(id->user != NULL)
+        for(str = id->user; *str != '\0'; str++)
+            if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return NULL;
+
+    return id;
+}
+
+jid jid_new(pool p, char *idstr)
+{
+    char *server, *resource, *type, *str;
+    jid id;
+
+    if(p == NULL || idstr == NULL || strlen(idstr) == 0)
+        return NULL;
+
+    /* user@server/resource */
+
+    str = pstrdup(p, idstr);
+
+    id = pmalloco(p,sizeof(struct jid_struct));
+    id->p = p;
+
+    resource = strstr(str,"/");
+    if(resource != NULL)
+    {
+        *resource = '\0';
+        ++resource;
+        if(strlen(resource) > 0)
+            id->resource = resource;
+    }else{
+        resource = str + strlen(str); /* point to end */
+    }
+
+    type = strstr(str,":");
+    if(type != NULL && type < resource)
+    {
+        *type = '\0';
+        ++type;
+        str = type; /* ignore the type: prefix */
+    }
+
+    server = strstr(str,"@");
+    if(server == NULL || server > resource)
+    { /* if there's no @, it's just the server address */
+        id->server = str;
+    }else{
+        *server = '\0';
+        ++server;
+        id->server = server;
+        if(strlen(str) > 0)
+            id->user = str;
+    }
+
+    return jid_safe(id);
+}
+
+char *jid_full(jid id)
+{
+    spool s;
+
+    if(id == NULL)
+        return NULL;
+
+    /* use cached copy */
+    if(id->full != NULL)
+        return id->full;
+
+    s = spool_new(id->p);
+
+    if(id->user != NULL)
+        spooler(s, id->user,"@",s);
+
+    spool_add(s, id->server);
+
+    if(id->resource != NULL)
+        spooler(s, "/",id->resource,s);
+
+    id->full = spool_print(s);
+    return id->full;
+}
+
+/* local utils */
+static int _jid_nullstrcmp(char *a, char *b)
+{
+    if(a == NULL && b == NULL) return 0;
+    if(a == NULL || b == NULL) return -1;
+    return strcmp(a,b);
+}
+static int _jid_nullstrcasecmp(char *a, char *b)
+{
+    if(a == NULL && b == NULL) return 0;
+    if(a == NULL || b == NULL) return -1;
+    return g_strcasecmp(a,b);
+}
+
+/* suggested by Anders Qvist <quest@valdez.netg.se> */
+int jid_cmpx(jid a, jid b, int parts)
+{
+    if(a == NULL || b == NULL)
+        return -1;
+
+    if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
+    if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
+    if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1;
+
+    return 0;
+}
Index: protocols/jabber/jpacket.c
===================================================================
--- protocols/jabber/jpacket.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jpacket.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,159 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+static jpacket jpacket_reset(jpacket p);
+
+jpacket jpacket_new(xmlnode x)
+{
+    jpacket p;
+
+    if(x == NULL)
+        return NULL;
+
+    p = pmalloc(xmlnode_pool(x),sizeof(_jpacket));
+    p->x = x;
+
+    return jpacket_reset(p);
+}
+
+static jpacket jpacket_reset(jpacket p)
+{
+    char *val;
+    xmlnode x;
+
+    x = p->x;
+    memset(p,0,sizeof(_jpacket));
+    p->x = x;
+    p->p = xmlnode_pool(x);
+
+    if(strncmp(xmlnode_get_name(x),"message",7) == 0)
+    {
+        p->type = JPACKET_MESSAGE;
+    }else if(strncmp(xmlnode_get_name(x),"presence",8) == 0)
+    {
+        p->type = JPACKET_PRESENCE;
+        val = xmlnode_get_attrib(x, "type");
+        if(val == NULL)
+            p->subtype = JPACKET__AVAILABLE;
+        else if(strcmp(val,"unavailable") == 0)
+            p->subtype = JPACKET__UNAVAILABLE;
+        else if(strcmp(val,"probe") == 0)
+            p->subtype = JPACKET__PROBE;
+        else if(strcmp(val,"error") == 0)
+            p->subtype = JPACKET__ERROR;
+        else if(strcmp(val,"invisible") == 0)
+            p->subtype = JPACKET__INVISIBLE;
+        else if(*val == 's' || *val == 'u')
+            p->type = JPACKET_S10N;
+        else if(strcmp(val,"available") == 0)
+        { /* someone is using type='available' which is frowned upon */
+            xmlnode_hide_attrib(x,"type");
+            p->subtype = JPACKET__AVAILABLE;
+        }else
+            p->type = JPACKET_UNKNOWN;
+    }else if(strncmp(xmlnode_get_name(x),"iq",2) == 0)
+    {
+        p->type = JPACKET_IQ;
+        p->iq = xmlnode_get_tag(x,"?xmlns");
+        p->iqns = xmlnode_get_attrib(p->iq,"xmlns");
+    }
+
+    /* set up the jids if any, flag packet as unknown if they are unparseable */
+    val = xmlnode_get_attrib(x,"to");
+    if(val != NULL)
+        if((p->to = jid_new(p->p, val)) == NULL)
+            p->type = JPACKET_UNKNOWN;
+    val = xmlnode_get_attrib(x,"from");
+    if(val != NULL)
+        if((p->from = jid_new(p->p, val)) == NULL)
+            p->type = JPACKET_UNKNOWN;
+
+    return p;
+}
+
+
+int jpacket_subtype(jpacket p)
+{
+    char *type;
+    int ret = p->subtype;
+
+    if(ret != JPACKET__UNKNOWN)
+        return ret;
+
+    ret = JPACKET__NONE; /* default, when no type attrib is specified */
+    type = xmlnode_get_attrib(p->x, "type");
+    if(j_strcmp(type,"error") == 0)
+        ret = JPACKET__ERROR;
+    else
+        switch(p->type)
+        {
+        case JPACKET_MESSAGE:
+            if(j_strcmp(type,"chat") == 0)
+                ret = JPACKET__CHAT;
+            else if(j_strcmp(type,"groupchat") == 0)
+                ret = JPACKET__GROUPCHAT;
+            else if(j_strcmp(type,"headline") == 0)
+                ret = JPACKET__HEADLINE;
+            break;
+        case JPACKET_S10N:
+            if(j_strcmp(type,"subscribe") == 0)
+                ret = JPACKET__SUBSCRIBE;
+            else if(j_strcmp(type,"subscribed") == 0)
+                ret = JPACKET__SUBSCRIBED;
+            else if(j_strcmp(type,"unsubscribe") == 0)
+                ret = JPACKET__UNSUBSCRIBE;
+            else if(j_strcmp(type,"unsubscribed") == 0)
+                ret = JPACKET__UNSUBSCRIBED;
+            break;
+        case JPACKET_IQ:
+            if(j_strcmp(type,"get") == 0)
+                ret = JPACKET__GET;
+            else if(j_strcmp(type,"set") == 0)
+                ret = JPACKET__SET;
+            else if(j_strcmp(type,"result") == 0)
+                ret = JPACKET__RESULT;
+            break;
+        }
+
+    p->subtype = ret;
+    return ret;
+}
Index: protocols/jabber/jutil.c
===================================================================
--- protocols/jabber/jutil.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jutil.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,122 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+#include "nogaim.h"
+
+/* util for making presence packets */
+xmlnode jutil_presnew(int type, char *to, char *status)
+{
+    xmlnode pres;
+
+    pres = xmlnode_new_tag("presence");
+    switch(type)
+    {
+    case JPACKET__SUBSCRIBE:
+        xmlnode_put_attrib(pres,"type","subscribe");
+        break;
+    case JPACKET__UNSUBSCRIBE:
+        xmlnode_put_attrib(pres,"type","unsubscribe");
+        break;
+    case JPACKET__SUBSCRIBED:
+        xmlnode_put_attrib(pres,"type","subscribed");
+        break;
+    case JPACKET__UNSUBSCRIBED:
+        xmlnode_put_attrib(pres,"type","unsubscribed");
+        break;
+    case JPACKET__PROBE:
+        xmlnode_put_attrib(pres,"type","probe");
+        break;
+    case JPACKET__UNAVAILABLE:
+        xmlnode_put_attrib(pres,"type","unavailable");
+        break;
+    case JPACKET__INVISIBLE:
+        xmlnode_put_attrib(pres,"type","invisible");
+        break;
+    }
+    if(to != NULL)
+        xmlnode_put_attrib(pres,"to",to);
+    if(status != NULL)
+        xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
+
+    return pres;
+}
+
+/* util for making IQ packets */
+xmlnode jutil_iqnew(int type, char *ns)
+{
+    xmlnode iq;
+
+    iq = xmlnode_new_tag("iq");
+    switch(type)
+    {
+    case JPACKET__GET:
+        xmlnode_put_attrib(iq,"type","get");
+        break;
+    case JPACKET__SET:
+        xmlnode_put_attrib(iq,"type","set");
+        break;
+    case JPACKET__RESULT:
+        xmlnode_put_attrib(iq,"type","result");
+        break;
+    case JPACKET__ERROR:
+        xmlnode_put_attrib(iq,"type","error");
+        break;
+    }
+    xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns);
+
+    return iq;
+}
+
+/* util for making stream packets */
+xmlnode jutil_header(char* xmlns, char* server)
+{
+     xmlnode result;
+     if ((xmlns == NULL)||(server == NULL))
+	  return NULL;
+     result = xmlnode_new_tag("stream:stream");
+     xmlnode_put_attrib(result, "xmlns:stream", "http://etherx.jabber.org/streams");
+     xmlnode_put_attrib(result, "xmlns", xmlns);
+     xmlnode_put_attrib(result, "to", server);
+
+     return result;
+}
Index: protocols/jabber/latin1tab.h
===================================================================
--- protocols/jabber/latin1tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/latin1tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,62 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME,
+/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER,
+/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER,
+/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER,
+/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
+/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT,
Index: protocols/jabber/lib.h
===================================================================
--- protocols/jabber/lib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/lib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,343 @@
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+
+#include "xmlparse.h"
+
+int j_strcmp(const char *a, const char *b);
+
+/*
+**  Arrange to use either varargs or stdargs
+*/
+
+#define MAXSHORTSTR	203		/* max short string length */
+#define QUAD_T	unsigned long long
+
+#if defined(__STDC__) || defined(_WIN32)
+
+#include <stdarg.h>
+
+# define VA_LOCAL_DECL	va_list ap;
+# define VA_START(f)	va_start(ap, f)
+# define VA_END		va_end(ap)
+
+#else /* __STDC__ */
+
+# include <varargs.h>
+
+# define VA_LOCAL_DECL	va_list ap;
+# define VA_START(f)	va_start(ap)
+# define VA_END		va_end(ap)
+
+#endif /* __STDC__ */
+
+
+#ifndef INCL_LIB_H
+#define INCL_LIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Pool-based memory management routines                     */
+/*                                                           */
+/* --------------------------------------------------------- */
+
+#undef POOL_DEBUG
+/*
+ flip these, this should be a prime number for top # of pools debugging
+#define POOL_DEBUG 40009 
+*/
+
+/* pheap - singular allocation of memory */
+struct pheap
+{
+    void *block;
+    int size, used;
+};
+
+/* pool_cleaner - callback type which is associated
+   with a pool entry; invoked when the pool entry is 
+   free'd */
+typedef void (*pool_cleaner)(void *arg);
+
+/* pfree - a linked list node which stores an
+   allocation chunk, plus a callback */
+struct pfree
+{
+    pool_cleaner f;
+    void *arg;
+    struct pheap *heap;
+    struct pfree *next;
+};
+
+/* pool - base node for a pool. Maintains a linked list
+   of pool entries (pfree) */
+typedef struct pool_struct
+{
+    int size;
+    struct pfree *cleanup;
+    struct pheap *heap;
+#ifdef POOL_DEBUG
+    char name[8], zone[32];
+    int lsize;
+} _pool, *pool;
+#define pool_new() _pool_new(ZONE) 
+#define pool_heap(i) _pool_new_heap(i,ZONE) 
+#else
+} _pool, *pool;
+#define pool_heap(i) _pool_new_heap(i,NULL) 
+#define pool_new() _pool_new(NULL)
+#endif
+
+pool _pool_new(char *zone); /* new pool :) */
+pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */
+void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */
+void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */
+void *pmalloco(pool p, int size); /* YAPW for zeroing the block */
+char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */
+void pool_stat(int full); /* print to stderr the changed pools and reset */
+void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */
+void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */
+
+
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Socket helper stuff                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define NETSOCKET_SERVER 0
+#define NETSOCKET_CLIENT 1
+#define NETSOCKET_UDP 2
+
+#ifndef WIN32
+int make_netsocket(u_short port, char *host, int type);
+struct in_addr *make_addr(char *host);
+int set_fd_close_on_exec(int fd, int flag);
+#endif
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Hashtable functions                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+typedef struct xhn_struct
+{
+    struct xhn_struct *next;
+    const char *key;
+    void *val;
+} *xhn, _xhn;
+
+char *strescape(pool p, char *);
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* String pools (spool) functions                            */
+/*                                                           */
+/* --------------------------------------------------------- */
+struct spool_node
+{
+    char *c;
+    struct spool_node *next;
+};
+
+typedef struct spool_struct
+{
+    pool p;
+    int len;
+    struct spool_node *last;
+    struct spool_node *first;
+} *spool;
+
+spool spool_new(pool p); /* create a string pool */
+void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */
+char *spool_print(spool s); /* return a big string */
+void spool_add(spool s, char *str); /* add a single char to the pool */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* xmlnodes - Document Object Model                          */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define NTYPE_TAG    0
+#define NTYPE_ATTRIB 1
+#define NTYPE_CDATA  2
+
+#define NTYPE_LAST   2
+#define NTYPE_UNDEF  -1
+
+/* -------------------------------------------------------------------------- 
+   Node structure. Do not use directly! Always use accessor macros 
+   and methods!
+   -------------------------------------------------------------------------- */
+typedef struct xmlnode_t
+{
+     char*               name;
+     unsigned short      type;
+     char*               data;
+     int                 data_sz;
+     int                 complete;
+     pool               p;
+     struct xmlnode_t*  parent;
+     struct xmlnode_t*  firstchild; 
+     struct xmlnode_t*  lastchild;
+     struct xmlnode_t*  prev; 
+     struct xmlnode_t*  next;
+     struct xmlnode_t*  firstattrib;
+     struct xmlnode_t*  lastattrib;
+} _xmlnode, *xmlnode;
+
+/* Node creation routines */
+xmlnode  xmlnode_wrap(xmlnode x,const char* wrapper);
+xmlnode  xmlnode_new_tag(const char* name);
+xmlnode  xmlnode_new_tag_pool(pool p, const char* name);
+xmlnode  xmlnode_insert_tag(xmlnode parent, const char* name); 
+xmlnode  xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size);
+xmlnode  xmlnode_insert_tag_node(xmlnode parent, xmlnode node);
+xmlnode  xmlnode_str(char *str, int len);
+xmlnode  xmlnode_dup(xmlnode x); /* duplicate x */
+xmlnode  xmlnode_dup_pool(pool p, xmlnode x);
+
+/* Node Memory Pool */
+pool xmlnode_pool(xmlnode node);
+
+/* Node editing */
+void xmlnode_hide(xmlnode child);
+void xmlnode_hide_attrib(xmlnode parent, const char *name);
+
+/* Node deletion routine, also frees the node pool! */
+void xmlnode_free(xmlnode node);
+
+/* Locates a child tag by name and returns it */
+xmlnode  xmlnode_get_tag(xmlnode parent, const char* name);
+char* xmlnode_get_tag_data(xmlnode parent, const char* name);
+
+/* Attribute accessors */
+void     xmlnode_put_attrib(xmlnode owner, const char* name, const char* value);
+char*    xmlnode_get_attrib(xmlnode owner, const char* name);
+void     xmlnode_put_expat_attribs(xmlnode owner, const char** atts);
+
+/* Bastard am I, but these are fun for internal use ;-) */
+void     xmlnode_put_vattrib(xmlnode owner, const char* name, void *value);
+void*    xmlnode_get_vattrib(xmlnode owner, const char* name);
+
+/* Node traversal routines */
+xmlnode  xmlnode_get_firstchild(xmlnode parent);
+xmlnode  xmlnode_get_lastchild(xmlnode parent);
+xmlnode  xmlnode_get_nextsibling(xmlnode sibling);
+xmlnode  xmlnode_get_prevsibling(xmlnode sibling);
+xmlnode  xmlnode_get_parent(xmlnode node);
+
+/* Node information routines */
+char*    xmlnode_get_name(xmlnode node);
+char*    xmlnode_get_data(xmlnode node);
+
+int      xmlnode_has_children(xmlnode node);
+
+/* Node-to-string translation */
+char*    xmlnode2str(xmlnode node);
+
+/********** END OLD libxode.h BEGIN OLD jabber.h *************/
+
+
+// #define KARMA_DEBUG
+// default to disable karma 
+#define KARMA_READ_MAX(k) (abs(k)*100) /* how much you are allowed to read off the sock */
+#define KARMA_INIT 5   /* internal "init" value */
+#define KARMA_HEARTBEAT 2 /* seconds to register for heartbeat */
+#define KARMA_MAX 10     /* total max karma you can have */
+#define KARMA_INC 1      /* how much to increment every KARMA_HEARTBEAT seconds */
+#define KARMA_DEC 0      /* how much to penalize for reading KARMA_READ_MAX in
+                            KARMA_HEARTBEAT seconds */
+#define KARMA_PENALTY -5 /* where you go when you hit 0 karma */
+#define KARMA_RESTORE 5  /* where you go when you payed your penelty or INIT */
+#define KARMA_RESETMETER 0 /* Reset byte meter on restore default is falst */
+
+struct karma
+{
+    int init; /* struct initialized */
+    int reset_meter; /* reset the byte meter on restore */
+    int val; /* current karma value */
+    long bytes; /* total bytes read (in that time period) */
+    int max;  /* max karma you can have */
+    int inc,dec; /* how much to increment/decrement */
+    int penalty,restore; /* what penalty (<0) or restore (>0) */
+    time_t last_update; /* time this was last incremented */
+};
+
+struct karma *karma_new(pool p); /* creates a new karma object, with default values */
+void karma_copy(struct karma *new, struct karma *old); /* makes a copy of old in new */
+void karma_increment(struct karma *k);          /* inteligently increments karma */
+void karma_decrement(struct karma *k, long bytes_read); /* inteligently decrements karma */
+int karma_check(struct karma *k,long bytes_read); /* checks to see if we have good karma */
+
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Namespace constants                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0)
+
+#define NS_CLIENT    "jabber:client"
+#define NS_SERVER    "jabber:server"
+#define NS_AUTH      "jabber:iq:auth"
+#define NS_REGISTER  "jabber:iq:register"
+#define NS_ROSTER    "jabber:iq:roster"
+#define NS_OFFLINE   "jabber:x:offline"
+#define NS_AGENT     "jabber:iq:agent"
+#define NS_AGENTS    "jabber:iq:agents"
+#define NS_DELAY     "jabber:x:delay"
+#define NS_VERSION   "jabber:iq:version"
+#define NS_TIME      "jabber:iq:time"
+#define NS_VCARD     "vcard-temp"
+#define NS_PRIVATE   "jabber:iq:private"
+#define NS_SEARCH    "jabber:iq:search"
+#define NS_OOB       "jabber:iq:oob"
+#define NS_XOOB      "jabber:x:oob"
+#define NS_ADMIN     "jabber:iq:admin"
+#define NS_FILTER    "jabber:iq:filter"
+#define NS_AUTH_0K   "jabber:iq:auth:0k"
+#define NS_BROWSE    "jabber:iq:browse"
+#define NS_EVENT     "jabber:x:event"
+#define NS_CONFERENCE "jabber:iq:conference"
+#define NS_SIGNED    "jabber:x:signed"
+#define NS_ENCRYPTED "jabber:x:encrypted"
+#define NS_GATEWAY   "jabber:iq:gateway"
+#define NS_LAST      "jabber:iq:last"
+#define NS_ENVELOPE  "jabber:x:envelope"
+#define NS_EXPIRE    "jabber:x:expire"
+#define NS_XHTML     "http://www.w3.org/1999/xhtml"
+
+#define NS_XDBGINSERT "jabber:xdb:ginsert"
+#define NS_XDBNSLIST  "jabber:xdb:nslist"
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif	/* INCL_LIB_H */
Index: protocols/jabber/libxode.h
===================================================================
--- protocols/jabber/libxode.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/libxode.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,398 @@
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <syslog.h>
+#include <strings.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#ifndef __CYGWIN__
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#include <sys/time.h>
+#include <time.h>
+
+#include "xmlparse.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+/*
+**  Arrange to use either varargs or stdargs
+*/
+
+#define MAXSHORTSTR	203		/* max short string length */
+#define QUAD_T	unsigned long long
+
+#ifdef __STDC__
+
+#include <stdarg.h>
+
+# define VA_LOCAL_DECL	va_list ap;
+# define VA_START(f)	va_start(ap, f)
+# define VA_END		va_end(ap)
+
+#else /* __STDC__ */
+
+# include <varargs.h>
+
+# define VA_LOCAL_DECL	va_list ap;
+# define VA_START(f)	va_start(ap)
+# define VA_END		va_end(ap)
+
+#endif /* __STDC__ */
+
+
+#ifndef INCL_LIBXODE_H
+#define INCL_LIBXODE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef HAVE_SNPRINTF
+extern int ap_snprintf(char *, size_t, const char *, ...);
+#define snprintf ap_snprintf
+#endif
+
+#ifndef HAVE_VSNPRINTF
+extern int ap_vsnprintf(char *, size_t, const char *, va_list ap);
+#define vsnprintf ap_vsnprintf
+#endif
+
+#define ZONE zonestr(__FILE__,__LINE__)
+char *zonestr(char *file, int line);
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Pool-based memory management routines                     */
+/*                                                           */
+/* --------------------------------------------------------- */
+
+#undef POOL_DEBUG
+/*
+ flip these, this should be a prime number for top # of pools debugging
+#define POOL_DEBUG 40009 
+*/
+
+/* pheap - singular allocation of memory */
+struct pheap
+{
+    void *block;
+    int size, used;
+};
+
+/* pool_cleaner - callback type which is associated
+   with a pool entry; invoked when the pool entry is 
+   free'd */
+typedef void (*pool_cleaner)(void *arg);
+
+/* pfree - a linked list node which stores an
+   allocation chunk, plus a callback */
+struct pfree
+{
+    pool_cleaner f;
+    void *arg;
+    struct pheap *heap;
+    struct pfree *next;
+};
+
+/* pool - base node for a pool. Maintains a linked list
+   of pool entries (pfree) */
+typedef struct pool_struct
+{
+    int size;
+    struct pfree *cleanup;
+    struct pheap *heap;
+#ifdef POOL_DEBUG
+    char name[8], zone[32];
+    int lsize;
+} _pool, *pool;
+#define pool_new() _pool_new(ZONE) 
+#define pool_heap(i) _pool_new_heap(i,ZONE) 
+#else
+} _pool, *pool;
+#define pool_heap(i) _pool_new_heap(i,NULL) 
+#define pool_new() _pool_new(NULL)
+#endif
+
+pool _pool_new(char *zone); /* new pool :) */
+pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */
+void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */
+void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */
+void *pmalloco(pool p, int size); /* YAPW for zeroing the block */
+char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */
+void pool_stat(int full); /* print to stderr the changed pools and reset */
+void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */
+void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */
+
+
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Socket helper stuff                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define NETSOCKET_SERVER 0
+#define NETSOCKET_CLIENT 1
+#define NETSOCKET_UDP 2
+
+#ifndef WIN32
+int make_netsocket(u_short port, char *host, int type);
+struct in_addr *make_addr(char *host);
+int set_fd_close_on_exec(int fd, int flag);
+#endif
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* SHA calculations                                          */
+/*                                                           */
+/* --------------------------------------------------------- */
+#if (SIZEOF_INT == 4)
+typedef unsigned int uint32;
+#elif (SIZEOF_SHORT == 4)
+typedef unsigned short uint32;
+#else
+typedef unsigned int uint32;
+#endif /* HAVEUINT32 */
+
+int sha_hash(int *data, int *hash);
+int sha_init(int *hash);
+char *shahash(char *str);	/* NOT THREAD SAFE */
+void shahash_r(const char* str, char hashbuf[40]); /* USE ME */
+
+int strprintsha(char *dest, int *hashval);
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* Hashtable functions                                       */
+/*                                                           */
+/* --------------------------------------------------------- */
+typedef int (*KEYHASHFUNC)(const void *key);
+typedef int (*KEYCOMPAREFUNC)(const void *key1, const void *key2);
+typedef int (*TABLEWALKFUNC)(void *user_data, const void *key, void *data);
+
+typedef void *HASHTABLE;
+
+HASHTABLE ghash_create(int buckets, KEYHASHFUNC hash, KEYCOMPAREFUNC cmp);
+void ghash_destroy(HASHTABLE tbl);
+void *ghash_get(HASHTABLE tbl, const void *key);
+int ghash_put(HASHTABLE tbl, const void *key, void *value);
+int ghash_remove(HASHTABLE tbl, const void *key);
+int ghash_walk(HASHTABLE tbl, TABLEWALKFUNC func, void *user_data);
+int str_hash_code(const char *s);
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* XML escaping utils                                        */
+/*                                                           */
+/* --------------------------------------------------------- */
+char *strescape(pool p, char *buf); /* Escape <>&'" chars */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* String pools (spool) functions                            */
+/*                                                           */
+/* --------------------------------------------------------- */
+struct spool_node
+{
+    char *c;
+    struct spool_node *next;
+};
+
+typedef struct spool_struct
+{
+    pool p;
+    int len;
+    struct spool_node *last;
+    struct spool_node *first;
+} *spool;
+
+spool spool_new(pool p); /* create a string pool */
+void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */
+char *spool_print(spool s); /* return a big string */
+void spool_add(spool s, char *str); /* add a single char to the pool */
+char *spools(pool p, ...); /* wrap all the spooler stuff in one function, the happy fun ball! */
+
+
+/* --------------------------------------------------------- */
+/*                                                           */
+/* xmlnodes - Document Object Model                          */
+/*                                                           */
+/* --------------------------------------------------------- */
+#define NTYPE_TAG    0
+#define NTYPE_ATTRIB 1
+#define NTYPE_CDATA  2
+
+#define NTYPE_LAST   2
+#define NTYPE_UNDEF  -1
+
+/* -------------------------------------------------------------------------- 
+   Node structure. Do not use directly! Always use accessor macros 
+   and methods!
+   -------------------------------------------------------------------------- */
+typedef struct xmlnode_t
+{
+     char*               name;
+     unsigned short      type;
+     char*               data;
+     int                 data_sz;
+     int                 complete;
+     pool               p;
+     struct xmlnode_t*  parent;
+     struct xmlnode_t*  firstchild; 
+     struct xmlnode_t*  lastchild;
+     struct xmlnode_t*  prev; 
+     struct xmlnode_t*  next;
+     struct xmlnode_t*  firstattrib;
+     struct xmlnode_t*  lastattrib;
+} _xmlnode, *xmlnode;
+
+/* Node creation routines */
+xmlnode  xmlnode_wrap(xmlnode x,const char* wrapper);
+xmlnode  xmlnode_new_tag(const char* name);
+xmlnode  xmlnode_new_tag_pool(pool p, const char* name);
+xmlnode  xmlnode_insert_tag(xmlnode parent, const char* name); 
+xmlnode  xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size);
+xmlnode  xmlnode_insert_tag_node(xmlnode parent, xmlnode node);
+void     xmlnode_insert_node(xmlnode parent, xmlnode node);
+xmlnode  xmlnode_str(char *str, int len);
+xmlnode  xmlnode_file(char *file);
+xmlnode  xmlnode_dup(xmlnode x); /* duplicate x */
+xmlnode  xmlnode_dup_pool(pool p, xmlnode x);
+
+/* Node Memory Pool */
+pool xmlnode_pool(xmlnode node);
+xmlnode _xmlnode_new(pool p, const char *name, unsigned int type);
+
+/* Node editing */
+void xmlnode_hide(xmlnode child);
+void xmlnode_hide_attrib(xmlnode parent, const char *name);
+
+/* Node deletion routine, also frees the node pool! */
+void xmlnode_free(xmlnode node);
+
+/* Locates a child tag by name and returns it */
+xmlnode  xmlnode_get_tag(xmlnode parent, const char* name);
+char* xmlnode_get_tag_data(xmlnode parent, const char* name);
+
+/* Attribute accessors */
+void     xmlnode_put_attrib(xmlnode owner, const char* name, const char* value);
+char*    xmlnode_get_attrib(xmlnode owner, const char* name);
+void     xmlnode_put_expat_attribs(xmlnode owner, const char** atts);
+
+/* Bastard am I, but these are fun for internal use ;-) */
+void     xmlnode_put_vattrib(xmlnode owner, const char* name, void *value);
+void*    xmlnode_get_vattrib(xmlnode owner, const char* name);
+
+/* Node traversal routines */
+xmlnode  xmlnode_get_firstattrib(xmlnode parent);
+xmlnode  xmlnode_get_firstchild(xmlnode parent);
+xmlnode  xmlnode_get_lastchild(xmlnode parent);
+xmlnode  xmlnode_get_nextsibling(xmlnode sibling);
+xmlnode  xmlnode_get_prevsibling(xmlnode sibling);
+xmlnode  xmlnode_get_parent(xmlnode node);
+
+/* Node information routines */
+char*    xmlnode_get_name(xmlnode node);
+char*    xmlnode_get_data(xmlnode node);
+int      xmlnode_get_datasz(xmlnode node);
+int      xmlnode_get_type(xmlnode node);
+
+int      xmlnode_has_children(xmlnode node);
+int      xmlnode_has_attribs(xmlnode node);
+
+/* Node-to-string translation */
+char*    xmlnode2str(xmlnode node);
+
+/* Node-to-terminated-string translation 
+   -- useful for interfacing w/ scripting langs */
+char*    xmlnode2tstr(xmlnode node);
+
+int      xmlnode_cmp(xmlnode a, xmlnode b); /* compares a and b for equality */
+
+int      xmlnode2file(char *file, xmlnode node); /* writes node to file */
+
+/* Expat callbacks */
+void expat_startElement(void* userdata, const char* name, const char** atts);
+void expat_endElement(void* userdata, const char* name);
+void expat_charData(void* userdata, const char* s, int len);
+
+/* SHA.H */
+/* 
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ * 
+ * The Original Code is SHA 180-1 Header File
+ * 
+ * The Initial Developer of the Original Code is Paul Kocher of
+ * Cryptography Research.  Portions created by Paul Kocher are 
+ * Copyright (C) 1995-9 by Cryptography Research, Inc.  All
+ * Rights Reserved.
+ * 
+ * Contributor(s):
+ *
+ *     Paul Kocher
+ * 
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable 
+ * instead of those above.  If you wish to allow use of your 
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL.  If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+typedef struct {
+  unsigned long H[5];
+  unsigned long W[80];
+  int lenW;
+  unsigned long sizeHi,sizeLo;
+} SHA_CTX;
+
+
+void shaInit(SHA_CTX *ctx);
+void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len);
+void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]);
+void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]);
+
+
+/* END SHA.H */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCL_LIBXODE_H */
Index: protocols/jabber/log.c
===================================================================
--- protocols/jabber/log.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/log.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,44 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Jabber
+ *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#include "jabber.h"
+#include "log.h"
+
+#ifdef DEBUG
+
+void jdebug(char *zone, const char *msgfmt, ...)
+{
+    va_list ap;
+    static char loghdr[LOGSIZE_HDR];
+    static char logmsg[LOGSIZE_TAIL];
+    static int size;
+
+    /* XXX: We may want to check the sizes eventually */
+    size = g_snprintf(loghdr, LOGSIZE_HDR, "debug/%s %s\n", zone, msgfmt);
+
+    va_start(ap, msgfmt);
+    size = vsnprintf(logmsg, LOGSIZE_TAIL, loghdr, ap);
+
+    fprintf(stderr,"%s",logmsg);
+
+    return;
+}
+
+
+#endif  /* DEBUG */
Index: protocols/jabber/log.h
===================================================================
--- protocols/jabber/log.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/log.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,36 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Jabber
+ *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
+ */
+
+#ifndef INCL_LOG_H
+#define INCL_LOG_H
+
+#define LOGSIZE_HDR 1024
+#define LOGSIZE_TAIL 2048
+
+
+#ifdef DEBUG
+	void jdebug(char *zone, const char *msgfmt, ...);
+#else
+	#define jdebug if(0) warn
+#endif
+
+
+
+#endif	/* INCL_LOG_H */
+
Index: protocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,205 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Handling of message(s) (tags), etc                       *
-*                                                                           *
-*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent)
-{
-	struct im_connection *ic = data;
-	char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
-	char *type = xt_find_attr(node, "type");
-	char *id = xt_find_attr(node, "id");
-	struct xt_node *body = xt_find_node(node->children, "body"), *c;
-	struct xt_node *request = xt_find_node(node->children, "request");
-	struct jabber_buddy *bud = NULL;
-	char *s, *room = NULL, *reason = NULL;
-
-	if (!from) {
-		return XT_HANDLED; /* Consider this packet corrupted. */
-	}
-
-	if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) {
-		/* Send a message receipt (XEP-0184), looking like this:
-		 * <message from='...' id='...' to='...'>
-		 *  <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
-		 * </message>
-		 *
-		 * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs
-		 * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors.
-		 */
-		struct xt_node *received, *receipt;
-
-		received = xt_new_node("received", NULL, NULL);
-		xt_add_attr(received, "xmlns", XMLNS_RECEIPTS);
-		xt_add_attr(received, "id", id);
-		receipt = jabber_make_packet("message", NULL, from, received);
-
-		jabber_write_packet(ic, receipt);
-		xt_free_node(receipt);
-	}
-
-	bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT);
-
-	if (type && strcmp(type, "error") == 0) {
-		/* Handle type=error packet. */
-	} else if (type && from && strcmp(type, "groupchat") == 0) {
-		jabber_chat_pkt_message(ic, bud, node);
-	} else { /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
-		GString *fullmsg = g_string_new("");
-
-		for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) {
-			char *ns = xt_find_attr(c, "xmlns");
-			struct xt_node *inv;
-
-			if (ns && strcmp(ns, XMLNS_MUC_USER) == 0 &&
-			    (inv = xt_find_node(c->children, "invite"))) {
-				/* This is an invitation. Set some vars which
-				   will be passed to imcb_chat_invite() below. */
-				room = from;
-				if ((from = xt_find_attr(inv, "from")) == NULL) {
-					from = room;
-				}
-				if ((inv = xt_find_node(inv->children, "reason")) && inv->text_len > 0) {
-					reason = inv->text;
-				}
-			}
-		}
-
-		if ((s = strchr(from, '/'))) {
-			if (bud) {
-				bud->last_msg = time(NULL);
-				from = bud->ext_jid ? bud->ext_jid : bud->bare_jid;
-			} else {
-				*s = 0; /* We need to generate a bare JID now. */
-			}
-		}
-
-		if (type && strcmp(type, "headline") == 0) {
-			if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0) {
-				g_string_append_printf(fullmsg, "Headline: %s\n", c->text);
-			}
-
-			/* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */
-			for (c = node->children; c; c = c->next) {
-				struct xt_node *url;
-
-				if ((url = xt_find_node(c->children, "url")) && url->text_len > 0) {
-					g_string_append_printf(fullmsg, "URL: %s\n", url->text);
-				}
-			}
-		} else if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0 &&
-		           (!bud || !(bud->flags & JBFLAG_HIDE_SUBJECT))) {
-			g_string_append_printf(fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text);
-			if (bud) {
-				bud->flags |= JBFLAG_HIDE_SUBJECT;
-			}
-		} else if (bud && !c) {
-			/* Yeah, possibly we're hiding changes to this field now. But nobody uses
-			   this for anything useful anyway, except GMail when people reply to an
-			   e-mail via chat, repeating the same subject all the time. I don't want
-			   to have to remember full subject strings for everyone. */
-			bud->flags &= ~JBFLAG_HIDE_SUBJECT;
-		}
-
-		if (body && body->text_len > 0) { /* Could be just a typing notification. */
-			fullmsg = g_string_append(fullmsg, body->text);
-		}
-
-		if (fullmsg->len > 0) {
-			imcb_buddy_msg(ic, from, fullmsg->str,
-			               carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node));
-		}
-		if (room) {
-			imcb_chat_invite(ic, room, from, reason);
-		}
-
-		g_string_free(fullmsg, TRUE);
-
-		/* Handling of incoming typing notifications. */
-		if (bud == NULL || carbons_sent) {
-			/* Can't handle these for unknown buddies.
-			   And ignore them if it's just carbons */
-		} else if (xt_find_node(node->children, "composing")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-			imcb_buddy_typing(ic, from, OPT_TYPING);
-		}
-		else if (xt_find_node(node->children, "active")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-
-			/* No need to send a "stopped typing" signal when there's a message. */
-			if (body == NULL) {
-				imcb_buddy_typing(ic, from, 0);
-			}
-		} else if (xt_find_node(node->children, "paused")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-			imcb_buddy_typing(ic, from, OPT_THINKING);
-		}
-
-		if (s) {
-			*s = '/'; /* And convert it back to a full JID. */
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_carbons_message(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct xt_node *wrap, *fwd, *msg;
-	gboolean carbons_sent;
-
-	if ((wrap = xt_find_node(node->children, "received"))) {
-		carbons_sent = FALSE;
-	} else if ((wrap = xt_find_node(node->children, "sent"))) {
-		carbons_sent = TRUE;
-	}
-
-	if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) {
-		return XT_NEXT;
-	}
-
-	if (!(fwd = xt_find_node(wrap->children, "forwarded")) ||
-	     (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) ||
-	    !(msg = xt_find_node(fwd->children, "message"))) {
-		imcb_log(ic, "Error: Invalid carbons message received");
-		return XT_ABORT;
-	}
-
-	return jabber_pkt_message_normal(msg, data, carbons_sent);
-}
-
-xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *from = xt_find_attr(node, "from");
-
-	if (jabber_compare_jid(jd->me, from)) {    /* Probably a Carbons message */
-		xt_status st = jabber_carbons_message(node, data);
-		if (st == XT_HANDLED || st == XT_ABORT) {
-			return st;
-		}
-	}
-	return jabber_pkt_message_normal(node, data, FALSE);
-}
Index: protocols/jabber/nametab.h
===================================================================
--- protocols/jabber/nametab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/nametab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,150 @@
+static const unsigned namingBitmap[] = {
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF,
+0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000,
+0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060,
+0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003,
+0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003,
+0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000,
+0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001,
+0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003,
+0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000,
+0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003,
+0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003,
+0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000,
+0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000,
+0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF,
+0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB,
+0x40000000, 0xF580C900, 0x00000007, 0x02010800,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF,
+0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF,
+0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF,
+0x00000000, 0x00004C40, 0x00000000, 0x00000000,
+0x00000007, 0x00000000, 0x00000000, 0x00000000,
+0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF,
+0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000,
+0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE,
+0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF,
+0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF,
+0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000,
+0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003,
+0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD,
+0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF,
+0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF,
+0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE,
+0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF,
+0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF,
+0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF,
+0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF,
+0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF,
+0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0,
+0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1,
+0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3,
+0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80,
+0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3,
+0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000,
+0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000,
+0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF,
+0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x00000000, 0x00000000,
+0x00000000, 0x00000000, 0x1FFF0000, 0x00000002,
+0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF,
+0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF,
+};
+static const unsigned char nmstrtPages[] = {
+0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00,
+0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+static const unsigned char namePages[] = {
+0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00,
+0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13,
+0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
Index: protocols/jabber/pool.c
===================================================================
--- protocols/jabber/pool.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/pool.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,247 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include "bitlbee.h"
+#include <glib.h>
+
+
+#ifdef POOL_DEBUG
+int pool__total = 0;
+int pool__ltotal = 0;
+HASHTABLE pool__disturbed = NULL;
+void *_pool__malloc(size_t size)
+{
+    pool__total++;
+    return g_malloc(size);
+}
+void _pool__free(void *block)
+{
+    pool__total--;
+    g_free(block);
+}
+#else
+#define _pool__malloc g_malloc
+#define _pool__free g_free
+#endif
+
+
+/* make an empty pool */
+pool _pool_new(char *zone)
+{
+    pool p;
+    while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1);
+    p->cleanup = NULL;
+    p->heap = NULL;
+    p->size = 0;
+
+#ifdef POOL_DEBUG
+    p->lsize = -1;
+    p->zone[0] = '\0';
+    strcat(p->zone,zone);
+    sprintf(p->name,"%X",p);
+
+    if(pool__disturbed == NULL)
+    {
+        pool__disturbed = 1; /* reentrancy flag! */
+        pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
+    }
+    if(pool__disturbed != 1)
+        ghash_put(pool__disturbed,p->name,p);
+#endif
+
+    return p;
+}
+
+/* free a heap */
+static void _pool_heap_free(void *arg)
+{
+    struct pheap *h = (struct pheap *)arg;
+
+    _pool__free(h->block);
+    _pool__free(h);
+}
+
+/* mem should always be freed last */
+static void _pool_cleanup_append(pool p, struct pfree *pf)
+{
+    struct pfree *cur;
+
+    if(p->cleanup == NULL)
+    {
+        p->cleanup = pf;
+        return;
+    }
+
+    /* fast forward to end of list */
+    for(cur = p->cleanup; cur->next != NULL; cur = cur->next);
+
+    cur->next = pf;
+}
+
+/* create a cleanup tracker */
+static struct pfree *_pool_free(pool p, pool_cleaner f, void *arg)
+{
+    struct pfree *ret;
+
+    /* make the storage for the tracker */
+    while((ret = _pool__malloc(sizeof(struct pfree))) == NULL) sleep(1);
+    ret->f = f;
+    ret->arg = arg;
+    ret->next = NULL;
+
+    return ret;
+}
+
+/* create a heap and make sure it get's cleaned up */
+static struct pheap *_pool_heap(pool p, int size)
+{
+    struct pheap *ret;
+    struct pfree *clean;
+
+    /* make the return heap */
+    while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1);
+    while((ret->block = _pool__malloc(size)) == NULL) sleep(1);
+    ret->size = size;
+    p->size += size;
+    ret->used = 0;
+
+    /* append to the cleanup list */
+    clean = _pool_free(p, _pool_heap_free, (void *)ret);
+    clean->heap = ret; /* for future use in finding used mem for pstrdup */
+    _pool_cleanup_append(p, clean);
+
+    return ret;
+}
+
+pool _pool_new_heap(int size, char *zone)
+{
+    pool p;
+    p = _pool_new(zone);
+    p->heap = _pool_heap(p,size);
+    return p;
+}
+
+void *pmalloc(pool p, int size)
+{
+    void *block;
+
+    if(p == NULL)
+    {
+        fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n");
+        abort();
+    }
+
+    /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */
+    if(p->heap == NULL || size > (p->heap->size / 2))
+    {
+        while((block = _pool__malloc(size)) == NULL) sleep(1);
+        p->size += size;
+        _pool_cleanup_append(p, _pool_free(p, _pool__free, block));
+        return block;
+    }
+
+    /* we have to preserve boundaries, long story :) */
+    if(size >= 4)
+        while(p->heap->used&7) p->heap->used++;
+
+    /* if we don't fit in the old heap, replace it */
+    if(size > (p->heap->size - p->heap->used))
+        p->heap = _pool_heap(p, p->heap->size);
+
+    /* the current heap has room */
+    block = (char *)p->heap->block + p->heap->used;
+    p->heap->used += size;
+    return block;
+}
+
+void *pmalloc_x(pool p, int size, char c)
+{
+   void* result = pmalloc(p, size);
+   if (result != NULL)
+           memset(result, c, size);
+   return result;
+}  
+
+/* easy safety utility (for creating blank mem for structs, etc) */
+void *pmalloco(pool p, int size)
+{
+    void *block = pmalloc(p, size);
+    memset(block, 0, size);
+    return block;
+}  
+
+/* XXX efficient: move this to const char * and then loop throug the existing heaps to see if src is within a block in this pool */
+char *pstrdup(pool p, const char *src)
+{
+    char *ret;
+
+    if(src == NULL)
+        return NULL;
+
+    ret = pmalloc(p,strlen(src) + 1);
+    strcpy(ret,src);
+
+    return ret;
+}
+
+void pool_free(pool p)
+{
+    struct pfree *cur, *stub;
+
+    if(p == NULL) return;
+
+    cur = p->cleanup;
+    while(cur != NULL)
+    {
+        (*cur->f)(cur->arg);
+        stub = cur->next;
+        _pool__free(cur);
+        cur = stub;
+    }
+
+#ifdef POOL_DEBUG
+    ghash_remove(pool__disturbed,p->name);
+#endif
+
+    _pool__free(p);
+
+}
Index: protocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,259 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Handling of presence (tags), etc                         *
-*                                                                           *
-*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-xt_status jabber_pkt_presence(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	char *from = xt_find_attr(node, "from");
-	char *type = xt_find_attr(node, "type");        /* NULL should mean the person is online. */
-	struct xt_node *c, *cap;
-	struct jabber_buddy *bud, *send_presence = NULL;
-	int is_chat = 0;
-	char *s;
-
-	if (!from) {
-		return XT_HANDLED;
-	}
-
-	if ((s = strchr(from, '/'))) {
-		*s = 0;
-		if (jabber_chat_by_jid(ic, from)) {
-			is_chat = 1;
-		}
-		*s = '/';
-	}
-
-	if (type == NULL) {
-		if (!(bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT))) {
-			/*
-			imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from );
-			*/
-			return XT_HANDLED;
-		}
-
-		g_free(bud->away_message);
-		if ((c = xt_find_node(node->children, "status")) && c->text_len > 0) {
-			bud->away_message = g_strdup(c->text);
-		} else {
-			bud->away_message = NULL;
-		}
-
-		if ((c = xt_find_node(node->children, "show")) && c->text_len > 0) {
-			bud->away_state = (void *) jabber_away_state_by_code(c->text);
-		} else {
-			bud->away_state = NULL;
-		}
-
-		if ((c = xt_find_node(node->children, "priority")) && c->text_len > 0) {
-			bud->priority = atoi(c->text);
-		} else {
-			bud->priority = 0;
-		}
-
-		if (bud && (cap = xt_find_node(node->children, "c")) &&
-		    (s = xt_find_attr(cap, "xmlns")) && strcmp(s, XMLNS_CAPS) == 0) {
-			/* This <presence> stanza includes an XEP-0115
-			   capabilities part. Not too interesting, but we can
-			   see if it has an ext= attribute. */
-			s = xt_find_attr(cap, "ext");
-			if (s && (strstr(s, "cstates") || strstr(s, "chatstate"))) {
-				bud->flags |= JBFLAG_DOES_XEP85;
-			}
-
-			/* This field can contain more information like xhtml
-			   support, but we don't support that ourselves.
-			   Officially the ext= tag was deprecated, but enough
-			   clients do send it.
-
-			   (I'm aware that this is not the right way to use
-			   this field.) See for an explanation of ext=:
-			   http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/
-		}
-
-		if (is_chat) {
-			jabber_chat_pkt_presence(ic, bud, node);
-		} else {
-			send_presence = jabber_buddy_by_jid(ic, bud->bare_jid, 0);
-		}
-	} else if (strcmp(type, "unavailable") == 0) {
-		if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-			/*
-			imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from );
-			*/
-			return XT_HANDLED;
-		}
-
-		/* Handle this before we delete the JID. */
-		if (is_chat) {
-			jabber_chat_pkt_presence(ic, bud, node);
-		}
-
-		if (strchr(from, '/') == NULL) {
-			/* Sometimes servers send a type="unavailable" from a
-			   bare JID, which should mean that suddenly all
-			   resources for this JID disappeared. */
-			jabber_buddy_remove_bare(ic, from);
-		} else {
-			jabber_buddy_remove(ic, from);
-		}
-
-		if (is_chat) {
-			/* Nothing else to do for now? */
-		} else if ((s = strchr(from, '/'))) {
-			*s = 0;
-
-			/* If another resource is still available, send its presence
-			   information. */
-			if ((send_presence = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-				/* Otherwise, count him/her as offline now. */
-				imcb_buddy_status(ic, from, 0, NULL, NULL);
-			}
-
-			*s = '/';
-		} else {
-			imcb_buddy_status(ic, from, 0, NULL, NULL);
-		}
-	} else if (strcmp(type, "subscribe") == 0) {
-		jabber_buddy_ask(ic, from);
-	} else if (strcmp(type, "subscribed") == 0) {
-		/* Not sure about this one, actually... */
-		imcb_log(ic, "%s just accepted your authorization request", from);
-	} else if (strcmp(type, "unsubscribe") == 0 || strcmp(type, "unsubscribed") == 0) {
-		/* Do nothing here. Plenty of control freaks or over-curious
-		   souls get excited when they can see who still has them in
-		   their buddy list and who finally removed them. Somehow I
-		   got the impression that those are the people who get
-		   removed from many buddy lists for "some" reason...
-
-		   If you're one of those people, this is your chance to write
-		   your first line of code in C... */
-	} else if (strcmp(type, "error") == 0) {
-		return jabber_cache_handle_packet(ic, node);
-
-		/*
-		struct jabber_error *err;
-		if( ( c = xt_find_node( node->children, "error" ) ) )
-		{
-		        err = jabber_error_parse( c, XMLNS_STANZA_ERROR );
-		        imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name,
-		                    err->code, err->text ? ": " : "",
-		                    err->text ? err->text : "" );
-		        jabber_error_free( err );
-		} */
-	}
-
-	if (send_presence) {
-		int is_away = 0;
-
-		if (send_presence->away_state &&
-		    strcmp(send_presence->away_state->code, "chat") != 0) {
-			is_away = OPT_AWAY;
-		}
-
-		imcb_buddy_status(ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
-		                  is_away ? send_presence->away_state->full_name : NULL,
-		                  send_presence->away_message);
-	}
-
-	return XT_HANDLED;
-}
-
-static char *choose_priority(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *prio = set_getstr(&ic->acc->set, "priority");
-
-	if (jd->away_state && jd->away_state->full_name != NULL) {
-		int new_prio = (atoi(prio) - 5);
-		if (new_prio < 0) {
-			new_prio = 0;
-		}
-		return g_strdup_printf("%d", new_prio);
-	}
-
-	return g_strdup(prio);
-}
-
-/* Whenever presence information is updated, call this function to inform the
-   server. */
-int presence_send_update(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node, *cap;
-	GSList *l;
-	int st;
-	char *prio = choose_priority(ic);
-
-	node = jabber_make_packet("presence", NULL, NULL, NULL);
-	xt_add_child(node, xt_new_node("priority", prio, NULL));
-	if (jd->away_state) {
-		xt_add_child(node, xt_new_node("show", jd->away_state->code, NULL));
-	}
-	if (jd->away_message) {
-		xt_add_child(node, xt_new_node("status", jd->away_message, NULL));
-	}
-
-	/* This makes the packet slightly bigger, but clients interested in
-	   capabilities can now cache the discovery info. This reduces the
-	   usual post-login iq-flood. See XEP-0115. At least libpurple and
-	   Trillian seem to do this right. */
-	cap = xt_new_node("c", NULL, NULL);
-	xt_add_attr(cap, "xmlns", XMLNS_CAPS);
-	xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
-	xt_add_attr(cap, "ver", BITLBEE_VERSION);   /* The XEP wants this hashed, but nobody's doing that. */
-	xt_add_child(node, cap);
-
-	st = jabber_write_packet(ic, node);
-
-	/* Have to send this update to all groupchats too, the server won't
-	   do this automatically. */
-	for (l = ic->groupchats; l && st; l = l->next) {
-		struct groupchat *c = l->data;
-		struct jabber_chat *jc = c->data;
-
-		xt_add_attr(node, "to", jc->my_full_jid);
-		st = jabber_write_packet(ic, node);
-	}
-
-	xt_free_node(node);
-	g_free(prio);
-	return st;
-}
-
-/* Send a subscribe/unsubscribe request to a buddy. */
-int presence_send_request(struct im_connection *ic, char *handle, char *request)
-{
-	struct xt_node *node;
-	int st;
-
-	node = jabber_make_packet("presence", NULL, NULL, NULL);
-	xt_add_attr(node, "to", handle);
-	xt_add_attr(node, "type", request);
-
-	st = jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-	return st;
-}
Index: protocols/jabber/s5bytestream.c
===================================================================
--- protocols/jabber/s5bytestream.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,1143 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SOCKS5 Bytestreams ( XEP-0065 )                          *
-*                                                                           *
-*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "lib/ftutil.h"
-#include <poll.h>
-
-struct bs_transfer {
-
-	struct jabber_transfer *tf;
-
-	jabber_streamhost_t *sh;
-	GSList *streamhosts;
-
-	enum {
-		BS_PHASE_CONNECT,
-		BS_PHASE_CONNECTED,
-		BS_PHASE_REQUEST,
-		BS_PHASE_REPLY
-	} phase;
-
-	/* SHA1( SID + Initiator JID + Target JID) */
-	char *pseudoaddr;
-
-	gint connect_timeout;
-
-	char peek_buf[64];
-	int peek_buf_len;
-};
-
-struct socks5_message {
-	unsigned char ver;
-	union {
-		unsigned char cmd;
-		unsigned char rep;
-	} cmdrep;
-	unsigned char rsv;
-	unsigned char atyp;
-	unsigned char addrlen;
-	unsigned char address[40];
-	in_port_t port;
-} __attribute__ ((packed));
-
-char *socks5_reply_code[] = {
-	"succeeded",
-	"general SOCKS server failure",
-	"connection not allowed by ruleset",
-	"Network unreachable",
-	"Host unreachable",
-	"Connection refused",
-	"TTL expired",
-	"Command not supported",
-	"Address type not supported",
-	"unassigned"
-};
-
-/* connect() timeout in seconds. */
-#define JABBER_BS_CONTIMEOUT 15
-/* listen timeout */
-#define JABBER_BS_LISTEN_TIMEOUT  90
-
-/* very useful */
-#define ASSERTSOCKOP(op, msg) \
-	if ((op) == -1) { \
-		return jabber_bs_abort(bt, msg ": %s", strerror(errno)); }
-
-gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...) G_GNUC_PRINTF(2, 3);
-void jabber_bs_canceled(file_transfer_t *ft, char *reason);
-void jabber_bs_free_transfer(file_transfer_t *ft);
-gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents);
-gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen);
-
-void jabber_bs_recv_answer_request(struct bs_transfer *bt);
-gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_recv_write_request(file_transfer_t *ft);
-gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error);
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode);
-
-gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error);
-gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts);
-gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond);
-static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-void jabber_bs_send_activate(struct bs_transfer *bt);
-
-/*
- * Frees a bs_transfer struct and calls the SI free function
- */
-void jabber_bs_free_transfer(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct bs_transfer *bt = tf->streamhandle;
-	jabber_streamhost_t *sh;
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	if (tf->watch_out) {
-		b_event_remove(tf->watch_out);
-		tf->watch_out = 0;
-	}
-
-	g_free(bt->pseudoaddr);
-
-	while (bt->streamhosts) {
-		sh = bt->streamhosts->data;
-		bt->streamhosts = g_slist_remove(bt->streamhosts, sh);
-		g_free(sh->jid);
-		g_free(sh->host);
-		g_free(sh);
-	}
-
-	g_free(bt);
-
-	jabber_si_free_transfer(ft);
-}
-
-/*
- * Checks if buflen data is available on the socket and
- * writes it to buffer if that's the case.
- */
-gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen)
-{
-	int ret;
-	int fd = bt->tf->fd;
-
-	if (buflen > sizeof(bt->peek_buf)) {
-		return jabber_bs_abort(bt, "BUG: %d > sizeof(peek_buf)", buflen);
-	}
-
-	ASSERTSOCKOP(ret = recv(fd, bt->peek_buf + bt->peek_buf_len,
-	                        buflen - bt->peek_buf_len, 0), "recv() on SOCKS5 connection");
-
-	if (ret == 0) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	bt->peek_buf_len += ret;
-	memcpy(buffer, bt->peek_buf, bt->peek_buf_len);
-
-	if (bt->peek_buf_len == buflen) {
-		/* If we have everything the caller wanted, reset the peek buffer. */
-		bt->peek_buf_len = 0;
-		return buflen;
-	} else {
-		return bt->peek_buf_len;
-	}
-}
-
-
-/*
- * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect().
- */
-gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-
-	bt->connect_timeout = 0;
-
-	jabber_bs_abort(bt, "no connection after %d seconds",
-	                bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT);
-
-	return FALSE;
-}
-
-/*
- * Polls the socket, checks for errors and removes a connect timer
- * if there is one.
- */
-gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents)
-{
-	struct pollfd pfd = { .fd = fd, .events = POLLHUP | POLLERR };
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-
-	ASSERTSOCKOP(poll(&pfd, 1, 0), "poll()")
-
-	if (pfd.revents & POLLERR) {
-		int sockerror;
-		socklen_t errlen = sizeof(sockerror);
-
-		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen)) {
-			return jabber_bs_abort(bt,
-			                       "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)");
-		}
-
-		if (bt->phase == BS_PHASE_CONNECTED) {
-			return jabber_bs_abort(bt, "connect failed: %s", strerror(sockerror));
-		}
-
-		return jabber_bs_abort(bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror(sockerror));
-	}
-
-	if (pfd.revents & POLLHUP) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	*revents = pfd.revents;
-
-	return TRUE;
-}
-
-/*
- * Used for receive and send path.
- */
-gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...)
-{
-	va_list params;
-
-	va_start(params, format);
-	char error[128];
-
-	if (vsnprintf(error, 128, format, params) < 0) {
-		sprintf(error, "internal error parsing error string (BUG)");
-	}
-	va_end(params);
-	if (bt->tf->ft->sending) {
-		return jabber_bs_send_handshake_abort(bt, error);
-	} else {
-		return jabber_bs_recv_handshake_abort(bt, error);
-	}
-}
-
-void jabber_bs_remove_events(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-
-	if (tf->watch_out) {
-		b_event_remove(tf->watch_out);
-		tf->watch_out = 0;
-	}
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	if (tf->fd != -1) {
-		closesocket(tf->fd);
-		tf->fd = -1;
-	}
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-}
-
-/* Bad luck */
-void jabber_bs_canceled(file_transfer_t *ft, char *reason)
-{
-	struct jabber_transfer *tf = ft->data;
-
-	imcb_log(tf->ic, "File transfer aborted: %s", reason);
-}
-
-static struct jabber_transfer *get_ft_by_sid(GSList *tflist, char *sid) {
-	GSList *l;
-	for (l = tflist; l; l = g_slist_next(l)) {
-		struct jabber_transfer *tft = l->data;
-		if ((strcmp(tft->sid, sid) == 0)) { 
-			return tft;
-		}
-	}
-	return NULL;
-}
-
-/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value
- * Returns a newly allocated string */
-static char *generate_pseudoaddr(char *sid, char *ini_jid, char *tgt_jid) {
-	char *contents = g_strconcat(sid, ini_jid, tgt_jid, NULL);
-	char *hash_hex = g_compute_checksum_for_string(G_CHECKSUM_SHA1, contents, -1);
-	g_free(contents);
-	return hash_hex;
-}
-
-static jabber_streamhost_t *jabber_streamhost_new(char *jid, char *host, int port)
-{
-	jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
-	sh->jid = g_strdup(jid);
-	sh->host = g_strdup(host);
-	if (port) {
-		g_snprintf(sh->port, sizeof(sh->port), "%u", port);
-	}
-	return sh;
-}
-
-/*
- * Parses an incoming bytestream request and calls jabber_bs_handshake on success.
- */
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode)
-{
-	char *sid, *ini_jid, *tgt_jid, *mode, *iq_id;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_transfer *tf = NULL;
-	struct bs_transfer *bt;
-	GSList *shlist = NULL;
-	struct xt_node *shnode;
-
-	if (!(iq_id   = xt_find_attr(node, "id")) ||
-	    !(ini_jid = xt_find_attr(node, "from")) ||
-	    !(tgt_jid = xt_find_attr(node, "to")) ||
-	    !(sid     = xt_find_attr(qnode, "sid"))) {
-		imcb_log(ic, "WARNING: Received incomplete SI bytestream request");
-		return XT_HANDLED;
-	}
-
-	if ((mode = xt_find_attr(qnode, "mode")) &&
-	    (strcmp(mode, "tcp") != 0)) {
-		imcb_log(ic, "WARNING: Received SI Request for unsupported bytestream mode %s",
-		         xt_find_attr(qnode, "mode"));
-		return XT_HANDLED;
-	}
-
-	shnode = qnode->children;
-	while ((shnode = xt_find_node(shnode, "streamhost"))) {
-		char *jid, *host, *port_s;
-		int port;
-		if ((jid = xt_find_attr(shnode, "jid")) &&
-		    (host = xt_find_attr(shnode, "host")) &&
-		    (port_s = xt_find_attr(shnode, "port")) &&
-		    (sscanf(port_s, "%d", &port) == 1)) {
-			shlist = g_slist_append(shlist, jabber_streamhost_new(jid, host, port));
-		}
-		shnode = shnode->next;
-	}
-
-	if (!shlist) {
-		imcb_log(ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries");
-		return XT_HANDLED;
-	}
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
-		return XT_HANDLED;
-	}
-
-	/* iq_id and canceled can be reused since SI is done */
-	g_free(tf->iq_id);
-	tf->iq_id = g_strdup(iq_id);
-
-	tf->ft->canceled = jabber_bs_canceled;
-
-	bt = g_new0(struct bs_transfer, 1);
-	bt->tf = tf;
-	bt->streamhosts = shlist;
-	bt->sh = shlist->data;
-	bt->phase = BS_PHASE_CONNECT;
-	bt->pseudoaddr = generate_pseudoaddr(sid, ini_jid, tgt_jid);
-	tf->streamhandle = bt;
-	tf->ft->free = jabber_bs_free_transfer;
-
-	jabber_bs_recv_handshake(bt, -1, 0);
-
-	return XT_HANDLED;
-}
-
-/*
- * This is what a protocol handshake can look like in cooperative multitasking :)
- * Might be confusing at first because it's called from different places and is recursing.
- * (places being the event thread, bs_request, bs_handshake_abort, and itself)
- *
- * All in all, it turned out quite nice :)
- */
-gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond)
-{
-
-	struct bs_transfer *bt = data;
-	short revents;
-	int gret;
-
-	if ((fd != -1) && !jabber_bs_poll(bt, fd, &revents)) {
-		return FALSE;
-	}
-
-	switch (bt->phase) {
-	case BS_PHASE_CONNECT:
-	{
-		struct addrinfo hints, *rp;
-
-		memset(&hints, 0, sizeof(struct addrinfo));
-		hints.ai_socktype = SOCK_STREAM;
-
-		if ((gret = getaddrinfo(bt->sh->host, bt->sh->port, &hints, &rp)) != 0) {
-			return jabber_bs_abort(bt, "getaddrinfo() failed: %s", gai_strerror(gret));
-		}
-
-		ASSERTSOCKOP(bt->tf->fd = fd = socket(rp->ai_family, rp->ai_socktype, 0), "Opening socket");
-
-		sock_make_nonblocking(fd);
-
-		imcb_log(bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host,
-		         bt->sh->port);
-
-		if ((connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) &&
-		    (errno != EINPROGRESS)) {
-			return jabber_bs_abort(bt, "connect() failed: %s", strerror(errno));
-		}
-
-		freeaddrinfo(rp);
-
-		bt->phase = BS_PHASE_CONNECTED;
-
-		bt->tf->watch_out = b_input_add(fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt);
-
-		/* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */
-		bt->connect_timeout = b_timeout_add(JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt);
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-	case BS_PHASE_CONNECTED:
-	{
-		struct {
-			unsigned char ver;
-			unsigned char nmethods;
-			unsigned char method;
-		} socks5_hello = {
-			.ver = 5,
-			.nmethods = 1,
-			.method = 0x00         /* no auth */
-			          /* one could also implement username/password. If you know
-			           * a jabber client or proxy that actually does it, tell me.
-			           */
-		};
-
-		ASSERTSOCKOP(send(fd, &socks5_hello, sizeof(socks5_hello), 0), "Sending auth request");
-
-		bt->phase = BS_PHASE_REQUEST;
-
-		bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt);
-
-		bt->tf->watch_out = 0;
-		return FALSE;
-	}
-	case BS_PHASE_REQUEST:
-	{
-		struct socks5_message socks5_connect =
-		{
-			.ver = 5,
-			.cmdrep.cmd = 0x01,
-			.rsv = 0,
-			.atyp = 0x03,
-			.addrlen = strlen(bt->pseudoaddr),
-			.port = 0
-		};
-		int ret;
-		char buf[2];
-
-		/* If someone's trying to be funny and sends only one byte at a time we'll fail :) */
-		ASSERTSOCKOP(ret = recv(fd, buf, 2, 0), "Receiving auth reply");
-
-		if (!(ret == 2) ||
-		    !(buf[0] == 5) ||
-		    !(buf[1] == 0)) {
-			return jabber_bs_abort(bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)",
-			                       ret, buf[0], buf[1]);
-		}
-
-		/* copy hash into connect message */
-		memcpy(socks5_connect.address, bt->pseudoaddr, socks5_connect.addrlen);
-
-		ASSERTSOCKOP(send(fd, &socks5_connect, sizeof(struct socks5_message), 0), "Sending SOCKS5 Connect");
-
-		bt->phase = BS_PHASE_REPLY;
-
-		return TRUE;
-	}
-	case BS_PHASE_REPLY:
-	{
-		struct socks5_message socks5_reply = {0};
-		int ret;
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_reply, sizeof(struct socks5_message)))) {
-			return FALSE;
-		}
-
-		if (ret < 5) {         /* header up to address length */
-			return TRUE;
-		} else if (ret < sizeof(struct socks5_message)) {
-			/* Either a buggy proxy or just one that doesnt regard
-			 * the SHOULD in XEP-0065 saying the reply SHOULD
-			 * contain the address. We'll take it, so make sure the
-			 * next jabber_bs_peek starts with an empty buffer. */
-			bt->peek_buf_len = 0;
-		}
-
-		if (!(socks5_reply.ver == 5) ||
-		    !(socks5_reply.cmdrep.rep == 0)) {
-			char errstr[128] = "";
-			if ((socks5_reply.ver == 5) && (socks5_reply.cmdrep.rep <
-			                                (sizeof(socks5_reply_code) / sizeof(socks5_reply_code[0])))) {
-				sprintf(errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ]);
-			}
-			return jabber_bs_abort(bt,
-			                       "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)",
-			                       errstr,
-			                       socks5_reply.ver,
-			                       socks5_reply.cmdrep.rep,
-			                       socks5_reply.atyp,
-			                       socks5_reply.addrlen);
-		}
-
-		/* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz)
-		 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect).
-		 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy
-		 * is sending, it should not matter */
-
-		if (bt->tf->ft->sending) {
-			jabber_bs_send_activate(bt);
-		} else {
-			jabber_bs_recv_answer_request(bt);
-		}
-
-		return FALSE;
-	}
-	default:
-		/* BUG */
-		imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-}
-
-/*
- * If the handshake failed we can try the next streamhost, if there is one.
- * An intelligent sender would probably specify himself as the first streamhost and
- * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
- * slow proxy is only used if necessary. This of course also means, that the timeout
- * per streamhost should be kept short. If one or two firewalled adresses are specified,
- * they have to timeout first before a proxy is tried.
- */
-gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct xt_node *reply, *iqnode;
-	GSList *shlist;
-
-	imcb_log(tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)",
-	         tf->ft->file_name,
-	         bt->sh->host,
-	         bt->sh->port,
-	         error);
-
-	/* Alright, this streamhost failed, let's try the next... */
-	bt->phase = BS_PHASE_CONNECT;
-	shlist = g_slist_find(bt->streamhosts, bt->sh);
-	if (shlist && shlist->next) {
-		bt->sh = shlist->next->data;
-		jabber_bs_remove_events(bt);
-		return jabber_bs_recv_handshake(bt, -1, 0);
-	}
-
-
-	/* out of stream hosts */
-
-	iqnode = jabber_make_packet("iq", "result", tf->ini_jid, NULL);
-	reply = jabber_make_error_packet(iqnode, "item-not-found", "cancel", "404");
-	xt_free_node(iqnode);
-
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error transmitting bytestream response");
-	}
-	xt_free_node(reply);
-
-	imcb_file_canceled(tf->ic, tf->ft, "couldn't connect to any streamhosts");
-
-	/* MUST always return FALSE! */
-	return FALSE;
-}
-
-/*
- * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose.
- * If he is the streamhost himself, he might already know that. However, if it's a proxy,
- * the initiator will have to make a connection himself.
- */
-void jabber_bs_recv_answer_request(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct xt_node *reply;
-
-	imcb_log(tf->ic, "File %s: established SOCKS5 connection to %s:%s",
-	         tf->ft->file_name,
-	         bt->sh->host,
-	         bt->sh->port);
-
-	tf->ft->data = tf;
-	tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
-	tf->ft->write_request = jabber_bs_recv_write_request;
-
-	reply = xt_new_node("streamhost-used", NULL, NULL);
-	xt_add_attr(reply, "jid", bt->sh->jid);
-
-	reply = xt_new_node("query", NULL, reply);
-	xt_add_attr(reply, "xmlns", XMLNS_BYTESTREAMS);
-
-	reply = jabber_make_packet("iq", "result", tf->ini_jid, reply);
-
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream response");
-	}
-	xt_free_node(reply);
-}
-
-/*
- * This function is called from write_request directly. If no data is available, it will install itself
- * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again.
- */
-gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond)
-{
-	int ret;
-	struct bs_transfer *bt = data;
-	struct jabber_transfer *tf = bt->tf;
-
-	if (fd != -1) { /* called via event thread */
-		tf->watch_in = 0;
-		ASSERTSOCKOP(ret = recv(fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0), "Receiving");
-	} else {
-		/* called directly. There might not be any data available. */
-		if (((ret = recv(tf->fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0)) == -1) &&
-		    (errno != EAGAIN)) {
-			return jabber_bs_abort(bt, "Receiving: %s", strerror(errno));
-		}
-
-		if ((ret == -1) && (errno == EAGAIN)) {
-			tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
-			return FALSE;
-		}
-	}
-
-	/* shouldn't happen since we know the file size */
-	if (ret == 0) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	tf->bytesread += ret;
-
-	if (tf->bytesread >= tf->ft->file_size) {
-		imcb_file_finished(tf->ic, tf->ft);
-	}
-
-	tf->ft->write(tf->ft, tf->ft->buffer, ret);
-
-	return FALSE;
-}
-
-/*
- * imc callback that is invoked when it is ready to receive some data.
- */
-gboolean jabber_bs_recv_write_request(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-
-	if (tf->watch_in) {
-		imcb_file_canceled(tf->ic, ft,
-		                   "BUG in jabber file transfer: write_request called when already watching for input");
-		return FALSE;
-	}
-
-	jabber_bs_recv_read(tf->streamhandle, -1, 0);
-
-	return TRUE;
-}
-
-/*
- * Issues a write_request to imc.
- * */
-gboolean jabber_bs_send_can_write(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-
-	bt->tf->watch_out = 0;
-
-	bt->tf->ft->write_request(bt->tf->ft);
-
-	return FALSE;
-}
-
-/*
- * This should only be called if we can write, so just do it.
- * Add a write watch so we can write more during the next cycle (if possible).
- */
-gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct bs_transfer *bt = tf->streamhandle;
-	int ret;
-
-	if (tf->watch_out) {
-		return jabber_bs_abort(bt, "BUG: write() called while watching ");
-	}
-
-	/* TODO: catch broken pipe */
-	ASSERTSOCKOP(ret = send(tf->fd, buffer, len, 0), "Sending");
-
-	tf->byteswritten += ret;
-
-	/* TODO: this should really not be fatal */
-	if (ret < len) {
-		return jabber_bs_abort(bt, "send() sent %d instead of %d (send buffer too big!)", ret, len);
-	}
-
-	if (tf->byteswritten >= ft->file_size) {
-		imcb_file_finished(tf->ic, ft);
-	} else {
-		bt->tf->watch_out = b_input_add(tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt);
-	}
-
-	return TRUE;
-}
-
-/*
- * Handles the reply by the receiver containing the used streamhost.
- */
-static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_transfer *tf = NULL;
-	struct jabber_data *jd = ic->proto_data;
-	struct bs_transfer *bt;
-	struct xt_node *c;
-	char *sid, *jid;
-
-	if (!(c = xt_find_node(node->children, "query")) ||
-	    !(c = xt_find_node(c->children, "streamhost-used")) ||
-	    !(jid = xt_find_attr(c, "jid"))) {
-		imcb_log(ic, "WARNING: Received incomplete bytestream reply");
-		return XT_HANDLED;
-	}
-
-	if (!(c = xt_find_node(orig->children, "query")) ||
-	    !(sid = xt_find_attr(c, "sid"))) {
-		imcb_log(ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply");
-		return XT_HANDLED;
-	}
-
-	/* Let's see if we can find out what this bytestream should be for... */
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply to unknown request");
-		return XT_HANDLED;
-	}
-
-	bt = tf->streamhandle;
-
-	tf->accepted = TRUE;
-
-	if (strcmp(jid, tf->ini_jid) == 0) {
-		/* we're streamhost and target */
-		if (bt->phase == BS_PHASE_REPLY) {
-			/* handshake went through, let's start transferring */
-			tf->ft->write_request(tf->ft);
-		}
-	} else {
-		/* using a proxy, abort listen */
-
-		jabber_bs_remove_events(bt);
-
-		GSList *shlist;
-		for (shlist = jd->streamhosts; shlist; shlist = g_slist_next(shlist)) {
-			jabber_streamhost_t *sh = shlist->data;
-			if (strcmp(sh->jid, jid) == 0) {
-				bt->sh = sh;
-				jabber_bs_recv_handshake(bt, -1, 0);
-				return XT_HANDLED;
-			}
-		}
-
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid);
-	}
-
-	return XT_HANDLED;
-}
-
-/*
- * Tell the proxy to activate the stream. Looks like this:
- *
- * <iq type=set>
- *      <query xmlns=bs sid=sid>
- *              <activate>tgt_jid</activate>
- *      </query>
- * </iq>
- */
-void jabber_bs_send_activate(struct bs_transfer *bt)
-{
-	struct xt_node *node;
-
-	node = xt_new_node("activate", bt->tf->tgt_jid, NULL);
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_BYTESTREAMS);
-	xt_add_attr(node, "sid", bt->tf->sid);
-	node = jabber_make_packet("iq", "set", bt->sh->jid, node);
-
-	jabber_cache_add(bt->tf->ic, node, jabber_bs_send_handle_activate);
-
-	jabber_write_packet(bt->tf->ic, node);
-}
-
-/*
- * The proxy has activated the bytestream.
- * We can finally start pushing some data out.
- */
-static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	char *sid;
-	struct jabber_transfer *tf = NULL;
-	struct xt_node *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	query = xt_find_node(orig->children, "query");
-	sid = xt_find_attr(query, "sid");
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream");
-		return XT_HANDLED;
-	}
-
-	imcb_log(tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...",
-	         tf->ft->file_name);
-
-	/* handshake went through, let's start transferring */
-	tf->ft->write_request(tf->ft);
-
-	return XT_HANDLED;
-}
-
-jabber_streamhost_t *jabber_si_parse_proxy(struct im_connection *ic, char *proxy)
-{
-	char *host, *port, *jid;
-	jabber_streamhost_t *sh;
-
-	if (((host = strchr(proxy, ',')) == 0) ||
-	    ((port = strchr(host + 1, ',')) == 0)) {
-		imcb_log(ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy);
-		return NULL;
-	}
-
-	jid = proxy;
-	*host++ = '\0';
-	*port++ = '\0';
-
-	sh = jabber_streamhost_new(jid, host, 0);
-	strncpy(sh->port, port, sizeof(sh->port));
-
-	return sh;
-}
-
-void jabber_si_set_proxies(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct jabber_data *jd = tf->ic->proto_data;
-	char *proxysetting = g_strdup(set_getstr(&tf->ic->acc->set, "proxy"));
-	char *proxy, *next, *errmsg = NULL;
-	char port[6];
-	char host[NI_MAXHOST + 1];
-	jabber_streamhost_t *sh, *sh2;
-	GSList *streamhosts = jd->streamhosts;
-
-	proxy = proxysetting;
-	while (proxy && (*proxy != '\0')) {
-		if ((next = strchr(proxy, ';'))) {
-			*next++ = '\0';
-		}
-
-		if (strcmp(proxy, "<local>") == 0) {
-			if ((tf->fd = ft_listen(&tf->saddr, host, port, jd->fd, FALSE, &errmsg)) != -1) {
-				sh = jabber_streamhost_new(tf->ini_jid, host, 0);
-				strncpy(sh->port, port, sizeof(sh->port));
-				bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-
-				bt->tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
-				bt->connect_timeout = b_timeout_add(JABBER_BS_LISTEN_TIMEOUT * 1000,
-				                                    jabber_bs_connect_timeout, bt);
-			} else {
-				imcb_log(tf->ic,
-				         "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s",
-				         tf->ft->file_name,
-				         errmsg);
-			}
-		} else if (strcmp(proxy, "<auto>") == 0) {
-			while (streamhosts) {
-				sh = g_new0(jabber_streamhost_t, 1);
-				sh2 = streamhosts->data;
-				sh->jid = g_strdup(sh2->jid);
-				sh->host = g_strdup(sh2->host);
-				strcpy(sh->port, sh2->port);
-				bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-				streamhosts = g_slist_next(streamhosts);
-			}
-		} else if ((sh = jabber_si_parse_proxy(tf->ic, proxy))) {
-			bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-		}
-		proxy = next;
-	}
-
-	g_free(proxysetting);
-}
-
-/*
- * Starts a bytestream.
- */
-gboolean jabber_bs_send_start(struct jabber_transfer *tf)
-{
-	struct bs_transfer *bt;
-
-	bt = g_new0(struct bs_transfer, 1);
-	bt->tf = tf;
-	bt->phase = BS_PHASE_CONNECT;
-	bt->pseudoaddr = generate_pseudoaddr(tf->sid, tf->ini_jid, tf->tgt_jid);
-	tf->streamhandle = bt;
-	tf->ft->free = jabber_bs_free_transfer;
-	tf->ft->canceled = jabber_bs_canceled;
-
-	jabber_si_set_proxies(bt);
-
-	return jabber_bs_send_request(tf, bt->streamhosts);
-}
-
-gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts)
-{
-	struct xt_node *shnode, *query, *iq;
-
-	query = xt_new_node("query", NULL, NULL);
-	xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS);
-	xt_add_attr(query, "sid", tf->sid);
-	xt_add_attr(query, "mode", "tcp");
-
-	while (streamhosts) {
-		jabber_streamhost_t *sh = streamhosts->data;
-		shnode = xt_new_node("streamhost", NULL, NULL);
-		xt_add_attr(shnode, "jid", sh->jid);
-		xt_add_attr(shnode, "host", sh->host);
-		xt_add_attr(shnode, "port", sh->port);
-
-		xt_add_child(query, shnode);
-
-		streamhosts = g_slist_next(streamhosts);
-	}
-
-
-	iq = jabber_make_packet("iq", "set", tf->tgt_jid, query);
-	xt_add_attr(iq, "from", tf->ini_jid);
-
-	jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply);
-
-	if (!jabber_write_packet(tf->ic, iq)) {
-		imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request");
-	}
-	return TRUE;
-}
-
-gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	/* TODO: did the receiver get here somehow??? */
-	imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",
-	         tf->ft->file_name,
-	         error);
-
-	if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */
-		imcb_file_canceled(tf->ic, tf->ft, error);
-	}
-
-	/* MUST always return FALSE! */
-	return FALSE;
-}
-
-/*
- * SOCKS5BYTESTREAM protocol for the sender
- */
-gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-	struct jabber_transfer *tf = bt->tf;
-	short revents;
-
-	if (!jabber_bs_poll(bt, fd, &revents)) {
-		return FALSE;
-	}
-
-	switch (bt->phase) {
-	case BS_PHASE_CONNECT:
-	{
-		struct sockaddr_storage clt_addr;
-		socklen_t ssize = sizeof(clt_addr);
-
-		/* Connect */
-
-		ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
-
-		closesocket(fd);
-		fd = tf->fd;
-		sock_make_nonblocking(fd);
-
-		bt->phase = BS_PHASE_CONNECTED;
-
-		bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
-		return FALSE;
-	}
-	case BS_PHASE_CONNECTED:
-	{
-		int ret, have_noauth = FALSE;
-		struct {
-			unsigned char ver;
-			unsigned char method;
-		} socks5_auth_reply = { .ver = 5, .method = 0 };
-		struct {
-			unsigned char ver;
-			unsigned char nmethods;
-			unsigned char method;
-		} socks5_hello = {0};
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) {
-			return FALSE;
-		}
-
-		if (ret < sizeof(socks5_hello)) {
-			return TRUE;
-		}
-
-		if (!(socks5_hello.ver == 5) ||
-		    !(socks5_hello.nmethods >= 1) ||
-		    !(socks5_hello.nmethods < 32)) {
-			return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d",
-			                       socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method);
-		}
-
-		have_noauth = socks5_hello.method == 0;
-
-		if (socks5_hello.nmethods > 1) {
-			char mbuf[32];
-			int i;
-			ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods");
-			if (ret < (socks5_hello.nmethods - 1)) {
-				return jabber_bs_abort(bt, "Partial auth request");
-			}
-			for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) {
-				if (mbuf[i] == 0) {
-					have_noauth = TRUE;
-				}
-			}
-		}
-
-		if (!have_noauth) {
-			return jabber_bs_abort(bt, "Auth request didn't include no authentication");
-		}
-
-		ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply");
-
-		bt->phase = BS_PHASE_REQUEST;
-
-		return TRUE;
-	}
-	case BS_PHASE_REQUEST:
-	{
-		struct socks5_message socks5_connect = {0};
-		int msgsize = sizeof(struct socks5_message);
-		int ret;
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) {
-			return FALSE;
-		}
-
-		if (ret < msgsize) {
-			return TRUE;
-		}
-
-		if (!(socks5_connect.ver == 5) ||
-		    !(socks5_connect.cmdrep.cmd == 1) ||
-		    !(socks5_connect.atyp == 3) ||
-		    !(socks5_connect.addrlen == 40)) {
-			return jabber_bs_abort(bt,
-			                       "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)",
-			                       socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd,
-			                       socks5_connect.atyp);
-		}
-		if (!(memcmp(socks5_connect.address, bt->pseudoaddr, 40) == 0)) {
-			return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest");
-		}
-
-		socks5_connect.cmdrep.rep = 0;
-
-		ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply");
-
-		bt->phase = BS_PHASE_REPLY;
-
-		imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name);
-
-		if (tf->accepted) {
-			/* streamhost-used message came already in(possible?), let's start sending */
-			tf->ft->write_request(tf->ft);
-		}
-
-		tf->watch_in = 0;
-		return FALSE;
-
-	}
-	default:
-		/* BUG */
-		imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-}
-#undef ASSERTSOCKOP
Index: protocols/jabber/sasl.c
===================================================================
--- protocols/jabber/sasl.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,550 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SASL authentication                                      *
-*                                                                           *
-*  Copyright 2006-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <ctype.h>
-
-#include "jabber.h"
-#include "base64.h"
-#include "oauth2.h"
-#include "oauth.h"
-
-const struct oauth2_service oauth2_service_google =
-{
-	"https://accounts.google.com/o/oauth2/auth",
-	"https://accounts.google.com/o/oauth2/token",
-	"urn:ietf:wg:oauth:2.0:oob",
-	"https://www.googleapis.com/auth/googletalk",
-	"783993391592.apps.googleusercontent.com",
-	"6C-Zgf7Tr7gEQTPlBhMUgo7R",
-};
-
-/* """"""""""""""""""""""""""""""oauth"""""""""""""""""""""""""""""" */
-#define HIPCHAT_SO_CALLED_OAUTH_URL "https://hipchat.com/account/api"
-
-xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply;
-	char *s;
-	int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0, sup_hipchat_oauth = 0;
-	int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
-	GString *mechs;
-
-	if (!sasl_supported(ic)) {
-		/* Should abort this now, since we should already be doing
-		   IQ authentication. Strange things happen when you try
-		   to do both... */
-		imcb_log(ic,
-		         "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!");
-		return XT_HANDLED;
-	}
-
-	s = xt_find_attr(node, "xmlns");
-	if (!s || strcmp(s, XMLNS_SASL) != 0) {
-		imcb_log(ic, "Stream error while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	want_anonymous = set_getbool(&ic->acc->set, "anonymous");
-	want_oauth = set_getbool(&ic->acc->set, "oauth");
-	want_hipchat = (jd->flags & JFLAG_HIPCHAT);
-
-	mechs = g_string_new("");
-	c = node->children;
-	while ((c = xt_find_node(c, "mechanism"))) {
-		if (c->text && g_strcasecmp(c->text, "PLAIN") == 0) {
-			sup_plain = 1;
-		} else if (c->text && g_strcasecmp(c->text, "DIGEST-MD5") == 0) {
-			sup_digest = 1;
-		} else if (c->text && g_strcasecmp(c->text, "ANONYMOUS") == 0) {
-			sup_anonymous = 1;
-		} else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
-			sup_gtalk = 1;
-		} else if (c->text && g_strcasecmp(c->text, "X-HIPCHAT-OAUTH2") == 0) {
-			sup_hipchat_oauth = 1;
-		}
-
-		if (c->text) {
-			g_string_append_printf(mechs, " %s", c->text);
-		}
-
-		c = c->next;
-	}
-
-	if (!want_oauth && !sup_plain && !sup_digest) {
-		if (sup_gtalk || sup_hipchat_oauth) {
-			imcb_error(ic, "This server requires OAuth "
-			           "(supported schemes:%s)", mechs->str);
-		} else {
-			imcb_error(ic, "BitlBee does not support any of the offered SASL "
-			           "authentication schemes:%s", mechs->str);
-		}
-		imc_logout(ic, FALSE);
-		g_string_free(mechs, TRUE);
-		return XT_ABORT;
-	}
-	g_string_free(mechs, TRUE);
-
-	reply = xt_new_node("auth", NULL, NULL);
-	if (!want_hipchat) {
-		xt_add_attr(reply, "xmlns", XMLNS_SASL);
-	} else {
-		xt_add_attr(reply, "xmlns", XMLNS_HIPCHAT);
-	}
-
-	if ((sup_gtalk || sup_hipchat_oauth) && want_oauth) {
-		GString *gs;
-
-		gs = g_string_sized_new(128);
-
-		g_string_append_c(gs, '\0');
-
-		if (sup_gtalk) {
-			/* X-OAUTH2 is not *the* standard OAuth2 SASL/XMPP implementation.
-			   It's currently used by GTalk and vaguely documented on
-			   http://code.google.com/apis/cloudprint/docs/rawxmpp.html */
-			xt_add_attr(reply, "mechanism", "X-OAUTH2");
-
-			g_string_append(gs, jd->username);
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, jd->oauth2_access_token);
-		} else if (sup_hipchat_oauth) {
-			/* Hipchat's variant, not standard either, is documented here:
-			   https://docs.atlassian.com/hipchat.xmpp/latest/xmpp/auth.html */
-			xt_add_attr(reply, "mechanism", "oauth2");
-
-			g_string_append(gs, jd->oauth2_access_token);
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
-		}
-
-		reply->text = base64_encode((unsigned char *) gs->str, gs->len);
-		reply->text_len = strlen(reply->text);
-		g_string_free(gs, TRUE);
-
-	} else if (want_oauth) {
-		imcb_error(ic, "OAuth requested, but not supported by server");
-		imc_logout(ic, FALSE);
-		xt_free_node(reply);
-		return XT_ABORT;
-	} else if (want_anonymous && sup_anonymous) {
-		xt_add_attr(reply, "mechanism", "ANONYMOUS");
-
-		/* Well, that was easy. */
-	} else if (want_anonymous) {
-		imcb_error(ic, "Anonymous login requested, but not supported by server");
-		imc_logout(ic, FALSE);
-		xt_free_node(reply);
-		return XT_ABORT;
-	} else if (sup_digest && !(jd->ssl && sup_plain)) {
-		/* Only try DIGEST-MD5 if there's no SSL/TLS or if PLAIN isn't supported.
-		 * Which in practice means "don't bother with DIGEST-MD5 most of the time".
-		 * It's weak, pointless over TLS, and often breaks with some servers (hi openfire) */
-
-		xt_add_attr(reply, "mechanism", "DIGEST-MD5");
-
-		/* The rest will be done later, when we receive a <challenge/>. */
-	} else if (sup_plain) {
-		GString *gs;
-		char *username;
-
-		if (!want_hipchat) {
-			xt_add_attr(reply, "mechanism", "PLAIN");
-			username = jd->username;
-		} else {
-			username = jd->me;
-		}
-
-		/* set an arbitrary initial size to avoid reallocations */
-		gs = g_string_sized_new(128);
-
-		/* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */
-		g_string_append_c(gs, '\0');
-		g_string_append(gs, username);
-		g_string_append_c(gs, '\0');
-		g_string_append(gs, ic->acc->pass);
-		if (want_hipchat) {
-			/* Hipchat's variation adds \0resource at the end */
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
-		}
-
-		reply->text = base64_encode((unsigned char *) gs->str, gs->len);
-		reply->text_len = strlen(reply->text);
-		g_string_free(gs, TRUE);
-	}
-
-	if (reply && !jabber_write_packet(ic, reply)) {
-		xt_free_node(reply);
-		return XT_ABORT;
-	}
-	xt_free_node(reply);
-
-	/* To prevent classic authentication from happening. */
-	jd->flags |= JFLAG_STREAM_STARTED;
-
-	return XT_HANDLED;
-}
-
-/* Non-static function, but not mentioned in jabber.h because it's for internal
-   use, just that the unittest should be able to reach it... */
-char *sasl_get_part(char *data, char *field)
-{
-	int i, len;
-
-	len = strlen(field);
-
-	while (g_ascii_isspace(*data) || *data == ',') {
-		data++;
-	}
-
-	if (g_strncasecmp(data, field, len) == 0 && data[len] == '=') {
-		i = strlen(field) + 1;
-	} else {
-		for (i = 0; data[i]; i++) {
-			/* If we have a ", skip until it's closed again. */
-			if (data[i] == '"') {
-				i++;
-				while (data[i] != '"' || data[i - 1] == '\\') {
-					i++;
-				}
-			}
-
-			/* If we got a comma, we got a new field. Check it,
-			   find the next key after it. */
-			if (data[i] == ',') {
-				while (g_ascii_isspace(data[i]) || data[i] == ',') {
-					i++;
-				}
-
-				if (g_strncasecmp(data + i, field, len) == 0 &&
-				    data[i + len] == '=') {
-					i += len + 1;
-					break;
-				}
-			}
-		}
-	}
-
-	if (data[i] == '"') {
-		int j;
-		char *ret;
-
-		i++;
-		len = 0;
-		while (data[i + len] != '"' || data[i + len - 1] == '\\') {
-			len++;
-		}
-
-		ret = g_strndup(data + i, len);
-		for (i = j = 0; ret[i]; i++) {
-			if (ret[i] == '\\') {
-				ret[j++] = ret[++i];
-			} else {
-				ret[j++] = ret[i];
-			}
-		}
-		ret[j] = 0;
-
-		return ret;
-	} else if (data[i]) {
-		len = 0;
-		while (data[i + len] && data[i + len] != ',') {
-			len++;
-		}
-
-		return g_strndup(data + i, len);
-	} else {
-		return NULL;
-	}
-}
-
-xt_status sasl_pkt_challenge(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *reply_pkt = NULL;
-	char *nonce = NULL, *realm = NULL, *cnonce = NULL;
-	unsigned char cnonce_bin[30];
-	char *digest_uri = NULL;
-	char *dec = NULL;
-	char *s = NULL, *reply = NULL;
-	xt_status ret = XT_ABORT;
-
-	if (node->text_len == 0) {
-		goto error;
-	}
-
-	dec = frombase64(node->text);
-
-	if (!(s = sasl_get_part(dec, "rspauth"))) {
-		/* See RFC 2831 for for information. */
-		GChecksum *A1, *A2, *H;
-		gsize digest_len = MD5_HASH_SIZE;
-		guint8 A1r[16], A2r[16], Hr[16];
-		char A1h[33], A2h[33], Hh[33];
-		int i;
-
-		nonce = sasl_get_part(dec, "nonce");
-		realm = sasl_get_part(dec, "realm");
-
-		if (!nonce) {
-			goto error;
-		}
-
-		/* Jabber.Org considers the realm part optional and doesn't
-		   specify one. Oh well, actually they're right, but still,
-		   don't know if this is right... */
-		if (!realm) {
-			realm = g_strdup(jd->server);
-		}
-
-		random_bytes(cnonce_bin, sizeof(cnonce_bin));
-		cnonce = base64_encode(cnonce_bin, sizeof(cnonce_bin));
-		digest_uri = g_strdup_printf("%s/%s", "xmpp", jd->server);
-
-		/* Generate the MD5 hash of username:realm:password,
-		   I decided to call it H. */
-		H = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s:%s", jd->username, realm, ic->acc->pass);
-		g_checksum_update(H, (guint8 *)s, strlen(s));
-		g_free(s);
-
-		g_checksum_get_digest(H, Hr, &digest_len);
-		g_checksum_free(H);
-
-		/* Now generate the hex. MD5 hash of H:nonce:cnonce, called A1. */
-		A1 = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf(":%s:%s", nonce, cnonce);
-		g_checksum_update(A1, Hr, 16);
-		g_checksum_update(A1, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(A1, A1r, &digest_len);
-		g_checksum_free(A1);
-		for (i = 0; i < 16; i++) {
-			sprintf(A1h + i * 2, "%02x", A1r[i]);
-		}
-
-		/* A2... */
-		A2 = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s", "AUTHENTICATE", digest_uri);
-		g_checksum_update(A2, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(A2, A2r, &digest_len);
-		g_checksum_free(A2);
-		for (i = 0; i < 16; i++) {
-			sprintf(A2h + i * 2, "%02x", A2r[i]);
-		}
-
-		/* Final result: A1:nonce:00000001:cnonce:auth:A2. Let's reuse H for it. */
-		H = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h);
-		g_checksum_update(H, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(H, Hr, &digest_len);
-		g_checksum_free(H);
-
-		for (i = 0; i < 16; i++) {
-			sprintf(Hh + i * 2, "%02x", Hr[i]);
-		}
-
-		/* Now build the SASL response string: */
-		reply = g_strdup_printf("username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
-		                        "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
-		                        jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8");
-	} else {
-		/* We found rspauth, but don't really care... */
-		g_free(s);
-	}
-
-	s = reply ? tobase64(reply) : NULL;
-	reply_pkt = xt_new_node("response", s, NULL);
-	xt_add_attr(reply_pkt, "xmlns", XMLNS_SASL);
-
-	if (!jabber_write_packet(ic, reply_pkt)) {
-		goto silent_error;
-	}
-
-	ret = XT_HANDLED;
-	goto silent_error;
-
-error:
-	imcb_error(ic, "Incorrect SASL challenge received");
-	imc_logout(ic, FALSE);
-
-silent_error:
-	g_free(digest_uri);
-	g_free(cnonce);
-	g_free(nonce);
-	g_free(reply);
-	g_free(realm);
-	g_free(dec);
-	g_free(s);
-	xt_free_node(reply_pkt);
-
-	return ret;
-}
-
-xt_status sasl_pkt_result(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *s;
-
-	s = xt_find_attr(node, "xmlns");
-	if (!s || strcmp(s, XMLNS_SASL) != 0) {
-		imcb_log(ic, "Stream error while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(node->name, "success") == 0) {
-		imcb_log(ic, "Authentication finished");
-		jd->flags |= JFLAG_AUTHENTICATED | JFLAG_STREAM_RESTART;
-
-		if (jd->flags & JFLAG_HIPCHAT) {
-			return hipchat_handle_success(ic, node);
-		}
-	} else if (strcmp(node->name, "failure") == 0) {
-		imcb_error(ic, "Authentication failure");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	return XT_HANDLED;
-}
-
-/* This one is needed to judge if we'll do authentication using IQ or SASL.
-   It's done by checking if the <stream:stream> from the server has a
-   version attribute. I don't know if this is the right way though... */
-gboolean sasl_supported(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return (jd->xt && jd->xt->root && xt_find_attr(jd->xt->root, "version")) != 0;
-}
-
-void sasl_oauth2_init(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	imcb_log(ic, "Starting OAuth authentication");
-
-	/* Temporary contact, just used to receive the OAuth response. */
-	imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
-
-	if (jd->flags & JFLAG_HIPCHAT) {
-		imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE,
-			"Open this URL and generate a token with 'View Group' and 'Send Message' scopes: "
-			HIPCHAT_SO_CALLED_OAUTH_URL, 0, 0);
-	} else {
-		char *msg, *url;
-
-		url = oauth2_url(jd->oauth2_service);
-		msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
-		imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
-
-		g_free(msg);
-		g_free(url);
-	}
-	imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned "
-	               "authorization token.", 0, 0);
-
-}
-
-static gboolean sasl_oauth2_remove_contact(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	if (g_slist_find(jabber_connections, ic)) {
-		imcb_remove_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
-	}
-	return FALSE;
-}
-
-int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *code;
-	int ret;
-
-	imcb_log(ic, "Requesting OAuth access token");
-
-	/* Don't do it here because the caller may get confused if the contact
-	   we're currently sending a message to is deleted. */
-	b_timeout_add(1, sasl_oauth2_remove_contact, ic);
-
-	code = g_strdup(msg);
-	g_strstrip(code);
-	ret = oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_CODE,
-	                          code, sasl_oauth2_got_token, ic);
-
-	g_free(code);
-	return ret;
-}
-
-int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_REFRESH,
-	                           refresh_token, sasl_oauth2_got_token, ic);
-}
-
-void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd;
-	GSList *auth = NULL;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return;
-	}
-
-	jd = ic->proto_data;
-
-	if (access_token == NULL) {
-		imcb_error(ic, "OAuth failure (%s)", error);
-		imc_logout(ic, TRUE);
-		return;
-	}
-
-	oauth_params_parse(&auth, ic->acc->pass);
-	if (refresh_token) {
-		oauth_params_set(&auth, "refresh_token", refresh_token);
-	}
-	if (access_token) {
-		oauth_params_set(&auth, "access_token", access_token);
-	}
-
-	g_free(ic->acc->pass);
-	ic->acc->pass = oauth_params_string(auth);
-	oauth_params_free(&auth);
-
-	g_free(jd->oauth2_access_token);
-	jd->oauth2_access_token = g_strdup(access_token);
-
-	jabber_connect(ic);
-}
Index: protocols/jabber/si.c
===================================================================
--- protocols/jabber/si.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,553 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SI packets                                               *
-*                                                                           *
-*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.com>                         *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-void jabber_si_answer_request(file_transfer_t *ft);
-int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf);
-
-/* file_transfer free() callback */
-void jabber_si_free_transfer(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	jd->filetransfers = g_slist_remove(jd->filetransfers, tf);
-
-	if (tf->fd != -1) {
-		closesocket(tf->fd);
-		tf->fd = -1;
-	}
-
-	if (tf->disco_timeout) {
-		b_event_remove(tf->disco_timeout);
-	}
-
-	g_free(tf->ini_jid);
-	g_free(tf->tgt_jid);
-	g_free(tf->iq_id);
-	g_free(tf->sid);
-	g_free(tf);
-}
-
-/* file_transfer canceled() callback */
-void jabber_si_canceled(file_transfer_t *ft, char *reason)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct xt_node *reply, *iqnode;
-
-	if (tf->accepted) {
-		return;
-	}
-
-	iqnode = jabber_make_packet("iq", "error", tf->ini_jid, NULL);
-	xt_add_attr(iqnode, "id", tf->iq_id);
-	reply = jabber_make_error_packet(iqnode, "forbidden", "cancel", "403");
-	xt_free_node(iqnode);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
-	}
-	xt_free_node(reply);
-
-}
-
-int jabber_si_check_features(struct jabber_transfer *tf, GSList *features)
-{
-	int foundft = FALSE, foundbt = FALSE, foundsi = FALSE;
-
-	while (features) {
-		if (!strcmp(features->data, XMLNS_FILETRANSFER)) {
-			foundft = TRUE;
-		}
-		if (!strcmp(features->data, XMLNS_BYTESTREAMS)) {
-			foundbt = TRUE;
-		}
-		if (!strcmp(features->data, XMLNS_SI)) {
-			foundsi = TRUE;
-		}
-
-		features = g_slist_next(features);
-	}
-
-	if (!foundft) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature file transfers");
-	} else if (!foundbt) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)");
-	} else if (!foundsi) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)");
-	}
-
-	return foundft && foundbt && foundsi;
-}
-
-void jabber_si_transfer_start(struct jabber_transfer *tf)
-{
-
-	if (!jabber_si_check_features(tf, tf->bud->features)) {
-		return;
-	}
-
-	/* send the request to our buddy */
-	jabber_si_send_request(tf->ic, tf->bud->full_jid, tf);
-
-	/* and start the receive logic */
-	imcb_file_recv_start(tf->ic, tf->ft);
-
-}
-
-gboolean jabber_si_waitfor_disco(gpointer data, gint fd, b_input_condition cond)
-{
-	struct jabber_transfer *tf = data;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	tf->disco_timeout_fired++;
-
-	if (tf->bud->features && jd->have_streamhosts == 1) {
-		tf->disco_timeout = 0;
-		jabber_si_transfer_start(tf);
-		return FALSE;
-	}
-
-	/* 8 seconds should be enough for server and buddy to respond */
-	if (tf->disco_timeout_fired < 16) {
-		return TRUE;
-	}
-
-	if (!tf->bud->features && jd->have_streamhosts != 1) {
-		imcb_log(tf->ic, "Couldn't get buddy's features nor discover all services of the server");
-	} else if (!tf->bud->features) {
-		imcb_log(tf->ic, "Couldn't get buddy's features");
-	} else {
-		imcb_log(tf->ic, "Couldn't discover some of the server's services");
-	}
-
-	tf->disco_timeout = 0;
-	jabber_si_transfer_start(tf);
-	return FALSE;
-}
-
-void jabber_si_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *who)
-{
-	struct jabber_transfer *tf;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud;
-	char *server = jd->server, *s;
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, 0);
-	}
-
-	if (bud == NULL) {
-		imcb_file_canceled(ic, ft, "Couldn't find buddy (BUG?)");
-		return;
-	}
-
-	imcb_log(ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who);
-
-	tf = g_new0(struct jabber_transfer, 1);
-
-	tf->ic = ic;
-	tf->ft = ft;
-	tf->fd = -1;
-	tf->ft->data = tf;
-	tf->ft->free = jabber_si_free_transfer;
-	tf->bud = bud;
-	ft->write = jabber_bs_send_write;
-
-	jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
-
-	/* query buddy's features and server's streaming proxies if necessary */
-
-	if (!tf->bud->features) {
-		jabber_iq_query_features(ic, bud->full_jid);
-	}
-
-	/* If <auto> is not set don't check for proxies */
-	if ((jd->have_streamhosts != 1) && (jd->streamhosts == NULL) &&
-	    (strstr(set_getstr(&ic->acc->set, "proxy"), "<auto>") != NULL)) {
-		jd->have_streamhosts = 0;
-		jabber_iq_query_server(ic, server, XMLNS_DISCO_ITEMS);
-	} else if (jd->streamhosts != NULL) {
-		jd->have_streamhosts = 1;
-	}
-
-	/* if we had to do a query, wait for the result.
-	 * Otherwise fire away. */
-	if (!tf->bud->features || jd->have_streamhosts != 1) {
-		tf->disco_timeout = b_timeout_add(500, jabber_si_waitfor_disco, tf);
-	} else {
-		jabber_si_transfer_start(tf);
-	}
-}
-
-/*
- * First function that gets called when a file transfer request comes in.
- * A lot to parse.
- *
- * We choose a stream type from the options given by the initiator.
- * Then we wait for imcb to call the accept or cancel callbacks.
- */
-int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode)
-{
-	struct xt_node *c, *d, *reply;
-	char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s;
-	struct jabber_buddy *bud;
-	int requestok = FALSE;
-	char *name, *cmp;
-	size_t size;
-	struct jabber_transfer *tf;
-	struct jabber_data *jd = ic->proto_data;
-	file_transfer_t *ft;
-
-	/* All this means we expect something like this: ( I think )
-	 * <iq from=... to=... id=...>
-	 *      <si id=id xmlns=si profile=ft>
-	 *              <file xmlns=ft/>
-	 *              <feature xmlns=feature>
-	 *                      <x xmlns=xdata type=submit>
-	 *                              <field var=stream-method>
-	 *
-	 */
-	if (!(ini_jid          = xt_find_attr(node, "from")) ||
-	    !(tgt_jid          = xt_find_attr(node, "to")) ||
-	    !(iq_id            = xt_find_attr(node, "id")) ||
-	    !(sid              = xt_find_attr(sinode, "id")) ||
-	    !(cmp              = xt_find_attr(sinode, "profile")) ||
-	    !(0               == strcmp(cmp, XMLNS_FILETRANSFER)) ||
-	    !(d                = xt_find_node(sinode->children, "file")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_FILETRANSFER)) ||
-	    !(name             = xt_find_attr(d, "name")) ||
-	    !(size_s           = xt_find_attr(d, "size")) ||
-	    !(1               == sscanf(size_s, "%zd", &size)) ||
-	    !(d                = xt_find_node(sinode->children, "feature")) ||
-	    !(cmp              = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_FEATURE)) ||
-	    !(d                = xt_find_node(d->children, "x")) ||
-	    !(cmp              = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_XDATA)) ||
-	    !(cmp              = xt_find_attr(d, "type")) ||
-	    !(0               == strcmp(cmp, "form")) ||
-	    !(d                = xt_find_node(d->children, "field")) ||
-	    !(cmp              = xt_find_attr(d, "var")) ||
-	    !(0               == strcmp(cmp, "stream-method"))) {
-		imcb_log(ic, "WARNING: Received incomplete Stream Initiation request");
-	} else {
-		/* Check if we support one of the options */
-
-		c = d->children;
-		while ((c = xt_find_node(c, "option"))) {
-			if ((d = xt_find_node(c->children, "value")) &&
-			    (d->text != NULL) &&
-			    (strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
-				requestok = TRUE;
-				break;
-			} else {
-				c = c->next;
-			}
-		}
-
-		if (!requestok) {
-			imcb_log(ic, "WARNING: Unsupported file transfer request from %s", ini_jid);
-		}
-	}
-
-	if (requestok) {
-		/* Figure out who the transfer should come from... */
-
-		ext_jid = ini_jid;
-		if ((s = strchr(ini_jid, '/'))) {
-			if ((bud = jabber_buddy_by_jid(ic, ini_jid, GET_BUDDY_EXACT))) {
-				bud->last_msg = time(NULL);
-				ext_jid = bud->ext_jid ? : bud->bare_jid;
-			} else {
-				*s = 0; /* We need to generate a bare JID now. */
-			}
-		}
-
-		if (!(ft = imcb_file_send_start(ic, ext_jid, name, size))) {
-			imcb_log(ic, "WARNING: Error handling transfer request from %s", ini_jid);
-			requestok = FALSE;
-		}
-
-		if (s) {
-			*s = '/';
-		}
-	}
-
-	if (!requestok) {
-		reply = jabber_make_error_packet(node, "item-not-found", "cancel", NULL);
-		if (!jabber_write_packet(ic, reply)) {
-			imcb_log(ic, "WARNING: Error generating reply to file transfer request");
-		}
-		xt_free_node(reply);
-		return XT_HANDLED;
-	}
-
-	/* Request is fine. */
-
-	tf = g_new0(struct jabber_transfer, 1);
-
-	tf->ini_jid = g_strdup(ini_jid);
-	tf->tgt_jid = g_strdup(tgt_jid);
-	tf->iq_id = g_strdup(iq_id);
-	tf->sid = g_strdup(sid);
-	tf->ic = ic;
-	tf->ft = ft;
-	tf->fd = -1;
-	tf->ft->data = tf;
-	tf->ft->accept = jabber_si_answer_request;
-	tf->ft->free = jabber_si_free_transfer;
-	tf->ft->canceled = jabber_si_canceled;
-
-	jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
-
-	return XT_HANDLED;
-}
-
-/*
- * imc called the accept callback which probably means that the user accepted this file transfer.
- * We send our response to the initiator.
- * In the next step, the initiator will send us a request for the given stream type.
- * (currently that can only be a SOCKS5 bytestream)
- */
-void jabber_si_answer_request(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct xt_node *node, *sinode, *reply;
-
-	/* generate response, start with the SI tag */
-	sinode = xt_new_node("si", NULL, NULL);
-	xt_add_attr(sinode, "xmlns", XMLNS_SI);
-	xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
-	xt_add_attr(sinode, "id", tf->sid);
-
-	/* now the file tag */
-	node = xt_new_node("file", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
-
-	xt_add_child(sinode, node);
-
-	/* and finally the feature tag */
-	node = xt_new_node("field", NULL, NULL);
-	xt_add_attr(node, "var", "stream-method");
-	xt_add_attr(node, "type", "list-single");
-
-	/* Currently all we can do. One could also implement in-band (IBB) */
-	xt_add_child(node, xt_new_node("value", XMLNS_BYTESTREAMS, NULL));
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_XDATA);
-	xt_add_attr(node, "type", "submit");
-
-	node = xt_new_node("feature", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FEATURE);
-
-	xt_add_child(sinode, node);
-
-	reply = jabber_make_packet("iq", "result", tf->ini_jid, sinode);
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
-	} else {
-		tf->accepted = TRUE;
-	}
-	xt_free_node(reply);
-}
-
-static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c, *d;
-	char *ini_jid = NULL, *tgt_jid, *iq_id, *cmp;
-	GSList *tflist;
-	struct jabber_transfer *tf = NULL;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_error *err;
-
-	if (!(tgt_jid = xt_find_attr(node, "from")) ||
-	    !(ini_jid = xt_find_attr(node, "to")) ||
-	    !(iq_id   = xt_find_attr(node, "id"))) {
-		imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
-		return XT_HANDLED;
-	}
-
-	/* Let's see if we can find out what this bytestream should be for... */
-
-	for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
-		struct jabber_transfer *tft = tflist->data;
-		if ((strcmp(tft->iq_id, iq_id) == 0)) {
-			tf = tft;
-			break;
-		}
-	}
-
-	if (!tf) {
-		imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
-		return XT_HANDLED;
-	}
-
-	err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
-
-	if (err) {
-		if (g_strcmp0(err->code, "forbidden") == 0) {
-			imcb_log(ic, "File %s: %s rejected the transfer", tf->ft->file_name, tgt_jid);
-		} else {
-			imcb_log(ic, "Error: Stream initiation request failed: %s (%s)", err->code, err->text);
-		}
-		imcb_file_canceled(ic, tf->ft, "Stream initiation request failed");
-		jabber_error_free(err);
-		return XT_HANDLED;
-	}
-
-	/* All this means we expect something like this: ( I think )
-	 * <iq from=... to=... id=...>
-	 *      <si xmlns=si>
-	 *      [	<file xmlns=ft/>    ] <-- not necessary
-	 *              <feature xmlns=feature>
-	 *                      <x xmlns=xdata type=submit>
-	 *                              <field var=stream-method>
-	 *                                      <value>
-	 */
-	if (!(c = xt_find_node(node->children, "si")) ||
-	    !(cmp = xt_find_attr(c, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_SI) == 0) ||
-	    !(d = xt_find_node(c->children, "feature")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_FEATURE) == 0) ||
-	    !(d = xt_find_node(d->children, "x")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_XDATA) == 0) ||
-	    !(cmp = xt_find_attr(d, "type")) ||
-	    !(strcmp(cmp, "submit") == 0) ||
-	    !(d = xt_find_node(d->children, "field")) ||
-	    !(cmp = xt_find_attr(d, "var")) ||
-	    !(strcmp(cmp, "stream-method") == 0) ||
-	    !(d = xt_find_node(d->children, "value"))) {
-		imcb_log(ic, "WARNING: Received incomplete Stream Initiation response");
-		return XT_HANDLED;
-	}
-
-	if (!(strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
-		/* since we should only have advertised what we can do and the peer should
-		 * only have chosen what we offered, this should never happen */
-		imcb_log(ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text);
-
-		return XT_HANDLED;
-	}
-
-	tf->ini_jid = g_strdup(ini_jid);
-	tf->tgt_jid = g_strdup(tgt_jid);
-
-	imcb_log(ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid);
-
-	jabber_bs_send_start(tf);
-
-	return XT_HANDLED;
-}
-
-int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf)
-{
-	struct xt_node *node, *sinode;
-	struct jabber_buddy *bud;
-
-	/* who knows how many bits the future holds :) */
-	char filesizestr[ 1 + ( int ) (0.301029995663981198f * sizeof(size_t) * 8) ];
-
-	const char *methods[] =
-	{
-		XMLNS_BYTESTREAMS,
-		//XMLNS_IBB,
-		NULL
-	};
-	const char **m;
-	char *s;
-
-	/* Maybe we should hash this? */
-	tf->sid = g_strdup_printf("BitlBeeJabberSID%d", tf->ft->local_id);
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, 0);
-	}
-
-	/* start with the SI tag */
-	sinode = xt_new_node("si", NULL, NULL);
-	xt_add_attr(sinode, "xmlns", XMLNS_SI);
-	xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
-	xt_add_attr(sinode, "id", tf->sid);
-
-/*	if( mimetype )
-                xt_add_attr( node, "mime-type", mimetype ); */
-
-	/* now the file tag */
-/*	if( desc )
-                node = xt_new_node( "desc", descr, NULL ); */
-	node = xt_new_node("range", NULL, NULL);
-
-	sprintf(filesizestr, "%zd", tf->ft->file_size);
-	node = xt_new_node("file", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
-	xt_add_attr(node, "name", tf->ft->file_name);
-	xt_add_attr(node, "size", filesizestr);
-/*	if (hash)
-                xt_add_attr( node, "hash", hash );
-        if (date)
-                xt_add_attr( node, "date", date ); */
-
-	xt_add_child(sinode, node);
-
-	/* and finally the feature tag */
-	node = xt_new_node("field", NULL, NULL);
-	xt_add_attr(node, "var", "stream-method");
-	xt_add_attr(node, "type", "list-single");
-
-	for (m = methods; *m; m++) {
-		xt_add_child(node, xt_new_node("option", NULL, xt_new_node("value", (char *) *m, NULL)));
-	}
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_XDATA);
-	xt_add_attr(node, "type", "form");
-
-	node = xt_new_node("feature", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FEATURE);
-
-	xt_add_child(sinode, node);
-
-	/* and we are there... */
-	node = jabber_make_packet("iq", "set", bud ? bud->full_jid : who, sinode);
-	jabber_cache_add(ic, node, jabber_si_handle_response);
-	tf->iq_id = g_strdup(xt_find_attr(node, "id"));
-
-	return jabber_write_packet(ic, node);
-}
Index: protocols/jabber/str.c
===================================================================
--- protocols/jabber/str.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/str.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,215 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static char *j_strcat(char *dest, char *txt)
+{
+    if(!txt) return(dest);
+
+    while(*txt)
+        *dest++ = *txt++;
+    *dest = '\0';
+
+    return(dest);
+}
+
+int j_strcmp(const char *a, const char *b)
+{
+    if(a == NULL || b == NULL)
+        return -1;
+
+    while(*a == *b && *a != '\0' && *b != '\0'){ a++; b++; }
+
+    if(*a == *b) return 0;
+
+    return -1;
+}
+
+spool spool_new(pool p)
+{
+    spool s;
+
+    s = pmalloc(p, sizeof(struct spool_struct));
+    s->p = p;
+    s->len = 0;
+    s->last = NULL;
+    s->first = NULL;
+    return s;
+}
+
+void spool_add(spool s, char *str)
+{
+    struct spool_node *sn;
+    int len;
+
+    if(str == NULL)
+        return;
+
+    len = strlen(str);
+    if(len == 0)
+        return;
+
+    sn = pmalloc(s->p, sizeof(struct spool_node));
+    sn->c = pstrdup(s->p, str);
+    sn->next = NULL;
+
+    s->len += len;
+    if(s->last != NULL)
+        s->last->next = sn;
+    s->last = sn;
+    if(s->first == NULL)
+        s->first = sn;
+}
+
+void spooler(spool s, ...)
+{
+    va_list ap;
+    char *arg = NULL;
+
+    if(s == NULL)
+        return;
+
+    VA_START(s);
+
+    /* loop till we hfit our end flag, the first arg */
+    while(1)
+    {
+        arg = va_arg(ap,char *);
+        if((spool)arg == s)
+            break;
+        else
+            spool_add(s, arg);
+    }
+
+    va_end(ap);
+}
+
+char *spool_print(spool s)
+{
+    char *ret,*tmp;
+    struct spool_node *next;
+
+    if(s == NULL || s->len == 0 || s->first == NULL)
+        return NULL;
+
+    ret = pmalloc(s->p, s->len + 1);
+    *ret = '\0';
+
+    next = s->first;
+    tmp = ret;
+    while(next != NULL)
+    {
+        tmp = j_strcat(tmp,next->c);
+        next = next->next;
+    }
+
+    return ret;
+}
+
+char *strescape(pool p, char *buf)
+{
+    int i,j,oldlen,newlen;
+    char *temp;
+
+    if (p == NULL || buf == NULL) return(NULL);
+
+    oldlen = newlen = strlen(buf);
+    for(i=0;i<oldlen;i++)
+    {
+        switch(buf[i])
+        {
+        case '&':
+            newlen+=5;
+            break;
+        case '\'':
+            newlen+=6;
+            break;
+        case '\"':
+            newlen+=6;
+            break;
+        case '<':
+            newlen+=4;
+            break;
+        case '>':
+            newlen+=4;
+            break;
+        }
+    }
+
+    if(oldlen == newlen) return buf;
+
+    temp = pmalloc(p,newlen+1);
+
+    if (temp==NULL) return(NULL);
+
+    for(i=j=0;i<oldlen;i++)
+    {
+        switch(buf[i])
+        {
+        case '&':
+            memcpy(&temp[j],"&amp;",5);
+            j += 5;
+            break;
+        case '\'':
+            memcpy(&temp[j],"&apos;",6);
+            j += 6;
+            break;
+        case '\"':
+            memcpy(&temp[j],"&quot;",6);
+            j += 6;
+            break;
+        case '<':
+            memcpy(&temp[j],"&lt;",4);
+            j += 4;
+            break;
+        case '>':
+            memcpy(&temp[j],"&gt;",4);
+            j += 4;
+            break;
+        default:
+            temp[j++] = buf[i];
+        }
+    }
+    temp[j] = '\0';
+    return temp;
+}
Index: protocols/jabber/utf8tab.h
===================================================================
--- protocols/jabber/utf8tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/utf8tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,63 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+
+/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL,
+/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2,
+/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3,
+/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4,
+/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML,
+/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM,
Index: protocols/jabber/xmldef.h
===================================================================
--- protocols/jabber/xmldef.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmldef.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,34 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+
Index: protocols/jabber/xmlnode.c
===================================================================
--- protocols/jabber/xmlnode.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlnode.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,705 @@
+/* --------------------------------------------------------------------------
+ *
+ * License
+ *
+ * The contents of this file are subject to the Jabber Open Source License
+ * Version 1.0 (the "JOSL").  You may not copy or use this file, in either
+ * source code or executable form, except in compliance with the JOSL. You
+ * may obtain a copy of the JOSL at http://www.jabber.org/ or at
+ * http://www.opensource.org/.  
+ *
+ * Software distributed under the JOSL is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the JOSL
+ * for the specific language governing rights and limitations under the
+ * JOSL.
+ *
+ * Copyrights
+ * 
+ * Portions created by or assigned to Jabber.com, Inc. are 
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ * 
+ * Acknowledgements
+ * 
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ * 
+ * Alternatively, the contents of this file may be used under the terms of the
+ * GNU General Public License Version 2 or later (the "GPL"), in which case
+ * the provisions of the GPL are applicable instead of those above.  If you
+ * wish to allow use of your version of this file only under the terms of the
+ * GPL and not to allow others to use your version of this file under the JOSL,
+ * indicate your decision by deleting the provisions above and replace them
+ * with the notice and other provisions required by the GPL.  If you do not
+ * delete the provisions above, a recipient may use your version of this file
+ * under either the JOSL or the GPL. 
+ * 
+ * 
+ * --------------------------------------------------------------------------*/
+
+#include "jabber.h"
+#include <glib.h>
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent);
+static int xmlnode_get_type(xmlnode node);
+static void xmlnode_insert_node(xmlnode parent, xmlnode node);
+
+/* Internal routines */
+static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type)
+{
+    xmlnode result = NULL;
+    if (type > NTYPE_LAST)
+        return NULL;
+
+    if (type != NTYPE_CDATA && name == NULL)
+        return NULL;
+
+    if (p == NULL)
+    {
+        p = pool_heap(1*1024);
+    }
+
+    /* Allocate & zero memory */
+    result = (xmlnode)pmalloco(p, sizeof(_xmlnode));
+
+    /* Initialize fields */
+    if (type != NTYPE_CDATA)
+        result->name = pstrdup(p,name);
+    result->type = type;
+    result->p = p;
+    return result;
+}
+
+static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type)
+{
+    xmlnode result;
+
+    result = _xmlnode_new(xmlnode_pool(lastsibling), name, type);
+    if (result != NULL)
+    {
+        /* Setup sibling pointers */
+        result->prev = lastsibling;
+        lastsibling->next = result;
+    }
+    return result;
+}
+
+static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type)
+{
+    xmlnode result;
+
+    if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL;
+
+    /* If parent->firstchild is NULL, simply create a new node for the first child */
+    if (parent->firstchild == NULL)
+    {
+        result = _xmlnode_new(parent->p, name, type);
+        parent->firstchild = result;
+    }
+    /* Otherwise, append this to the lastchild */
+    else
+    {
+        result= _xmlnode_append_sibling(parent->lastchild, name, type);
+    }
+    result->parent = parent;
+    parent->lastchild = result;
+    return result;
+
+}
+
+static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type)
+{
+    xmlnode current;
+
+    /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with
+    the specified name */
+    current = firstsibling;
+    while (current != NULL)
+    {
+        if ((current->type == type) && (j_strcmp(current->name, name) == 0))
+            return current;
+        else
+            current = current->next;
+    }
+    return NULL;
+}
+
+static void _xmlnode_merge(xmlnode data)
+{
+    xmlnode cur;
+    char *merge, *scur;
+    int imerge;
+
+    /* get total size of all merged cdata */
+    imerge = 0;
+    for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+        imerge += cur->data_sz;
+
+    /* copy in current data and then spin through all of them and merge */
+    scur = merge = pmalloc(data->p,imerge + 1);
+    for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next)
+    {
+        memcpy(scur,cur->data,cur->data_sz);
+        scur += cur->data_sz;
+    }
+    *scur = '\0';
+
+    /* this effectively hides all of the merged-in chunks */
+    data->next = cur;
+    if(cur == NULL)
+        data->parent->lastchild = data;
+    else
+        cur->prev = data;
+
+    /* reset data */
+    data->data = merge;
+    data->data_sz = imerge;
+    
+}
+
+static void _xmlnode_hide_sibling(xmlnode child)
+{
+    if(child == NULL)
+        return;
+
+    if(child->prev != NULL)
+        child->prev->next = child->next;
+    if(child->next != NULL)
+        child->next->prev = child->prev;
+}
+
+static void _xmlnode_tag2str(spool s, xmlnode node, int flag)
+{
+    xmlnode tmp;
+
+    if(flag==0 || flag==1)
+    {
+	    spooler(s,"<",xmlnode_get_name(node),s);
+	    tmp = xmlnode_get_firstattrib(node);
+	    while(tmp) {
+	        spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s);
+	        tmp = xmlnode_get_nextsibling(tmp);
+	    }
+	    if(flag==0)
+	        spool_add(s,"/>");
+	    else
+	        spool_add(s,">");
+    }
+    else
+    {
+	    spooler(s,"</",xmlnode_get_name(node),">",s);
+    }
+}
+
+static spool _xmlnode2spool(xmlnode node)
+{
+    spool s;
+    int level=0,dir=0;
+    xmlnode tmp;
+
+    if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
+        return NULL;
+
+    s = spool_new(xmlnode_pool(node));
+    if(!s) return(NULL);
+
+    while(1)
+    {
+        if(dir==0)
+        {
+    	    if(xmlnode_get_type(node) == NTYPE_TAG)
+            {
+                if(xmlnode_has_children(node))
+                {
+                    _xmlnode_tag2str(s,node,1);
+                    node = xmlnode_get_firstchild(node);
+                    level++;
+                    continue;
+                }else{
+                    _xmlnode_tag2str(s,node,0);
+                }
+            }else{
+                spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
+            }
+        }
+
+    	tmp = xmlnode_get_nextsibling(node);
+        if(!tmp)
+        {
+            node = xmlnode_get_parent(node);
+            level--;
+            if(level>=0) _xmlnode_tag2str(s,node,2);
+            if(level<1) break;
+            dir = 1;
+        }else{
+            node = tmp;
+            dir = 0;
+        }
+    }
+
+    return s;
+}
+
+
+/* External routines */
+
+
+/*
+ *  xmlnode_new_tag -- create a tag node
+ *  Automatically creates a memory pool for the node.
+ *
+ *  parameters
+ *      name -- name of the tag
+ *
+ *  returns
+ *      a pointer to the tag node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_new_tag(const char* name)
+{
+    return _xmlnode_new(NULL, name, NTYPE_TAG);
+}
+
+
+/*
+ *  xmlnode_insert_tag -- append a child tag to a tag
+ *
+ *  parameters
+ *      parent -- pointer to the parent tag
+ *      name -- name of the child tag
+ *
+ *  returns
+ *      a pointer to the child tag node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_tag(xmlnode parent, const char* name)
+{
+    return _xmlnode_insert(parent, name, NTYPE_TAG);
+}
+
+
+/*
+ *  xmlnode_insert_cdata -- append character data to a tag
+ *
+ *  parameters
+ *      parent -- parent tag
+ *      CDATA -- character data
+ *      size -- size of CDATA
+ *              or -1 for null-terminated CDATA strings
+ *
+ *  returns
+ *      a pointer to the child CDATA node
+ *      or NULL if it was unsuccessfull
+ */
+xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size)
+{
+    xmlnode result;
+
+    if(CDATA == NULL || parent == NULL)
+        return NULL;
+
+    if(size == -1)
+        size = strlen(CDATA);
+
+    result = _xmlnode_insert(parent, NULL, NTYPE_CDATA);
+    if (result != NULL)
+    {
+        result->data = (char*)pmalloc(result->p, size + 1);
+        memcpy(result->data, CDATA, size);
+        result->data[size] = '\0';
+        result->data_sz = size;
+    }
+
+    return result;
+}
+
+
+/*
+ *  xmlnode_get_tag -- find given tag in an xmlnode tree
+ *
+ *  parameters
+ *      parent -- pointer to the parent tag
+ *      name -- "name" for the child tag of that name
+ *              "name/name" for a sub child (recurses)
+ *              "?attrib" to match the first tag with that attrib defined
+ *              "?attrib=value" to match the first tag with that attrib and value
+ *              "=cdata" to match the cdata contents of the child
+ *              or any combination: "name/name/?attrib", "name=cdata", etc
+ *
+ *  results
+ *      a pointer to the tag matching search criteria
+ *      or NULL if search was unsuccessfull
+ */
+xmlnode xmlnode_get_tag(xmlnode parent, const char* name)
+{
+    char *str, *slash, *qmark, *equals;
+    xmlnode step, ret;
+
+
+    if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL;
+
+    if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL)
+        return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
+
+    str = g_strdup(name);
+    slash = strstr(str, "/");
+    qmark = strstr(str, "?");
+    equals = strstr(str, "=");
+
+    if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark))
+    { /* of type =cdata */
+
+        *equals = '\0';
+        equals++;
+
+        for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+        {
+            if(xmlnode_get_type(step) != NTYPE_TAG)
+                continue;
+
+            if(*str != '\0')
+                if(j_strcmp(xmlnode_get_name(step),str) != 0)
+                    continue;
+
+            if(j_strcmp(xmlnode_get_data(step),equals) != 0)
+                continue;
+
+            break;
+        }
+
+        g_free(str);
+        return step;
+    }
+
+
+    if(qmark != NULL && (slash == NULL || qmark < slash))
+    { /* of type ?attrib */
+
+        *qmark = '\0';
+        qmark++;
+        if(equals != NULL)
+        {
+            *equals = '\0';
+            equals++;
+        }
+
+        for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+        {
+            if(xmlnode_get_type(step) != NTYPE_TAG)
+                continue;
+
+            if(*str != '\0')
+                if(j_strcmp(xmlnode_get_name(step),str) != 0)
+                    continue;
+
+            if(xmlnode_get_attrib(step,qmark) == NULL)
+                continue;
+
+            if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0)
+                continue;
+
+            break;
+        }
+
+        g_free(str);
+        return step;
+    }
+
+
+    *slash = '\0';
+    ++slash;
+
+    for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step))
+    {
+        if(xmlnode_get_type(step) != NTYPE_TAG) continue;
+
+        if(j_strcmp(xmlnode_get_name(step),str) != 0)
+            continue;
+
+        ret = xmlnode_get_tag(step, slash);
+        if(ret != NULL)
+        {
+            g_free(str);
+            return ret;
+        }
+    }
+
+    g_free(str);
+    return NULL;
+}
+
+
+/* return the cdata from any tag */
+char *xmlnode_get_tag_data(xmlnode parent, const char *name)
+{
+    xmlnode tag;
+
+    tag = xmlnode_get_tag(parent, name);
+    if(tag == NULL) return NULL;
+
+    return xmlnode_get_data(tag);
+}
+
+
+void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value)
+{
+    xmlnode attrib;
+
+    if(owner == NULL || name == NULL || value == NULL) return;
+
+    /* If there are no existing attributs, allocate a new one to start
+    the list */
+    if (owner->firstattrib == NULL)
+    {
+        attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB);
+        owner->firstattrib = attrib;
+        owner->lastattrib  = attrib;
+    }
+    else
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if(attrib == NULL)
+        {
+            attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB);
+            owner->lastattrib = attrib;
+        }
+    }
+    /* Update the value of the attribute */
+    attrib->data_sz = strlen(value);
+    attrib->data    = pstrdup(owner->p, value);
+
+}
+
+char* xmlnode_get_attrib(xmlnode owner, const char* name)
+{
+    xmlnode attrib;
+
+    if (owner != NULL && owner->firstattrib != NULL)
+    {
+        attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB);
+        if (attrib != NULL)
+            return (char*)attrib->data;
+    }
+    return NULL;
+}
+
+static xmlnode xmlnode_get_firstattrib(xmlnode parent)
+{
+    if (parent != NULL)
+        return parent->firstattrib;
+    return NULL;
+}
+
+xmlnode xmlnode_get_firstchild(xmlnode parent)
+{
+    if (parent != NULL)
+        return parent->firstchild;
+    return NULL;
+}
+
+xmlnode xmlnode_get_nextsibling(xmlnode sibling)
+{
+    if (sibling != NULL)
+        return sibling->next;
+    return NULL;
+}
+
+xmlnode xmlnode_get_parent(xmlnode node)
+{
+    if (node != NULL)
+        return node->parent;
+    return NULL;
+}
+
+char* xmlnode_get_name(xmlnode node)
+{
+    if (node != NULL)
+        return node->name;
+    return NULL;
+}
+
+char* xmlnode_get_data(xmlnode node)
+{
+    if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */
+        for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node))
+            if(xmlnode_get_type(node) == NTYPE_CDATA) break;
+
+    if(node == NULL) return NULL;
+
+    /* check for a dirty node w/ unassembled cdata chunks */
+    if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+        _xmlnode_merge(node);
+
+    return node->data;
+}
+
+static int xmlnode_get_datasz(xmlnode node)
+{
+    if(xmlnode_get_type(node) != NTYPE_CDATA) return 0;
+
+    /* check for a dirty node w/ unassembled cdata chunks */
+    if(xmlnode_get_type(node->next) == NTYPE_CDATA)
+        _xmlnode_merge(node);
+    return node->data_sz;
+}
+
+static int xmlnode_get_type(xmlnode node)
+{
+    if (node != NULL)
+        return node->type;
+    return NTYPE_UNDEF;
+}
+
+int xmlnode_has_children(xmlnode node)
+{
+    if ((node != NULL) && (node->firstchild != NULL))
+        return 1;
+    return 0;
+}
+
+static int xmlnode_has_attribs(xmlnode node)
+{
+    if ((node != NULL) && (node->firstattrib != NULL))
+        return 1;
+    return 0;
+}
+
+pool xmlnode_pool(xmlnode node)
+{
+    if (node != NULL)
+        return node->p;
+    return (pool)NULL;
+}
+
+void xmlnode_hide_attrib(xmlnode parent, const char *name)
+{
+    xmlnode attrib;
+
+    if(parent == NULL || parent->firstattrib == NULL || name == NULL)
+        return;
+
+    attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB);
+    if(attrib == NULL)
+        return;
+
+    /* first fix up at the child level */
+    _xmlnode_hide_sibling(attrib);
+
+    /* next fix up at the parent level */
+    if(parent->firstattrib == attrib)
+        parent->firstattrib = attrib->next;
+    if(parent->lastattrib == attrib)
+        parent->lastattrib = attrib->prev;
+}
+
+
+
+/*
+ *  xmlnode2str -- convert given xmlnode tree into a string
+ *
+ *  parameters
+ *      node -- pointer to the xmlnode structure
+ *
+ *  results
+ *      a pointer to the created string
+ *      or NULL if it was unsuccessfull
+ */
+char *xmlnode2str(xmlnode node)
+{
+     return spool_print(_xmlnode2spool(node));
+}
+
+/* loop through both a and b comparing everything, attribs, cdata, children, etc */
+static int xmlnode_cmp(xmlnode a, xmlnode b)
+{
+    int ret = 0;
+
+    while(1)
+    {
+        if(a == NULL && b == NULL)
+            return 0;
+
+        if(a == NULL || b == NULL)
+            return -1;
+
+        if(xmlnode_get_type(a) != xmlnode_get_type(b))
+            return -1;
+
+        switch(xmlnode_get_type(a))
+        {
+        case NTYPE_ATTRIB:
+            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+            if(ret != 0)
+                return -1;
+            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+            if(ret != 0)
+                return -1;
+            break;
+        case NTYPE_TAG:
+            ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b));
+            if(ret != 0)
+                return -1;
+            ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b));
+            if(ret != 0)
+                return -1;
+            ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b));
+            if(ret != 0)
+                return -1;
+            break;
+        case NTYPE_CDATA:
+            ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b));
+            if(ret != 0)
+                return -1;
+        }
+        a = xmlnode_get_nextsibling(a);
+        b = xmlnode_get_nextsibling(b);
+    }
+}
+
+
+xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node)
+{
+    xmlnode child;
+
+    child = xmlnode_insert_tag(parent, xmlnode_get_name(node));
+    if (xmlnode_has_attribs(node))
+        xmlnode_insert_node(child, xmlnode_get_firstattrib(node));
+    if (xmlnode_has_children(node))
+        xmlnode_insert_node(child, xmlnode_get_firstchild(node));
+
+    return child;
+}
+
+/* places copy of node and node's siblings in parent */
+static void xmlnode_insert_node(xmlnode parent, xmlnode node)
+{
+    if(node == NULL || parent == NULL)
+        return;
+
+    while(node != NULL)
+    {
+        switch(xmlnode_get_type(node))
+        {
+        case NTYPE_ATTRIB:
+            xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node));
+            break;
+        case NTYPE_TAG:
+            xmlnode_insert_tag_node(parent, node);
+            break;
+        case NTYPE_CDATA:
+            xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node));
+        }
+        node = xmlnode_get_nextsibling(node);
+    }
+}
+
+
+void xmlnode_free(xmlnode node)
+{
+    if(node == NULL)
+        return;
+
+    pool_free(node->p);
+}
Index: protocols/jabber/xmlparse.c
===================================================================
--- protocols/jabber/xmlparse.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlparse.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,2629 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmlparse.h"
+
+#ifdef XML_UNICODE
+#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX
+#define XmlConvert XmlUtf16Convert
+#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS
+#define XmlEncode XmlUtf16Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1))
+typedef unsigned short ICHAR;
+#else
+#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX
+#define XmlConvert XmlUtf8Convert
+#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding
+#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS
+#define XmlEncode XmlUtf8Encode
+#define MUST_CONVERT(enc, s) (!(enc)->isUtf8)
+typedef char ICHAR;
+#endif
+
+
+#ifndef XML_NS
+
+#define XmlInitEncodingNS XmlInitEncoding
+#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding
+#undef XmlGetInternalEncodingNS
+#define XmlGetInternalEncodingNS XmlGetInternalEncoding
+#define XmlParseXmlDeclNS XmlParseXmlDecl
+
+#endif
+
+
+#ifdef XML_UNICODE_WCHAR_T
+#define XML_T(x) L ## x
+#else
+#define XML_T(x) x
+#endif
+
+/* Round up n to be a multiple of sz, where sz is a power of 2. */
+#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1))
+
+#include "xmltok.h"
+#include "xmlrole.h"
+#include "hashtable.h"
+
+#define INIT_TAG_BUF_SIZE 32  /* must be a multiple of sizeof(XML_Char) */
+#define INIT_DATA_BUF_SIZE 1024
+#define INIT_ATTS_SIZE 16
+#define INIT_BLOCK_SIZE 1024
+#define INIT_BUFFER_SIZE 1024
+
+#define EXPAND_SPARE 24
+
+typedef struct binding {
+    struct prefix *prefix;
+    struct binding *nextTagBinding;
+    struct binding *prevPrefixBinding;
+    const struct attribute_id *attId;
+    XML_Char *uri;
+    int uriLen;
+    int uriAlloc;
+} BINDING;
+
+typedef struct prefix {
+    const XML_Char *name;
+    BINDING *binding;
+} PREFIX;
+
+typedef struct {
+    const XML_Char *str;
+    const XML_Char *localPart;
+    int uriLen;
+} TAG_NAME;
+
+typedef struct tag {
+    struct tag *parent;
+    const char *rawName;
+    int rawNameLength;
+    TAG_NAME name;
+    char *buf;
+    char *bufEnd;
+    BINDING *bindings;
+} TAG;
+
+typedef struct {
+    const XML_Char *name;
+    const XML_Char *textPtr;
+    int textLen;
+    const XML_Char *systemId;
+    const XML_Char *base;
+    const XML_Char *publicId;
+    const XML_Char *notation;
+    char open;
+} ENTITY;
+
+typedef struct block {
+    struct block *next;
+    int size;
+    XML_Char s[1];
+} BLOCK;
+
+typedef struct {
+    BLOCK *blocks;
+    BLOCK *freeBlocks;
+    const XML_Char *end;
+    XML_Char *ptr;
+    XML_Char *start;
+} STRING_POOL;
+
+/* The XML_Char before the name is used to determine whether
+an attribute has been specified. */
+typedef struct attribute_id {
+    XML_Char *name;
+    PREFIX *prefix;
+    char maybeTokenized;
+    char xmlns;
+} ATTRIBUTE_ID;
+
+typedef struct {
+    const ATTRIBUTE_ID *id;
+    char isCdata;
+    const XML_Char *value;
+} DEFAULT_ATTRIBUTE;
+
+typedef struct {
+    const XML_Char *name;
+    PREFIX *prefix;
+    int nDefaultAtts;
+    int allocDefaultAtts;
+    DEFAULT_ATTRIBUTE *defaultAtts;
+} ELEMENT_TYPE;
+
+typedef struct {
+    HASH_TABLE generalEntities;
+    HASH_TABLE elementTypes;
+    HASH_TABLE attributeIds;
+    HASH_TABLE prefixes;
+    STRING_POOL pool;
+    int complete;
+    int standalone;
+    const XML_Char *base;
+    PREFIX defaultPrefix;
+} DTD;
+
+typedef struct open_internal_entity {
+    const char *internalEventPtr;
+    const char *internalEventEndPtr;
+    struct open_internal_entity *next;
+    ENTITY *entity;
+} OPEN_INTERNAL_ENTITY;
+
+typedef enum XML_Error Processor(XML_Parser parser,
+                                 const char *start,
+                                 const char *end,
+                                 const char **endPtr);
+
+static Processor prologProcessor;
+static Processor prologInitProcessor;
+static Processor contentProcessor;
+static Processor cdataSectionProcessor;
+static Processor epilogProcessor;
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName);
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *, const char *);
+static enum XML_Error
+initializeEncoding(XML_Parser parser);
+static enum XML_Error
+doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
+          const char *start, const char *end, const char **endPtr);
+static enum XML_Error
+doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr);
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s,
+                                TAG_NAME *tagNamePtr, BINDING **bindingsPtr);
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr);
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, const XML_Char *dfltValue);
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+                    STRING_POOL *);
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *,
+                     STRING_POOL *);
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
+static enum XML_Error
+storeEntityValue(XML_Parser parser, const char *start, const char *end);
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, const char *end);
+
+static const XML_Char *getContext(XML_Parser parser);
+static void normalizePublicId(XML_Char *s);
+static int dtdInit(DTD *);
+static void dtdDestroy(DTD *);
+static void poolInit(STRING_POOL *);
+static void poolClear(STRING_POOL *);
+static void poolDestroy(STRING_POOL *);
+static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+                            const char *ptr, const char *end);
+static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+                                 const char *ptr, const char *end);
+static int poolGrow(STRING_POOL *pool);
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s);
+static void *XML_GetBuffer(XML_Parser parser, int len);
+static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal);
+
+#define poolStart(pool) ((pool)->start)
+#define poolEnd(pool) ((pool)->ptr)
+#define poolLength(pool) ((pool)->ptr - (pool)->start)
+#define poolChop(pool) ((void)--(pool->ptr))
+#define poolLastChar(pool) (((pool)->ptr)[-1])
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+#define poolAppendChar(pool, c) \
+  (((pool)->ptr == (pool)->end && !poolGrow(pool)) \
+   ? 0 \
+   : ((*((pool)->ptr)++ = c), 1))
+
+typedef struct {
+    /* The first member must be userData so that the XML_GetUserData macro works. */
+    void *m_userData;
+    void *m_handlerArg;
+    char *m_buffer;
+    /* first character to be parsed */
+    const char *m_bufferPtr;
+    /* past last character to be parsed */
+    char *m_bufferEnd;
+    /* allocated end of buffer */
+    const char *m_bufferLim;
+    long m_parseEndByteIndex;
+    const char *m_parseEndPtr;
+    XML_Char *m_dataBuf;
+    XML_Char *m_dataBufEnd;
+    XML_StartElementHandler m_startElementHandler;
+    XML_EndElementHandler m_endElementHandler;
+    XML_CharacterDataHandler m_characterDataHandler;
+    XML_ProcessingInstructionHandler m_processingInstructionHandler;
+    XML_CommentHandler m_commentHandler;
+    XML_StartCdataSectionHandler m_startCdataSectionHandler;
+    XML_EndCdataSectionHandler m_endCdataSectionHandler;
+    XML_DefaultHandler m_defaultHandler;
+    XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler;
+    XML_NotationDeclHandler m_notationDeclHandler;
+    XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler;
+    XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler;
+    XML_NotStandaloneHandler m_notStandaloneHandler;
+    XML_ExternalEntityRefHandler m_externalEntityRefHandler;
+    void *m_externalEntityRefHandlerArg;
+    XML_UnknownEncodingHandler m_unknownEncodingHandler;
+    const ENCODING *m_encoding;
+    INIT_ENCODING m_initEncoding;
+    const XML_Char *m_protocolEncodingName;
+    int m_ns;
+    void *m_unknownEncodingMem;
+    void *m_unknownEncodingData;
+    void *m_unknownEncodingHandlerData;
+    void (*m_unknownEncodingRelease)(void *);
+    PROLOG_STATE m_prologState;
+    Processor *m_processor;
+    enum XML_Error m_errorCode;
+    const char *m_eventPtr;
+    const char *m_eventEndPtr;
+    const char *m_positionPtr;
+    OPEN_INTERNAL_ENTITY *m_openInternalEntities;
+    int m_defaultExpandInternalEntities;
+    int m_tagLevel;
+    ENTITY *m_declEntity;
+    const XML_Char *m_declNotationName;
+    const XML_Char *m_declNotationPublicId;
+    ELEMENT_TYPE *m_declElementType;
+    ATTRIBUTE_ID *m_declAttributeId;
+    char m_declAttributeIsCdata;
+    DTD m_dtd;
+    TAG *m_tagStack;
+    TAG *m_freeTagList;
+    BINDING *m_inheritedBindings;
+    BINDING *m_freeBindingList;
+    int m_attsSize;
+    int m_nSpecifiedAtts;
+    ATTRIBUTE *m_atts;
+    POSITION m_position;
+    STRING_POOL m_tempPool;
+    STRING_POOL m_temp2Pool;
+    char *m_groupConnector;
+    unsigned m_groupSize;
+    int m_hadExternalDoctype;
+    XML_Char m_namespaceSeparator;
+} Parser;
+
+#define userData (((Parser *)parser)->m_userData)
+#define handlerArg (((Parser *)parser)->m_handlerArg)
+#define startElementHandler (((Parser *)parser)->m_startElementHandler)
+#define endElementHandler (((Parser *)parser)->m_endElementHandler)
+#define characterDataHandler (((Parser *)parser)->m_characterDataHandler)
+#define processingInstructionHandler (((Parser *)parser)->m_processingInstructionHandler)
+#define commentHandler (((Parser *)parser)->m_commentHandler)
+#define startCdataSectionHandler (((Parser *)parser)->m_startCdataSectionHandler)
+#define endCdataSectionHandler (((Parser *)parser)->m_endCdataSectionHandler)
+#define defaultHandler (((Parser *)parser)->m_defaultHandler)
+#define unparsedEntityDeclHandler (((Parser *)parser)->m_unparsedEntityDeclHandler)
+#define notationDeclHandler (((Parser *)parser)->m_notationDeclHandler)
+#define startNamespaceDeclHandler (((Parser *)parser)->m_startNamespaceDeclHandler)
+#define endNamespaceDeclHandler (((Parser *)parser)->m_endNamespaceDeclHandler)
+#define notStandaloneHandler (((Parser *)parser)->m_notStandaloneHandler)
+#define externalEntityRefHandler (((Parser *)parser)->m_externalEntityRefHandler)
+#define externalEntityRefHandlerArg (((Parser *)parser)->m_externalEntityRefHandlerArg)
+#define unknownEncodingHandler (((Parser *)parser)->m_unknownEncodingHandler)
+#define encoding (((Parser *)parser)->m_encoding)
+#define initEncoding (((Parser *)parser)->m_initEncoding)
+#define unknownEncodingMem (((Parser *)parser)->m_unknownEncodingMem)
+#define unknownEncodingData (((Parser *)parser)->m_unknownEncodingData)
+#define unknownEncodingHandlerData \
+  (((Parser *)parser)->m_unknownEncodingHandlerData)
+#define unknownEncodingRelease (((Parser *)parser)->m_unknownEncodingRelease)
+#define protocolEncodingName (((Parser *)parser)->m_protocolEncodingName)
+#define ns (((Parser *)parser)->m_ns)
+#define prologState (((Parser *)parser)->m_prologState)
+#define processor (((Parser *)parser)->m_processor)
+#define errorCode (((Parser *)parser)->m_errorCode)
+#define eventPtr (((Parser *)parser)->m_eventPtr)
+#define eventEndPtr (((Parser *)parser)->m_eventEndPtr)
+#define positionPtr (((Parser *)parser)->m_positionPtr)
+#define position (((Parser *)parser)->m_position)
+#define openInternalEntities (((Parser *)parser)->m_openInternalEntities)
+#define defaultExpandInternalEntities (((Parser *)parser)->m_defaultExpandInternalEntities)
+#define tagLevel (((Parser *)parser)->m_tagLevel)
+#define buffer (((Parser *)parser)->m_buffer)
+#define bufferPtr (((Parser *)parser)->m_bufferPtr)
+#define bufferEnd (((Parser *)parser)->m_bufferEnd)
+#define parseEndByteIndex (((Parser *)parser)->m_parseEndByteIndex)
+#define parseEndPtr (((Parser *)parser)->m_parseEndPtr)
+#define bufferLim (((Parser *)parser)->m_bufferLim)
+#define dataBuf (((Parser *)parser)->m_dataBuf)
+#define dataBufEnd (((Parser *)parser)->m_dataBufEnd)
+#define dtd (((Parser *)parser)->m_dtd)
+#define declEntity (((Parser *)parser)->m_declEntity)
+#define declNotationName (((Parser *)parser)->m_declNotationName)
+#define declNotationPublicId (((Parser *)parser)->m_declNotationPublicId)
+#define declElementType (((Parser *)parser)->m_declElementType)
+#define declAttributeId (((Parser *)parser)->m_declAttributeId)
+#define declAttributeIsCdata (((Parser *)parser)->m_declAttributeIsCdata)
+#define freeTagList (((Parser *)parser)->m_freeTagList)
+#define freeBindingList (((Parser *)parser)->m_freeBindingList)
+#define inheritedBindings (((Parser *)parser)->m_inheritedBindings)
+#define tagStack (((Parser *)parser)->m_tagStack)
+#define atts (((Parser *)parser)->m_atts)
+#define attsSize (((Parser *)parser)->m_attsSize)
+#define nSpecifiedAtts (((Parser *)parser)->m_nSpecifiedAtts)
+#define tempPool (((Parser *)parser)->m_tempPool)
+#define temp2Pool (((Parser *)parser)->m_temp2Pool)
+#define groupConnector (((Parser *)parser)->m_groupConnector)
+#define groupSize (((Parser *)parser)->m_groupSize)
+#define hadExternalDoctype (((Parser *)parser)->m_hadExternalDoctype)
+#define namespaceSeparator (((Parser *)parser)->m_namespaceSeparator)
+
+#ifdef _MSC_VER
+#ifdef _DEBUG
+Parser *asParser(XML_Parser parser)
+{
+    return parser;
+}
+#endif
+#endif
+
+XML_Parser XML_ParserCreate(const XML_Char *encodingName)
+{
+    XML_Parser parser = malloc(sizeof(Parser));
+    if (!parser)
+        return parser;
+    processor = prologInitProcessor;
+    XmlPrologStateInit(&prologState);
+    userData = 0;
+    handlerArg = 0;
+    startElementHandler = 0;
+    endElementHandler = 0;
+    characterDataHandler = 0;
+    processingInstructionHandler = 0;
+    commentHandler = 0;
+    startCdataSectionHandler = 0;
+    endCdataSectionHandler = 0;
+    defaultHandler = 0;
+    unparsedEntityDeclHandler = 0;
+    notationDeclHandler = 0;
+    startNamespaceDeclHandler = 0;
+    endNamespaceDeclHandler = 0;
+    notStandaloneHandler = 0;
+    externalEntityRefHandler = 0;
+    externalEntityRefHandlerArg = parser;
+    unknownEncodingHandler = 0;
+    buffer = 0;
+    bufferPtr = 0;
+    bufferEnd = 0;
+    parseEndByteIndex = 0;
+    parseEndPtr = 0;
+    bufferLim = 0;
+    declElementType = 0;
+    declAttributeId = 0;
+    declEntity = 0;
+    declNotationName = 0;
+    declNotationPublicId = 0;
+    memset(&position, 0, sizeof(POSITION));
+    errorCode = XML_ERROR_NONE;
+    eventPtr = 0;
+    eventEndPtr = 0;
+    positionPtr = 0;
+    openInternalEntities = 0;
+    tagLevel = 0;
+    tagStack = 0;
+    freeTagList = 0;
+    freeBindingList = 0;
+    inheritedBindings = 0;
+    attsSize = INIT_ATTS_SIZE;
+    atts = malloc(attsSize * sizeof(ATTRIBUTE));
+    nSpecifiedAtts = 0;
+    dataBuf = malloc(INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+    groupSize = 0;
+    groupConnector = 0;
+    hadExternalDoctype = 0;
+    unknownEncodingMem = 0;
+    unknownEncodingRelease = 0;
+    unknownEncodingData = 0;
+    unknownEncodingHandlerData = 0;
+    namespaceSeparator = '!';
+    ns = 0;
+    poolInit(&tempPool);
+    poolInit(&temp2Pool);
+    protocolEncodingName = encodingName ? poolCopyString(&tempPool, encodingName) : 0;
+    if (!dtdInit(&dtd) || !atts || !dataBuf
+            || (encodingName && !protocolEncodingName)) {
+        XML_ParserFree(parser);
+        return 0;
+    }
+    dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE;
+    XmlInitEncoding(&initEncoding, &encoding, 0);
+    return parser;
+}
+
+static
+void destroyBindings(BINDING *bindings)
+{
+    for (;;) {
+        BINDING *b = bindings;
+        if (!b)
+            break;
+        bindings = b->nextTagBinding;
+        g_free(b->uri);
+        g_free(b);
+    }
+}
+
+void XML_ParserFree(XML_Parser parser)
+{
+    for (;;) {
+        TAG *p;
+        if (tagStack == 0) {
+            if (freeTagList == 0)
+                break;
+            tagStack = freeTagList;
+            freeTagList = 0;
+        }
+        p = tagStack;
+        tagStack = tagStack->parent;
+        g_free(p->buf);
+        destroyBindings(p->bindings);
+        g_free(p);
+    }
+    destroyBindings(freeBindingList);
+    destroyBindings(inheritedBindings);
+    poolDestroy(&tempPool);
+    poolDestroy(&temp2Pool);
+    dtdDestroy(&dtd);
+    g_free((void *)atts);
+    g_free(groupConnector);
+    g_free(buffer);
+    g_free(dataBuf);
+    g_free(unknownEncodingMem);
+    if (unknownEncodingRelease)
+        unknownEncodingRelease(unknownEncodingData);
+    g_free(parser);
+}
+
+void XML_SetUserData(XML_Parser parser, void *p)
+{
+    if (handlerArg == userData)
+        handlerArg = userData = p;
+    else
+        userData = p;
+}
+
+void XML_SetElementHandler(XML_Parser parser,
+                           XML_StartElementHandler start,
+                           XML_EndElementHandler end)
+{
+    startElementHandler = start;
+    endElementHandler = end;
+}
+
+void XML_SetCharacterDataHandler(XML_Parser parser,
+                                 XML_CharacterDataHandler handler)
+{
+    characterDataHandler = handler;
+}
+
+int XML_Parse(XML_Parser parser, const char *s, int len, int isFinal)
+{
+    if (len == 0) {
+        if (!isFinal)
+            return 1;
+        positionPtr = bufferPtr;
+        errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0);
+        if (errorCode == XML_ERROR_NONE)
+            return 1;
+        eventEndPtr = eventPtr;
+        return 0;
+    }
+    else if (bufferPtr == bufferEnd) {
+        const char *end;
+        int nLeftOver;
+        parseEndByteIndex += len;
+        positionPtr = s;
+        if (isFinal) {
+            errorCode = processor(parser, s, parseEndPtr = s + len, 0);
+            if (errorCode == XML_ERROR_NONE)
+                return 1;
+            eventEndPtr = eventPtr;
+            return 0;
+        }
+        errorCode = processor(parser, s, parseEndPtr = s + len, &end);
+        if (errorCode != XML_ERROR_NONE) {
+            eventEndPtr = eventPtr;
+            return 0;
+        }
+        XmlUpdatePosition(encoding, positionPtr, end, &position);
+        nLeftOver = s + len - end;
+        if (nLeftOver) {
+            if (buffer == 0 || nLeftOver > bufferLim - buffer) {
+                /* FIXME avoid integer overflow */
+                buffer = buffer == 0 ? malloc(len * 2) : realloc(buffer, len * 2);
+                if (!buffer) {
+                    errorCode = XML_ERROR_NO_MEMORY;
+                    eventPtr = eventEndPtr = 0;
+                    return 0;
+                }
+                bufferLim = buffer + len * 2;
+            }
+            memcpy(buffer, end, nLeftOver);
+            bufferPtr = buffer;
+            bufferEnd = buffer + nLeftOver;
+        }
+        return 1;
+    }
+    else {
+        memcpy(XML_GetBuffer(parser, len), s, len);
+        return XML_ParseBuffer(parser, len, isFinal);
+    }
+}
+
+static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal)
+{
+    const char *start = bufferPtr;
+    positionPtr = start;
+    bufferEnd += len;
+    parseEndByteIndex += len;
+    errorCode = processor(parser, start, parseEndPtr = bufferEnd,
+                          isFinal ? (const char **)0 : &bufferPtr);
+    if (errorCode == XML_ERROR_NONE) {
+        if (!isFinal)
+            XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position);
+        return 1;
+    }
+    else {
+        eventEndPtr = eventPtr;
+        return 0;
+    }
+}
+
+static void *XML_GetBuffer(XML_Parser parser, int len)
+{
+    if (len > bufferLim - bufferEnd) {
+        /* FIXME avoid integer overflow */
+        int neededSize = len + (bufferEnd - bufferPtr);
+        if (neededSize  <= bufferLim - buffer) {
+            memmove(buffer, bufferPtr, bufferEnd - bufferPtr);
+            bufferEnd = buffer + (bufferEnd - bufferPtr);
+            bufferPtr = buffer;
+        }
+        else {
+            char *newBuf;
+            int bufferSize = bufferLim - bufferPtr;
+            if (bufferSize == 0)
+                bufferSize = INIT_BUFFER_SIZE;
+            do {
+                bufferSize *= 2;
+            } while (bufferSize < neededSize);
+            newBuf = malloc(bufferSize);
+            if (newBuf == 0) {
+                errorCode = XML_ERROR_NO_MEMORY;
+                return 0;
+            }
+            bufferLim = newBuf + bufferSize;
+            if (bufferPtr) {
+                memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr);
+                g_free(buffer);
+            }
+            bufferEnd = newBuf + (bufferEnd - bufferPtr);
+            bufferPtr = buffer = newBuf;
+        }
+    }
+    return bufferEnd;
+}
+
+static
+enum XML_Error contentProcessor(XML_Parser parser,
+                                const char *start,
+                                const char *end,
+                                const char **endPtr)
+{
+    return doContent(parser, 0, encoding, start, end, endPtr);
+}
+
+static enum XML_Error
+doContent(XML_Parser parser,
+          int startTagLevel,
+          const ENCODING *enc,
+          const char *s,
+          const char *end,
+          const char **nextPtr)
+{
+    const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+    const char **eventPP;
+    const char **eventEndPP;
+    if (enc == encoding) {
+        eventPP = &eventPtr;
+        eventEndPP = &eventEndPtr;
+    }
+    else {
+        eventPP = &(openInternalEntities->internalEventPtr);
+        eventEndPP = &(openInternalEntities->internalEventEndPtr);
+    }
+    *eventPP = s;
+    for (;;) {
+        const char *next = s; /* XmlContentTok doesn't always set the last arg */
+        int tok = XmlContentTok(enc, s, end, &next);
+        *eventEndPP = next;
+        switch (tok) {
+        case XML_TOK_TRAILING_CR:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            *eventEndPP = end;
+            if (characterDataHandler) {
+                XML_Char c = 0xA;
+                characterDataHandler(handlerArg, &c, 1);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, end);
+            if (startTagLevel == 0)
+                return XML_ERROR_NO_ELEMENTS;
+            if (tagLevel != startTagLevel)
+                return XML_ERROR_ASYNC_ENTITY;
+            return XML_ERROR_NONE;
+        case XML_TOK_NONE:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            if (startTagLevel > 0) {
+                if (tagLevel != startTagLevel)
+                    return XML_ERROR_ASYNC_ENTITY;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_NO_ELEMENTS;
+        case XML_TOK_INVALID:
+            *eventPP = next;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_PARTIAL:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_UNCLOSED_TOKEN;
+        case XML_TOK_PARTIAL_CHAR:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_PARTIAL_CHAR;
+        case XML_TOK_ENTITY_REF:
+            {
+                const XML_Char *name;
+                ENTITY *entity;
+                XML_Char ch = XmlPredefinedEntityName(enc,
+                                                      s + enc->minBytesPerChar,
+                                                      next - enc->minBytesPerChar);
+                if (ch) {
+                    if (characterDataHandler)
+                        characterDataHandler(handlerArg, &ch, 1);
+                    else if (defaultHandler)
+                        reportDefault(parser, enc, s, next);
+                    break;
+                }
+                name = poolStoreString(&dtd.pool, enc,
+                                       s + enc->minBytesPerChar,
+                                       next - enc->minBytesPerChar);
+                if (!name)
+                    return XML_ERROR_NO_MEMORY;
+                entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+                poolDiscard(&dtd.pool);
+                if (!entity) {
+                    if (dtd.complete || dtd.standalone)
+                        return XML_ERROR_UNDEFINED_ENTITY;
+                    if (defaultHandler)
+                        reportDefault(parser, enc, s, next);
+                    break;
+                }
+                if (entity->open)
+                    return XML_ERROR_RECURSIVE_ENTITY_REF;
+                if (entity->notation)
+                    return XML_ERROR_BINARY_ENTITY_REF;
+                if (entity) {
+                    if (entity->textPtr) {
+                        enum XML_Error result;
+                        OPEN_INTERNAL_ENTITY openEntity;
+                        if (defaultHandler && !defaultExpandInternalEntities) {
+                            reportDefault(parser, enc, s, next);
+                            break;
+                        }
+                        entity->open = 1;
+                        openEntity.next = openInternalEntities;
+                        openInternalEntities = &openEntity;
+                        openEntity.entity = entity;
+                        openEntity.internalEventPtr = 0;
+                        openEntity.internalEventEndPtr = 0;
+                        result = doContent(parser,
+                                           tagLevel,
+                                           internalEnc,
+                                           (char *)entity->textPtr,
+                                           (char *)(entity->textPtr + entity->textLen),
+                                           0);
+                        entity->open = 0;
+                        openInternalEntities = openEntity.next;
+                        if (result)
+                            return result;
+                    }
+                    else if (externalEntityRefHandler) {
+                        const XML_Char *context;
+                        entity->open = 1;
+                        context = getContext(parser);
+                        entity->open = 0;
+                        if (!context)
+                            return XML_ERROR_NO_MEMORY;
+                        if (!externalEntityRefHandler(externalEntityRefHandlerArg,
+                                                      context,
+                                                      dtd.base,
+                                                      entity->systemId,
+                                                      entity->publicId))
+                            return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
+                        poolDiscard(&tempPool);
+                    }
+                    else if (defaultHandler)
+                        reportDefault(parser, enc, s, next);
+                }
+                break;
+            }
+        case XML_TOK_START_TAG_WITH_ATTS:
+            if (!startElementHandler) {
+                enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+                if (result)
+                    return result;
+            }
+            /* fall through */
+        case XML_TOK_START_TAG_NO_ATTS:
+            {
+                TAG *tag;
+                if (freeTagList) {
+                    tag = freeTagList;
+                    freeTagList = freeTagList->parent;
+                }
+                else {
+                    tag = malloc(sizeof(TAG));
+                    if (!tag)
+                        return XML_ERROR_NO_MEMORY;
+                    tag->buf = malloc(INIT_TAG_BUF_SIZE);
+                    if (!tag->buf)
+                        return XML_ERROR_NO_MEMORY;
+                    tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE;
+                }
+                tag->bindings = 0;
+                tag->parent = tagStack;
+                tagStack = tag;
+                tag->name.localPart = 0;
+                tag->rawName = s + enc->minBytesPerChar;
+                tag->rawNameLength = XmlNameLength(enc, tag->rawName);
+                if (nextPtr) {
+                    /* Need to guarantee that:
+                       tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)) <= tag->bufEnd - sizeof(XML_Char) */
+                    if (tag->rawNameLength + (int)(sizeof(XML_Char) - 1) + (int)sizeof(XML_Char) > tag->bufEnd - tag->buf) {
+                        int bufSize = tag->rawNameLength * 4;
+                        bufSize = ROUND_UP(bufSize, sizeof(XML_Char));
+                        tag->buf = realloc(tag->buf, bufSize);
+                        if (!tag->buf)
+                            return XML_ERROR_NO_MEMORY;
+                        tag->bufEnd = tag->buf + bufSize;
+                    }
+                    memcpy(tag->buf, tag->rawName, tag->rawNameLength);
+                    tag->rawName = tag->buf;
+                }
+                ++tagLevel;
+                if (startElementHandler) {
+                    enum XML_Error result;
+                    XML_Char *toPtr;
+                    for (;;) {
+                        const char *rawNameEnd = tag->rawName + tag->rawNameLength;
+                        const char *fromPtr = tag->rawName;
+                        int bufSize;
+                        if (nextPtr)
+                            toPtr = (XML_Char *)(tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)));
+                        else
+                            toPtr = (XML_Char *)tag->buf;
+                        tag->name.str = toPtr;
+                        XmlConvert(enc,
+                                   &fromPtr, rawNameEnd,
+                                   (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1);
+                        if (fromPtr == rawNameEnd)
+                            break;
+                        bufSize = (tag->bufEnd - tag->buf) << 1;
+                        tag->buf = realloc(tag->buf, bufSize);
+                        if (!tag->buf)
+                            return XML_ERROR_NO_MEMORY;
+                        tag->bufEnd = tag->buf + bufSize;
+                        if (nextPtr)
+                            tag->rawName = tag->buf;
+                    }
+                    *toPtr = XML_T('\0');
+                    result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
+                    if (result)
+                        return result;
+                    startElementHandler(handlerArg, tag->name.str, (const XML_Char **)atts);
+                    poolClear(&tempPool);
+                }
+                else {
+                    tag->name.str = 0;
+                    if (defaultHandler)
+                        reportDefault(parser, enc, s, next);
+                }
+                break;
+            }
+        case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+            if (!startElementHandler) {
+                enum XML_Error result = storeAtts(parser, enc, s, 0, 0);
+                if (result)
+                    return result;
+            }
+            /* fall through */
+        case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+            if (startElementHandler || endElementHandler) {
+                const char *rawName = s + enc->minBytesPerChar;
+                enum XML_Error result;
+                BINDING *bindings = 0;
+                TAG_NAME name;
+                name.str = poolStoreString(&tempPool, enc, rawName,
+                                           rawName + XmlNameLength(enc, rawName));
+                if (!name.str)
+                    return XML_ERROR_NO_MEMORY;
+                poolFinish(&tempPool);
+                result = storeAtts(parser, enc, s, &name, &bindings);
+                if (result)
+                    return result;
+                poolFinish(&tempPool);
+                if (startElementHandler)
+                    startElementHandler(handlerArg, name.str, (const XML_Char **)atts);
+                if (endElementHandler) {
+                    if (startElementHandler)
+                        *eventPP = *eventEndPP;
+                    endElementHandler(handlerArg, name.str);
+                }
+                poolClear(&tempPool);
+                while (bindings) {
+                    BINDING *b = bindings;
+                    if (endNamespaceDeclHandler)
+                        endNamespaceDeclHandler(handlerArg, b->prefix->name);
+                    bindings = bindings->nextTagBinding;
+                    b->nextTagBinding = freeBindingList;
+                    freeBindingList = b;
+                    b->prefix->binding = b->prevPrefixBinding;
+                }
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            if (tagLevel == 0)
+                return epilogProcessor(parser, next, end, nextPtr);
+            break;
+        case XML_TOK_END_TAG:
+            if (tagLevel == startTagLevel)
+                return XML_ERROR_ASYNC_ENTITY;
+            else {
+                int len;
+                const char *rawName;
+                TAG *tag = tagStack;
+                tagStack = tag->parent;
+                tag->parent = freeTagList;
+                freeTagList = tag;
+                rawName = s + enc->minBytesPerChar*2;
+                len = XmlNameLength(enc, rawName);
+                if (len != tag->rawNameLength
+                        || memcmp(tag->rawName, rawName, len) != 0) {
+                    *eventPP = rawName;
+                    return XML_ERROR_TAG_MISMATCH;
+                }
+                --tagLevel;
+                if (endElementHandler && tag->name.str) {
+                    if (tag->name.localPart) {
+                        XML_Char *to = (XML_Char *)tag->name.str + tag->name.uriLen;
+                        const XML_Char *from = tag->name.localPart;
+                        while ((*to++ = *from++) != 0)
+                            ;
+                    }
+                    endElementHandler(handlerArg, tag->name.str);
+                }
+                else if (defaultHandler)
+                    reportDefault(parser, enc, s, next);
+                while (tag->bindings) {
+                    BINDING *b = tag->bindings;
+                    if (endNamespaceDeclHandler)
+                        endNamespaceDeclHandler(handlerArg, b->prefix->name);
+                    tag->bindings = tag->bindings->nextTagBinding;
+                    b->nextTagBinding = freeBindingList;
+                    freeBindingList = b;
+                    b->prefix->binding = b->prevPrefixBinding;
+                }
+                if (tagLevel == 0)
+                    return epilogProcessor(parser, next, end, nextPtr);
+            }
+            break;
+        case XML_TOK_CHAR_REF:
+            {
+                int n = XmlCharRefNumber(enc, s);
+                if (n < 0)
+                    return XML_ERROR_BAD_CHAR_REF;
+                if (characterDataHandler) {
+                    XML_Char buf[XML_ENCODE_MAX];
+                    characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf));
+                }
+                else if (defaultHandler)
+                    reportDefault(parser, enc, s, next);
+            }
+            break;
+        case XML_TOK_XML_DECL:
+            return XML_ERROR_MISPLACED_XML_PI;
+        case XML_TOK_DATA_NEWLINE:
+            if (characterDataHandler) {
+                XML_Char c = 0xA;
+                characterDataHandler(handlerArg, &c, 1);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            break;
+        case XML_TOK_CDATA_SECT_OPEN:
+            {
+                enum XML_Error result;
+                if (startCdataSectionHandler)
+                    startCdataSectionHandler(handlerArg);
+#if 0
+                /* Suppose you doing a transformation on a document that involves
+                   changing only the character data.  You set up a defaultHandler
+                   and a characterDataHandler.  The defaultHandler simply copies
+                   characters through.  The characterDataHandler does the transformation
+                   and writes the characters out escaping them as necessary.  This case
+                   will fail to work if we leave out the following two lines (because &
+                   and < inside CDATA sections will be incorrectly escaped).
+
+                   However, now we have a start/endCdataSectionHandler, so it seems
+                   easier to let the user deal with this. */
+
+                else if (characterDataHandler)
+                    characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+                else if (defaultHandler)
+                    reportDefault(parser, enc, s, next);
+                result = doCdataSection(parser, enc, &next, end, nextPtr);
+                if (!next) {
+                    processor = cdataSectionProcessor;
+                    return result;
+                }
+            }
+            break;
+        case XML_TOK_TRAILING_RSQB:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            if (characterDataHandler) {
+                if (MUST_CONVERT(enc, s)) {
+                    ICHAR *dataPtr = (ICHAR *)dataBuf;
+                    XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+                    characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+                }
+                else
+                    characterDataHandler(handlerArg,
+                                         (XML_Char *)s,
+                                         (XML_Char *)end - (XML_Char *)s);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, end);
+            if (startTagLevel == 0) {
+                *eventPP = end;
+                return XML_ERROR_NO_ELEMENTS;
+            }
+            if (tagLevel != startTagLevel) {
+                *eventPP = end;
+                return XML_ERROR_ASYNC_ENTITY;
+            }
+            return XML_ERROR_NONE;
+        case XML_TOK_DATA_CHARS:
+            if (characterDataHandler) {
+                if (MUST_CONVERT(enc, s)) {
+                    for (;;) {
+                        ICHAR *dataPtr = (ICHAR *)dataBuf;
+                        XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+                        *eventEndPP = s;
+                        characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+                        if (s == next)
+                            break;
+                        *eventPP = s;
+                    }
+                }
+                else
+                    characterDataHandler(handlerArg,
+                                         (XML_Char *)s,
+                                         (XML_Char *)next - (XML_Char *)s);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            break;
+        case XML_TOK_PI:
+            if (!reportProcessingInstruction(parser, enc, s, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_TOK_COMMENT:
+            if (!reportComment(parser, enc, s, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        default:
+            if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            break;
+        }
+        *eventPP = s = next;
+    }
+    /* not reached */
+}
+
+/* If tagNamePtr is non-null, build a real list of attributes,
+otherwise just check the attributes for well-formedness. */
+
+static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc,
+                                const char *s, TAG_NAME *tagNamePtr,
+                                BINDING **bindingsPtr)
+{
+    ELEMENT_TYPE *elementType = 0;
+    int nDefaultAtts = 0;
+    const XML_Char **appAtts;
+    int attIndex = 0;
+    int i;
+    int n;
+    int nPrefixes = 0;
+    BINDING *binding;
+    const XML_Char *localPart;
+
+    if (tagNamePtr) {
+        elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, 0);
+        if (!elementType) {
+            tagNamePtr->str = poolCopyString(&dtd.pool, tagNamePtr->str);
+            if (!tagNamePtr->str)
+                return XML_ERROR_NO_MEMORY;
+            elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, sizeof(ELEMENT_TYPE));
+            if (!elementType)
+                return XML_ERROR_NO_MEMORY;
+            if (ns && !setElementTypePrefix(parser, elementType))
+                return XML_ERROR_NO_MEMORY;
+        }
+        nDefaultAtts = elementType->nDefaultAtts;
+    }
+    n = XmlGetAttributes(enc, s, attsSize, atts);
+    if (n + nDefaultAtts > attsSize) {
+        int oldAttsSize = attsSize;
+        attsSize = n + nDefaultAtts + INIT_ATTS_SIZE;
+        atts = realloc((void *)atts, attsSize * sizeof(ATTRIBUTE));
+        if (!atts)
+            return XML_ERROR_NO_MEMORY;
+        if (n > oldAttsSize)
+            XmlGetAttributes(enc, s, n, atts);
+    }
+    appAtts = (const XML_Char **)atts;
+    for (i = 0; i < n; i++) {
+        ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name,
+                                             atts[i].name
+                                             + XmlNameLength(enc, atts[i].name));
+        if (!attId)
+            return XML_ERROR_NO_MEMORY;
+        if ((attId->name)[-1]) {
+            if (enc == encoding)
+                eventPtr = atts[i].name;
+            return XML_ERROR_DUPLICATE_ATTRIBUTE;
+        }
+        (attId->name)[-1] = 1;
+        appAtts[attIndex++] = attId->name;
+        if (!atts[i].normalized) {
+            enum XML_Error result;
+            int isCdata = 1;
+
+            if (attId->maybeTokenized) {
+                int j;
+                for (j = 0; j < nDefaultAtts; j++) {
+                    if (attId == elementType->defaultAtts[j].id) {
+                        isCdata = elementType->defaultAtts[j].isCdata;
+                        break;
+                    }
+                }
+            }
+
+            result = storeAttributeValue(parser, enc, isCdata,
+                                         atts[i].valuePtr, atts[i].valueEnd,
+                                         &tempPool);
+            if (result)
+                return result;
+            if (tagNamePtr) {
+                appAtts[attIndex] = poolStart(&tempPool);
+                poolFinish(&tempPool);
+            }
+            else
+                poolDiscard(&tempPool);
+        }
+        else if (tagNamePtr) {
+            appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd);
+            if (appAtts[attIndex] == 0)
+                return XML_ERROR_NO_MEMORY;
+            poolFinish(&tempPool);
+        }
+        if (attId->prefix && tagNamePtr) {
+            if (attId->xmlns) {
+                if (!addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr))
+                    return XML_ERROR_NO_MEMORY;
+                --attIndex;
+            }
+            else {
+                attIndex++;
+                nPrefixes++;
+                (attId->name)[-1] = 2;
+            }
+        }
+        else
+            attIndex++;
+    }
+    nSpecifiedAtts = attIndex;
+    if (tagNamePtr) {
+        int j;
+        for (j = 0; j < nDefaultAtts; j++) {
+            const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + j;
+            if (!(da->id->name)[-1] && da->value) {
+                if (da->id->prefix) {
+                    if (da->id->xmlns) {
+                        if (!addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr))
+                            return XML_ERROR_NO_MEMORY;
+                    }
+                    else {
+                        (da->id->name)[-1] = 2;
+                        nPrefixes++;
+                        appAtts[attIndex++] = da->id->name;
+                        appAtts[attIndex++] = da->value;
+                    }
+                }
+                else {
+                    (da->id->name)[-1] = 1;
+                    appAtts[attIndex++] = da->id->name;
+                    appAtts[attIndex++] = da->value;
+                }
+            }
+        }
+        appAtts[attIndex] = 0;
+    }
+    i = 0;
+    if (nPrefixes) {
+        for (; i < attIndex; i += 2) {
+            if (appAtts[i][-1] == 2) {
+                ATTRIBUTE_ID *id;
+                ((XML_Char *)(appAtts[i]))[-1] = 0;
+                id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, appAtts[i], 0);
+                if (id->prefix->binding) {
+                    int j;
+                    const BINDING *b = id->prefix->binding;
+                    const XML_Char *s = appAtts[i];
+                    for (j = 0; j < b->uriLen; j++) {
+                        if (!poolAppendChar(&tempPool, b->uri[j]))
+                            return XML_ERROR_NO_MEMORY;
+                    }
+                    while (*s++ != ':')
+                        ;
+                    do {
+                        if (!poolAppendChar(&tempPool, *s))
+                            return XML_ERROR_NO_MEMORY;
+                    } while (*s++);
+                    appAtts[i] = poolStart(&tempPool);
+                    poolFinish(&tempPool);
+                }
+                if (!--nPrefixes)
+                    break;
+            }
+            else
+                ((XML_Char *)(appAtts[i]))[-1] = 0;
+        }
+    }
+    for (; i < attIndex; i += 2)
+        ((XML_Char *)(appAtts[i]))[-1] = 0;
+    if (!tagNamePtr)
+        return XML_ERROR_NONE;
+    for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding)
+        binding->attId->name[-1] = 0;
+    if (elementType->prefix) {
+        binding = elementType->prefix->binding;
+        if (!binding)
+            return XML_ERROR_NONE;
+        localPart = tagNamePtr->str;
+        while (*localPart++ != XML_T(':'))
+            ;
+    }
+    else if (dtd.defaultPrefix.binding) {
+        binding = dtd.defaultPrefix.binding;
+        localPart = tagNamePtr->str;
+    }
+    else
+        return XML_ERROR_NONE;
+    tagNamePtr->localPart = localPart;
+    tagNamePtr->uriLen = binding->uriLen;
+    i = binding->uriLen;
+    do {
+        if (i == binding->uriAlloc) {
+            binding->uri = realloc(binding->uri, binding->uriAlloc *= 2);
+            if (!binding->uri)
+                return XML_ERROR_NO_MEMORY;
+        }
+        binding->uri[i++] = *localPart;
+    } while (*localPart++);
+    tagNamePtr->str = binding->uri;
+    return XML_ERROR_NONE;
+}
+
+static
+int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr)
+{
+    BINDING *b;
+    int len;
+    for (len = 0; uri[len]; len++)
+        ;
+    if (namespaceSeparator)
+        len++;
+    if (freeBindingList) {
+        b = freeBindingList;
+        if (len > b->uriAlloc) {
+            b->uri = realloc(b->uri, len + EXPAND_SPARE);
+            if (!b->uri)
+                return 0;
+            b->uriAlloc = len + EXPAND_SPARE;
+        }
+        freeBindingList = b->nextTagBinding;
+    }
+    else {
+        b = malloc(sizeof(BINDING));
+        if (!b)
+            return 0;
+        b->uri = malloc(sizeof(XML_Char) * len + EXPAND_SPARE);
+        if (!b->uri) {
+            g_free(b);
+            return 0;
+        }
+        b->uriAlloc = len;
+    }
+    b->uriLen = len;
+    memcpy(b->uri, uri, len * sizeof(XML_Char));
+    if (namespaceSeparator)
+        b->uri[len - 1] = namespaceSeparator;
+    b->prefix = prefix;
+    b->attId = attId;
+    b->prevPrefixBinding = prefix->binding;
+    if (*uri == XML_T('\0') && prefix == &dtd.defaultPrefix)
+        prefix->binding = 0;
+    else
+        prefix->binding = b;
+    b->nextTagBinding = *bindingsPtr;
+    *bindingsPtr = b;
+    if (startNamespaceDeclHandler)
+        startNamespaceDeclHandler(handlerArg, prefix->name,
+                                  prefix->binding ? uri : 0);
+    return 1;
+}
+
+/* The idea here is to avoid using stack for each CDATA section when
+the whole file is parsed with one call. */
+
+static
+enum XML_Error cdataSectionProcessor(XML_Parser parser,
+                                     const char *start,
+                                     const char *end,
+                                     const char **endPtr)
+{
+    enum XML_Error result = doCdataSection(parser, encoding, &start, end, endPtr);
+    if (start) {
+        processor = contentProcessor;
+        return contentProcessor(parser, start, end, endPtr);
+    }
+    return result;
+}
+
+/* startPtr gets set to non-null is the section is closed, and to null if
+the section is not yet closed. */
+
+static
+enum XML_Error doCdataSection(XML_Parser parser,
+                              const ENCODING *enc,
+                              const char **startPtr,
+                              const char *end,
+                              const char **nextPtr)
+{
+    const char *s = *startPtr;
+    const char **eventPP;
+    const char **eventEndPP;
+    if (enc == encoding) {
+        eventPP = &eventPtr;
+        *eventPP = s;
+        eventEndPP = &eventEndPtr;
+    }
+    else {
+        eventPP = &(openInternalEntities->internalEventPtr);
+        eventEndPP = &(openInternalEntities->internalEventEndPtr);
+    }
+    *eventPP = s;
+    *startPtr = 0;
+    for (;;) {
+        const char *next;
+        int tok = XmlCdataSectionTok(enc, s, end, &next);
+        *eventEndPP = next;
+        switch (tok) {
+        case XML_TOK_CDATA_SECT_CLOSE:
+            if (endCdataSectionHandler)
+                endCdataSectionHandler(handlerArg);
+#if 0
+            /* see comment under XML_TOK_CDATA_SECT_OPEN */
+            else if (characterDataHandler)
+                characterDataHandler(handlerArg, dataBuf, 0);
+#endif
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            *startPtr = next;
+            return XML_ERROR_NONE;
+        case XML_TOK_DATA_NEWLINE:
+            if (characterDataHandler) {
+                XML_Char c = 0xA;
+                characterDataHandler(handlerArg, &c, 1);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            break;
+        case XML_TOK_DATA_CHARS:
+            if (characterDataHandler) {
+                if (MUST_CONVERT(enc, s)) {
+                    for (;;) {
+                        ICHAR *dataPtr = (ICHAR *)dataBuf;
+                        XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd);
+                        *eventEndPP = next;
+                        characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+                        if (s == next)
+                            break;
+                        *eventPP = s;
+                    }
+                }
+                else
+                    characterDataHandler(handlerArg,
+                                         (XML_Char *)s,
+                                         (XML_Char *)next - (XML_Char *)s);
+            }
+            else if (defaultHandler)
+                reportDefault(parser, enc, s, next);
+            break;
+        case XML_TOK_INVALID:
+            *eventPP = next;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_PARTIAL_CHAR:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_PARTIAL_CHAR;
+        case XML_TOK_PARTIAL:
+        case XML_TOK_NONE:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_UNCLOSED_CDATA_SECTION;
+        default:
+            abort();
+        }
+        *eventPP = s = next;
+    }
+    /* not reached */
+}
+
+static enum XML_Error
+initializeEncoding(XML_Parser parser)
+{
+    const char *s;
+#ifdef XML_UNICODE
+    char encodingBuf[128];
+    if (!protocolEncodingName)
+        s = 0;
+    else {
+        int i;
+        for (i = 0; protocolEncodingName[i]; i++) {
+            if (i == sizeof(encodingBuf) - 1
+                    || protocolEncodingName[i] >= 0x80
+                    || protocolEncodingName[i] < 0) {
+                encodingBuf[0] = '\0';
+                break;
+            }
+            encodingBuf[i] = (char)protocolEncodingName[i];
+        }
+        encodingBuf[i] = '\0';
+        s = encodingBuf;
+    }
+#else
+s = protocolEncodingName;
+#endif
+    if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s))
+        return XML_ERROR_NONE;
+    return handleUnknownEncoding(parser, protocolEncodingName);
+}
+
+static enum XML_Error
+processXmlDecl(XML_Parser parser, int isGeneralTextEntity,
+               const char *s, const char *next)
+{
+    const char *encodingName = 0;
+    const ENCODING *newEncoding = 0;
+    const char *version;
+    int standalone = -1;
+    if (!(ns
+            ? XmlParseXmlDeclNS
+            : XmlParseXmlDecl)(isGeneralTextEntity,
+                               encoding,
+                               s,
+                               next,
+                               &eventPtr,
+                               &version,
+                               &encodingName,
+                               &newEncoding,
+                               &standalone))
+        return XML_ERROR_SYNTAX;
+    if (!isGeneralTextEntity && standalone == 1)
+        dtd.standalone = 1;
+    if (defaultHandler)
+        reportDefault(parser, encoding, s, next);
+    if (!protocolEncodingName) {
+        if (newEncoding) {
+            if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) {
+                eventPtr = encodingName;
+                return XML_ERROR_INCORRECT_ENCODING;
+            }
+            encoding = newEncoding;
+        }
+        else if (encodingName) {
+            enum XML_Error result;
+            const XML_Char *s = poolStoreString(&tempPool,
+                                                encoding,
+                                                encodingName,
+                                                encodingName
+                                                + XmlNameLength(encoding, encodingName));
+            if (!s)
+                return XML_ERROR_NO_MEMORY;
+            result = handleUnknownEncoding(parser, s);
+            poolDiscard(&tempPool);
+            if (result == XML_ERROR_UNKNOWN_ENCODING)
+                eventPtr = encodingName;
+            return result;
+        }
+    }
+    return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName)
+{
+    if (unknownEncodingHandler) {
+        XML_Encoding info;
+        int i;
+        for (i = 0; i < 256; i++)
+            info.map[i] = -1;
+        info.convert = 0;
+        info.data = 0;
+        info.release = 0;
+        if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) {
+            ENCODING *enc;
+            unknownEncodingMem = malloc(XmlSizeOfUnknownEncoding());
+            if (!unknownEncodingMem) {
+                if (info.release)
+                    info.release(info.data);
+                return XML_ERROR_NO_MEMORY;
+            }
+            enc = (ns
+                   ? XmlInitUnknownEncodingNS
+                   : XmlInitUnknownEncoding)(unknownEncodingMem,
+                                             info.map,
+                                             info.convert,
+                                             info.data);
+            if (enc) {
+                unknownEncodingData = info.data;
+                unknownEncodingRelease = info.release;
+                encoding = enc;
+                return XML_ERROR_NONE;
+            }
+        }
+        if (info.release)
+            info.release(info.data);
+    }
+    return XML_ERROR_UNKNOWN_ENCODING;
+}
+
+static enum XML_Error
+prologInitProcessor(XML_Parser parser,
+                    const char *s,
+                    const char *end,
+                    const char **nextPtr)
+{
+    enum XML_Error result = initializeEncoding(parser);
+    if (result != XML_ERROR_NONE)
+        return result;
+    processor = prologProcessor;
+    return prologProcessor(parser, s, end, nextPtr);
+}
+
+static enum XML_Error
+prologProcessor(XML_Parser parser,
+                const char *s,
+                const char *end,
+                const char **nextPtr)
+{
+    for (;;) {
+        const char *next;
+        int tok = XmlPrologTok(encoding, s, end, &next);
+        if (tok <= 0) {
+            if (nextPtr != 0 && tok != XML_TOK_INVALID) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            switch (tok) {
+            case XML_TOK_INVALID:
+                eventPtr = next;
+                return XML_ERROR_INVALID_TOKEN;
+            case XML_TOK_NONE:
+                return XML_ERROR_NO_ELEMENTS;
+            case XML_TOK_PARTIAL:
+                return XML_ERROR_UNCLOSED_TOKEN;
+            case XML_TOK_PARTIAL_CHAR:
+                return XML_ERROR_PARTIAL_CHAR;
+            case XML_TOK_TRAILING_CR:
+                eventPtr = s + encoding->minBytesPerChar;
+                return XML_ERROR_NO_ELEMENTS;
+            default:
+                abort();
+            }
+        }
+        switch (XmlTokenRole(&prologState, tok, s, next, encoding)) {
+        case XML_ROLE_XML_DECL:
+            {
+                enum XML_Error result = processXmlDecl(parser, 0, s, next);
+                if (result != XML_ERROR_NONE)
+                    return result;
+            }
+            break;
+        case XML_ROLE_DOCTYPE_SYSTEM_ID:
+            if (!dtd.standalone
+                    && notStandaloneHandler
+                    && !notStandaloneHandler(handlerArg))
+                return XML_ERROR_NOT_STANDALONE;
+            hadExternalDoctype = 1;
+            break;
+        case XML_ROLE_DOCTYPE_PUBLIC_ID:
+        case XML_ROLE_ENTITY_PUBLIC_ID:
+            if (!XmlIsPublicId(encoding, s, next, &eventPtr))
+                return XML_ERROR_SYNTAX;
+            if (declEntity) {
+                XML_Char *tem = poolStoreString(&dtd.pool,
+                                                encoding,
+                                                s + encoding->minBytesPerChar,
+                                                next - encoding->minBytesPerChar);
+                if (!tem)
+                    return XML_ERROR_NO_MEMORY;
+                normalizePublicId(tem);
+                declEntity->publicId = tem;
+                poolFinish(&dtd.pool);
+            }
+            break;
+        case XML_ROLE_INSTANCE_START:
+            processor = contentProcessor;
+            if (hadExternalDoctype)
+                dtd.complete = 0;
+            return contentProcessor(parser, s, end, nextPtr);
+        case XML_ROLE_ATTLIST_ELEMENT_NAME:
+            {
+                const XML_Char *name = poolStoreString(&dtd.pool, encoding, s, next);
+                if (!name)
+                    return XML_ERROR_NO_MEMORY;
+                declElementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, name, sizeof(ELEMENT_TYPE));
+                if (!declElementType)
+                    return XML_ERROR_NO_MEMORY;
+                if (declElementType->name != name)
+                    poolDiscard(&dtd.pool);
+                else {
+                    poolFinish(&dtd.pool);
+                    if (!setElementTypePrefix(parser, declElementType))
+                        return XML_ERROR_NO_MEMORY;
+                }
+                break;
+            }
+        case XML_ROLE_ATTRIBUTE_NAME:
+            declAttributeId = getAttributeId(parser, encoding, s, next);
+            if (!declAttributeId)
+                return XML_ERROR_NO_MEMORY;
+            declAttributeIsCdata = 0;
+            break;
+        case XML_ROLE_ATTRIBUTE_TYPE_CDATA:
+            declAttributeIsCdata = 1;
+            break;
+        case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE:
+        case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE:
+            if (dtd.complete
+                    && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, 0))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+        case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+            {
+                const XML_Char *attVal;
+                enum XML_Error result
+                = storeAttributeValue(parser, encoding, declAttributeIsCdata,
+                                      s + encoding->minBytesPerChar,
+                                      next - encoding->minBytesPerChar,
+                                      &dtd.pool);
+                if (result)
+                    return result;
+                attVal = poolStart(&dtd.pool);
+                poolFinish(&dtd.pool);
+                if (dtd.complete
+                        && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, attVal))
+                    return XML_ERROR_NO_MEMORY;
+                break;
+            }
+        case XML_ROLE_ENTITY_VALUE:
+            {
+                enum XML_Error result = storeEntityValue(parser, s, next);
+                if (result != XML_ERROR_NONE)
+                    return result;
+            }
+            break;
+        case XML_ROLE_ENTITY_SYSTEM_ID:
+            if (declEntity) {
+                declEntity->systemId = poolStoreString(&dtd.pool, encoding,
+                                                       s + encoding->minBytesPerChar,
+                                                       next - encoding->minBytesPerChar);
+                if (!declEntity->systemId)
+                    return XML_ERROR_NO_MEMORY;
+                declEntity->base = dtd.base;
+                poolFinish(&dtd.pool);
+            }
+            break;
+        case XML_ROLE_ENTITY_NOTATION_NAME:
+            if (declEntity) {
+                declEntity->notation = poolStoreString(&dtd.pool, encoding, s, next);
+                if (!declEntity->notation)
+                    return XML_ERROR_NO_MEMORY;
+                poolFinish(&dtd.pool);
+                if (unparsedEntityDeclHandler) {
+                    eventPtr = eventEndPtr = s;
+                    unparsedEntityDeclHandler(handlerArg,
+                                              declEntity->name,
+                                              declEntity->base,
+                                              declEntity->systemId,
+                                              declEntity->publicId,
+                                              declEntity->notation);
+                }
+
+            }
+            break;
+        case XML_ROLE_GENERAL_ENTITY_NAME:
+            {
+                const XML_Char *name;
+                if (XmlPredefinedEntityName(encoding, s, next)) {
+                    declEntity = 0;
+                    break;
+                }
+                name = poolStoreString(&dtd.pool, encoding, s, next);
+                if (!name)
+                    return XML_ERROR_NO_MEMORY;
+                if (dtd.complete) {
+                    declEntity = (ENTITY *)lookup(&dtd.generalEntities, name, sizeof(ENTITY));
+                    if (!declEntity)
+                        return XML_ERROR_NO_MEMORY;
+                    if (declEntity->name != name) {
+                        poolDiscard(&dtd.pool);
+                        declEntity = 0;
+                    }
+                    else
+                        poolFinish(&dtd.pool);
+                }
+                else {
+                    poolDiscard(&dtd.pool);
+                    declEntity = 0;
+                }
+            }
+            break;
+        case XML_ROLE_PARAM_ENTITY_NAME:
+            declEntity = 0;
+            break;
+        case XML_ROLE_NOTATION_NAME:
+            declNotationPublicId = 0;
+            declNotationName = 0;
+            if (notationDeclHandler) {
+                declNotationName = poolStoreString(&tempPool, encoding, s, next);
+                if (!declNotationName)
+                    return XML_ERROR_NO_MEMORY;
+                poolFinish(&tempPool);
+            }
+            break;
+        case XML_ROLE_NOTATION_PUBLIC_ID:
+            if (!XmlIsPublicId(encoding, s, next, &eventPtr))
+                return XML_ERROR_SYNTAX;
+            if (declNotationName) {
+                XML_Char *tem = poolStoreString(&tempPool,
+                                                encoding,
+                                                s + encoding->minBytesPerChar,
+                                                next - encoding->minBytesPerChar);
+                if (!tem)
+                    return XML_ERROR_NO_MEMORY;
+                normalizePublicId(tem);
+                declNotationPublicId = tem;
+                poolFinish(&tempPool);
+            }
+            break;
+        case XML_ROLE_NOTATION_SYSTEM_ID:
+            if (declNotationName && notationDeclHandler) {
+                const XML_Char *systemId
+                = poolStoreString(&tempPool, encoding,
+                                  s + encoding->minBytesPerChar,
+                                  next - encoding->minBytesPerChar);
+                if (!systemId)
+                    return XML_ERROR_NO_MEMORY;
+                eventPtr = eventEndPtr = s;
+                notationDeclHandler(handlerArg,
+                                    declNotationName,
+                                    dtd.base,
+                                    systemId,
+                                    declNotationPublicId);
+            }
+            poolClear(&tempPool);
+            break;
+        case XML_ROLE_NOTATION_NO_SYSTEM_ID:
+            if (declNotationPublicId && notationDeclHandler) {
+                eventPtr = eventEndPtr = s;
+                notationDeclHandler(handlerArg,
+                                    declNotationName,
+                                    dtd.base,
+                                    0,
+                                    declNotationPublicId);
+            }
+            poolClear(&tempPool);
+            break;
+        case XML_ROLE_ERROR:
+            eventPtr = s;
+            switch (tok) {
+            case XML_TOK_PARAM_ENTITY_REF:
+                return XML_ERROR_PARAM_ENTITY_REF;
+            case XML_TOK_XML_DECL:
+                return XML_ERROR_MISPLACED_XML_PI;
+            default:
+                return XML_ERROR_SYNTAX;
+            }
+        case XML_ROLE_GROUP_OPEN:
+            if (prologState.level >= groupSize) {
+                if (groupSize)
+                    groupConnector = realloc(groupConnector, groupSize *= 2);
+                else
+                    groupConnector = malloc(groupSize = 32);
+                if (!groupConnector)
+                    return XML_ERROR_NO_MEMORY;
+            }
+            groupConnector[prologState.level] = 0;
+            break;
+        case XML_ROLE_GROUP_SEQUENCE:
+            if (groupConnector[prologState.level] == '|') {
+                eventPtr = s;
+                return XML_ERROR_SYNTAX;
+            }
+            groupConnector[prologState.level] = ',';
+            break;
+        case XML_ROLE_GROUP_CHOICE:
+            if (groupConnector[prologState.level] == ',') {
+                eventPtr = s;
+                return XML_ERROR_SYNTAX;
+            }
+            groupConnector[prologState.level] = '|';
+            break;
+        case XML_ROLE_PARAM_ENTITY_REF:
+            if (!dtd.standalone
+                    && notStandaloneHandler
+                    && !notStandaloneHandler(handlerArg))
+                return XML_ERROR_NOT_STANDALONE;
+            dtd.complete = 0;
+            break;
+        case XML_ROLE_NONE:
+            switch (tok) {
+            case XML_TOK_PI:
+                eventPtr = s;
+                eventEndPtr = next;
+                if (!reportProcessingInstruction(parser, encoding, s, next))
+                    return XML_ERROR_NO_MEMORY;
+                break;
+            case XML_TOK_COMMENT:
+                eventPtr = s;
+                eventEndPtr = next;
+                if (!reportComment(parser, encoding, s, next))
+                    return XML_ERROR_NO_MEMORY;
+                break;
+            }
+            break;
+        }
+        if (defaultHandler) {
+            switch (tok) {
+            case XML_TOK_PI:
+            case XML_TOK_COMMENT:
+            case XML_TOK_BOM:
+            case XML_TOK_XML_DECL:
+                break;
+            default:
+                eventPtr = s;
+                eventEndPtr = next;
+                reportDefault(parser, encoding, s, next);
+            }
+        }
+        s = next;
+    }
+    /* not reached */
+}
+
+static
+enum XML_Error epilogProcessor(XML_Parser parser,
+                               const char *s,
+                               const char *end,
+                               const char **nextPtr)
+{
+    processor = epilogProcessor;
+    eventPtr = s;
+    for (;;) {
+        const char *next;
+        int tok = XmlPrologTok(encoding, s, end, &next);
+        eventEndPtr = next;
+        switch (tok) {
+        case XML_TOK_TRAILING_CR:
+            if (defaultHandler) {
+                eventEndPtr = end;
+                reportDefault(parser, encoding, s, end);
+            }
+            /* fall through */
+        case XML_TOK_NONE:
+            if (nextPtr)
+                *nextPtr = end;
+            return XML_ERROR_NONE;
+        case XML_TOK_PROLOG_S:
+            if (defaultHandler)
+                reportDefault(parser, encoding, s, next);
+            break;
+        case XML_TOK_PI:
+            if (!reportProcessingInstruction(parser, encoding, s, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_TOK_COMMENT:
+            if (!reportComment(parser, encoding, s, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_TOK_INVALID:
+            eventPtr = next;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_PARTIAL:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_UNCLOSED_TOKEN;
+        case XML_TOK_PARTIAL_CHAR:
+            if (nextPtr) {
+                *nextPtr = s;
+                return XML_ERROR_NONE;
+            }
+            return XML_ERROR_PARTIAL_CHAR;
+        default:
+            return XML_ERROR_JUNK_AFTER_DOC_ELEMENT;
+        }
+        eventPtr = s = next;
+    }
+}
+
+static enum XML_Error
+storeAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+                    const char *ptr, const char *end,
+                    STRING_POOL *pool)
+{
+    enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
+    if (result)
+        return result;
+    if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
+        poolChop(pool);
+    if (!poolAppendChar(pool, XML_T('\0')))
+        return XML_ERROR_NO_MEMORY;
+    return XML_ERROR_NONE;
+}
+
+static enum XML_Error
+appendAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata,
+                     const char *ptr, const char *end,
+                     STRING_POOL *pool)
+{
+    const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+    for (;;) {
+        const char *next;
+        int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+        switch (tok) {
+        case XML_TOK_NONE:
+            return XML_ERROR_NONE;
+        case XML_TOK_INVALID:
+            if (enc == encoding)
+                eventPtr = next;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_PARTIAL:
+            if (enc == encoding)
+                eventPtr = ptr;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_CHAR_REF:
+            {
+                XML_Char buf[XML_ENCODE_MAX];
+                int i;
+                int n = XmlCharRefNumber(enc, ptr);
+                if (n < 0) {
+                    if (enc == encoding)
+                        eventPtr = ptr;
+                    return XML_ERROR_BAD_CHAR_REF;
+                }
+                if (!isCdata
+                        && n == 0x20 /* space */
+                        && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+                    break;
+                n = XmlEncode(n, (ICHAR *)buf);
+                if (!n) {
+                    if (enc == encoding)
+                        eventPtr = ptr;
+                    return XML_ERROR_BAD_CHAR_REF;
+                }
+                for (i = 0; i < n; i++) {
+                    if (!poolAppendChar(pool, buf[i]))
+                        return XML_ERROR_NO_MEMORY;
+                }
+            }
+            break;
+        case XML_TOK_DATA_CHARS:
+            if (!poolAppend(pool, enc, ptr, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+            break;
+        case XML_TOK_TRAILING_CR:
+            next = ptr + enc->minBytesPerChar;
+            /* fall through */
+        case XML_TOK_ATTRIBUTE_VALUE_S:
+        case XML_TOK_DATA_NEWLINE:
+            if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20))
+                break;
+            if (!poolAppendChar(pool, 0x20))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_TOK_ENTITY_REF:
+            {
+                const XML_Char *name;
+                ENTITY *entity;
+                XML_Char ch = XmlPredefinedEntityName(enc,
+                                                      ptr + enc->minBytesPerChar,
+                                                      next - enc->minBytesPerChar);
+                if (ch) {
+                    if (!poolAppendChar(pool, ch))
+                        return XML_ERROR_NO_MEMORY;
+                    break;
+                }
+                name = poolStoreString(&temp2Pool, enc,
+                                       ptr + enc->minBytesPerChar,
+                                       next - enc->minBytesPerChar);
+                if (!name)
+                    return XML_ERROR_NO_MEMORY;
+                entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0);
+                poolDiscard(&temp2Pool);
+                if (!entity) {
+                    if (dtd.complete) {
+                        if (enc == encoding)
+                            eventPtr = ptr;
+                        return XML_ERROR_UNDEFINED_ENTITY;
+                    }
+                }
+                else if (entity->open) {
+                    if (enc == encoding)
+                        eventPtr = ptr;
+                    return XML_ERROR_RECURSIVE_ENTITY_REF;
+                }
+                else if (entity->notation) {
+                    if (enc == encoding)
+                        eventPtr = ptr;
+                    return XML_ERROR_BINARY_ENTITY_REF;
+                }
+                else if (!entity->textPtr) {
+                    if (enc == encoding)
+                        eventPtr = ptr;
+                    return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF;
+                }
+                else {
+                    enum XML_Error result;
+                    const XML_Char *textEnd = entity->textPtr + entity->textLen;
+                    entity->open = 1;
+                    result = appendAttributeValue(parser, internalEnc, isCdata, (char *)entity->textPtr, (char *)textEnd, pool);
+                    entity->open = 0;
+                    if (result)
+                        return result;
+                }
+            }
+            break;
+        default:
+            abort();
+        }
+        ptr = next;
+    }
+    /* not reached */
+}
+
+static
+enum XML_Error storeEntityValue(XML_Parser parser,
+                                const char *entityTextPtr,
+                                const char *entityTextEnd)
+{
+    const ENCODING *internalEnc;
+    STRING_POOL *pool = &(dtd.pool);
+    entityTextPtr += encoding->minBytesPerChar;
+    entityTextEnd -= encoding->minBytesPerChar;
+    internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding();
+    for (;;) {
+        const char *next;
+        int tok = XmlEntityValueTok(encoding, entityTextPtr, entityTextEnd, &next);
+        switch (tok) {
+        case XML_TOK_PARAM_ENTITY_REF:
+            eventPtr = entityTextPtr;
+            return XML_ERROR_SYNTAX;
+        case XML_TOK_NONE:
+            if (declEntity) {
+                declEntity->textPtr = pool->start;
+                declEntity->textLen = pool->ptr - pool->start;
+                poolFinish(pool);
+            }
+            else
+                poolDiscard(pool);
+            return XML_ERROR_NONE;
+        case XML_TOK_ENTITY_REF:
+        case XML_TOK_DATA_CHARS:
+            if (!poolAppend(pool, encoding, entityTextPtr, next))
+                return XML_ERROR_NO_MEMORY;
+            break;
+        case XML_TOK_TRAILING_CR:
+            next = entityTextPtr + encoding->minBytesPerChar;
+            /* fall through */
+        case XML_TOK_DATA_NEWLINE:
+            if (pool->end == pool->ptr && !poolGrow(pool))
+                return XML_ERROR_NO_MEMORY;
+            *(pool->ptr)++ = 0xA;
+            break;
+        case XML_TOK_CHAR_REF:
+            {
+                XML_Char buf[XML_ENCODE_MAX];
+                int i;
+                int n = XmlCharRefNumber(encoding, entityTextPtr);
+                if (n < 0) {
+                    eventPtr = entityTextPtr;
+                    return XML_ERROR_BAD_CHAR_REF;
+                }
+                n = XmlEncode(n, (ICHAR *)buf);
+                if (!n) {
+                    eventPtr = entityTextPtr;
+                    return XML_ERROR_BAD_CHAR_REF;
+                }
+                for (i = 0; i < n; i++) {
+                    if (pool->end == pool->ptr && !poolGrow(pool))
+                        return XML_ERROR_NO_MEMORY;
+                    *(pool->ptr)++ = buf[i];
+                }
+            }
+            break;
+        case XML_TOK_PARTIAL:
+            eventPtr = entityTextPtr;
+            return XML_ERROR_INVALID_TOKEN;
+        case XML_TOK_INVALID:
+            eventPtr = next;
+            return XML_ERROR_INVALID_TOKEN;
+        default:
+            abort();
+        }
+        entityTextPtr = next;
+    }
+    /* not reached */
+}
+
+static void
+normalizeLines(XML_Char *s)
+{
+    XML_Char *p;
+    for (;; s++) {
+        if (*s == XML_T('\0'))
+            return;
+        if (*s == 0xD)
+            break;
+    }
+    p = s;
+    do {
+        if (*s == 0xD) {
+            *p++ = 0xA;
+            if (*++s == 0xA)
+                s++;
+        }
+        else
+            *p++ = *s++;
+    } while (*s);
+    *p = XML_T('\0');
+}
+
+static int
+reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+    const XML_Char *target;
+    XML_Char *data;
+    const char *tem;
+    if (!processingInstructionHandler) {
+        if (defaultHandler)
+            reportDefault(parser, enc, start, end);
+        return 1;
+    }
+    start += enc->minBytesPerChar * 2;
+    tem = start + XmlNameLength(enc, start);
+    target = poolStoreString(&tempPool, enc, start, tem);
+    if (!target)
+        return 0;
+    poolFinish(&tempPool);
+    data = poolStoreString(&tempPool, enc,
+                           XmlSkipS(enc, tem),
+                           end - enc->minBytesPerChar*2);
+    if (!data)
+        return 0;
+    normalizeLines(data);
+    processingInstructionHandler(handlerArg, target, data);
+    poolClear(&tempPool);
+    return 1;
+}
+
+static int
+reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+    XML_Char *data;
+    if (!commentHandler) {
+        if (defaultHandler)
+            reportDefault(parser, enc, start, end);
+        return 1;
+    }
+    data = poolStoreString(&tempPool,
+                           enc,
+                           start + enc->minBytesPerChar * 4,
+                           end - enc->minBytesPerChar * 3);
+    if (!data)
+        return 0;
+    normalizeLines(data);
+    commentHandler(handlerArg, data);
+    poolClear(&tempPool);
+    return 1;
+}
+
+static void
+reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, const char *end)
+{
+    if (MUST_CONVERT(enc, s)) {
+        const char **eventPP;
+        const char **eventEndPP;
+        if (enc == encoding) {
+            eventPP = &eventPtr;
+            eventEndPP = &eventEndPtr;
+        }
+        else {
+            eventPP = &(openInternalEntities->internalEventPtr);
+            eventEndPP = &(openInternalEntities->internalEventEndPtr);
+        }
+        do {
+            ICHAR *dataPtr = (ICHAR *)dataBuf;
+            XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd);
+            *eventEndPP = s;
+            defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf);
+            *eventPP = s;
+        } while (s != end);
+    }
+    else
+        defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s);
+}
+
+
+static int
+defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, const XML_Char *value)
+{
+    DEFAULT_ATTRIBUTE *att;
+    if (type->nDefaultAtts == type->allocDefaultAtts) {
+        if (type->allocDefaultAtts == 0) {
+            type->allocDefaultAtts = 8;
+            type->defaultAtts = malloc(type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+        }
+        else {
+            type->allocDefaultAtts *= 2;
+            type->defaultAtts = realloc(type->defaultAtts,
+                                        type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE));
+        }
+        if (!type->defaultAtts)
+            return 0;
+    }
+    att = type->defaultAtts + type->nDefaultAtts;
+    att->id = attId;
+    att->value = value;
+    att->isCdata = isCdata;
+    if (!isCdata)
+        attId->maybeTokenized = 1;
+    type->nDefaultAtts += 1;
+    return 1;
+}
+
+static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType)
+{
+    const XML_Char *name;
+    for (name = elementType->name; *name; name++) {
+        if (*name == XML_T(':')) {
+            PREFIX *prefix;
+            const XML_Char *s;
+            for (s = elementType->name; s != name; s++) {
+                if (!poolAppendChar(&dtd.pool, *s))
+                    return 0;
+            }
+            if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+                return 0;
+            prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+            if (!prefix)
+                return 0;
+            if (prefix->name == poolStart(&dtd.pool))
+                poolFinish(&dtd.pool);
+            else
+                poolDiscard(&dtd.pool);
+            elementType->prefix = prefix;
+
+        }
+    }
+    return 1;
+}
+
+static ATTRIBUTE_ID *
+getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end)
+{
+    ATTRIBUTE_ID *id;
+    const XML_Char *name;
+    if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+        return 0;
+    name = poolStoreString(&dtd.pool, enc, start, end);
+    if (!name)
+        return 0;
+    ++name;
+    id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID));
+    if (!id)
+        return 0;
+    if (id->name != name)
+        poolDiscard(&dtd.pool);
+    else {
+        poolFinish(&dtd.pool);
+        if (!ns)
+            ;
+        else if (name[0] == 'x'
+                 && name[1] == 'm'
+                 && name[2] == 'l'
+                 && name[3] == 'n'
+                 && name[4] == 's'
+                 && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) {
+            if (name[5] == '\0')
+                id->prefix = &dtd.defaultPrefix;
+            else
+                id->prefix = (PREFIX *)lookup(&dtd.prefixes, name + 6, sizeof(PREFIX));
+            id->xmlns = 1;
+        }
+        else {
+            int i;
+            for (i = 0; name[i]; i++) {
+                if (name[i] == XML_T(':')) {
+                    int j;
+                    for (j = 0; j < i; j++) {
+                        if (!poolAppendChar(&dtd.pool, name[j]))
+                            return 0;
+                    }
+                    if (!poolAppendChar(&dtd.pool, XML_T('\0')))
+                        return 0;
+                    id->prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX));
+                    if (id->prefix->name == poolStart(&dtd.pool))
+                        poolFinish(&dtd.pool);
+                    else
+                        poolDiscard(&dtd.pool);
+                    break;
+                }
+            }
+        }
+    }
+    return id;
+}
+
+#define CONTEXT_SEP XML_T('\f')
+
+static
+const XML_Char *getContext(XML_Parser parser)
+{
+    HASH_TABLE_ITER iter;
+    int needSep = 0;
+
+    if (dtd.defaultPrefix.binding) {
+        int i;
+        int len;
+        if (!poolAppendChar(&tempPool, XML_T('=')))
+            return 0;
+        len = dtd.defaultPrefix.binding->uriLen;
+        if (namespaceSeparator != XML_T('\0'))
+            len--;
+        for (i = 0; i < len; i++)
+            if (!poolAppendChar(&tempPool, dtd.defaultPrefix.binding->uri[i]))
+                return 0;
+        needSep = 1;
+    }
+
+    hashTableIterInit(&iter, &(dtd.prefixes));
+    for (;;) {
+        int i;
+        int len;
+        const XML_Char *s;
+        PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter);
+        if (!prefix)
+            break;
+        if (!prefix->binding)
+            continue;
+        if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+            return 0;
+        for (s = prefix->name; *s; s++)
+            if (!poolAppendChar(&tempPool, *s))
+                return 0;
+        if (!poolAppendChar(&tempPool, XML_T('=')))
+            return 0;
+        len = prefix->binding->uriLen;
+        if (namespaceSeparator != XML_T('\0'))
+            len--;
+        for (i = 0; i < len; i++)
+            if (!poolAppendChar(&tempPool, prefix->binding->uri[i]))
+                return 0;
+        needSep = 1;
+    }
+
+
+    hashTableIterInit(&iter, &(dtd.generalEntities));
+    for (;;) {
+        const XML_Char *s;
+        ENTITY *e = (ENTITY *)hashTableIterNext(&iter);
+        if (!e)
+            break;
+        if (!e->open)
+            continue;
+        if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP))
+            return 0;
+        for (s = e->name; *s; s++)
+            if (!poolAppendChar(&tempPool, *s))
+                return 0;
+        needSep = 1;
+    }
+
+    if (!poolAppendChar(&tempPool, XML_T('\0')))
+        return 0;
+    return tempPool.start;
+}
+
+static
+void normalizePublicId(XML_Char *publicId)
+{
+    XML_Char *p = publicId;
+    XML_Char *s;
+    for (s = publicId; *s; s++) {
+        switch (*s) {
+        case 0x20:
+        case 0xD:
+        case 0xA:
+            if (p != publicId && p[-1] != 0x20)
+                *p++ = 0x20;
+            break;
+        default:
+            *p++ = *s;
+        }
+    }
+    if (p != publicId && p[-1] == 0x20)
+        --p;
+    *p = XML_T('\0');
+}
+
+static int dtdInit(DTD *p)
+{
+    poolInit(&(p->pool));
+    hashTableInit(&(p->generalEntities));
+    hashTableInit(&(p->elementTypes));
+    hashTableInit(&(p->attributeIds));
+    hashTableInit(&(p->prefixes));
+    p->complete = 1;
+    p->standalone = 0;
+    p->base = 0;
+    p->defaultPrefix.name = 0;
+    p->defaultPrefix.binding = 0;
+    return 1;
+}
+
+static void dtdDestroy(DTD *p)
+{
+    HASH_TABLE_ITER iter;
+    hashTableIterInit(&iter, &(p->elementTypes));
+    for (;;) {
+        ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter);
+        if (!e)
+            break;
+        if (e->allocDefaultAtts != 0)
+            g_free(e->defaultAtts);
+    }
+    hashTableDestroy(&(p->generalEntities));
+    hashTableDestroy(&(p->elementTypes));
+    hashTableDestroy(&(p->attributeIds));
+    hashTableDestroy(&(p->prefixes));
+    poolDestroy(&(p->pool));
+}
+
+static
+void poolInit(STRING_POOL *pool)
+{
+    pool->blocks = 0;
+    pool->freeBlocks = 0;
+    pool->start = 0;
+    pool->ptr = 0;
+    pool->end = 0;
+}
+
+static
+void poolClear(STRING_POOL *pool)
+{
+    if (!pool->freeBlocks)
+        pool->freeBlocks = pool->blocks;
+    else {
+        BLOCK *p = pool->blocks;
+        while (p) {
+            BLOCK *tem = p->next;
+            p->next = pool->freeBlocks;
+            pool->freeBlocks = p;
+            p = tem;
+        }
+    }
+    pool->blocks = 0;
+    pool->start = 0;
+    pool->ptr = 0;
+    pool->end = 0;
+}
+
+static
+void poolDestroy(STRING_POOL *pool)
+{
+    BLOCK *p = pool->blocks;
+    while (p) {
+        BLOCK *tem = p->next;
+        g_free(p);
+        p = tem;
+    }
+    pool->blocks = 0;
+    p = pool->freeBlocks;
+    while (p) {
+        BLOCK *tem = p->next;
+        g_free(p);
+        p = tem;
+    }
+    pool->freeBlocks = 0;
+    pool->ptr = 0;
+    pool->start = 0;
+    pool->end = 0;
+}
+
+static
+XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+                     const char *ptr, const char *end)
+{
+    if (!pool->ptr && !poolGrow(pool))
+        return 0;
+    for (;;) {
+        XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end);
+        if (ptr == end)
+            break;
+        if (!poolGrow(pool))
+            return 0;
+    }
+    return pool->start;
+}
+
+static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s)
+{
+    do {
+        if (!poolAppendChar(pool, *s))
+            return 0;
+    } while (*s++);
+    s = pool->start;
+    poolFinish(pool);
+    return s;
+}
+
+static
+XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+                          const char *ptr, const char *end)
+{
+    if (!poolAppend(pool, enc, ptr, end))
+        return 0;
+    if (pool->ptr == pool->end && !poolGrow(pool))
+        return 0;
+    *(pool->ptr)++ = 0;
+    return pool->start;
+}
+
+static
+int poolGrow(STRING_POOL *pool)
+{
+    if (pool->freeBlocks) {
+        if (pool->start == 0) {
+            pool->blocks = pool->freeBlocks;
+            pool->freeBlocks = pool->freeBlocks->next;
+            pool->blocks->next = 0;
+            pool->start = pool->blocks->s;
+            pool->end = pool->start + pool->blocks->size;
+            pool->ptr = pool->start;
+            return 1;
+        }
+        if (pool->end - pool->start < pool->freeBlocks->size) {
+            BLOCK *tem = pool->freeBlocks->next;
+            pool->freeBlocks->next = pool->blocks;
+            pool->blocks = pool->freeBlocks;
+            pool->freeBlocks = tem;
+            memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char));
+            pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+            pool->start = pool->blocks->s;
+            pool->end = pool->start + pool->blocks->size;
+            return 1;
+        }
+    }
+    if (pool->blocks && pool->start == pool->blocks->s) {
+        int blockSize = (pool->end - pool->start)*2;
+        pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+        if (!pool->blocks)
+            return 0;
+        pool->blocks->size = blockSize;
+        pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+        pool->start = pool->blocks->s;
+        pool->end = pool->start + blockSize;
+    }
+    else {
+        BLOCK *tem;
+        int blockSize = pool->end - pool->start;
+        if (blockSize < INIT_BLOCK_SIZE)
+            blockSize = INIT_BLOCK_SIZE;
+        else
+            blockSize *= 2;
+        tem = malloc(offsetof(BLOCK, s) + blockSize * sizeof(XML_Char));
+        if (!tem)
+            return 0;
+        tem->size = blockSize;
+        tem->next = pool->blocks;
+        pool->blocks = tem;
+        memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char));
+        pool->ptr = tem->s + (pool->ptr - pool->start);
+        pool->start = tem->s;
+        pool->end = tem->s + blockSize;
+    }
+    return 1;
+}
Index: protocols/jabber/xmlparse.h
===================================================================
--- protocols/jabber/xmlparse.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlparse.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,476 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlParse_INCLUDED
+#define XmlParse_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLPARSEAPI
+#define XMLPARSEAPI /* as nothing */
+#endif
+
+typedef void *XML_Parser;
+
+#ifdef XML_UNICODE_WCHAR_T
+
+/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t
+uses Unicode. */
+/* Information is UTF-16 encoded as wchar_ts */
+
+#ifndef XML_UNICODE
+#define XML_UNICODE
+#endif
+
+#include <stddef.h>
+typedef wchar_t XML_Char;
+typedef wchar_t XML_LChar;
+
+#else /* not XML_UNICODE_WCHAR_T */
+
+#ifdef XML_UNICODE
+
+/* Information is UTF-16 encoded as unsigned shorts */
+typedef unsigned short XML_Char;
+typedef char XML_LChar;
+
+#else /* not XML_UNICODE */
+
+/* Information is UTF-8 encoded. */
+typedef char XML_Char;
+typedef char XML_LChar;
+
+#endif /* not XML_UNICODE */
+
+#endif /* not XML_UNICODE_WCHAR_T */
+
+
+/* Constructs a new parser; encoding is the encoding specified by the external
+protocol or null if there is none specified. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreate(const XML_Char *encoding);
+
+/* Constructs a new parser and namespace processor.  Element type names
+and attribute names that belong to a namespace will be expanded;
+unprefixed attribute names are never expanded; unprefixed element type
+names are expanded only if there is a default namespace. The expanded
+name is the concatenation of the namespace URI, the namespace separator character,
+and the local part of the name.  If the namespace separator is '\0' then
+the namespace URI and the local part will be concatenated without any
+separator.  When a namespace is not declared, the name and prefix will be
+passed through without expansion. */
+
+XML_Parser XMLPARSEAPI
+XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator);
+
+
+/* atts is array of name/value pairs, terminated by 0;
+   names and values are 0 terminated. */
+
+typedef void (*XML_StartElementHandler)(void *userData,
+					const XML_Char *name,
+					const XML_Char **atts);
+
+typedef void (*XML_EndElementHandler)(void *userData,
+				      const XML_Char *name);
+
+/* s is not 0 terminated. */
+typedef void (*XML_CharacterDataHandler)(void *userData,
+					 const XML_Char *s,
+					 int len);
+
+/* target and data are 0 terminated */
+typedef void (*XML_ProcessingInstructionHandler)(void *userData,
+						 const XML_Char *target,
+						 const XML_Char *data);
+
+/* data is 0 terminated */
+typedef void (*XML_CommentHandler)(void *userData, const XML_Char *data);
+
+typedef void (*XML_StartCdataSectionHandler)(void *userData);
+typedef void (*XML_EndCdataSectionHandler)(void *userData);
+
+/* This is called for any characters in the XML document for
+which there is no applicable handler.  This includes both
+characters that are part of markup which is of a kind that is
+not reported (comments, markup declarations), or characters
+that are part of a construct which could be reported but
+for which no handler has been supplied. The characters are passed
+exactly as they were in the XML document except that
+they will be encoded in UTF-8.  Line boundaries are not normalized.
+Note that a byte order mark character is not passed to the default handler.
+There are no guarantees about how characters are divided between calls
+to the default handler: for example, a comment might be split between
+multiple calls. */
+
+typedef void (*XML_DefaultHandler)(void *userData,
+				   const XML_Char *s,
+				   int len);
+
+/* This is called for a declaration of an unparsed (NDATA)
+entity.  The base argument is whatever was set by XML_SetBase.
+The entityName, systemId and notationName arguments will never be null.
+The other arguments may be. */
+
+typedef void (*XML_UnparsedEntityDeclHandler)(void *userData,
+					      const XML_Char *entityName,
+					      const XML_Char *base,
+					      const XML_Char *systemId,
+					      const XML_Char *publicId,
+					      const XML_Char *notationName);
+
+/* This is called for a declaration of notation.
+The base argument is whatever was set by XML_SetBase.
+The notationName will never be null.  The other arguments can be. */
+
+typedef void (*XML_NotationDeclHandler)(void *userData,
+					const XML_Char *notationName,
+					const XML_Char *base,
+					const XML_Char *systemId,
+					const XML_Char *publicId);
+
+/* When namespace processing is enabled, these are called once for
+each namespace declaration. The call to the start and end element
+handlers occur between the calls to the start and end namespace
+declaration handlers. For an xmlns attribute, prefix will be null.
+For an xmlns="" attribute, uri will be null. */
+
+typedef void (*XML_StartNamespaceDeclHandler)(void *userData,
+					      const XML_Char *prefix,
+					      const XML_Char *uri);
+
+typedef void (*XML_EndNamespaceDeclHandler)(void *userData,
+					    const XML_Char *prefix);
+
+/* This is called if the document is not standalone (it has an
+external subset or a reference to a parameter entity, but does not
+have standalone="yes"). If this handler returns 0, then processing
+will not continue, and the parser will return a
+XML_ERROR_NOT_STANDALONE error. */
+
+typedef int (*XML_NotStandaloneHandler)(void *userData);
+
+/* This is called for a reference to an external parsed general entity.
+The referenced entity is not automatically parsed.
+The application can parse it immediately or later using
+XML_ExternalEntityParserCreate.
+The parser argument is the parser parsing the entity containing the reference;
+it can be passed as the parser argument to XML_ExternalEntityParserCreate.
+The systemId argument is the system identifier as specified in the entity declaration;
+it will not be null.
+The base argument is the system identifier that should be used as the base for
+resolving systemId if systemId was relative; this is set by XML_SetBase;
+it may be null.
+The publicId argument is the public identifier as specified in the entity declaration,
+or null if none was specified; the whitespace in the public identifier
+will have been normalized as required by the XML spec.
+The context argument specifies the parsing context in the format
+expected by the context argument to
+XML_ExternalEntityParserCreate; context is valid only until the handler
+returns, so if the referenced entity is to be parsed later, it must be copied.
+The handler should return 0 if processing should not continue because of
+a fatal error in the handling of the external entity.
+In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING
+error.
+Note that unlike other handlers the first argument is the parser, not userData. */
+
+typedef int (*XML_ExternalEntityRefHandler)(XML_Parser parser,
+					    const XML_Char *context,
+					    const XML_Char *base,
+					    const XML_Char *systemId,
+					    const XML_Char *publicId);
+
+/* This structure is filled in by the XML_UnknownEncodingHandler
+to provide information to the parser about encodings that are unknown
+to the parser.
+The map[b] member gives information about byte sequences
+whose first byte is b.
+If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c.
+If map[b] is -1, then the byte sequence is malformed.
+If map[b] is -n, where n >= 2, then b is the first byte of an n-byte
+sequence that encodes a single Unicode scalar value.
+The data member will be passed as the first argument to the convert function.
+The convert function is used to convert multibyte sequences;
+s will point to a n-byte sequence where map[(unsigned char)*s] == -n.
+The convert function must return the Unicode scalar value
+represented by this byte sequence or -1 if the byte sequence is malformed.
+The convert function may be null if the encoding is a single-byte encoding,
+that is if map[b] >= -1 for all bytes b.
+When the parser is finished with the encoding, then if release is not null,
+it will call release passing it the data member;
+once release has been called, the convert function will not be called again.
+
+Expat places certain restrictions on the encodings that are supported
+using this mechanism.
+
+1. Every ASCII character that can appear in a well-formed XML document,
+other than the characters
+
+  $@\^`{}~
+
+must be represented by a single byte, and that byte must be the
+same byte that represents that character in ASCII.
+
+2. No character may require more than 4 bytes to encode.
+
+3. All characters encoded must have Unicode scalar values <= 0xFFFF,
+(ie characters that would be encoded by surrogates in UTF-16
+are  not allowed).  Note that this restriction doesn't apply to
+the built-in support for UTF-8 and UTF-16.
+
+4. No Unicode character may be encoded by more than one distinct sequence
+of bytes. */
+
+typedef struct {
+  int map[256];
+  void *data;
+  int (*convert)(void *data, const char *s);
+  void (*release)(void *data);
+} XML_Encoding;
+
+/* This is called for an encoding that is unknown to the parser.
+The encodingHandlerData argument is that which was passed as the
+second argument to XML_SetUnknownEncodingHandler.
+The name argument gives the name of the encoding as specified in
+the encoding declaration.
+If the callback can provide information about the encoding,
+it must fill in the XML_Encoding structure, and return 1.
+Otherwise it must return 0.
+If info does not describe a suitable encoding,
+then the parser will return an XML_UNKNOWN_ENCODING error. */
+
+typedef int (*XML_UnknownEncodingHandler)(void *encodingHandlerData,
+					  const XML_Char *name,
+					  XML_Encoding *info);
+
+void XMLPARSEAPI
+XML_SetElementHandler(XML_Parser parser,
+		      XML_StartElementHandler start,
+		      XML_EndElementHandler end);
+
+void XMLPARSEAPI
+XML_SetCharacterDataHandler(XML_Parser parser,
+			    XML_CharacterDataHandler handler);
+
+void XMLPARSEAPI
+XML_SetProcessingInstructionHandler(XML_Parser parser,
+				    XML_ProcessingInstructionHandler handler);
+void XMLPARSEAPI
+XML_SetCommentHandler(XML_Parser parser,
+                      XML_CommentHandler handler);
+
+void XMLPARSEAPI
+XML_SetCdataSectionHandler(XML_Parser parser,
+			   XML_StartCdataSectionHandler start,
+			   XML_EndCdataSectionHandler end);
+
+/* This sets the default handler and also inhibits expansion of internal entities.
+The entity reference will be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandler(XML_Parser parser,
+		      XML_DefaultHandler handler);
+
+/* This sets the default handler but does not inhibit expansion of internal entities.
+The entity reference will not be passed to the default handler. */
+
+void XMLPARSEAPI
+XML_SetDefaultHandlerExpand(XML_Parser parser,
+		            XML_DefaultHandler handler);
+
+void XMLPARSEAPI
+XML_SetUnparsedEntityDeclHandler(XML_Parser parser,
+				 XML_UnparsedEntityDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNotationDeclHandler(XML_Parser parser,
+			   XML_NotationDeclHandler handler);
+
+void XMLPARSEAPI
+XML_SetNamespaceDeclHandler(XML_Parser parser,
+			    XML_StartNamespaceDeclHandler start,
+			    XML_EndNamespaceDeclHandler end);
+
+void XMLPARSEAPI
+XML_SetNotStandaloneHandler(XML_Parser parser,
+			    XML_NotStandaloneHandler handler);
+
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandler(XML_Parser parser,
+				XML_ExternalEntityRefHandler handler);
+
+/* If a non-null value for arg is specified here, then it will be passed
+as the first argument to the external entity ref handler instead
+of the parser object. */
+void XMLPARSEAPI
+XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg);
+
+void XMLPARSEAPI
+XML_SetUnknownEncodingHandler(XML_Parser parser,
+			      XML_UnknownEncodingHandler handler,
+			      void *encodingHandlerData);
+
+/* This can be called within a handler for a start element, end element,
+processing instruction or character data.  It causes the corresponding
+markup to be passed to the default handler. */
+void XMLPARSEAPI XML_DefaultCurrent(XML_Parser parser);
+
+/* This value is passed as the userData argument to callbacks. */
+void XMLPARSEAPI
+XML_SetUserData(XML_Parser parser, void *userData);
+
+/* Returns the last value set by XML_SetUserData or null. */
+#define XML_GetUserData(parser) (*(void **)(parser))
+
+/* This is equivalent to supplying an encoding argument
+to XML_CreateParser. It must not be called after XML_Parse
+or XML_ParseBuffer. */
+
+int XMLPARSEAPI
+XML_SetEncoding(XML_Parser parser, const XML_Char *encoding);
+
+/* If this function is called, then the parser will be passed
+as the first argument to callbacks instead of userData.
+The userData will still be accessible using XML_GetUserData. */
+
+void XMLPARSEAPI
+XML_UseParserAsHandlerArg(XML_Parser parser);
+
+/* Sets the base to be used for resolving relative URIs in system identifiers in
+declarations.  Resolving relative identifiers is left to the application:
+this value will be passed through as the base argument to the
+XML_ExternalEntityRefHandler, XML_NotationDeclHandler
+and XML_UnparsedEntityDeclHandler. The base argument will be copied.
+Returns zero if out of memory, non-zero otherwise. */
+
+int XMLPARSEAPI
+XML_SetBase(XML_Parser parser, const XML_Char *base);
+
+const XML_Char XMLPARSEAPI *
+XML_GetBase(XML_Parser parser);
+
+/* Returns the number of the attributes passed in last call to the
+XML_StartElementHandler that were specified in the start-tag rather
+than defaulted. */
+
+int XMLPARSEAPI XML_GetSpecifiedAttributeCount(XML_Parser parser);
+
+/* Parses some input. Returns 0 if a fatal error is detected.
+The last call to XML_Parse must have isFinal true;
+len may be zero for this call (or any other). */
+int XMLPARSEAPI
+XML_Parse(XML_Parser parser, const char *s, int len, int isFinal);
+
+/* Creates an XML_Parser object that can parse an external general entity;
+context is a '\0'-terminated string specifying the parse context;
+encoding is a '\0'-terminated string giving the name of the externally specified encoding,
+or null if there is no externally specified encoding.
+The context string consists of a sequence of tokens separated by formfeeds (\f);
+a token consisting of a name specifies that the general entity of the name
+is open; a token of the form prefix=uri specifies the namespace for a particular
+prefix; a token of the form =uri specifies the default namespace.
+This can be called at any point after the first call to an ExternalEntityRefHandler
+so longer as the parser has not yet been freed.
+The new parser is completely independent and may safely be used in a separate thread.
+The handlers and userData are initialized from the parser argument.
+Returns 0 if out of memory.  Otherwise returns a new XML_Parser object. */
+XML_Parser XMLPARSEAPI
+XML_ExternalEntityParserCreate(XML_Parser parser,
+			       const XML_Char *context,
+			       const XML_Char *encoding);
+
+enum XML_Error {
+  XML_ERROR_NONE,
+  XML_ERROR_NO_MEMORY,
+  XML_ERROR_SYNTAX,
+  XML_ERROR_NO_ELEMENTS,
+  XML_ERROR_INVALID_TOKEN,
+  XML_ERROR_UNCLOSED_TOKEN,
+  XML_ERROR_PARTIAL_CHAR,
+  XML_ERROR_TAG_MISMATCH,
+  XML_ERROR_DUPLICATE_ATTRIBUTE,
+  XML_ERROR_JUNK_AFTER_DOC_ELEMENT,
+  XML_ERROR_PARAM_ENTITY_REF,
+  XML_ERROR_UNDEFINED_ENTITY,
+  XML_ERROR_RECURSIVE_ENTITY_REF,
+  XML_ERROR_ASYNC_ENTITY,
+  XML_ERROR_BAD_CHAR_REF,
+  XML_ERROR_BINARY_ENTITY_REF,
+  XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF,
+  XML_ERROR_MISPLACED_XML_PI,
+  XML_ERROR_UNKNOWN_ENCODING,
+  XML_ERROR_INCORRECT_ENCODING,
+  XML_ERROR_UNCLOSED_CDATA_SECTION,
+  XML_ERROR_EXTERNAL_ENTITY_HANDLING,
+  XML_ERROR_NOT_STANDALONE
+};
+
+/* If XML_Parse or XML_ParseBuffer have returned 0, then XML_GetErrorCode
+returns information about the error. */
+
+enum XML_Error XMLPARSEAPI XML_GetErrorCode(XML_Parser parser);
+
+/* These functions return information about the current parse location.
+They may be called when XML_Parse or XML_ParseBuffer return 0;
+in this case the location is the location of the character at which
+the error was detected.
+They may also be called from any other callback called to report
+some parse event; in this the location is the location of the first
+of the sequence of characters that generated the event. */
+
+int XMLPARSEAPI XML_GetCurrentLineNumber(XML_Parser parser);
+int XMLPARSEAPI XML_GetCurrentColumnNumber(XML_Parser parser);
+long XMLPARSEAPI XML_GetCurrentByteIndex(XML_Parser parser);
+
+/* Return the number of bytes in the current event.
+Returns 0 if the event is in an internal entity. */
+
+int XMLPARSEAPI XML_GetCurrentByteCount(XML_Parser parser);
+
+/* For backwards compatibility with previous versions. */
+#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+
+/* Frees memory used by the parser. */
+void XMLPARSEAPI
+XML_ParserFree(XML_Parser parser);
+
+/* Returns a string describing the error. */
+const XML_LChar XMLPARSEAPI *XML_ErrorString(int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlParse_INCLUDED */
Index: protocols/jabber/xmlrole.c
===================================================================
--- protocols/jabber/xmlrole.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlrole.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,1104 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmlrole.h"
+
+/* Doesn't check:
+
+ that ,| are not mixed in a model group
+ content of literals
+
+*/
+
+#ifndef MIN_BYTES_PER_CHAR
+#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar)
+#endif
+
+typedef int PROLOG_HANDLER(struct prolog_state *state,
+                           int tok,
+                           const char *ptr,
+                           const char *end,
+                           const ENCODING *enc);
+
+static PROLOG_HANDLER
+prolog0, prolog1, prolog2,
+doctype0, doctype1, doctype2, doctype3, doctype4, doctype5,
+internalSubset,
+entity0, entity1, entity2, entity3, entity4, entity5, entity6,
+entity7, entity8, entity9,
+notation0, notation1, notation2, notation3, notation4,
+attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6,
+attlist7, attlist8, attlist9,
+element0, element1, element2, element3, element4, element5, element6,
+element7,
+declClose,
+error;
+
+static
+int syntaxError(PROLOG_STATE *);
+
+static
+int prolog0(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        state->handler = prolog1;
+        return XML_ROLE_NONE;
+    case XML_TOK_XML_DECL:
+        state->handler = prolog1;
+        return XML_ROLE_XML_DECL;
+    case XML_TOK_PI:
+        state->handler = prolog1;
+        return XML_ROLE_NONE;
+    case XML_TOK_COMMENT:
+        state->handler = prolog1;
+    case XML_TOK_BOM:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_OPEN:
+        if (!XmlNameMatchesAscii(enc,
+                                 ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                 "DOCTYPE"))
+            break;
+        state->handler = doctype0;
+        return XML_ROLE_NONE;
+    case XML_TOK_INSTANCE_START:
+        state->handler = error;
+        return XML_ROLE_INSTANCE_START;
+    }
+    return syntaxError(state);
+}
+
+static
+int prolog1(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_PI:
+    case XML_TOK_COMMENT:
+    case XML_TOK_BOM:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_OPEN:
+        if (!XmlNameMatchesAscii(enc,
+                                 ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                 "DOCTYPE"))
+            break;
+        state->handler = doctype0;
+        return XML_ROLE_NONE;
+    case XML_TOK_INSTANCE_START:
+        state->handler = error;
+        return XML_ROLE_INSTANCE_START;
+    }
+    return syntaxError(state);
+}
+
+static
+int prolog2(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_PI:
+    case XML_TOK_COMMENT:
+        return XML_ROLE_NONE;
+    case XML_TOK_INSTANCE_START:
+        state->handler = error;
+        return XML_ROLE_INSTANCE_START;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype0(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = doctype1;
+        return XML_ROLE_DOCTYPE_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype1(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_OPEN_BRACKET:
+        state->handler = internalSubset;
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = prolog2;
+        return XML_ROLE_DOCTYPE_CLOSE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+            state->handler = doctype3;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+            state->handler = doctype2;
+            return XML_ROLE_NONE;
+        }
+        break;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype2(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = doctype3;
+        return XML_ROLE_DOCTYPE_PUBLIC_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype3(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = doctype4;
+        return XML_ROLE_DOCTYPE_SYSTEM_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype4(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_OPEN_BRACKET:
+        state->handler = internalSubset;
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = prolog2;
+        return XML_ROLE_DOCTYPE_CLOSE;
+    }
+    return syntaxError(state);
+}
+
+static
+int doctype5(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = prolog2;
+        return XML_ROLE_DOCTYPE_CLOSE;
+    }
+    return syntaxError(state);
+}
+
+static
+int internalSubset(PROLOG_STATE *state,
+                   int tok,
+                   const char *ptr,
+                   const char *end,
+                   const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_OPEN:
+        if (XmlNameMatchesAscii(enc,
+                                ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                "ENTITY")) {
+            state->handler = entity0;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc,
+                                ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                "ATTLIST")) {
+            state->handler = attlist0;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc,
+                                ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                "ELEMENT")) {
+            state->handler = element0;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc,
+                                ptr + 2 * MIN_BYTES_PER_CHAR(enc),
+                                "NOTATION")) {
+            state->handler = notation0;
+            return XML_ROLE_NONE;
+        }
+        break;
+    case XML_TOK_PI:
+    case XML_TOK_COMMENT:
+        return XML_ROLE_NONE;
+    case XML_TOK_PARAM_ENTITY_REF:
+        return XML_ROLE_PARAM_ENTITY_REF;
+    case XML_TOK_CLOSE_BRACKET:
+        state->handler = doctype5;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity0(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_PERCENT:
+        state->handler = entity1;
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        state->handler = entity2;
+        return XML_ROLE_GENERAL_ENTITY_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity1(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        state->handler = entity7;
+        return XML_ROLE_PARAM_ENTITY_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity2(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+            state->handler = entity4;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+            state->handler = entity3;
+            return XML_ROLE_NONE;
+        }
+        break;
+    case XML_TOK_LITERAL:
+        state->handler = declClose;
+        return XML_ROLE_ENTITY_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity3(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = entity4;
+        return XML_ROLE_ENTITY_PUBLIC_ID;
+    }
+    return syntaxError(state);
+}
+
+
+static
+int entity4(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = entity5;
+        return XML_ROLE_ENTITY_SYSTEM_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity5(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = internalSubset;
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "NDATA")) {
+            state->handler = entity6;
+            return XML_ROLE_NONE;
+        }
+        break;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity6(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        state->handler = declClose;
+        return XML_ROLE_ENTITY_NOTATION_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity7(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+            state->handler = entity9;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+            state->handler = entity8;
+            return XML_ROLE_NONE;
+        }
+        break;
+    case XML_TOK_LITERAL:
+        state->handler = declClose;
+        return XML_ROLE_ENTITY_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity8(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = entity9;
+        return XML_ROLE_ENTITY_PUBLIC_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int entity9(PROLOG_STATE *state,
+            int tok,
+            const char *ptr,
+            const char *end,
+            const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = declClose;
+        return XML_ROLE_ENTITY_SYSTEM_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int notation0(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        state->handler = notation1;
+        return XML_ROLE_NOTATION_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int notation1(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) {
+            state->handler = notation3;
+            return XML_ROLE_NONE;
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) {
+            state->handler = notation2;
+            return XML_ROLE_NONE;
+        }
+        break;
+    }
+    return syntaxError(state);
+}
+
+static
+int notation2(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = notation4;
+        return XML_ROLE_NOTATION_PUBLIC_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int notation3(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = declClose;
+        return XML_ROLE_NOTATION_SYSTEM_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int notation4(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = declClose;
+        return XML_ROLE_NOTATION_SYSTEM_ID;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = internalSubset;
+        return XML_ROLE_NOTATION_NO_SYSTEM_ID;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist0(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = attlist1;
+        return XML_ROLE_ATTLIST_ELEMENT_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist1(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = internalSubset;
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = attlist2;
+        return XML_ROLE_ATTRIBUTE_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist2(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        {
+            static const char *types[] = {
+                "CDATA",
+                "ID",
+                "IDREF",
+                "IDREFS",
+                "ENTITY",
+                "ENTITIES",
+                "NMTOKEN",
+                "NMTOKENS",
+            };
+            int i;
+            for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++)
+                if (XmlNameMatchesAscii(enc, ptr, types[i])) {
+                    state->handler = attlist8;
+                    return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i;
+                }
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "NOTATION")) {
+            state->handler = attlist5;
+            return XML_ROLE_NONE;
+        }
+        break;
+    case XML_TOK_OPEN_PAREN:
+        state->handler = attlist3;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist3(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NMTOKEN:
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = attlist4;
+        return XML_ROLE_ATTRIBUTE_ENUM_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist4(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_CLOSE_PAREN:
+        state->handler = attlist8;
+        return XML_ROLE_NONE;
+    case XML_TOK_OR:
+        state->handler = attlist3;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist5(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_OPEN_PAREN:
+        state->handler = attlist6;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+
+static
+int attlist6(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        state->handler = attlist7;
+        return XML_ROLE_ATTRIBUTE_NOTATION_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist7(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_CLOSE_PAREN:
+        state->handler = attlist8;
+        return XML_ROLE_NONE;
+    case XML_TOK_OR:
+        state->handler = attlist6;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+/* default value */
+static
+int attlist8(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_POUND_NAME:
+        if (XmlNameMatchesAscii(enc,
+                                ptr + MIN_BYTES_PER_CHAR(enc),
+                                "IMPLIED")) {
+            state->handler = attlist1;
+            return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE;
+        }
+        if (XmlNameMatchesAscii(enc,
+                                ptr + MIN_BYTES_PER_CHAR(enc),
+                                "REQUIRED")) {
+            state->handler = attlist1;
+            return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE;
+        }
+        if (XmlNameMatchesAscii(enc,
+                                ptr + MIN_BYTES_PER_CHAR(enc),
+                                "FIXED")) {
+            state->handler = attlist9;
+            return XML_ROLE_NONE;
+        }
+        break;
+    case XML_TOK_LITERAL:
+        state->handler = attlist1;
+        return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int attlist9(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_LITERAL:
+        state->handler = attlist1;
+        return XML_ROLE_FIXED_ATTRIBUTE_VALUE;
+    }
+    return syntaxError(state);
+}
+
+static
+int element0(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = element1;
+        return XML_ROLE_ELEMENT_NAME;
+    }
+    return syntaxError(state);
+}
+
+static
+int element1(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+        if (XmlNameMatchesAscii(enc, ptr, "EMPTY")) {
+            state->handler = declClose;
+            return XML_ROLE_CONTENT_EMPTY;
+        }
+        if (XmlNameMatchesAscii(enc, ptr, "ANY")) {
+            state->handler = declClose;
+            return XML_ROLE_CONTENT_ANY;
+        }
+        break;
+    case XML_TOK_OPEN_PAREN:
+        state->handler = element2;
+        state->level = 1;
+        return XML_ROLE_GROUP_OPEN;
+    }
+    return syntaxError(state);
+}
+
+static
+int element2(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_POUND_NAME:
+        if (XmlNameMatchesAscii(enc,
+                                ptr + MIN_BYTES_PER_CHAR(enc),
+                                "PCDATA")) {
+            state->handler = element3;
+            return XML_ROLE_CONTENT_PCDATA;
+        }
+        break;
+    case XML_TOK_OPEN_PAREN:
+        state->level = 2;
+        state->handler = element6;
+        return XML_ROLE_GROUP_OPEN;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT;
+    case XML_TOK_NAME_QUESTION:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_OPT;
+    case XML_TOK_NAME_ASTERISK:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_REP;
+    case XML_TOK_NAME_PLUS:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_PLUS;
+    }
+    return syntaxError(state);
+}
+
+static
+int element3(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_CLOSE_PAREN:
+    case XML_TOK_CLOSE_PAREN_ASTERISK:
+        state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE_REP;
+    case XML_TOK_OR:
+        state->handler = element4;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+static
+int element4(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = element5;
+        return XML_ROLE_CONTENT_ELEMENT;
+    }
+    return syntaxError(state);
+}
+
+static
+int element5(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_CLOSE_PAREN_ASTERISK:
+        state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE_REP;
+    case XML_TOK_OR:
+        state->handler = element4;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+static
+int element6(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_OPEN_PAREN:
+        state->level += 1;
+        return XML_ROLE_GROUP_OPEN;
+    case XML_TOK_NAME:
+    case XML_TOK_PREFIXED_NAME:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT;
+    case XML_TOK_NAME_QUESTION:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_OPT;
+    case XML_TOK_NAME_ASTERISK:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_REP;
+    case XML_TOK_NAME_PLUS:
+        state->handler = element7;
+        return XML_ROLE_CONTENT_ELEMENT_PLUS;
+    }
+    return syntaxError(state);
+}
+
+static
+int element7(PROLOG_STATE *state,
+             int tok,
+             const char *ptr,
+             const char *end,
+             const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_CLOSE_PAREN:
+        state->level -= 1;
+        if (state->level == 0)
+            state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE;
+    case XML_TOK_CLOSE_PAREN_ASTERISK:
+        state->level -= 1;
+        if (state->level == 0)
+            state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE_REP;
+    case XML_TOK_CLOSE_PAREN_QUESTION:
+        state->level -= 1;
+        if (state->level == 0)
+            state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE_OPT;
+    case XML_TOK_CLOSE_PAREN_PLUS:
+        state->level -= 1;
+        if (state->level == 0)
+            state->handler = declClose;
+        return XML_ROLE_GROUP_CLOSE_PLUS;
+    case XML_TOK_COMMA:
+        state->handler = element6;
+        return XML_ROLE_GROUP_SEQUENCE;
+    case XML_TOK_OR:
+        state->handler = element6;
+        return XML_ROLE_GROUP_CHOICE;
+    }
+    return syntaxError(state);
+}
+
+static
+int declClose(PROLOG_STATE *state,
+              int tok,
+              const char *ptr,
+              const char *end,
+              const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_PROLOG_S:
+        return XML_ROLE_NONE;
+    case XML_TOK_DECL_CLOSE:
+        state->handler = internalSubset;
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+
+#if 0
+
+static
+int ignore(PROLOG_STATE *state,
+           int tok,
+           const char *ptr,
+           const char *end,
+           const ENCODING *enc)
+{
+    switch (tok) {
+    case XML_TOK_DECL_CLOSE:
+        state->handler = internalSubset;
+        return 0;
+    default:
+        return XML_ROLE_NONE;
+    }
+    return syntaxError(state);
+}
+#endif
+
+static
+int error(PROLOG_STATE *state,
+          int tok,
+          const char *ptr,
+          const char *end,
+          const ENCODING *enc)
+{
+    return XML_ROLE_NONE;
+}
+
+static
+int syntaxError(PROLOG_STATE *state)
+{
+    state->handler = error;
+    return XML_ROLE_ERROR;
+}
+
+void XmlPrologStateInit(PROLOG_STATE *state)
+{
+    state->handler = prolog0;
+}
Index: protocols/jabber/xmlrole.h
===================================================================
--- protocols/jabber/xmlrole.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlrole.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,111 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlRole_INCLUDED
+#define XmlRole_INCLUDED 1
+
+#include "xmltok.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+  XML_ROLE_ERROR = -1,
+  XML_ROLE_NONE = 0,
+  XML_ROLE_XML_DECL,
+  XML_ROLE_INSTANCE_START,
+  XML_ROLE_DOCTYPE_NAME,
+  XML_ROLE_DOCTYPE_SYSTEM_ID,
+  XML_ROLE_DOCTYPE_PUBLIC_ID,
+  XML_ROLE_DOCTYPE_CLOSE,
+  XML_ROLE_GENERAL_ENTITY_NAME,
+  XML_ROLE_PARAM_ENTITY_NAME,
+  XML_ROLE_ENTITY_VALUE,
+  XML_ROLE_ENTITY_SYSTEM_ID,
+  XML_ROLE_ENTITY_PUBLIC_ID,
+  XML_ROLE_ENTITY_NOTATION_NAME,
+  XML_ROLE_NOTATION_NAME,
+  XML_ROLE_NOTATION_SYSTEM_ID,
+  XML_ROLE_NOTATION_NO_SYSTEM_ID,
+  XML_ROLE_NOTATION_PUBLIC_ID,
+  XML_ROLE_ATTRIBUTE_NAME,
+  XML_ROLE_ATTRIBUTE_TYPE_CDATA,
+  XML_ROLE_ATTRIBUTE_TYPE_ID,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREF,
+  XML_ROLE_ATTRIBUTE_TYPE_IDREFS,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITY,
+  XML_ROLE_ATTRIBUTE_TYPE_ENTITIES,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN,
+  XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS,
+  XML_ROLE_ATTRIBUTE_ENUM_VALUE,
+  XML_ROLE_ATTRIBUTE_NOTATION_VALUE,
+  XML_ROLE_ATTLIST_ELEMENT_NAME,
+  XML_ROLE_IMPLIED_ATTRIBUTE_VALUE,
+  XML_ROLE_REQUIRED_ATTRIBUTE_VALUE,
+  XML_ROLE_DEFAULT_ATTRIBUTE_VALUE,
+  XML_ROLE_FIXED_ATTRIBUTE_VALUE,
+  XML_ROLE_ELEMENT_NAME,
+  XML_ROLE_CONTENT_ANY,
+  XML_ROLE_CONTENT_EMPTY,
+  XML_ROLE_CONTENT_PCDATA,
+  XML_ROLE_GROUP_OPEN,
+  XML_ROLE_GROUP_CLOSE,
+  XML_ROLE_GROUP_CLOSE_REP,
+  XML_ROLE_GROUP_CLOSE_OPT,
+  XML_ROLE_GROUP_CLOSE_PLUS,
+  XML_ROLE_GROUP_CHOICE,
+  XML_ROLE_GROUP_SEQUENCE,
+  XML_ROLE_CONTENT_ELEMENT,
+  XML_ROLE_CONTENT_ELEMENT_REP,
+  XML_ROLE_CONTENT_ELEMENT_OPT,
+  XML_ROLE_CONTENT_ELEMENT_PLUS,
+  XML_ROLE_PARAM_ENTITY_REF
+};
+
+typedef struct prolog_state {
+  int (*handler)(struct prolog_state *state,
+	         int tok,
+		 const char *ptr,
+		 const char *end,
+		 const ENCODING *enc);
+  unsigned level;
+} PROLOG_STATE;
+
+void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *);
+
+#define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlRole_INCLUDED */
Index: protocols/jabber/xmltok.c
===================================================================
--- protocols/jabber/xmltok.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,1518 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#include "xmldef.h"
+#include "xmltok.h"
+#include "nametab.h"
+
+#define VTABLE1 \
+  { PREFIX(prologTok), PREFIX(contentTok), PREFIX(cdataSectionTok) }, \
+  { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \
+  PREFIX(sameName), \
+  PREFIX(nameMatchesAscii), \
+  PREFIX(nameLength), \
+  PREFIX(skipS), \
+  PREFIX(getAtts), \
+  PREFIX(charRefNumber), \
+  PREFIX(predefinedEntityName), \
+  PREFIX(updatePosition), \
+  PREFIX(isPublicId)
+
+#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16)
+
+#define UCS2_GET_NAMING(pages, hi, lo) \
+   (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F)))
+
+/* A 2 byte UTF-8 representation splits the characters 11 bits
+between the bottom 5 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING2(pages, byte) \
+    (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \
+                      + ((((byte)[0]) & 3) << 1) \
+                      + ((((byte)[1]) >> 5) & 1)] \
+         & (1 << (((byte)[1]) & 0x1F)))
+
+/* A 3 byte UTF-8 representation splits the characters 16 bits
+between the bottom 4, 6 and 6 bits of the bytes.
+We need 8 bits to index into pages, 3 bits to add to that index and
+5 bits to generate the mask. */
+#define UTF8_GET_NAMING3(pages, byte) \
+  (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \
+                             + ((((byte)[1]) >> 2) & 0xF)] \
+               << 3) \
+                      + ((((byte)[1]) & 3) << 1) \
+                      + ((((byte)[2]) >> 5) & 1)] \
+         & (1 << (((byte)[2]) & 0x1F)))
+
+#define UTF8_GET_NAMING(pages, p, n) \
+  ((n) == 2 \
+  ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \
+  : ((n) == 3 \
+     ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \
+     : 0))
+
+#define UTF8_INVALID3(p) \
+  ((*p) == 0xED \
+  ? (((p)[1] & 0x20) != 0) \
+  : ((*p) == 0xEF \
+     ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \
+     : 0))
+
+#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0)
+
+static
+int isNever(const ENCODING *enc, const char *p)
+{
+    return 0;
+}
+
+static
+int utf8_isName2(const ENCODING *enc, const char *p)
+{
+    return UTF8_GET_NAMING2(namePages, (const unsigned char *)p);
+}
+
+static
+int utf8_isName3(const ENCODING *enc, const char *p)
+{
+    return UTF8_GET_NAMING3(namePages, (const unsigned char *)p);
+}
+
+#define utf8_isName4 isNever
+
+static
+int utf8_isNmstrt2(const ENCODING *enc, const char *p)
+{
+    return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p);
+}
+
+static
+int utf8_isNmstrt3(const ENCODING *enc, const char *p)
+{
+    return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p);
+}
+
+#define utf8_isNmstrt4 isNever
+
+#define utf8_isInvalid2 isNever
+
+static
+int utf8_isInvalid3(const ENCODING *enc, const char *p)
+{
+    return UTF8_INVALID3((const unsigned char *)p);
+}
+
+static
+int utf8_isInvalid4(const ENCODING *enc, const char *p)
+{
+    return UTF8_INVALID4((const unsigned char *)p);
+}
+
+struct normal_encoding {
+    ENCODING enc;
+    unsigned char type[256];
+#ifdef XML_MIN_SIZE
+    int (*byteType)(const ENCODING *, const char *);
+    int (*isNameMin)(const ENCODING *, const char *);
+    int (*isNmstrtMin)(const ENCODING *, const char *);
+    int (*byteToAscii)(const ENCODING *, const char *);
+    int (*charMatches)(const ENCODING *, const char *, int);
+#endif /* XML_MIN_SIZE */
+    int (*isName2)(const ENCODING *, const char *);
+    int (*isName3)(const ENCODING *, const char *);
+    int (*isName4)(const ENCODING *, const char *);
+    int (*isNmstrt2)(const ENCODING *, const char *);
+    int (*isNmstrt3)(const ENCODING *, const char *);
+    int (*isNmstrt4)(const ENCODING *, const char *);
+    int (*isInvalid2)(const ENCODING *, const char *);
+    int (*isInvalid3)(const ENCODING *, const char *);
+    int (*isInvalid4)(const ENCODING *, const char *);
+};
+
+#ifdef XML_MIN_SIZE
+
+#define STANDARD_VTABLE(E) \
+ E ## byteType, \
+ E ## isNameMin, \
+ E ## isNmstrtMin, \
+ E ## byteToAscii, \
+ E ## charMatches,
+
+#else
+
+#define STANDARD_VTABLE(E) /* as nothing */
+
+#endif
+
+#define NORMAL_VTABLE(E) \
+ E ## isName2, \
+ E ## isName3, \
+ E ## isName4, \
+ E ## isNmstrt2, \
+ E ## isNmstrt3, \
+ E ## isNmstrt4, \
+ E ## isInvalid2, \
+ E ## isInvalid3, \
+ E ## isInvalid4
+
+static int checkCharRefNumber(int);
+
+#include "xmltok_impl.h"
+
+#ifdef XML_MIN_SIZE
+#define sb_isNameMin isNever
+#define sb_isNmstrtMin isNever
+#endif
+
+#ifdef XML_MIN_SIZE
+#define MINBPC(enc) ((enc)->minBytesPerChar)
+#else
+/* minimum bytes per character */
+#define MINBPC(enc) 1
+#endif
+
+#define SB_BYTE_TYPE(enc, p) \
+  (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)])
+
+#ifdef XML_MIN_SIZE
+static
+int sb_byteType(const ENCODING *enc, const char *p)
+{
+    return SB_BYTE_TYPE(enc, p);
+}
+#define BYTE_TYPE(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteType(enc, p))
+#else
+#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define BYTE_TO_ASCII(enc, p) \
+ (((const struct normal_encoding *)(enc))->byteToAscii(enc, p))
+static
+int sb_byteToAscii(const ENCODING *enc, const char *p)
+{
+    return *p;
+}
+#else
+#define BYTE_TO_ASCII(enc, p) (*p)
+#endif
+
+#define IS_NAME_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isName ## n(enc, p))
+#define IS_NMSTRT_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p))
+#define IS_INVALID_CHAR(enc, p, n) \
+ (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p))
+
+#ifdef XML_MIN_SIZE
+#define IS_NAME_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNameMin(enc, p))
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) \
+ (((const struct normal_encoding *)(enc))->isNmstrtMin(enc, p))
+#else
+#define IS_NAME_CHAR_MINBPC(enc, p) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0)
+#endif
+
+#ifdef XML_MIN_SIZE
+#define CHAR_MATCHES(enc, p, c) \
+ (((const struct normal_encoding *)(enc))->charMatches(enc, p, c))
+static
+int sb_charMatches(const ENCODING *enc, const char *p, int c)
+{
+    return *p == c;
+}
+#else
+/* c is an ASCII character */
+#define CHAR_MATCHES(enc, p, c) (*(p) == c)
+#endif
+
+#define PREFIX(ident) normal_ ## ident
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+enum {  /* UTF8_cvalN is value of masked first byte of N byte sequence */
+    UTF8_cval1 = 0x00,
+    UTF8_cval2 = 0xc0,
+    UTF8_cval3 = 0xe0,
+    UTF8_cval4 = 0xf0
+};
+
+static
+void utf8_toUtf8(const ENCODING *enc,
+                 const char **fromP, const char *fromLim,
+                 char **toP, const char *toLim)
+{
+    char *to;
+    const char *from;
+    if (fromLim - *fromP > toLim - *toP) {
+        /* Avoid copying partial characters. */
+        for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--)
+            if (((unsigned char)fromLim[-1] & 0xc0) != 0x80)
+                break;
+    }
+    for (to = *toP, from = *fromP; from != fromLim; from++, to++)
+        *to = *from;
+    *fromP = from;
+    *toP = to;
+}
+
+static
+void utf8_toUtf16(const ENCODING *enc,
+                  const char **fromP, const char *fromLim,
+                  unsigned short **toP, const unsigned short *toLim)
+{
+    unsigned short *to = *toP;
+    const char *from = *fromP;
+    while (from != fromLim && to != toLim) {
+        switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) {
+        case BT_LEAD2:
+            *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f);
+            from += 2;
+            break;
+        case BT_LEAD3:
+            *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f);
+            from += 3;
+            break;
+        case BT_LEAD4:
+            {
+                unsigned long n;
+                if (to + 1 == toLim)
+                    break;
+                n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f);
+                n -= 0x10000;
+                to[0] = (unsigned short)((n >> 10) | 0xD800);
+                to[1] = (unsigned short)((n & 0x3FF) | 0xDC00);
+                to += 2;
+                from += 4;
+            }
+            break;
+        default:
+            *to++ = *from++;
+            break;
+        }
+    }
+    *fromP = from;
+    *toP = to;
+}
+
+#ifdef XML_NS
+static const struct normal_encoding utf8_encoding_ns = {
+        { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+            {
+#include "asciitab.h"
+#include "utf8tab.h"
+            },
+            STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+        };
+#endif
+
+static const struct normal_encoding utf8_encoding = {
+        { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+            },
+            STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+        };
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_utf8_encoding_ns = {
+        { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+            {
+#include "iasciitab.h"
+#include "utf8tab.h"
+            },
+            STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+        };
+
+#endif
+
+static const struct normal_encoding internal_utf8_encoding = {
+        { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "utf8tab.h"
+            },
+            STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_)
+        };
+
+static
+void latin1_toUtf8(const ENCODING *enc,
+                   const char **fromP, const char *fromLim,
+                   char **toP, const char *toLim)
+{
+    for (;;) {
+        unsigned char c;
+        if (*fromP == fromLim)
+            break;
+        c = (unsigned char)**fromP;
+        if (c & 0x80) {
+            if (toLim - *toP < 2)
+                break;
+            *(*toP)++ = ((c >> 6) | UTF8_cval2);
+            *(*toP)++ = ((c & 0x3f) | 0x80);
+            (*fromP)++;
+        }
+        else {
+            if (*toP == toLim)
+                break;
+            *(*toP)++ = *(*fromP)++;
+        }
+    }
+}
+
+static
+void latin1_toUtf16(const ENCODING *enc,
+                    const char **fromP, const char *fromLim,
+                    unsigned short **toP, const unsigned short *toLim)
+{
+    while (*fromP != fromLim && *toP != toLim)
+        *(*toP)++ = (unsigned char)*(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding latin1_encoding_ns = {
+        { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+            {
+#include "asciitab.h"
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(sb_)
+        };
+
+#endif
+
+static const struct normal_encoding latin1_encoding = {
+        { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(sb_)
+        };
+
+static
+void ascii_toUtf8(const ENCODING *enc,
+                  const char **fromP, const char *fromLim,
+                  char **toP, const char *toLim)
+{
+    while (*fromP != fromLim && *toP != toLim)
+        *(*toP)++ = *(*fromP)++;
+}
+
+#ifdef XML_NS
+
+static const struct normal_encoding ascii_encoding_ns = {
+        { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+            {
+#include "asciitab.h"
+                /* BT_NONXML == 0 */
+            },
+            STANDARD_VTABLE(sb_)
+        };
+
+#endif
+
+static const struct normal_encoding ascii_encoding = {
+        { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+                /* BT_NONXML == 0 */
+            },
+            STANDARD_VTABLE(sb_)
+        };
+
+static int unicode_byte_type(char hi, char lo)
+{
+    switch ((unsigned char)hi) {
+case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+        return BT_LEAD4;
+case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+        return BT_TRAIL;
+    case 0xFF:
+        switch ((unsigned char)lo) {
+        case 0xFF:
+        case 0xFE:
+            return BT_NONXML;
+        }
+        break;
+    }
+    return BT_NONASCII;
+}
+
+#define DEFINE_UTF16_TO_UTF8(E) \
+static \
+void E ## toUtf8(const ENCODING *enc, \
+         const char **fromP, const char *fromLim, \
+         char **toP, const char *toLim) \
+{ \
+  const char *from; \
+  for (from = *fromP; from != fromLim; from += 2) { \
+    int plane; \
+    unsigned char lo2; \
+    unsigned char lo = GET_LO(from); \
+    unsigned char hi = GET_HI(from); \
+    switch (hi) { \
+    case 0: \
+      if (lo < 0x80) { \
+        if (*toP == toLim) { \
+          *fromP = from; \
+      return; \
+        } \
+        *(*toP)++ = lo; \
+        break; \
+      } \
+      /* fall through */ \
+    case 0x1: case 0x2: case 0x3: \
+    case 0x4: case 0x5: case 0x6: case 0x7: \
+      if (toLim -  *toP < 2) { \
+        *fromP = from; \
+    return; \
+      } \
+      *(*toP)++ = ((lo >> 6) | (hi << 2) |  UTF8_cval2); \
+      *(*toP)++ = ((lo & 0x3f) | 0x80); \
+      break; \
+    default: \
+      if (toLim -  *toP < 3)  { \
+        *fromP = from; \
+    return; \
+      } \
+      /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \
+      *(*toP)++ = ((hi >> 4) | UTF8_cval3); \
+      *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \
+      *(*toP)++ = ((lo & 0x3f) | 0x80); \
+      break; \
+    case 0xD8: case 0xD9: case 0xDA: case 0xDB: \
+      if (toLim -  *toP < 4) { \
+    *fromP = from; \
+    return; \
+      } \
+      plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \
+      *(*toP)++ = ((plane >> 2) | UTF8_cval4); \
+      *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \
+      from += 2; \
+      lo2 = GET_LO(from); \
+      *(*toP)++ = (((lo & 0x3) << 4) \
+               | ((GET_HI(from) & 0x3) << 2) \
+           | (lo2 >> 6) \
+           | 0x80); \
+      *(*toP)++ = ((lo2 & 0x3f) | 0x80); \
+      break; \
+    } \
+  } \
+  *fromP = from; \
+}
+
+#define DEFINE_UTF16_TO_UTF16(E) \
+static \
+void E ## toUtf16(const ENCODING *enc, \
+          const char **fromP, const char *fromLim, \
+          unsigned short **toP, const unsigned short *toLim) \
+{ \
+  /* Avoid copying first half only of surrogate */ \
+  if (fromLim - *fromP > ((toLim - *toP) << 1) \
+      && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \
+    fromLim -= 2; \
+  for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \
+    *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \
+}
+
+#define SET2(ptr, ch) \
+  (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[0])
+#define GET_HI(ptr) ((unsigned char)(ptr)[1])
+
+DEFINE_UTF16_TO_UTF8(little2_)
+DEFINE_UTF16_TO_UTF16(little2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define SET2(ptr, ch) \
+  (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF)))
+#define GET_LO(ptr) ((unsigned char)(ptr)[1])
+#define GET_HI(ptr) ((unsigned char)(ptr)[0])
+
+DEFINE_UTF16_TO_UTF8(big2_)
+DEFINE_UTF16_TO_UTF16(big2_)
+
+#undef SET2
+#undef GET_LO
+#undef GET_HI
+
+#define LITTLE2_BYTE_TYPE(enc, p) \
+ ((p)[1] == 0 \
+  ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \
+  : unicode_byte_type((p)[1], (p)[0]))
+#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1)
+#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c)
+#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0])
+#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0])
+
+#ifdef XML_MIN_SIZE
+
+static
+int little2_byteType(const ENCODING *enc, const char *p)
+{
+    return LITTLE2_BYTE_TYPE(enc, p);
+}
+
+static
+int little2_byteToAscii(const ENCODING *enc, const char *p)
+{
+    return LITTLE2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int little2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+    return LITTLE2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int little2_isNameMin(const ENCODING *enc, const char *p)
+{
+    return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int little2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+    return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) little2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) 
+#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding little2_encoding_ns = {
+            { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+                1
+#else
+0
+#endif
+            },
+            {
+#include "asciitab.h"
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(little2_)
+        };
+
+#endif
+
+static const struct normal_encoding little2_encoding = {
+            { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 12
+                1
+#else
+                0
+#endif
+            },
+            {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(little2_)
+        };
+
+#if XML_BYTE_ORDER != 21
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_little2_encoding_ns = {
+        { VTABLE, 2, 0, 1 },
+            {
+#include "iasciitab.h"
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(little2_)
+        };
+
+#endif
+
+static const struct normal_encoding internal_little2_encoding = {
+        { VTABLE, 2, 0, 1 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(little2_)
+        };
+
+#endif
+
+
+#define BIG2_BYTE_TYPE(enc, p) \
+ ((p)[0] == 0 \
+  ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \
+  : unicode_byte_type((p)[0], (p)[1]))
+#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1)
+#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c)
+#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1])
+#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \
+  UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1])
+
+#ifdef XML_MIN_SIZE
+
+static
+int big2_byteType(const ENCODING *enc, const char *p)
+{
+    return BIG2_BYTE_TYPE(enc, p);
+}
+
+static
+int big2_byteToAscii(const ENCODING *enc, const char *p)
+{
+    return BIG2_BYTE_TO_ASCII(enc, p);
+}
+
+static
+int big2_charMatches(const ENCODING *enc, const char *p, int c)
+{
+    return BIG2_CHAR_MATCHES(enc, p, c);
+}
+
+static
+int big2_isNameMin(const ENCODING *enc, const char *p)
+{
+    return BIG2_IS_NAME_CHAR_MINBPC(enc, p);
+}
+
+static
+int big2_isNmstrtMin(const ENCODING *enc, const char *p)
+{
+    return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p);
+}
+
+#undef VTABLE
+#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16
+
+#else /* not XML_MIN_SIZE */
+
+#undef PREFIX
+#define PREFIX(ident) big2_ ## ident
+#define MINBPC(enc) 2
+/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */
+#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p)
+#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) 
+#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c)
+#define IS_NAME_CHAR(enc, p, n) 0
+#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p)
+#define IS_NMSTRT_CHAR(enc, p, n) (0)
+#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p)
+
+#include "xmltok_impl.c"
+
+#undef MINBPC
+#undef BYTE_TYPE
+#undef BYTE_TO_ASCII
+#undef CHAR_MATCHES
+#undef IS_NAME_CHAR
+#undef IS_NAME_CHAR_MINBPC
+#undef IS_NMSTRT_CHAR
+#undef IS_NMSTRT_CHAR_MINBPC
+#undef IS_INVALID_CHAR
+
+#endif /* not XML_MIN_SIZE */
+
+#ifdef XML_NS
+
+static const struct normal_encoding big2_encoding_ns = {
+            { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+                1
+#else
+0
+#endif
+            },
+            {
+#include "asciitab.h"
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(big2_)
+        };
+
+#endif
+
+static const struct normal_encoding big2_encoding = {
+            { VTABLE, 2, 0,
+#if XML_BYTE_ORDER == 21
+                1
+#else
+                0
+#endif
+            },
+            {
+#define BT_COLON BT_NMSTRT
+#include "asciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(big2_)
+        };
+
+#if XML_BYTE_ORDER != 12
+
+#ifdef XML_NS
+
+static const struct normal_encoding internal_big2_encoding_ns = {
+        { VTABLE, 2, 0, 1 },
+            {
+#include "iasciitab.h"
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(big2_)
+        };
+
+#endif
+
+static const struct normal_encoding internal_big2_encoding = {
+        { VTABLE, 2, 0, 1 },
+            {
+#define BT_COLON BT_NMSTRT
+#include "iasciitab.h"
+#undef BT_COLON
+#include "latin1tab.h"
+            },
+            STANDARD_VTABLE(big2_)
+        };
+
+#endif
+
+#undef PREFIX
+
+static
+int streqci(const char *s1, const char *s2)
+{
+    for (;;) {
+        char c1 = *s1++;
+        char c2 = *s2++;
+        if ('a' <= c1 && c1 <= 'z')
+            c1 += 'A' - 'a';
+        if ('a' <= c2 && c2 <= 'z')
+            c2 += 'A' - 'a';
+        if (c1 != c2)
+            return 0;
+        if (!c1)
+            break;
+    }
+    return 1;
+}
+
+static
+void initUpdatePosition(const ENCODING *enc, const char *ptr,
+                        const char *end, POSITION *pos)
+{
+    normal_updatePosition(&utf8_encoding.enc, ptr, end, pos);
+}
+
+static
+int toAscii(const ENCODING *enc, const char *ptr, const char *end)
+{
+    char buf[1];
+    char *p = buf;
+    XmlUtf8Convert(enc, &ptr, end, &p, p + 1);
+    if (p == buf)
+        return -1;
+    else
+        return buf[0];
+}
+
+static
+int isSpace(int c)
+{
+    switch (c) {
+    case 0x20:
+    case 0xD:
+    case 0xA:
+    case 0x9:
+        return 1;
+    }
+    return 0;
+}
+
+/* Return 1 if there's just optional white space
+or there's an S followed by name=val. */
+static
+int parsePseudoAttribute(const ENCODING *enc,
+                         const char *ptr,
+                         const char *end,
+                         const char **namePtr,
+                         const char **valPtr,
+                         const char **nextTokPtr)
+{
+    int c;
+    char open;
+    if (ptr == end) {
+        *namePtr = 0;
+        return 1;
+    }
+    if (!isSpace(toAscii(enc, ptr, end))) {
+        *nextTokPtr = ptr;
+        return 0;
+    }
+    do {
+        ptr += enc->minBytesPerChar;
+    } while (isSpace(toAscii(enc, ptr, end)));
+    if (ptr == end) {
+        *namePtr = 0;
+        return 1;
+    }
+    *namePtr = ptr;
+    for (;;) {
+        c = toAscii(enc, ptr, end);
+        if (c == -1) {
+            *nextTokPtr = ptr;
+            return 0;
+        }
+        if (c == '=')
+            break;
+        if (isSpace(c)) {
+            do {
+                ptr += enc->minBytesPerChar;
+            } while (isSpace(c = toAscii(enc, ptr, end)));
+            if (c != '=') {
+                *nextTokPtr = ptr;
+                return 0;
+            }
+            break;
+        }
+        ptr += enc->minBytesPerChar;
+    }
+    if (ptr == *namePtr) {
+        *nextTokPtr = ptr;
+        return 0;
+    }
+    ptr += enc->minBytesPerChar;
+    c = toAscii(enc, ptr, end);
+    while (isSpace(c)) {
+        ptr += enc->minBytesPerChar;
+        c = toAscii(enc, ptr, end);
+    }
+    if (c != '"' && c != '\'') {
+        *nextTokPtr = ptr;
+        return 0;
+    }
+    open = c;
+    ptr += enc->minBytesPerChar;
+    *valPtr = ptr;
+    for (;; ptr += enc->minBytesPerChar) {
+        c = toAscii(enc, ptr, end);
+        if (c == open)
+            break;
+        if (!('a' <= c && c <= 'z')
+                && !('A' <= c && c <= 'Z')
+                && !('0' <= c && c <= '9')
+                && c != '.'
+                && c != '-'
+                && c != '_') {
+            *nextTokPtr = ptr;
+            return 0;
+        }
+    }
+    *nextTokPtr = ptr + enc->minBytesPerChar;
+    return 1;
+}
+
+static
+int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *,
+                   const char *,
+                   const char *),
+                   int isGeneralTextEntity,
+                   const ENCODING *enc,
+                   const char *ptr,
+                   const char *end,
+                   const char **badPtr,
+                   const char **versionPtr,
+                   const char **encodingName,
+                   const ENCODING **encoding,
+                   int *standalone)
+{
+    const char *val = 0;
+    const char *name = 0;
+    ptr += 5 * enc->minBytesPerChar;
+    end -= 2 * enc->minBytesPerChar;
+    if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr) || !name) {
+        *badPtr = ptr;
+        return 0;
+    }
+    if (!XmlNameMatchesAscii(enc, name, "version")) {
+        if (!isGeneralTextEntity) {
+            *badPtr = name;
+            return 0;
+        }
+    }
+    else {
+        if (versionPtr)
+            *versionPtr = val;
+        if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) {
+            *badPtr = ptr;
+            return 0;
+        }
+        if (!name) {
+            if (isGeneralTextEntity) {
+                /* a TextDecl must have an EncodingDecl */
+                *badPtr = ptr;
+                return 0;
+            }
+            return 1;
+        }
+    }
+    if (XmlNameMatchesAscii(enc, name, "encoding")) {
+        int c = toAscii(enc, val, end);
+        if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) {
+            *badPtr = val;
+            return 0;
+        }
+        if (encodingName)
+            *encodingName = val;
+        if (encoding)
+            *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar);
+        if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) {
+            *badPtr = ptr;
+            return 0;
+        }
+        if (!name)
+            return 1;
+    }
+    if (!XmlNameMatchesAscii(enc, name, "standalone") || isGeneralTextEntity) {
+        *badPtr = name;
+        return 0;
+    }
+    if (XmlNameMatchesAscii(enc, val, "yes")) {
+        if (standalone)
+            *standalone = 1;
+    }
+    else if (XmlNameMatchesAscii(enc, val, "no")) {
+        if (standalone)
+            *standalone = 0;
+    }
+    else {
+        *badPtr = val;
+        return 0;
+    }
+    while (isSpace(toAscii(enc, ptr, end)))
+        ptr += enc->minBytesPerChar;
+    if (ptr != end) {
+        *badPtr = ptr;
+        return 0;
+    }
+    return 1;
+}
+
+static
+int checkCharRefNumber(int result)
+{
+    switch (result >> 8) {
+case 0xD8: case 0xD9: case 0xDA: case 0xDB:
+case 0xDC: case 0xDD: case 0xDE: case 0xDF:
+        return -1;
+    case 0:
+        if (latin1_encoding.type[result] == BT_NONXML)
+            return -1;
+        break;
+    case 0xFF:
+        if (result == 0xFFFE || result == 0xFFFF)
+            return -1;
+        break;
+    }
+    return result;
+}
+
+int XmlUtf8Encode(int c, char *buf)
+{
+    enum {
+        /* minN is minimum legal resulting value for N byte sequence */
+        min2 = 0x80,
+        min3 = 0x800,
+        min4 = 0x10000
+    };
+
+    if (c < 0)
+        return 0;
+    if (c < min2) {
+        buf[0] = (c | UTF8_cval1);
+        return 1;
+    }
+    if (c < min3) {
+        buf[0] = ((c >> 6) | UTF8_cval2);
+        buf[1] = ((c & 0x3f) | 0x80);
+        return 2;
+    }
+    if (c < min4) {
+        buf[0] = ((c >> 12) | UTF8_cval3);
+        buf[1] = (((c >> 6) & 0x3f) | 0x80);
+        buf[2] = ((c & 0x3f) | 0x80);
+        return 3;
+    }
+    if (c < 0x110000) {
+        buf[0] = ((c >> 18) | UTF8_cval4);
+        buf[1] = (((c >> 12) & 0x3f) | 0x80);
+        buf[2] = (((c >> 6) & 0x3f) | 0x80);
+        buf[3] = ((c & 0x3f) | 0x80);
+        return 4;
+    }
+    return 0;
+}
+
+int XmlUtf16Encode(int charNum, unsigned short *buf)
+{
+    if (charNum < 0)
+        return 0;
+    if (charNum < 0x10000) {
+        buf[0] = charNum;
+        return 1;
+    }
+    if (charNum < 0x110000) {
+        charNum -= 0x10000;
+        buf[0] = (charNum >> 10) + 0xD800;
+        buf[1] = (charNum & 0x3FF) + 0xDC00;
+        return 2;
+    }
+    return 0;
+}
+
+struct unknown_encoding {
+    struct normal_encoding normal;
+    int (*convert)(void *userData, const char *p);
+    void *userData;
+    unsigned short utf16[256];
+    char utf8[256][4];
+};
+
+int XmlSizeOfUnknownEncoding()
+{
+    return sizeof(struct unknown_encoding);
+}
+
+static
+int unknown_isName(const ENCODING *enc, const char *p)
+{
+    int c = ((const struct unknown_encoding *)enc)
+            ->convert(((const struct unknown_encoding *)enc)->userData, p);
+    if (c & ~0xFFFF)
+        return 0;
+    return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isNmstrt(const ENCODING *enc, const char *p)
+{
+    int c = ((const struct unknown_encoding *)enc)
+            ->convert(((const struct unknown_encoding *)enc)->userData, p);
+    if (c & ~0xFFFF)
+        return 0;
+    return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF);
+}
+
+static
+int unknown_isInvalid(const ENCODING *enc, const char *p)
+{
+    int c = ((const struct unknown_encoding *)enc)
+            ->convert(((const struct unknown_encoding *)enc)->userData, p);
+    return (c & ~0xFFFF) || checkCharRefNumber(c) < 0;
+}
+
+static
+void unknown_toUtf8(const ENCODING *enc,
+                    const char **fromP, const char *fromLim,
+                    char **toP, const char *toLim)
+{
+    char buf[XML_UTF8_ENCODE_MAX];
+    for (;;) {
+        const char *utf8;
+        int n;
+        if (*fromP == fromLim)
+            break;
+        utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP];
+        n = *utf8++;
+        if (n == 0) {
+            int c = ((const struct unknown_encoding *)enc)
+                    ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+            n = XmlUtf8Encode(c, buf);
+            if (n > toLim - *toP)
+                break;
+            utf8 = buf;
+            *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+                      - (BT_LEAD2 - 2);
+        }
+        else {
+            if (n > toLim - *toP)
+                break;
+            (*fromP)++;
+        }
+        do {
+            *(*toP)++ = *utf8++;
+        } while (--n != 0);
+    }
+}
+
+static
+void unknown_toUtf16(const ENCODING *enc,
+                     const char **fromP, const char *fromLim,
+                     unsigned short **toP, const unsigned short *toLim)
+{
+    while (*fromP != fromLim && *toP != toLim) {
+        unsigned short c
+        = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP];
+        if (c == 0) {
+            c = (unsigned short)((const struct unknown_encoding *)enc)
+                ->convert(((const struct unknown_encoding *)enc)->userData, *fromP);
+            *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP]
+                      - (BT_LEAD2 - 2);
+        }
+        else
+            (*fromP)++;
+        *(*toP)++ = c;
+    }
+}
+
+ENCODING *
+XmlInitUnknownEncoding(void *mem,
+                       int *table,
+                       int (*convert)(void *userData, const char *p),
+                       void *userData)
+{
+    int i;
+    struct unknown_encoding *e = mem;
+    for (i = 0; i < sizeof(struct normal_encoding); i++)
+        ((char *)mem)[i] = ((char *)&latin1_encoding)[i];
+    for (i = 0; i < 128; i++)
+        if (latin1_encoding.type[i] != BT_OTHER
+                && latin1_encoding.type[i] != BT_NONXML
+                && table[i] != i)
+            return 0;
+    for (i = 0; i < 256; i++) {
+        int c = table[i];
+        if (c == -1) {
+            e->normal.type[i] = BT_MALFORM;
+            /* This shouldn't really get used. */
+            e->utf16[i] = 0xFFFF;
+            e->utf8[i][0] = 1;
+            e->utf8[i][1] = 0;
+        }
+        else if (c < 0) {
+            if (c < -4)
+                return 0;
+            e->normal.type[i] = BT_LEAD2 - (c + 2);
+            e->utf8[i][0] = 0;
+            e->utf16[i] = 0;
+        }
+        else if (c < 0x80) {
+            if (latin1_encoding.type[c] != BT_OTHER
+                    && latin1_encoding.type[c] != BT_NONXML
+                    && c != i)
+                return 0;
+            e->normal.type[i] = latin1_encoding.type[c];
+            e->utf8[i][0] = 1;
+            e->utf8[i][1] = (char)c;
+            e->utf16[i] = c == 0 ? 0xFFFF : c;
+        }
+        else if (checkCharRefNumber(c) < 0) {
+            e->normal.type[i] = BT_NONXML;
+            /* This shouldn't really get used. */
+            e->utf16[i] = 0xFFFF;
+            e->utf8[i][0] = 1;
+            e->utf8[i][1] = 0;
+        }
+        else {
+            if (c > 0xFFFF)
+                return 0;
+            if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff))
+                e->normal.type[i] = BT_NMSTRT;
+            else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff))
+                e->normal.type[i] = BT_NAME;
+            else
+                e->normal.type[i] = BT_OTHER;
+            e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1);
+            e->utf16[i] = c;
+        }
+    }
+    e->userData = userData;
+    e->convert = convert;
+    if (convert) {
+        e->normal.isName2 = unknown_isName;
+        e->normal.isName3 = unknown_isName;
+        e->normal.isName4 = unknown_isName;
+        e->normal.isNmstrt2 = unknown_isNmstrt;
+        e->normal.isNmstrt3 = unknown_isNmstrt;
+        e->normal.isNmstrt4 = unknown_isNmstrt;
+        e->normal.isInvalid2 = unknown_isInvalid;
+        e->normal.isInvalid3 = unknown_isInvalid;
+        e->normal.isInvalid4 = unknown_isInvalid;
+    }
+    e->normal.enc.utf8Convert = unknown_toUtf8;
+    e->normal.enc.utf16Convert = unknown_toUtf16;
+    return &(e->normal.enc);
+}
+
+/* If this enumeration is changed, getEncodingIndex and encodings
+must also be changed. */
+enum {
+    UNKNOWN_ENC = -1,
+    ISO_8859_1_ENC = 0,
+    US_ASCII_ENC,
+    UTF_8_ENC,
+    UTF_16_ENC,
+    UTF_16BE_ENC,
+    UTF_16LE_ENC,
+    /* must match encodingNames up to here */
+    NO_ENC
+};
+
+static
+int getEncodingIndex(const char *name)
+{
+    static const char *encodingNames[] = {
+        "ISO-8859-1",
+        "US-ASCII",
+        "UTF-8",
+        "UTF-16",
+        "UTF-16BE"
+        "UTF-16LE",
+    };
+    int i;
+    if (name == 0)
+        return NO_ENC;
+    for (i = 0; i < sizeof(encodingNames)/sizeof(encodingNames[0]); i++)
+        if (streqci(name, encodingNames[i]))
+            return i;
+    return UNKNOWN_ENC;
+}
+
+/* For binary compatibility, we store the index of the encoding specified
+at initialization in the isUtf16 member. */
+
+#define INIT_ENC_INDEX(enc) ((enc)->initEnc.isUtf16)
+
+/* This is what detects the encoding.
+encodingTable maps from encoding indices to encodings;
+INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding;
+state is XML_CONTENT_STATE if we're parsing an external text entity,
+and XML_PROLOG_STATE otherwise.
+*/
+
+
+static
+int initScan(const ENCODING **encodingTable,
+             const INIT_ENCODING *enc,
+             int state,
+             const char *ptr,
+             const char *end,
+             const char **nextTokPtr)
+{
+    const ENCODING **encPtr;
+
+    if (ptr == end)
+        return XML_TOK_NONE;
+    encPtr = enc->encPtr;
+    if (ptr + 1 == end) {
+        /* only a single byte available for auto-detection */
+        /* a well-formed document entity must have more than one byte */
+        if (state != XML_CONTENT_STATE)
+            return XML_TOK_PARTIAL;
+        /* so we're parsing an external text entity... */
+        /* if UTF-16 was externally specified, then we need at least 2 bytes */
+        switch (INIT_ENC_INDEX(enc)) {
+        case UTF_16_ENC:
+        case UTF_16LE_ENC:
+        case UTF_16BE_ENC:
+            return XML_TOK_PARTIAL;
+        }
+        switch ((unsigned char)*ptr) {
+        case 0xFE:
+        case 0xFF:
+        case 0xEF: /* possibly first byte of UTF-8 BOM */
+            if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+                    && state == XML_CONTENT_STATE)
+                break;
+            /* fall through */
+        case 0x00:
+        case 0x3C:
+            return XML_TOK_PARTIAL;
+        }
+    }
+    else {
+        switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) {
+        case 0xFEFF:
+            if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+                    && state == XML_CONTENT_STATE)
+                break;
+            *nextTokPtr = ptr + 2;
+            *encPtr = encodingTable[UTF_16BE_ENC];
+            return XML_TOK_BOM;
+            /* 00 3C is handled in the default case */
+        case 0x3C00:
+            if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC
+                    || INIT_ENC_INDEX(enc) == UTF_16_ENC)
+                    && state == XML_CONTENT_STATE)
+                break;
+            *encPtr = encodingTable[UTF_16LE_ENC];
+            return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+        case 0xFFFE:
+            if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC
+                    && state == XML_CONTENT_STATE)
+                break;
+            *nextTokPtr = ptr + 2;
+            *encPtr = encodingTable[UTF_16LE_ENC];
+            return XML_TOK_BOM;
+        case 0xEFBB:
+            /* Maybe a UTF-8 BOM (EF BB BF) */
+            /* If there's an explicitly specified (external) encoding
+               of ISO-8859-1 or some flavour of UTF-16
+               and this is an external text entity,
+            don't look for the BOM,
+               because it might be a legal data. */
+            if (state == XML_CONTENT_STATE) {
+                int e = INIT_ENC_INDEX(enc);
+                if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC || e == UTF_16_ENC)
+                    break;
+            }
+            if (ptr + 2 == end)
+                return XML_TOK_PARTIAL;
+            if ((unsigned char)ptr[2] == 0xBF) {
+                *encPtr = encodingTable[UTF_8_ENC];
+                return XML_TOK_BOM;
+            }
+            break;
+        default:
+            if (ptr[0] == '\0') {
+                /* 0 isn't a legal data character. Furthermore a document entity can only
+                   start with ASCII characters.  So the only way this can fail to be big-endian
+                   UTF-16 if it it's an external parsed general entity that's labelled as
+                   UTF-16LE. */
+                if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC)
+                    break;
+                *encPtr = encodingTable[UTF_16BE_ENC];
+                return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+            }
+            else if (ptr[1] == '\0') {
+                /* We could recover here in the case:
+                    - parsing an external entity
+                    - second byte is 0
+                    - no externally specified encoding
+                    - no encoding declaration
+                   by assuming UTF-16LE.  But we don't, because this would mean when
+                   presented just with a single byte, we couldn't reliably determine
+                   whether we needed further bytes. */
+                if (state == XML_CONTENT_STATE)
+                    break;
+                *encPtr = encodingTable[UTF_16LE_ENC];
+                return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+            }
+            break;
+        }
+    }
+    *encPtr = encodingTable[(int)INIT_ENC_INDEX(enc)];
+    return XmlTok(*encPtr, state, ptr, end, nextTokPtr);
+}
+
+
+#define NS(x) x
+#define ns(x) x
+#include "xmltok_ns.c"
+#undef NS
+#undef ns
+
+#ifdef XML_NS
+
+#define NS(x) x ## NS
+#define ns(x) x ## _ns
+
+#include "xmltok_ns.c"
+
+#undef NS
+#undef ns
+
+ENCODING *
+XmlInitUnknownEncodingNS(void *mem,
+                         int *table,
+                         int (*convert)(void *userData, const char *p),
+                         void *userData)
+{
+    ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
+    if (enc)
+        ((struct normal_encoding *)enc)->type[':'] = BT_COLON;
+    return enc;
+}
+
+#endif /* XML_NS */
Index: protocols/jabber/xmltok.h
===================================================================
--- protocols/jabber/xmltok.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,307 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+#ifndef XmlTok_INCLUDED
+#define XmlTok_INCLUDED 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef XMLTOKAPI
+#define XMLTOKAPI /* as nothing */
+#endif
+
+/* The following token may be returned by XmlContentTok */
+#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of
+                                    illegal ]]> sequence */
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_NONE -4    /* The string to be scanned is empty */
+#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan;
+                                  might be part of CRLF sequence */ 
+#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+#define XML_TOK_PARTIAL -1 /* only part of a token */
+#define XML_TOK_INVALID 0
+
+/* The following tokens are returned by XmlContentTok; some are also
+  returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */
+
+#define XML_TOK_START_TAG_WITH_ATTS 1
+#define XML_TOK_START_TAG_NO_ATTS 2
+#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+#define XML_TOK_END_TAG 5
+#define XML_TOK_DATA_CHARS 6
+#define XML_TOK_DATA_NEWLINE 7
+#define XML_TOK_CDATA_SECT_OPEN 8
+#define XML_TOK_ENTITY_REF 9
+#define XML_TOK_CHAR_REF 10     /* numeric character reference */
+
+/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */
+#define XML_TOK_PI 11      /* processing instruction */
+#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+#define XML_TOK_COMMENT 13
+#define XML_TOK_BOM 14     /* Byte order mark */
+
+/* The following tokens are returned only by XmlPrologTok */
+#define XML_TOK_PROLOG_S 15
+#define XML_TOK_DECL_OPEN 16 /* <!foo */
+#define XML_TOK_DECL_CLOSE 17 /* > */
+#define XML_TOK_NAME 18
+#define XML_TOK_NMTOKEN 19
+#define XML_TOK_POUND_NAME 20 /* #name */
+#define XML_TOK_OR 21 /* | */
+#define XML_TOK_PERCENT 22
+#define XML_TOK_OPEN_PAREN 23
+#define XML_TOK_CLOSE_PAREN 24
+#define XML_TOK_OPEN_BRACKET 25
+#define XML_TOK_CLOSE_BRACKET 26
+#define XML_TOK_LITERAL 27
+#define XML_TOK_PARAM_ENTITY_REF 28
+#define XML_TOK_INSTANCE_START 29
+
+/* The following occur only in element type declarations */
+#define XML_TOK_NAME_QUESTION 30 /* name? */
+#define XML_TOK_NAME_ASTERISK 31 /* name* */
+#define XML_TOK_NAME_PLUS 32 /* name+ */
+#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
+#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
+#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
+#define XML_TOK_COMMA 38
+
+/* The following token is returned only by XmlAttributeValueTok */
+#define XML_TOK_ATTRIBUTE_VALUE_S 39
+
+/* The following token is returned only by XmlCdataSectionTok */
+#define XML_TOK_CDATA_SECT_CLOSE 40
+
+/* With namespace processing this is returned by XmlPrologTok
+   for a name with a colon. */
+#define XML_TOK_PREFIXED_NAME 41
+
+#define XML_N_STATES 3
+#define XML_PROLOG_STATE 0
+#define XML_CONTENT_STATE 1
+#define XML_CDATA_SECTION_STATE 2
+
+#define XML_N_LITERAL_TYPES 2
+#define XML_ATTRIBUTE_VALUE_LITERAL 0
+#define XML_ENTITY_VALUE_LITERAL 1
+
+/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
+#define XML_UTF8_ENCODE_MAX 4
+/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
+#define XML_UTF16_ENCODE_MAX 2
+
+typedef struct position {
+  /* first line and first column are 0 not 1 */
+  unsigned long lineNumber;
+  unsigned long columnNumber;
+} POSITION;
+
+typedef struct {
+  const char *name;
+  const char *valuePtr;
+  const char *valueEnd;
+  char normalized;
+} ATTRIBUTE;
+
+struct encoding;
+typedef struct encoding ENCODING;
+
+struct encoding {
+  int (*scanners[XML_N_STATES])(const ENCODING *,
+			        const char *,
+			        const char *,
+			        const char **);
+  int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *,
+					      const char *,
+					      const char *,
+					      const char **);
+  int (*sameName)(const ENCODING *,
+	          const char *, const char *);
+  int (*nameMatchesAscii)(const ENCODING *,
+			  const char *, const char *);
+  int (*nameLength)(const ENCODING *, const char *);
+  const char *(*skipS)(const ENCODING *, const char *);
+  int (*getAtts)(const ENCODING *enc, const char *ptr,
+	         int attsMax, ATTRIBUTE *atts);
+  int (*charRefNumber)(const ENCODING *enc, const char *ptr);
+  int (*predefinedEntityName)(const ENCODING *, const char *, const char *);
+  void (*updatePosition)(const ENCODING *,
+			 const char *ptr,
+			 const char *end,
+			 POSITION *);
+  int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+		    const char **badPtr);
+  void (*utf8Convert)(const ENCODING *enc,
+		      const char **fromP,
+		      const char *fromLim,
+		      char **toP,
+		      const char *toLim);
+  void (*utf16Convert)(const ENCODING *enc,
+		       const char **fromP,
+		       const char *fromLim,
+		       unsigned short **toP,
+		       const unsigned short *toLim);
+  int minBytesPerChar;
+  char isUtf8;
+  char isUtf16;
+};
+
+/*
+Scan the string starting at ptr until the end of the next complete token,
+but do not scan past eptr.  Return an integer giving the type of token.
+
+Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set.
+
+Return XML_TOK_PARTIAL when the string does not contain a complete token;
+nextTokPtr will not be set.
+
+Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr
+will be set to point to the character which made the token invalid.
+
+Otherwise the string starts with a valid token; nextTokPtr will be set to point
+to the character following the end of that token.
+
+Each data character counts as a single token, but adjacent data characters
+may be returned together.  Similarly for characters in the prolog outside
+literals, comments and processing instructions.
+*/
+
+
+#define XmlTok(enc, state, ptr, end, nextTokPtr) \
+  (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+
+#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+
+#define XmlContentTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+
+#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+   XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+
+/* This is used for performing a 2nd-level tokenization on
+the content of a literal that has already been returned by XmlTok. */ 
+
+#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+  (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+
+#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+   XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+   XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+
+#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2))
+
+#define XmlNameMatchesAscii(enc, ptr1, ptr2) \
+  (((enc)->nameMatchesAscii)(enc, ptr1, ptr2))
+
+#define XmlNameLength(enc, ptr) \
+  (((enc)->nameLength)(enc, ptr))
+
+#define XmlSkipS(enc, ptr) \
+  (((enc)->skipS)(enc, ptr))
+
+#define XmlGetAttributes(enc, ptr, attsMax, atts) \
+  (((enc)->getAtts)(enc, ptr, attsMax, atts))
+
+#define XmlCharRefNumber(enc, ptr) \
+  (((enc)->charRefNumber)(enc, ptr))
+
+#define XmlPredefinedEntityName(enc, ptr, end) \
+  (((enc)->predefinedEntityName)(enc, ptr, end))
+
+#define XmlUpdatePosition(enc, ptr, end, pos) \
+  (((enc)->updatePosition)(enc, ptr, end, pos))
+
+#define XmlIsPublicId(enc, ptr, end, badPtr) \
+  (((enc)->isPublicId)(enc, ptr, end, badPtr))
+
+#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+  (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+
+#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+  (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+
+typedef struct {
+  ENCODING initEnc;
+  const ENCODING **encPtr;
+} INIT_ENCODING;
+
+int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity,
+			      const ENCODING *enc,
+			      const char *ptr,
+	  		      const char *end,
+			      const char **badPtr,
+			      const char **versionPtr,
+			      const char **encodingNamePtr,
+			      const ENCODING **namedEncodingPtr,
+			      int *standalonePtr);
+
+int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding();
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding();
+int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf);
+int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf);
+
+int XMLTOKAPI XmlSizeOfUnknownEncoding();
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncoding(void *mem,
+		       int *table,
+		       int (*conv)(void *userData, const char *p),
+		       void *userData);
+
+int XMLTOKAPI XmlParseXmlDeclNS(int isGeneralTextEntity,
+			        const ENCODING *enc,
+			        const char *ptr,
+	  		        const char *end,
+			        const char **badPtr,
+			        const char **versionPtr,
+			        const char **encodingNamePtr,
+			        const ENCODING **namedEncodingPtr,
+			        int *standalonePtr);
+int XMLTOKAPI XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name);
+const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncodingNS();
+const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncodingNS();
+ENCODING XMLTOKAPI *
+XmlInitUnknownEncodingNS(void *mem,
+		         int *table,
+		         int (*conv)(void *userData, const char *p),
+		         void *userData);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not XmlTok_INCLUDED */
Index: protocols/jabber/xmltok_impl.c
===================================================================
--- protocols/jabber/xmltok_impl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_impl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,1737 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+#ifndef IS_INVALID_CHAR
+#define IS_INVALID_CHAR(enc, ptr, n) (0)
+#endif
+
+#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n) \
+    return XML_TOK_PARTIAL_CHAR; \
+      if (IS_INVALID_CHAR(enc, ptr, n)) { \
+        *(nextTokPtr) = (ptr); \
+        return XML_TOK_INVALID; \
+      } \
+      ptr += n; \
+      break;
+
+#define INVALID_CASES(ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(2, ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(3, ptr, nextTokPtr) \
+  INVALID_LEAD_CASE(4, ptr, nextTokPtr) \
+  case BT_NONXML: \
+  case BT_MALFORM: \
+  case BT_TRAIL: \
+    *(nextTokPtr) = (ptr); \
+    return XML_TOK_INVALID;
+
+#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \
+   case BT_LEAD ## n: \
+     if (end - ptr < n) \
+       return XML_TOK_PARTIAL_CHAR; \
+     if (!IS_NAME_CHAR(enc, ptr, n)) { \
+       *nextTokPtr = ptr; \
+       return XML_TOK_INVALID; \
+     } \
+     ptr += n; \
+     break;
+
+#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \
+  case BT_NONASCII: \
+    if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \
+      *nextTokPtr = ptr; \
+      return XML_TOK_INVALID; \
+    } \
+  case BT_NMSTRT: \
+  case BT_HEX: \
+  case BT_DIGIT: \
+  case BT_NAME: \
+  case BT_MINUS: \
+    ptr += MINBPC(enc); \
+    break; \
+  CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \
+  CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \
+  CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr)
+
+#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \
+   case BT_LEAD ## n: \
+     if (end - ptr < n) \
+       return XML_TOK_PARTIAL_CHAR; \
+     if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \
+       *nextTokPtr = ptr; \
+       return XML_TOK_INVALID; \
+     } \
+     ptr += n; \
+     break;
+
+#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \
+  case BT_NONASCII: \
+    if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \
+      *nextTokPtr = ptr; \
+      return XML_TOK_INVALID; \
+    } \
+  case BT_NMSTRT: \
+  case BT_HEX: \
+    ptr += MINBPC(enc); \
+    break; \
+  CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \
+  CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \
+  CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr)
+
+#ifndef PREFIX
+#define PREFIX(ident) ident
+#endif
+
+/* ptr points to character following "<!-" */
+
+static
+int PREFIX(scanComment)(const ENCODING *enc, const char *ptr, const char *end,
+                        const char **nextTokPtr)
+{
+    if (ptr != end) {
+        if (!CHAR_MATCHES(enc, ptr, '-')) {
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+        ptr += MINBPC(enc);
+        while (ptr != end) {
+            switch (BYTE_TYPE(enc, ptr)) {
+                INVALID_CASES(ptr, nextTokPtr)
+            case BT_MINUS:
+                if ((ptr += MINBPC(enc)) == end)
+                    return XML_TOK_PARTIAL;
+                if (CHAR_MATCHES(enc, ptr, '-')) {
+                    if ((ptr += MINBPC(enc)) == end)
+                        return XML_TOK_PARTIAL;
+                    if (!CHAR_MATCHES(enc, ptr, '>')) {
+                        *nextTokPtr = ptr;
+                        return XML_TOK_INVALID;
+                    }
+                    *nextTokPtr = ptr + MINBPC(enc);
+                    return XML_TOK_COMMENT;
+                }
+                break;
+            default:
+                ptr += MINBPC(enc);
+                break;
+            }
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<!" */
+
+static
+int PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end,
+                     const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_MINUS:
+        return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_LSQB:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_COND_SECT_OPEN;
+    case BT_NMSTRT:
+    case BT_HEX:
+        ptr += MINBPC(enc);
+        break;
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_PERCNT:
+            if (ptr + MINBPC(enc) == end)
+                return XML_TOK_PARTIAL;
+            /* don't allow <!ENTITY% foo "whatever"> */
+            switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) {
+case BT_S: case BT_CR: case BT_LF: case BT_PERCNT:
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            /* fall through */
+case BT_S: case BT_CR: case BT_LF:
+            *nextTokPtr = ptr;
+            return XML_TOK_DECL_OPEN;
+        case BT_NMSTRT:
+        case BT_HEX:
+            ptr += MINBPC(enc);
+            break;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr)
+{
+    int upper = 0;
+    *tokPtr = XML_TOK_PI;
+    if (end - ptr != MINBPC(enc)*3)
+        return 1;
+    switch (BYTE_TO_ASCII(enc, ptr)) {
+    case 'x':
+        break;
+    case 'X':
+        upper = 1;
+        break;
+    default:
+        return 1;
+    }
+    ptr += MINBPC(enc);
+    switch (BYTE_TO_ASCII(enc, ptr)) {
+    case 'm':
+        break;
+    case 'M':
+        upper = 1;
+        break;
+    default:
+        return 1;
+    }
+    ptr += MINBPC(enc);
+    switch (BYTE_TO_ASCII(enc, ptr)) {
+    case 'l':
+        break;
+    case 'L':
+        upper = 1;
+        break;
+    default:
+        return 1;
+    }
+    if (upper)
+        return 0;
+    *tokPtr = XML_TOK_XML_DECL;
+    return 1;
+}
+
+/* ptr points to character following "<?" */
+
+static
+int PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr)
+{
+    int tok;
+    const char *target = ptr;
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_CR: case BT_LF:
+            if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            ptr += MINBPC(enc);
+            while (ptr != end) {
+                switch (BYTE_TYPE(enc, ptr)) {
+                    INVALID_CASES(ptr, nextTokPtr)
+                case BT_QUEST:
+                    ptr += MINBPC(enc);
+                    if (ptr == end)
+                        return XML_TOK_PARTIAL;
+                    if (CHAR_MATCHES(enc, ptr, '>')) {
+                        *nextTokPtr = ptr + MINBPC(enc);
+                        return tok;
+                    }
+                    break;
+                default:
+                    ptr += MINBPC(enc);
+                    break;
+                }
+            }
+            return XML_TOK_PARTIAL;
+        case BT_QUEST:
+            if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            if (CHAR_MATCHES(enc, ptr, '>')) {
+                *nextTokPtr = ptr + MINBPC(enc);
+                return tok;
+            }
+            /* fall through */
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+
+static
+int PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end,
+                             const char **nextTokPtr)
+{
+    int i;
+    /* CDATA[ */
+    if (end - ptr < 6 * MINBPC(enc))
+        return XML_TOK_PARTIAL;
+    for (i = 0; i < 6; i++, ptr += MINBPC(enc)) {
+        if (!CHAR_MATCHES(enc, ptr, "CDATA["[i])) {
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_CDATA_SECT_OPEN;
+}
+
+static
+int PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end,
+                            const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_NONE;
+    if (MINBPC(enc) > 1) {
+        size_t n = end - ptr;
+        if (n & (MINBPC(enc) - 1)) {
+            n &= ~(MINBPC(enc) - 1);
+            if (n == 0)
+                return XML_TOK_PARTIAL;
+            end = ptr + n;
+        }
+    }
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_RSQB:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_PARTIAL;
+        if (!CHAR_MATCHES(enc, ptr, ']'))
+            break;
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_PARTIAL;
+        if (!CHAR_MATCHES(enc, ptr, '>')) {
+            ptr -= MINBPC(enc);
+            break;
+        }
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_CDATA_SECT_CLOSE;
+    case BT_CR:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_PARTIAL;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+            ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+    case BT_LF:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+        INVALID_CASES(ptr, nextTokPtr)
+    default:
+        ptr += MINBPC(enc);
+        break;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+    *nextTokPtr = ptr; \
+    return XML_TOK_DATA_CHARS; \
+      } \
+      ptr += n; \
+      break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_NONXML:
+        case BT_MALFORM:
+        case BT_TRAIL:
+        case BT_CR:
+        case BT_LF:
+        case BT_RSQB:
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "</" */
+
+static
+int PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_CR: case BT_LF:
+            for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+                switch (BYTE_TYPE(enc, ptr)) {
+        case BT_S: case BT_CR: case BT_LF:
+                    break;
+                case BT_GT:
+                    *nextTokPtr = ptr + MINBPC(enc);
+                    return XML_TOK_END_TAG;
+                default:
+                    *nextTokPtr = ptr;
+                    return XML_TOK_INVALID;
+                }
+            }
+            return XML_TOK_PARTIAL;
+#ifdef XML_NS
+        case BT_COLON:
+            /* no need to check qname syntax here, since end-tag must match exactly */
+            ptr += MINBPC(enc);
+            break;
+#endif
+        case BT_GT:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_END_TAG;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#X" */
+
+static
+int PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+                           const char **nextTokPtr)
+{
+    if (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_DIGIT:
+        case BT_HEX:
+            break;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+        for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+            switch (BYTE_TYPE(enc, ptr)) {
+            case BT_DIGIT:
+            case BT_HEX:
+                break;
+            case BT_SEMI:
+                *nextTokPtr = ptr + MINBPC(enc);
+                return XML_TOK_CHAR_REF;
+            default:
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&#" */
+
+static
+int PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, const char *end,
+                        const char **nextTokPtr)
+{
+    if (ptr != end) {
+        if (CHAR_MATCHES(enc, ptr, 'x'))
+            return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_DIGIT:
+            break;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+        for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) {
+            switch (BYTE_TYPE(enc, ptr)) {
+            case BT_DIGIT:
+                break;
+            case BT_SEMI:
+                *nextTokPtr = ptr + MINBPC(enc);
+                return XML_TOK_CHAR_REF;
+            default:
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "&" */
+
+static
+int PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end,
+                    const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+    case BT_NUM:
+        return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+        case BT_SEMI:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_ENTITY_REF;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following first character of attribute name */
+
+static
+int PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end,
+                     const char **nextTokPtr)
+{
+#ifdef XML_NS
+    int hadColon = 0;
+#endif
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+        case BT_COLON:
+            if (hadColon) {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            hadColon = 1;
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            switch (BYTE_TYPE(enc, ptr)) {
+                CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+            default:
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            break;
+#endif
+case BT_S: case BT_CR: case BT_LF:
+            for (;;) {
+                int t;
+
+                ptr += MINBPC(enc);
+                if (ptr == end)
+                    return XML_TOK_PARTIAL;
+                t = BYTE_TYPE(enc, ptr);
+                if (t == BT_EQUALS)
+                    break;
+                switch (t) {
+                case BT_S:
+                case BT_LF:
+                case BT_CR:
+                    break;
+                default:
+                    *nextTokPtr = ptr;
+                    return XML_TOK_INVALID;
+                }
+            }
+            /* fall through */
+        case BT_EQUALS:
+            {
+                int open;
+#ifdef XML_NS
+                hadColon = 0;
+#endif
+                for (;;) {
+
+                    ptr += MINBPC(enc);
+                    if (ptr == end)
+                        return XML_TOK_PARTIAL;
+                    open = BYTE_TYPE(enc, ptr);
+                    if (open == BT_QUOT || open == BT_APOS)
+                        break;
+                    switch (open) {
+                    case BT_S:
+                    case BT_LF:
+                    case BT_CR:
+                        break;
+                    default:
+                        *nextTokPtr = ptr;
+                        return XML_TOK_INVALID;
+                    }
+                }
+                ptr += MINBPC(enc);
+                /* in attribute value */
+                for (;;) {
+                    int t;
+                    if (ptr == end)
+                        return XML_TOK_PARTIAL;
+                    t = BYTE_TYPE(enc, ptr);
+                    if (t == open)
+                        break;
+                    switch (t) {
+                        INVALID_CASES(ptr, nextTokPtr)
+                    case BT_AMP:
+                        {
+                            int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr);
+                            if (tok <= 0) {
+                                if (tok == XML_TOK_INVALID)
+                                    *nextTokPtr = ptr;
+                                return tok;
+                            }
+                            break;
+                        }
+                    case BT_LT:
+                        *nextTokPtr = ptr;
+                        return XML_TOK_INVALID;
+                    default:
+                        ptr += MINBPC(enc);
+                        break;
+                    }
+                }
+                ptr += MINBPC(enc);
+                if (ptr == end)
+                    return XML_TOK_PARTIAL;
+                switch (BYTE_TYPE(enc, ptr)) {
+                case BT_S:
+                case BT_CR:
+                case BT_LF:
+                    break;
+                case BT_SOL:
+                    goto sol;
+                case BT_GT:
+                    goto gt;
+                default:
+                    *nextTokPtr = ptr;
+                    return XML_TOK_INVALID;
+                }
+                /* ptr points to closing quote */
+                for (;;) {
+                    ptr += MINBPC(enc);
+                    if (ptr == end)
+                        return XML_TOK_PARTIAL;
+                    switch (BYTE_TYPE(enc, ptr)) {
+                        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+            case BT_S: case BT_CR: case BT_LF:
+                        continue;
+                    case BT_GT:
+gt:
+                        *nextTokPtr = ptr + MINBPC(enc);
+                        return XML_TOK_START_TAG_WITH_ATTS;
+                    case BT_SOL:
+sol:
+                        ptr += MINBPC(enc);
+                        if (ptr == end)
+                            return XML_TOK_PARTIAL;
+                        if (!CHAR_MATCHES(enc, ptr, '>')) {
+                            *nextTokPtr = ptr;
+                            return XML_TOK_INVALID;
+                        }
+                        *nextTokPtr = ptr + MINBPC(enc);
+                        return XML_TOK_EMPTY_ELEMENT_WITH_ATTS;
+                    default:
+                        *nextTokPtr = ptr;
+                        return XML_TOK_INVALID;
+                    }
+                    break;
+                }
+                break;
+            }
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+/* ptr points to character following "<" */
+
+static
+int PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end,
+                   const char **nextTokPtr)
+{
+#ifdef XML_NS
+    int hadColon;
+#endif
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+    case BT_EXCL:
+        if ((ptr += MINBPC(enc)) == end)
+            return XML_TOK_PARTIAL;
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_MINUS:
+            return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+        case BT_LSQB:
+            return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+        }
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    case BT_QUEST:
+        return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_SOL:
+        return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+#ifdef XML_NS
+    hadColon = 0;
+#endif
+    /* we have a start-tag */
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+#ifdef XML_NS
+        case BT_COLON:
+            if (hadColon) {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            hadColon = 1;
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            switch (BYTE_TYPE(enc, ptr)) {
+                CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+            default:
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            break;
+#endif
+case BT_S: case BT_CR: case BT_LF:
+            {
+                ptr += MINBPC(enc);
+                while (ptr != end) {
+                    switch (BYTE_TYPE(enc, ptr)) {
+                        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+                    case BT_GT:
+                        goto gt;
+                    case BT_SOL:
+                        goto sol;
+            case BT_S: case BT_CR: case BT_LF:
+                        ptr += MINBPC(enc);
+                        continue;
+                    default:
+                        *nextTokPtr = ptr;
+                        return XML_TOK_INVALID;
+                    }
+                    return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr);
+                }
+                return XML_TOK_PARTIAL;
+            }
+        case BT_GT:
+gt:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_START_TAG_NO_ATTS;
+        case BT_SOL:
+sol:
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            if (!CHAR_MATCHES(enc, ptr, '>')) {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_EMPTY_ELEMENT_NO_ATTS;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_NONE;
+    if (MINBPC(enc) > 1) {
+        size_t n = end - ptr;
+        if (n & (MINBPC(enc) - 1)) {
+            n &= ~(MINBPC(enc) - 1);
+            if (n == 0)
+                return XML_TOK_PARTIAL;
+            end = ptr + n;
+        }
+    }
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_LT:
+        return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_AMP:
+        return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_CR:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_TRAILING_CR;
+        if (BYTE_TYPE(enc, ptr) == BT_LF)
+            ptr += MINBPC(enc);
+        *nextTokPtr = ptr;
+        return XML_TOK_DATA_NEWLINE;
+    case BT_LF:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DATA_NEWLINE;
+    case BT_RSQB:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_TRAILING_RSQB;
+        if (!CHAR_MATCHES(enc, ptr, ']'))
+            break;
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_TRAILING_RSQB;
+        if (!CHAR_MATCHES(enc, ptr, '>')) {
+            ptr -= MINBPC(enc);
+            break;
+        }
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+        INVALID_CASES(ptr, nextTokPtr)
+    default:
+        ptr += MINBPC(enc);
+        break;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \
+    *nextTokPtr = ptr; \
+    return XML_TOK_DATA_CHARS; \
+      } \
+      ptr += n; \
+      break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_RSQB:
+            if (ptr + MINBPC(enc) != end) {
+                if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ']')) {
+                    ptr += MINBPC(enc);
+                    break;
+                }
+                if (ptr + 2*MINBPC(enc) != end) {
+                    if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), '>')) {
+                        ptr += MINBPC(enc);
+                        break;
+                    }
+                    *nextTokPtr = ptr + 2*MINBPC(enc);
+                    return XML_TOK_INVALID;
+                }
+            }
+            /* fall through */
+        case BT_AMP:
+        case BT_LT:
+        case BT_NONXML:
+        case BT_MALFORM:
+        case BT_TRAIL:
+        case BT_CR:
+        case BT_LF:
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_CHARS;
+}
+
+/* ptr points to character following "%" */
+
+static
+int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end,
+                        const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+case BT_S: case BT_LF: case BT_CR: case BT_PERCNT:
+        *nextTokPtr = ptr;
+        return XML_TOK_PERCENT;
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+        case BT_SEMI:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_PARAM_ENTITY_REF;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end,
+                          const char **nextTokPtr)
+{
+    if (ptr == end)
+        return XML_TOK_PARTIAL;
+    switch (BYTE_TYPE(enc, ptr)) {
+        CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr)
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_CR: case BT_LF: case BT_S:
+case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR:
+            *nextTokPtr = ptr;
+            return XML_TOK_POUND_NAME;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(scanLit)(int open, const ENCODING *enc,
+                    const char *ptr, const char *end,
+                    const char **nextTokPtr)
+{
+    while (ptr != end) {
+        int t = BYTE_TYPE(enc, ptr);
+        switch (t) {
+            INVALID_CASES(ptr, nextTokPtr)
+        case BT_QUOT:
+        case BT_APOS:
+            ptr += MINBPC(enc);
+            if (t != open)
+                break;
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            *nextTokPtr = ptr;
+            switch (BYTE_TYPE(enc, ptr)) {
+    case BT_S: case BT_CR: case BT_LF:
+    case BT_GT: case BT_PERCNT: case BT_LSQB:
+                return XML_TOK_LITERAL;
+            default:
+                return XML_TOK_INVALID;
+            }
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end,
+                      const char **nextTokPtr)
+{
+    int tok;
+    if (ptr == end)
+        return XML_TOK_NONE;
+    if (MINBPC(enc) > 1) {
+        size_t n = end - ptr;
+        if (n & (MINBPC(enc) - 1)) {
+            n &= ~(MINBPC(enc) - 1);
+            if (n == 0)
+                return XML_TOK_PARTIAL;
+            end = ptr + n;
+        }
+    }
+    switch (BYTE_TYPE(enc, ptr)) {
+    case BT_QUOT:
+        return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_APOS:
+        return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_LT:
+        {
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                return XML_TOK_PARTIAL;
+            switch (BYTE_TYPE(enc, ptr)) {
+            case BT_EXCL:
+                return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+            case BT_QUEST:
+                return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+            case BT_NMSTRT:
+            case BT_HEX:
+            case BT_NONASCII:
+            case BT_LEAD2:
+            case BT_LEAD3:
+            case BT_LEAD4:
+                *nextTokPtr = ptr - MINBPC(enc);
+                return XML_TOK_INSTANCE_START;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    case BT_CR:
+        if (ptr + MINBPC(enc) == end)
+            return XML_TOK_TRAILING_CR;
+        /* fall through */
+case BT_S: case BT_LF:
+        for (;;) {
+            ptr += MINBPC(enc);
+            if (ptr == end)
+                break;
+            switch (BYTE_TYPE(enc, ptr)) {
+        case BT_S: case BT_LF:
+                break;
+            case BT_CR:
+                /* don't split CR/LF pair */
+                if (ptr + MINBPC(enc) != end)
+                    break;
+                /* fall through */
+            default:
+                *nextTokPtr = ptr;
+                return XML_TOK_PROLOG_S;
+            }
+        }
+        *nextTokPtr = ptr;
+        return XML_TOK_PROLOG_S;
+    case BT_PERCNT:
+        return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+    case BT_COMMA:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_COMMA;
+    case BT_LSQB:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_OPEN_BRACKET;
+    case BT_RSQB:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_PARTIAL;
+        if (CHAR_MATCHES(enc, ptr, ']')) {
+            if (ptr + MINBPC(enc) == end)
+                return XML_TOK_PARTIAL;
+            if (CHAR_MATCHES(enc, ptr + MINBPC(enc), '>')) {
+                *nextTokPtr = ptr + 2*MINBPC(enc);
+                return XML_TOK_COND_SECT_CLOSE;
+            }
+        }
+        *nextTokPtr = ptr;
+        return XML_TOK_CLOSE_BRACKET;
+    case BT_LPAR:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_OPEN_PAREN;
+    case BT_RPAR:
+        ptr += MINBPC(enc);
+        if (ptr == end)
+            return XML_TOK_PARTIAL;
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_AST:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_CLOSE_PAREN_ASTERISK;
+        case BT_QUEST:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_CLOSE_PAREN_QUESTION;
+        case BT_PLUS:
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_CLOSE_PAREN_PLUS;
+case BT_CR: case BT_LF: case BT_S:
+case BT_GT: case BT_COMMA: case BT_VERBAR:
+        case BT_RPAR:
+            *nextTokPtr = ptr;
+            return XML_TOK_CLOSE_PAREN;
+        }
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    case BT_VERBAR:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_OR;
+    case BT_GT:
+        *nextTokPtr = ptr + MINBPC(enc);
+        return XML_TOK_DECL_CLOSE;
+    case BT_NUM:
+        return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+#define LEAD_CASE(n) \
+  case BT_LEAD ## n: \
+    if (end - ptr < n) \
+      return XML_TOK_PARTIAL_CHAR; \
+    if (IS_NMSTRT_CHAR(enc, ptr, n)) { \
+      ptr += n; \
+      tok = XML_TOK_NAME; \
+      break; \
+    } \
+    if (IS_NAME_CHAR(enc, ptr, n)) { \
+      ptr += n; \
+      tok = XML_TOK_NMTOKEN; \
+      break; \
+    } \
+    *nextTokPtr = ptr; \
+    return XML_TOK_INVALID;
+        LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+    case BT_NMSTRT:
+    case BT_HEX:
+        tok = XML_TOK_NAME;
+        ptr += MINBPC(enc);
+        break;
+    case BT_DIGIT:
+    case BT_NAME:
+    case BT_MINUS:
+#ifdef XML_NS
+    case BT_COLON:
+#endif
+        tok = XML_TOK_NMTOKEN;
+        ptr += MINBPC(enc);
+        break;
+    case BT_NONASCII:
+        if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) {
+            ptr += MINBPC(enc);
+            tok = XML_TOK_NAME;
+            break;
+        }
+        if (IS_NAME_CHAR_MINBPC(enc, ptr)) {
+            ptr += MINBPC(enc);
+            tok = XML_TOK_NMTOKEN;
+            break;
+        }
+        /* fall through */
+    default:
+        *nextTokPtr = ptr;
+        return XML_TOK_INVALID;
+    }
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+            CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+case BT_GT: case BT_RPAR: case BT_COMMA:
+case BT_VERBAR: case BT_LSQB: case BT_PERCNT:
+case BT_S: case BT_CR: case BT_LF:
+            *nextTokPtr = ptr;
+            return tok;
+#ifdef XML_NS
+        case BT_COLON:
+            ptr += MINBPC(enc);
+            switch (tok) {
+            case XML_TOK_NAME:
+                if (ptr == end)
+                    return XML_TOK_PARTIAL;
+                tok = XML_TOK_PREFIXED_NAME;
+                switch (BYTE_TYPE(enc, ptr)) {
+                    CHECK_NAME_CASES(enc, ptr, end, nextTokPtr)
+                default:
+                    tok = XML_TOK_NMTOKEN;
+                    break;
+                }
+                break;
+            case XML_TOK_PREFIXED_NAME:
+                tok = XML_TOK_NMTOKEN;
+                break;
+            }
+            break;
+#endif
+        case BT_PLUS:
+            if (tok == XML_TOK_NMTOKEN)  {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_NAME_PLUS;
+        case BT_AST:
+            if (tok == XML_TOK_NMTOKEN)  {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_NAME_ASTERISK;
+        case BT_QUEST:
+            if (tok == XML_TOK_NMTOKEN)  {
+                *nextTokPtr = ptr;
+                return XML_TOK_INVALID;
+            }
+            *nextTokPtr = ptr + MINBPC(enc);
+            return XML_TOK_NAME_QUESTION;
+        default:
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        }
+    }
+    return XML_TOK_PARTIAL;
+}
+
+static
+int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+                              const char **nextTokPtr)
+{
+    const char *start;
+    if (ptr == end)
+        return XML_TOK_NONE;
+    start = ptr;
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_AMP:
+            if (ptr == start)
+                return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_LT:
+            /* this is for inside entity references */
+            *nextTokPtr = ptr;
+            return XML_TOK_INVALID;
+        case BT_LF:
+            if (ptr == start) {
+                *nextTokPtr = ptr + MINBPC(enc);
+                return XML_TOK_DATA_NEWLINE;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_CR:
+            if (ptr == start) {
+                ptr += MINBPC(enc);
+                if (ptr == end)
+                    return XML_TOK_TRAILING_CR;
+                if (BYTE_TYPE(enc, ptr) == BT_LF)
+                    ptr += MINBPC(enc);
+                *nextTokPtr = ptr;
+                return XML_TOK_DATA_NEWLINE;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_S:
+            if (ptr == start) {
+                *nextTokPtr = ptr + MINBPC(enc);
+                return XML_TOK_ATTRIBUTE_VALUE_S;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_CHARS;
+}
+
+static
+int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end,
+                           const char **nextTokPtr)
+{
+    const char *start;
+    if (ptr == end)
+        return XML_TOK_NONE;
+    start = ptr;
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_AMP:
+            if (ptr == start)
+                return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_PERCNT:
+            if (ptr == start)
+                return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr);
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_LF:
+            if (ptr == start) {
+                *nextTokPtr = ptr + MINBPC(enc);
+                return XML_TOK_DATA_NEWLINE;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        case BT_CR:
+            if (ptr == start) {
+                ptr += MINBPC(enc);
+                if (ptr == end)
+                    return XML_TOK_TRAILING_CR;
+                if (BYTE_TYPE(enc, ptr) == BT_LF)
+                    ptr += MINBPC(enc);
+                *nextTokPtr = ptr;
+                return XML_TOK_DATA_NEWLINE;
+            }
+            *nextTokPtr = ptr;
+            return XML_TOK_DATA_CHARS;
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+    }
+    *nextTokPtr = ptr;
+    return XML_TOK_DATA_CHARS;
+}
+
+static
+int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **badPtr)
+{
+    ptr += MINBPC(enc);
+    end -= MINBPC(enc);
+    for (; ptr != end; ptr += MINBPC(enc)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_DIGIT:
+        case BT_HEX:
+        case BT_MINUS:
+        case BT_APOS:
+        case BT_LPAR:
+        case BT_RPAR:
+        case BT_PLUS:
+        case BT_COMMA:
+        case BT_SOL:
+        case BT_EQUALS:
+        case BT_QUEST:
+        case BT_CR:
+        case BT_LF:
+        case BT_SEMI:
+        case BT_EXCL:
+        case BT_AST:
+        case BT_PERCNT:
+        case BT_NUM:
+#ifdef XML_NS
+        case BT_COLON:
+#endif
+            break;
+        case BT_S:
+            if (CHAR_MATCHES(enc, ptr, '\t')) {
+                *badPtr = ptr;
+                return 0;
+            }
+            break;
+        case BT_NAME:
+        case BT_NMSTRT:
+            if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f))
+                break;
+        default:
+            switch (BYTE_TO_ASCII(enc, ptr)) {
+            case 0x24: /* $ */
+            case 0x40: /* @ */
+                break;
+            default:
+                *badPtr = ptr;
+                return 0;
+            }
+            break;
+        }
+    }
+    return 1;
+}
+
+/* This must only be called for a well-formed start-tag or empty element tag.
+Returns the number of attributes.  Pointers to the first attsMax attributes 
+are stored in atts. */
+
+static
+int PREFIX(getAtts)(const ENCODING *enc, const char *ptr,
+                    int attsMax, ATTRIBUTE *atts)
+{
+    enum { other, inName, inValue } state = inName;
+    int nAtts = 0;
+    int open = 0;
+
+    for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define START_NAME \
+      if (state == other) { \
+    if (nAtts < attsMax) { \
+      atts[nAtts].name = ptr; \
+      atts[nAtts].normalized = 1; \
+    } \
+    state = inName; \
+      }
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_NONASCII:
+        case BT_NMSTRT:
+        case BT_HEX:
+            START_NAME
+            break;
+#undef START_NAME
+        case BT_QUOT:
+            if (state != inValue) {
+                if (nAtts < attsMax)
+                    atts[nAtts].valuePtr = ptr + MINBPC(enc);
+                state = inValue;
+                open = BT_QUOT;
+            }
+            else if (open == BT_QUOT) {
+                state = other;
+                if (nAtts < attsMax)
+                    atts[nAtts].valueEnd = ptr;
+                nAtts++;
+            }
+            break;
+        case BT_APOS:
+            if (state != inValue) {
+                if (nAtts < attsMax)
+                    atts[nAtts].valuePtr = ptr + MINBPC(enc);
+                state = inValue;
+                open = BT_APOS;
+            }
+            else if (open == BT_APOS) {
+                state = other;
+                if (nAtts < attsMax)
+                    atts[nAtts].valueEnd = ptr;
+                nAtts++;
+            }
+            break;
+        case BT_AMP:
+            if (nAtts < attsMax)
+                atts[nAtts].normalized = 0;
+            break;
+        case BT_S:
+            if (state == inName)
+                state = other;
+            else if (state == inValue
+                     && nAtts < attsMax
+                     && atts[nAtts].normalized
+                     && (ptr == atts[nAtts].valuePtr
+                         || BYTE_TO_ASCII(enc, ptr) != ' '
+                         || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ' '
+                         || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open))
+                atts[nAtts].normalized = 0;
+            break;
+    case BT_CR: case BT_LF:
+            /* This case ensures that the first attribute name is counted
+               Apart from that we could just change state on the quote. */
+            if (state == inName)
+                state = other;
+            else if (state == inValue && nAtts < attsMax)
+                atts[nAtts].normalized = 0;
+            break;
+        case BT_GT:
+        case BT_SOL:
+            if (state != inValue)
+                return nAtts;
+            break;
+        default:
+            break;
+        }
+    }
+    /* not reached */
+}
+
+static
+int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr)
+{
+    int result = 0;
+    /* skip &# */
+    ptr += 2*MINBPC(enc);
+    if (CHAR_MATCHES(enc, ptr, 'x')) {
+        for (ptr += MINBPC(enc); !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) {
+            int c = BYTE_TO_ASCII(enc, ptr);
+            switch (c) {
+case '0': case '1': case '2': case '3': case '4':
+case '5': case '6': case '7': case '8': case '9':
+                result <<= 4;
+                result |= (c - '0');
+                break;
+case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+                result <<= 4;
+                result += 10 + (c - 'A');
+                break;
+case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+                result <<= 4;
+                result += 10 + (c - 'a');
+                break;
+            }
+            if (result >= 0x110000)
+                return -1;
+        }
+    }
+    else {
+        for (; !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) {
+            int c = BYTE_TO_ASCII(enc, ptr);
+            result *= 10;
+            result += (c - '0');
+            if (result >= 0x110000)
+                return -1;
+        }
+    }
+    return checkCharRefNumber(result);
+}
+
+static
+int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end)
+{
+    switch ((end - ptr)/MINBPC(enc)) {
+    case 2:
+        if (CHAR_MATCHES(enc, ptr + MINBPC(enc), 't')) {
+            switch (BYTE_TO_ASCII(enc, ptr)) {
+            case 'l':
+                return '<';
+            case 'g':
+                return '>';
+            }
+        }
+        break;
+    case 3:
+        if (CHAR_MATCHES(enc, ptr, 'a')) {
+            ptr += MINBPC(enc);
+            if (CHAR_MATCHES(enc, ptr, 'm')) {
+                ptr += MINBPC(enc);
+                if (CHAR_MATCHES(enc, ptr, 'p'))
+                    return '&';
+            }
+        }
+        break;
+    case 4:
+        switch (BYTE_TO_ASCII(enc, ptr)) {
+        case 'q':
+            ptr += MINBPC(enc);
+            if (CHAR_MATCHES(enc, ptr, 'u')) {
+                ptr += MINBPC(enc);
+                if (CHAR_MATCHES(enc, ptr, 'o')) {
+                    ptr += MINBPC(enc);
+                    if (CHAR_MATCHES(enc, ptr, 't'))
+                        return '"';
+                }
+            }
+            break;
+        case 'a':
+            ptr += MINBPC(enc);
+            if (CHAR_MATCHES(enc, ptr, 'p')) {
+                ptr += MINBPC(enc);
+                if (CHAR_MATCHES(enc, ptr, 'o')) {
+                    ptr += MINBPC(enc);
+                    if (CHAR_MATCHES(enc, ptr, 's'))
+                        return '\'';
+                }
+            }
+            break;
+        }
+    }
+    return 0;
+}
+
+static
+int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+    for (;;) {
+        switch (BYTE_TYPE(enc, ptr1)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      if (*ptr1++ != *ptr2++) \
+    return 0;
+            LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2)
+#undef LEAD_CASE
+            /* fall through */
+            if (*ptr1++ != *ptr2++)
+                return 0;
+            break;
+        case BT_NONASCII:
+        case BT_NMSTRT:
+#ifdef XML_NS
+        case BT_COLON:
+#endif
+        case BT_HEX:
+        case BT_DIGIT:
+        case BT_NAME:
+        case BT_MINUS:
+            if (*ptr2++ != *ptr1++)
+                return 0;
+            if (MINBPC(enc) > 1) {
+                if (*ptr2++ != *ptr1++)
+                    return 0;
+                if (MINBPC(enc) > 2) {
+                    if (*ptr2++ != *ptr1++)
+                        return 0;
+                    if (MINBPC(enc) > 3) {
+                        if (*ptr2++ != *ptr1++)
+                            return 0;
+                    }
+                }
+            }
+            break;
+        default:
+            if (MINBPC(enc) == 1 && *ptr1 == *ptr2)
+                return 1;
+            switch (BYTE_TYPE(enc, ptr2)) {
+            case BT_LEAD2:
+            case BT_LEAD3:
+            case BT_LEAD4:
+            case BT_NONASCII:
+            case BT_NMSTRT:
+#ifdef XML_NS
+            case BT_COLON:
+#endif
+            case BT_HEX:
+            case BT_DIGIT:
+            case BT_NAME:
+            case BT_MINUS:
+                return 0;
+            default:
+                return 1;
+            }
+        }
+    }
+    /* not reached */
+}
+
+static
+int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *ptr2)
+{
+    for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) {
+        if (!CHAR_MATCHES(enc, ptr1, *ptr2))
+            return 0;
+    }
+    switch (BYTE_TYPE(enc, ptr1)) {
+    case BT_LEAD2:
+    case BT_LEAD3:
+    case BT_LEAD4:
+    case BT_NONASCII:
+    case BT_NMSTRT:
+#ifdef XML_NS
+    case BT_COLON:
+#endif
+    case BT_HEX:
+    case BT_DIGIT:
+    case BT_NAME:
+    case BT_MINUS:
+        return 0;
+    default:
+        return 1;
+    }
+}
+
+static
+int PREFIX(nameLength)(const ENCODING *enc, const char *ptr)
+{
+    const char *start = ptr;
+    for (;;) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: ptr += n; break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_NONASCII:
+        case BT_NMSTRT:
+#ifdef XML_NS
+        case BT_COLON:
+#endif
+        case BT_HEX:
+        case BT_DIGIT:
+        case BT_NAME:
+        case BT_MINUS:
+            ptr += MINBPC(enc);
+            break;
+        default:
+            return ptr - start;
+        }
+    }
+}
+
+static
+const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr)
+{
+    for (;;) {
+        switch (BYTE_TYPE(enc, ptr)) {
+        case BT_LF:
+        case BT_CR:
+        case BT_S:
+            ptr += MINBPC(enc);
+            break;
+        default:
+            return ptr;
+        }
+    }
+}
+
+static
+void PREFIX(updatePosition)(const ENCODING *enc,
+                            const char *ptr,
+                            const char *end,
+                            POSITION *pos)
+{
+    while (ptr != end) {
+        switch (BYTE_TYPE(enc, ptr)) {
+#define LEAD_CASE(n) \
+    case BT_LEAD ## n: \
+      ptr += n; \
+      break;
+            LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4)
+#undef LEAD_CASE
+        case BT_LF:
+            pos->columnNumber = (unsigned)-1;
+            pos->lineNumber++;
+            ptr += MINBPC(enc);
+            break;
+        case BT_CR:
+            pos->lineNumber++;
+            ptr += MINBPC(enc);
+            if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF)
+                ptr += MINBPC(enc);
+            pos->columnNumber = (unsigned)-1;
+            break;
+        default:
+            ptr += MINBPC(enc);
+            break;
+        }
+        pos->columnNumber++;
+    }
+}
+
+#undef DO_LEAD_CASE
+#undef MULTIBYTE_CASES
+#undef INVALID_CASES
+#undef CHECK_NAME_CASE
+#undef CHECK_NAME_CASES
+#undef CHECK_NMSTRT_CASE
+#undef CHECK_NMSTRT_CASES
Index: protocols/jabber/xmltok_impl.h
===================================================================
--- protocols/jabber/xmltok_impl.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_impl.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,71 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+Alternatively, the contents of this file may be used under the terms
+of the GNU General Public License (the "GPL"), in which case the
+provisions of the GPL are applicable instead of those above.  If you
+wish to allow use of your version of this file only under the terms of
+the GPL and not to allow others to use your version of this file under
+the MPL, indicate your decision by deleting the provisions above and
+replace them with the notice and other provisions required by the
+GPL. If you do not delete the provisions above, a recipient may use
+your version of this file under either the MPL or the GPL.
+*/
+
+enum {
+  BT_NONXML,
+  BT_MALFORM,
+  BT_LT,
+  BT_AMP,
+  BT_RSQB,
+  BT_LEAD2,
+  BT_LEAD3,
+  BT_LEAD4,
+  BT_TRAIL,
+  BT_CR,
+  BT_LF,
+  BT_GT,
+  BT_QUOT,
+  BT_APOS,
+  BT_EQUALS,
+  BT_QUEST,
+  BT_EXCL,
+  BT_SOL,
+  BT_SEMI,
+  BT_NUM,
+  BT_LSQB,
+  BT_S,
+  BT_NMSTRT,
+  BT_COLON,
+  BT_HEX,
+  BT_DIGIT,
+  BT_NAME,
+  BT_MINUS,
+  BT_OTHER, /* known not to be a name or name start character */
+  BT_NONASCII, /* might be a name or name start character */
+  BT_PERCNT,
+  BT_LPAR,
+  BT_RPAR,
+  BT_AST,
+  BT_PLUS,
+  BT_COMMA,
+  BT_VERBAR
+};
+
+#include <stddef.h>
Index: protocols/jabber/xmltok_ns.c
===================================================================
--- protocols/jabber/xmltok_ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,117 @@
+/*
+The contents of this file are subject to the Mozilla Public License
+Version 1.1 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+http://www.mozilla.org/MPL/
+
+Software distributed under the License is distributed on an "AS IS"
+basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+License for the specific language governing rights and limitations
+under the License.
+
+The Original Code is expat.
+
+The Initial Developer of the Original Code is James Clark.
+Portions created by James Clark are Copyright (C) 1998, 1999
+James Clark. All Rights Reserved.
+
+Contributor(s):
+
+*/
+
+const ENCODING *NS(XmlGetUtf8InternalEncoding)()
+{
+    return &ns(internal_utf8_encoding).enc;
+}
+
+const ENCODING *NS(XmlGetUtf16InternalEncoding)()
+{
+#if XML_BYTE_ORDER == 12
+    return &ns(internal_little2_encoding).enc;
+#elif XML_BYTE_ORDER == 21
+return &ns(internal_big2_encoding).enc;
+#else
+const short n = 1;
+    return *(const char *)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc;
+#endif
+}
+
+static
+const ENCODING *NS(encodings)[] = {
+    &ns(latin1_encoding).enc,
+    &ns(ascii_encoding).enc,
+    &ns(utf8_encoding).enc,
+    &ns(big2_encoding).enc,
+    &ns(big2_encoding).enc,
+    &ns(little2_encoding).enc,
+    &ns(utf8_encoding).enc /* NO_ENC */
+};
+
+static
+int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end,
+                       const char **nextTokPtr)
+{
+    return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr);
+}
+
+static
+int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end,
+                        const char **nextTokPtr)
+{
+    return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr);
+}
+
+int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name)
+{
+    int i = getEncodingIndex(name);
+    if (i == UNKNOWN_ENC)
+        return 0;
+    INIT_ENC_INDEX(p) = (char)i;
+    p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog);
+    p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent);
+    p->initEnc.updatePosition = initUpdatePosition;
+    p->encPtr = encPtr;
+    *encPtr = &(p->initEnc);
+    return 1;
+}
+
+static
+const ENCODING *NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end)
+{
+#define ENCODING_MAX 128
+    char buf[ENCODING_MAX];
+    char *p = buf;
+    int i;
+    XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1);
+    if (ptr != end)
+        return 0;
+    *p = 0;
+    if (streqci(buf, "UTF-16") && enc->minBytesPerChar == 2)
+        return enc;
+    i = getEncodingIndex(buf);
+    if (i == UNKNOWN_ENC)
+        return 0;
+    return NS(encodings)[i];
+}
+
+int NS(XmlParseXmlDecl)(int isGeneralTextEntity,
+                        const ENCODING *enc,
+                        const char *ptr,
+                        const char *end,
+                        const char **badPtr,
+                        const char **versionPtr,
+                        const char **encodingName,
+                        const ENCODING **encoding,
+                        int *standalone)
+{
+    return doParseXmlDecl(NS(findEncoding),
+                          isGeneralTextEntity,
+                          enc,
+                          ptr,
+                          end,
+                          badPtr,
+                          versionPtr,
+                          encodingName,
+                          encoding,
+                          standalone);
+}
Index: protocols/md5.c
===================================================================
--- protocols/md5.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/md5.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,392 @@
+/*
+  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321.
+  It is derived directly from the text of the RFC and not from the
+  reference implementation.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+#include <string.h>
+main()
+{
+    static const char *const test[7] = {
+	"", /*d41d8cd98f00b204e9800998ecf8427e*/
+	"945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+	"abc", /*900150983cd24fb0d6963f7d28e17f72*/
+	"message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+	"abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+				/*d174ab98d277d9f5a5611c2c9f419d9f*/
+	"12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+    };
+    int i;
+
+    for (i = 0; i < 7; ++i) {
+	md5_state_t state;
+	md5_byte_t digest[16];
+	int di;
+
+	md5_init(&state);
+	md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+	md5_finish(&state, digest);
+	printf("MD5 (\"%s\") = ", test[i]);
+	for (di = 0; di < 16; ++di)
+	    printf("%02x", digest[di]);
+	printf("\n");
+    }
+    return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+    int i;
+    for (i = 1; i <= 64; ++i) {
+	unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+	printf("#define T%d 0x%08lx\n", i, v);
+    }
+    return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+	a = pms->abcd[0], b = pms->abcd[1],
+	c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1	/* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+    /*
+     * On big-endian machines, we must arrange the bytes in the right
+     * order.  (This also works on machines of unknown byte order.)
+     */
+    md5_word_t X[16];
+    const md5_byte_t *xp = data;
+    int i;
+
+    for (i = 0; i < 16; ++i, xp += 4)
+	X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else  /* !ARCH_IS_BIG_ENDIAN */
+
+    /*
+     * On little-endian machines, we can process properly aligned data
+     * without copying it.
+     */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+
+    if (!((data - (const md5_byte_t *)0) & 3)) {
+	/* data are properly aligned */
+	X = (const md5_word_t *)data;
+    } else {
+	/* not aligned */
+	memcpy(xbuf, data, 64);
+	X = xbuf;
+    }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = 0xefcdab89;
+    pms->abcd[2] = 0x98badcfe;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+	return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+	pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+	int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+	memcpy(pms->buf + offset, p, copy);
+	if (offset + copy < 64)
+	    return;
+	p += copy;
+	left -= copy;
+	md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+	md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+	memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+	data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+	digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
Index: protocols/md5.h
===================================================================
--- protocols/md5.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/md5.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,85 @@
+/*
+  Copyright (C) 1999 Aladdin Enterprises.  All rights reserved.
+  
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321.
+  It is derived directly from the text of the RFC and not from the
+  reference implementation.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2004-03-09 Jelmer Vernooij add G_MODULE_EXPORT for Bitlbee
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+	added conditionalization for C++ compilation from Martin
+	Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+#include <glib.h>
+#include <gmodule.h>
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints.  Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];	/* message length in bits, lsw first */
+    md5_word_t abcd[4];		/* digest buffer */
+    md5_byte_t buf[64];		/* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+G_MODULE_EXPORT void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+G_MODULE_EXPORT void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+G_MODULE_EXPORT void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
Index: protocols/msn/Makefile
===================================================================
--- protocols/msn/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,39 @@
+###########################
+## Makefile for BitlBee  ##
+##                       ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = msn.o msn_util.o ns.o passport.o sb.o tables.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: msnn.o
+	
+.PHONY: all clean distclean
+
+clean:
+	rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+	@echo '*' Compiling $<
+	@$(CC) -c $(CFLAGS) $< -o $@
+
+msnn.o: $(objects)
+	@echo '*' Linking msnn.o
+	@$(LD) $(LFLAGS) $(objects) -o msnn.o
+	
+
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,402 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - Main file; functions to be called from BitlBee          */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+
+static struct prpl *my_protocol = NULL;
+
+static void msn_login( struct aim_user *acct )
+{
+	struct gaim_connection *gc = new_gaim_conn( acct );
+	struct msn_data *md = g_new0( struct msn_data, 1 );
+	
+	set_login_progress( gc, 1, "Connecting" );
+	
+	gc->proto_data = md;
+	md->fd = -1;
+	
+	if( strchr( acct->username, '@' ) == NULL )
+	{
+		hide_login_progress( gc, "Invalid account name" );
+		signoff( gc );
+		return;
+	}
+	
+	md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, gc );
+	if( md->fd < 0 )
+	{
+		hide_login_progress( gc, "Could not connect to server" );
+		signoff( gc );
+	}
+	else
+	{
+		md->gc = gc;
+		md->away_state = msn_away_state_list;
+		
+		msn_connections = g_slist_append( msn_connections, gc );
+	}
+}
+
+static void msn_close( struct gaim_connection *gc )
+{
+	struct msn_data *md = gc->proto_data;
+	GSList *l;
+	
+	if( md->fd >= 0 )
+		closesocket( md->fd );
+	
+	if( md->handler )
+	{
+		if( md->handler->rxq ) g_free( md->handler->rxq );
+		if( md->handler->cmd_text ) g_free( md->handler->cmd_text );
+		g_free( md->handler );
+	}
+	
+	while( md->switchboards )
+		msn_sb_destroy( md->switchboards->data );
+	
+	if( md->msgq )
+	{
+		struct msn_message *m;
+		
+		for( l = md->msgq; l; l = l->next )
+		{
+			m = l->data;
+			g_free( m->who );
+			g_free( m->text );
+			g_free( m );
+		}
+		g_slist_free( md->msgq );
+		
+		serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message(s), you'll have to resend them." );
+	}
+	
+	for( l = gc->permit; l; l = l->next )
+		g_free( l->data );
+	g_slist_free( gc->permit );
+	
+	for( l = gc->deny; l; l = l->next )
+		g_free( l->data );
+	g_slist_free( gc->deny );
+	
+	g_free( md );
+	
+	msn_connections = g_slist_remove( msn_connections, gc );
+}
+
+static int msn_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away )
+{
+	struct msn_switchboard *sb;
+	struct msn_data *md = gc->proto_data;
+	
+	if( ( sb = msn_sb_by_handle( gc, who ) ) )
+	{
+		return( msn_sb_sendmessage( sb, message ) );
+	}
+	else
+	{
+		struct msn_message *m;
+		char buf[1024];
+		
+		/* Create a message. We have to arrange a usable switchboard, and send the message later. */
+		m = g_new0( struct msn_message, 1 );
+		m->who = g_strdup( who );
+		m->text = g_strdup( message );
+		
+		/* FIXME: *CHECK* the reliability of using spare sb's! */
+		if( ( sb = msn_sb_spare( gc ) ) )
+		{
+			debug( "Trying to use a spare switchboard to message %s", who );
+			
+			sb->who = g_strdup( who );
+			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+			if( msn_sb_write( sb, buf, strlen( buf ) ) )
+			{
+				/* He/She should join the switchboard soon, let's queue the message. */
+				sb->msgq = g_slist_append( sb->msgq, m );
+				return( 1 );
+			}
+		}
+		
+		debug( "Creating a new switchboard to message %s", who );
+		
+		/* If we reach this line, there was no spare switchboard, so let's make one. */
+		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+		if( !msn_write( gc, buf, strlen( buf ) ) )
+		{
+			g_free( m->who );
+			g_free( m->text );
+			g_free( m );
+			
+			return( 0 );
+		}
+		
+		/* And queue the message to md. We'll pick it up when the switchboard comes up. */
+		md->msgq = g_slist_append( md->msgq, m );
+		
+		/* FIXME: If the switchboard creation fails, the message will not be sent. */
+		
+		return( 1 );
+	}
+	
+	return( 0 );
+}
+
+static GList *msn_away_states( struct gaim_connection *gc )
+{
+	GList *l = NULL;
+	int i;
+	
+	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+		l = g_list_append( l, msn_away_state_list[i].name );
+	
+	return( l );
+}
+
+static char *msn_get_status_string( struct gaim_connection *gc, int number )
+{
+	struct msn_away_state *st = msn_away_state_by_number( number );
+	
+	if( st )
+		return( st->name );
+	else
+		return( "" );
+}
+
+static void msn_set_away( struct gaim_connection *gc, char *state, char *message )
+{
+	char buf[1024];
+	struct msn_data *md = gc->proto_data;
+	struct msn_away_state *st;
+	
+	if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 )
+		st = msn_away_state_by_name( "Away" );
+	else
+		st = msn_away_state_by_name( state );
+	
+	if( !st ) st = msn_away_state_list;
+	md->away_state = st;
+	
+	g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code );
+	msn_write( gc, buf, strlen( buf ) );
+}
+
+static void msn_set_info( struct gaim_connection *gc, char *info )
+{
+	int i;
+	char buf[1024], *fn, *s;
+	struct msn_data *md = gc->proto_data;
+	
+	if( strlen( info ) > 129 )
+	{
+		do_error_dialog( gc, "Maximum name length exceeded", "MSN" );
+		return;
+	}
+	
+	/* Of course we could use http_encode() here, but when we encode
+	   every character, the server is less likely to complain about the
+	   chosen name. However, the MSN server doesn't seem to like escaped
+	   non-ASCII chars, so we keep those unescaped. */
+	s = fn = g_new0( char, strlen( info ) * 3 + 1 );
+	for( i = 0; info[i]; i ++ )
+		if( info[i] & 128 )
+		{
+			*s = info[i];
+			s ++;
+		}
+		else
+		{
+			g_snprintf( s, 4, "%%%02X", info[i] );
+			s += 3;
+		}
+	
+	g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn );
+	msn_write( gc, buf, strlen( buf ) );
+	g_free( fn );
+}
+
+static void msn_get_info(struct gaim_connection *gc, char *who) 
+{
+	/* Just make an URL and let the user fetch the info */
+	serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who );
+}
+
+static void msn_add_buddy( struct gaim_connection *gc, char *who )
+{
+	msn_buddy_list_add( gc, "FL", who, who );
+}
+
+static void msn_remove_buddy( struct gaim_connection *gc, char *who, char *group )
+{
+	msn_buddy_list_remove( gc, "FL", who );
+}
+
+static int msn_chat_send( struct gaim_connection *gc, int id, char *message )
+{
+	struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+	
+	if( sb )
+		return( msn_sb_sendmessage( sb, message ) );
+	else
+		return( 0 );
+}
+
+static void msn_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
+{
+	struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+	char buf[1024];
+	
+	if( sb )
+	{
+		g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+		msn_sb_write( sb, buf, strlen( buf ) );
+	}
+}
+
+static void msn_chat_leave( struct gaim_connection *gc, int id )
+{
+	struct msn_switchboard *sb = msn_sb_by_id( gc, id );
+	
+	if( sb )
+		msn_sb_write( sb, "OUT\r\n", 5 );
+}
+
+static int msn_chat_open( struct gaim_connection *gc, char *who )
+{
+	struct msn_switchboard *sb;
+	struct msn_data *md = gc->proto_data;
+	char buf[1024];
+	
+	if( ( sb = msn_sb_by_handle( gc, who ) ) )
+	{
+		debug( "Converting existing switchboard to %s to a groupchat", who );
+		msn_sb_to_chat( sb );
+		return( 1 );
+	}
+	else
+	{
+		struct msn_message *m;
+		
+		if( ( sb = msn_sb_spare( gc ) ) )
+		{
+			debug( "Trying to reuse an existing switchboard as a groupchat with %s", who );
+			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who );
+			if( msn_sb_write( sb, buf, strlen( buf ) ) )
+			{
+				msn_sb_to_chat( sb );
+				return( 1 );
+			}
+		}
+		
+		/* If the stuff above failed for some reason: */
+		debug( "Creating a new switchboard to groupchat with %s", who );
+		
+		/* Request a new switchboard. */
+		g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId );
+		if( !msn_write( gc, buf, strlen( buf ) ) )
+			return( 0 );
+		
+		/* Create a magic message. This is quite hackish, but who cares? :-P */
+		m = g_new0( struct msn_message, 1 );
+		m->who = g_strdup( who );
+		m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE );
+		
+		/* Queue the magic message and cross your fingers. */
+		md->msgq = g_slist_append( md->msgq, m );
+		
+		return( 1 );
+	}
+	
+	return( 0 );
+}
+
+static void msn_keepalive( struct gaim_connection *gc )
+{
+	msn_write( gc, "PNG\r\n", strlen( "PNG\r\n" ) );
+}
+
+static void msn_add_permit( struct gaim_connection *gc, char *who )
+{
+	msn_buddy_list_add( gc, "AL", who, who );
+}
+
+static void msn_rem_permit( struct gaim_connection *gc, char *who )
+{
+	msn_buddy_list_remove( gc, "AL", who );
+}
+
+static void msn_add_deny( struct gaim_connection *gc, char *who )
+{
+	struct msn_switchboard *sb;
+	
+	msn_buddy_list_add( gc, "BL", who, who );
+	
+	/* If there's still a conversation with this person, close it. */
+	if( ( sb = msn_sb_by_handle( gc, who ) ) )
+	{
+		msn_sb_destroy( sb );
+	}
+}
+
+static void msn_rem_deny( struct gaim_connection *gc, char *who )
+{
+	msn_buddy_list_remove( gc, "BL", who );
+}
+
+static int msn_send_typing( struct gaim_connection *gc, char *who, int typing )
+{
+	if( typing )
+		return( msn_send_im( gc, who, TYPING_NOTIFICATION_MESSAGE, strlen( TYPING_NOTIFICATION_MESSAGE ), 0 ) );
+	else
+		return( 1 );
+}
+
+void msn_init(struct prpl *ret)
+{
+	ret->protocol = PROTO_MSN;
+	ret->login = msn_login;
+	ret->close = msn_close;
+	ret->send_im = msn_send_im;
+	ret->away_states = msn_away_states;
+	ret->get_status_string = msn_get_status_string;
+	ret->set_away = msn_set_away;
+	ret->set_info = msn_set_info;
+	ret->get_info = msn_get_info;
+	ret->add_buddy = msn_add_buddy;
+	ret->remove_buddy = msn_remove_buddy;
+	ret->chat_send = msn_chat_send;
+	ret->chat_invite = msn_chat_invite;
+	ret->chat_leave = msn_chat_leave;
+	ret->chat_open = msn_chat_open;
+	ret->keepalive = msn_keepalive;
+	ret->add_permit = msn_add_permit;
+	ret->rem_permit = msn_rem_permit;
+	ret->add_deny = msn_add_deny;
+	ret->rem_deny = msn_rem_deny;
+	ret->send_typing = msn_send_typing;
+
+	my_protocol = ret;
+}
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,170 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module                                                           */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* Some hackish magicstrings to make special-purpose messages/switchboards.
+ */
+#define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"
+#define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r"
+
+#ifdef _WIN32
+#define debug 
+#else
+#define debug( text... ) irc_usermsg( IRC, text );
+#undef debug
+#define debug( text... )
+#endif
+
+#define QRY_NAME "msmsgs@msnmsgr.com"
+#define QRY_CODE "Q1P7W2E4J9R8U3S5"
+
+#define MSN_SB_NEW         -24062002
+
+#define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \
+                            "Content-Type: text/plain; charset=UTF-8\r\n" \
+                            "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+                            "X-MMS-IM-Format: FN=MS%20Shell%20Dlg; EF=; CO=0; CS=0; PF=0\r\n" \
+                            "\r\n"
+
+#define MSN_TYPING_HEADERS "MIME-Version: 1.0\r\n" \
+                           "Content-Type: text/x-msmsgscontrol\r\n" \
+                           "TypingUser: %s\r\n" \
+                           "\r\n\r\n"
+
+#define PROFILE_URL "http://members.msn.com/"
+
+struct msn_data
+{
+	struct gaim_connection *gc;
+	
+	int fd;
+	struct msn_handler_data *handler;
+	
+	int trId;
+	
+	GSList *msgq;
+	GSList *switchboards;
+	int buddycount;
+	struct msn_away_state *away_state;
+};
+
+struct msn_switchboard
+{
+	struct gaim_connection *gc;
+	
+	int fd;
+	gint inp;
+	struct msn_handler_data *handler;
+	
+	int trId;
+	int ready;
+	
+	int session;
+	char *key;
+	
+	GSList *msgq;
+	char *who;
+	struct conversation *chat;
+};
+
+struct msn_away_state
+{
+	int number;
+	char code[4];
+	char name[16];
+};
+
+struct msn_status_code
+{
+	int number;
+	char *text;
+	int flags;
+};
+
+struct msn_message
+{
+	char *who;
+	char *text;
+};
+
+struct msn_handler_data
+{
+	int fd;
+	int rxlen;
+	char *rxq;
+	
+	int msglen;
+	char *cmd_text;
+	
+	gpointer data;
+	
+	int (*exec_command) ( gpointer data, char **cmd, int count );
+	int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count );
+};
+
+/* Bitfield values for msn_status_code.flags */
+#define STATUS_FATAL            1
+#define STATUS_SB_FATAL         2
+
+int msn_chat_id;
+extern struct msn_away_state msn_away_state_list[];
+extern struct msn_status_code msn_status_code_list[];
+
+/* Keep a list of all the active connections. We need these lists because
+   "connected" callbacks might be called when the connection they belong too
+   is down already (for example, when an impatient user disabled the
+   connection), the callback should check whether it's still listed here
+   before doing *anything* else. */
+GSList *msn_connections;
+GSList *msn_switchboards;
+
+/* ns.c */
+void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond );
+
+/* msn_util.c */
+int msn_write( struct gaim_connection *gc, char *s, int len );
+int msn_logged_in( struct gaim_connection *gc );
+int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname );
+int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who );
+void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname );
+char *msn_findheader( char *text, char *header, int len );
+char **msn_linesplit( char *line );
+int msn_handler( struct msn_handler_data *h );
+
+/* tables.c */
+struct msn_away_state *msn_away_state_by_number( int number );
+struct msn_away_state *msn_away_state_by_code( char *code );
+struct msn_away_state *msn_away_state_by_name( char *name );
+struct msn_status_code *msn_status_by_number( int number );
+
+/* sb.c */
+int msn_sb_write( struct msn_switchboard *sb, char *s, int len );
+struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session );
+struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle );
+struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id );
+struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc );
+int msn_sb_sendmessage( struct msn_switchboard *sb, char *text );
+void msn_sb_to_chat( struct msn_switchboard *sb );
+void msn_sb_destroy( struct msn_switchboard *sb );
+void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond );
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,359 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - Miscellaneous utilities                                 */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+#include <ctype.h>
+
+int msn_write( struct gaim_connection *gc, char *s, int len )
+{
+	struct msn_data *md = gc->proto_data;
+	int st;
+	
+	st = write( md->fd, s, len );
+	if( st != len )
+	{
+		hide_login_progress_error( gc, "Short write() to main server" );
+		signoff( gc );
+		return( 0 );
+	}
+	
+	return( 1 );
+}
+
+int msn_logged_in( struct gaim_connection *gc )
+{
+	struct msn_data *md = gc->proto_data;
+	char buf[1024];
+	
+	account_online( gc );
+	
+	/* account_online() sets an away state if there is any, so only
+	   execute this code if we're not away. */
+	if( md->away_state == msn_away_state_list )
+	{
+		g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
+		return( msn_write( gc, buf, strlen( buf ) ) );
+	}
+	
+	return( 0 );
+}
+
+int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname_ )
+{
+	struct msn_data *md = gc->proto_data;
+	GSList *l, **lp = NULL;
+	char buf[1024], *realname;
+	
+	if( strcmp( list, "AL" ) == 0 )
+		lp = &gc->permit;
+	else if( strcmp( list, "BL" ) == 0 )
+		lp = &gc->deny;
+	
+	if( lp )
+		for( l = *lp; l; l = l->next )
+			if( g_strcasecmp( l->data, who ) == 0 )
+				return( 1 );
+	
+	realname = g_new0( char, strlen( realname_ ) * 3 + 1 );
+	strcpy( realname, realname_ );
+	http_encode( realname );
+	
+	g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname );
+	if( msn_write( gc, buf, strlen( buf ) ) )
+	{
+		g_free( realname );
+		
+		if( lp )
+			*lp = g_slist_append( *lp, g_strdup( who ) );
+		
+		return( 1 );
+	}
+	
+	g_free( realname );
+	
+	return( 0 );
+}
+
+int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who )
+{
+	struct msn_data *md = gc->proto_data;
+	GSList *l = NULL, **lp = NULL;
+	char buf[1024];
+	
+	if( strcmp( list, "AL" ) == 0 )
+		lp = &gc->permit;
+	else if( strcmp( list, "BL" ) == 0 )
+		lp = &gc->deny;
+	
+	if( lp )
+	{
+		for( l = *lp; l; l = l->next )
+			if( g_strcasecmp( l->data, who ) == 0 )
+				break;
+		
+		if( !l )
+			return( 1 );
+	}
+	
+	g_snprintf( buf, sizeof( buf ), "REM %d %s %s\r\n", ++md->trId, list, who );
+	if( msn_write( gc, buf, strlen( buf ) ) )
+	{
+		if( lp )
+			*lp = g_slist_remove( *lp, l->data );
+		
+		return( 1 );
+	}
+	
+	return( 0 );
+}
+
+struct msn_buddy_ask_data
+{
+	struct gaim_connection *gc;
+	char *handle;
+	char *realname;
+};
+
+static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
+{
+	msn_buddy_list_add( bla->gc, "AL", bla->handle, bla->realname );
+	
+	g_free( bla->handle );
+	g_free( bla->realname );
+	g_free( bla );
+}
+
+static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
+{
+	msn_buddy_list_add( bla->gc, "BL", bla->handle, bla->realname );
+	
+	g_free( bla->handle );
+	g_free( bla->realname );
+	g_free( bla );
+}
+
+void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname )
+{
+	struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 );
+	char buf[1024];
+	
+	bla->gc = gc;
+	bla->handle = g_strdup( handle );
+	bla->realname = g_strdup( realname );
+	
+	g_snprintf( buf, sizeof( buf ),
+	            "The user %s (%s) wants to add you to his/her buddy list. Do you want to allow this?",
+	            handle, realname );
+	do_ask_dialog( gc, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no );
+}
+
+char *msn_findheader( char *text, char *header, int len )
+{
+	int hlen = strlen( header ), i;
+	char *ret;
+	
+	if( len == 0 )
+		len = strlen( text );
+	
+	i = 0;
+	while( ( i + hlen ) < len )
+	{
+		/* Maybe this is a bit over-commented, but I just hate this part... */
+		if( g_strncasecmp( text + i, header, hlen ) == 0 )
+		{
+			/* Skip to the (probable) end of the header */
+			i += hlen;
+			
+			/* Find the first non-[: \t] character */
+			while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++;
+			
+			/* Make sure we're still inside the string */
+			if( i >= len ) return( NULL );
+			
+			/* Save the position */
+			ret = text + i;
+			
+			/* Search for the end of this line */
+			while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++;
+			
+			/* Make sure we're still inside the string */
+			if( i >= len ) return( NULL );
+			
+			/* Copy the found data */
+			return( g_strndup( ret, text + i - ret ) );
+		}
+		
+		/* This wasn't the header we were looking for, skip to the next line. */
+		while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++;
+		while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++;
+		
+		/* End of headers? */
+		if( strncmp( text + i - 2, "\n\n", 2 ) == 0 ||
+		    strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 ||
+		    strncmp( text + i - 2, "\r\r", 2 ) == 0 )
+		{
+			break;
+		}
+	}
+	
+	return( NULL );
+}
+
+/* *NOT* thread-safe, but that's not a problem for now... */
+char **msn_linesplit( char *line )
+{
+	static char **ret = NULL;
+	static int size = 3;
+	int i, n = 0;
+	
+	if( ret == NULL )
+		ret = g_new0( char*, size );
+	
+	for( i = 0; line[i] && line[i] == ' '; i ++ );
+	if( line[i] )
+	{
+		ret[n++] = line + i;
+		for( i ++; line[i]; i ++ )
+		{
+			if( line[i] == ' ' )
+				line[i] = 0;
+			else if( line[i] != ' ' && !line[i-1] )
+				ret[n++] = line + i;
+			
+			if( n >= size )
+				ret = g_renew( char*, ret, size += 2 );
+		}
+	}
+	ret[n] = NULL;
+	
+	return( ret );
+}
+
+/* This one handles input from a MSN Messenger server. Both the NS and SB servers usually give
+   commands, but sometimes they give additional data (payload). This function tries to handle
+   this all in a nice way and send all data to the right places. */
+
+/* Return values: -1: Read error, abort connection.
+                   0: Command reported error; Abort *immediately*. (The connection does not exist anymore)
+                   1: OK */
+
+int msn_handler( struct msn_handler_data *h )
+{
+	int st;
+	
+	h->rxq = g_renew( char, h->rxq, h->rxlen + 1024 );
+	st = read( h->fd, h->rxq + h->rxlen, 1024 );
+	h->rxlen += st;
+	
+	if( st <= 0 )
+		return( -1 );
+	
+	while( st )
+	{
+		int i;
+		
+		if( h->msglen == 0 )
+		{
+			for( i = 0; i < h->rxlen; i ++ )
+			{
+				if( h->rxq[i] == '\r' || h->rxq[i] == '\n' )
+				{
+					char *cmd_text, **cmd;
+					int count;
+					
+					cmd_text = g_strndup( h->rxq, i );
+					cmd = msn_linesplit( cmd_text );
+					for( count = 0; cmd[count]; count ++ );
+					st = h->exec_command( h->data, cmd, count );
+					g_free( cmd_text );
+					
+					/* If the connection broke, don't continue. We don't even exist anymore. */
+					if( !st )
+						return( 0 );
+					
+					if( h->msglen )
+						h->cmd_text = g_strndup( h->rxq, i );
+					
+					/* Skip to the next non-emptyline */
+					while( i < h->rxlen && ( h->rxq[i] == '\r' || h->rxq[i] == '\n' ) ) i ++;
+					
+					break;
+				}
+			}
+			
+			/* If we reached the end of the buffer, there's still an incomplete command there.
+			   Return and wait for more data. */
+			if( i == h->rxlen && h->rxq[i-1] != '\r' && h->rxq[i-1] != '\n' )
+				break;
+		}
+		else
+		{
+			char *msg, **cmd;
+			int count;
+			
+			/* Do we have the complete message already? */
+			if( h->msglen > h->rxlen )
+				break;
+			
+			msg = g_strndup( h->rxq, h->msglen );
+			cmd = msn_linesplit( h->cmd_text );
+			for( count = 0; cmd[count]; count ++ );
+			
+			st = h->exec_message( h->data, msg, h->msglen, cmd, count );
+			g_free( msg );
+			g_free( h->cmd_text );
+			h->cmd_text = NULL;
+			
+			if( !st )
+				return( 0 );
+			
+			i = h->msglen;
+			h->msglen = 0;
+		}
+		
+		/* More data after this block? */
+		if( i < h->rxlen )
+		{
+			char *tmp;
+			
+			tmp = g_memdup( h->rxq + i, h->rxlen - i );
+			g_free( h->rxq );
+			h->rxq = tmp;
+			h->rxlen -= i;
+			i = 0;
+		}
+		else
+		/* If not, reset the rx queue and get lost. */
+		{
+			g_free( h->rxq );
+			h->rxq = g_new0( char, 1 );
+			h->rxlen = 0;
+			return( 1 );
+		}
+	}
+	
+	return( 1 );
+}
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,661 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - Notification server callbacks                           */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <ctype.h>
+#include "nogaim.h"
+#include "msn.h"
+#include "passport.h"
+#include "md5.h"
+
+static void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond );
+static int msn_ns_command( gpointer data, char **cmd, int num_parts );
+static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
+
+static void msn_auth_got_passport_id( struct passport_reply *rep );
+
+void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct gaim_connection *gc = data;
+	struct msn_data *md;
+	char s[1024];
+	
+	if( !g_slist_find( msn_connections, gc ) )
+		return;
+	
+	if( source == -1 )
+	{
+		hide_login_progress( gc, "Could not connect to server" );
+		signoff( gc );
+		return;
+	}
+	
+	md = gc->proto_data;
+	
+	if( !md->handler )
+	{
+		md->handler = g_new0( struct msn_handler_data, 1 );
+		md->handler->data = gc;
+		md->handler->exec_command = msn_ns_command;
+		md->handler->exec_message = msn_ns_message;
+	}
+	else
+	{
+		if( md->handler->rxq )
+			g_free( md->handler->rxq );
+		
+		md->handler->rxlen = 0;
+	}
+	
+	md->handler->fd = md->fd;
+	md->handler->rxq = g_new0( char, 1 );
+	
+	g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId );
+	if( msn_write( gc, s, strlen( s ) ) )
+	{
+		gc->inpa = gaim_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, gc );
+		set_login_progress( gc, 1, "Connected to server, waiting for reply" );
+	}
+}
+
+void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct gaim_connection *gc = data;
+	struct msn_data *md = gc->proto_data;
+	
+	if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */
+	{
+		hide_login_progress( gc, "Error while reading from server" );
+		signoff( gc );
+	}
+}
+
+static int msn_ns_command( gpointer data, char **cmd, int num_parts )
+{
+	struct gaim_connection *gc = data;
+	struct msn_data *md = gc->proto_data;
+	char buf[1024];
+	
+	if( num_parts == 0 )
+	{
+		/* Hrrm... Empty command...? Ignore? */
+		return( 1 );
+	}
+	
+	if( strcmp( cmd[0], "VER" ) == 0 )
+	{
+		if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 )
+		{
+			hide_login_progress( gc, "Unsupported protocol" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n",
+		                                ++md->trId, gc->username );
+		return( msn_write( gc, buf, strlen( buf ) ) );
+	}
+	else if( strcmp( cmd[0], "CVR" ) == 0 )
+	{
+		/* We don't give a damn about the information we just received */
+		g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, gc->username );
+		return( msn_write( gc, buf, strlen( buf ) ) );
+	}
+	else if( strcmp( cmd[0], "XFR" ) == 0 )
+	{
+		char *server;
+		int port;
+		
+		if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 )
+		{
+			gaim_input_remove( gc->inpa );
+			gc->inpa = 0;
+			closesocket( md->fd );
+			
+			server = strchr( cmd[3], ':' );
+			if( !server )
+			{
+				hide_login_progress_error( gc, "Syntax error" );
+				signoff( gc );
+				return( 0 );
+			}
+			*server = 0;
+			port = atoi( server + 1 );
+			server = cmd[3];
+			
+			set_login_progress( gc, 1, "Transferring to other server" );
+			
+			md->fd = proxy_connect( server, port, msn_ns_connected, gc );
+		}
+		else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 )
+		{
+			struct msn_switchboard *sb;
+			
+			server = strchr( cmd[3], ':' );
+			if( !server )
+			{
+				hide_login_progress_error( gc, "Syntax error" );
+				signoff( gc );
+				return( 0 );
+			}
+			*server = 0;
+			port = atoi( server + 1 );
+			server = cmd[3];
+			
+			if( strcmp( cmd[4], "CKI" ) != 0 )
+			{
+				hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
+				signoff( gc );
+				return( 0 );
+			}
+			
+			debug( "Connecting to a new switchboard with key %s", cmd[5] );
+			sb = msn_sb_create( gc, server, port, cmd[5], MSN_SB_NEW );
+			
+			if( md->msgq )
+			{
+				struct msn_message *m = md->msgq->data;
+				GSList *l;
+				
+				sb->who = g_strdup( m->who );
+				
+				/* Move all the messages to the first user in the message
+				   queue to the switchboard message queue. */
+				l = md->msgq;
+				while( l )
+				{
+					m = l->data;
+					l = l->next;
+					if( strcmp( m->who, sb->who ) == 0 )
+					{
+						sb->msgq = g_slist_append( sb->msgq, m );
+						md->msgq = g_slist_remove( md->msgq, m );
+					}
+				}
+			}
+		}
+		else
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else if( strcmp( cmd[0], "USR" ) == 0 )
+	{
+		if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 )
+		{
+			/* Time for some Passport black magic... */
+			if( !passport_get_id( gc, gc->username, gc->password, cmd[4], msn_auth_got_passport_id ) )
+			{
+				hide_login_progress_error( gc, "Error while contacting Passport server" );
+				signoff( gc );
+				return( 0 );
+			}
+		}
+		else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 )
+		{
+			http_decode( cmd[4] );
+			
+			strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
+			gc->displayname[sizeof(gc->displayname)-1] = 0;
+			
+			set_login_progress( gc, 1, "Authenticated, getting buddy list" );
+			
+			g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId );
+			return( msn_write( gc, buf, strlen( buf ) ) );
+		}
+		else
+		{
+			hide_login_progress( gc, "Unknown authentication type" );
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else if( strcmp( cmd[0], "MSG" ) == 0 )
+	{
+		if( num_parts != 4 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		md->handler->msglen = atoi( cmd[3] );
+		
+		if( md->handler->msglen <= 0 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else if( strcmp( cmd[0], "SYN" ) == 0 )
+	{
+		if( num_parts == 5 )
+		{
+			md->buddycount = atoi( cmd[3] );
+			
+			if( !*cmd[3] || md->buddycount == 0 )
+				msn_logged_in( gc );
+		}
+		else
+		{
+			/* Hrrm... This SYN reply doesn't really look like something we expected.
+			   Let's assume everything is okay. */
+			
+			msn_logged_in( gc );
+		}
+	}
+	else if( strcmp( cmd[0], "GTC" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "BLP" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "PRP" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "LSG" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "LST" ) == 0 )
+	{
+		int list;
+		
+		if( num_parts != 4 && num_parts != 5 )
+		{
+			hide_login_progress( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		http_decode( cmd[2] );
+		list = atoi( cmd[3] );
+		
+		if( list & 1 ) /* FL */
+		{
+			add_buddy( gc, NULL, cmd[1], cmd[2] );
+		}
+		if( list & 2 ) /* AL */
+		{
+			gc->permit = g_slist_append( gc->permit, g_strdup( cmd[1] ) );
+		}
+		if( list & 4 ) /* BL */
+		{
+			gc->deny = g_slist_append( gc->deny, g_strdup( cmd[1] ) );
+		}
+		if( list & 8 ) /* RL */
+		{
+			if( ( list & 6 ) == 0 )
+				msn_buddy_ask( gc, cmd[1], cmd[2] );
+		}
+		
+		if( --md->buddycount == 0 )
+		{
+			if( gc->flags & OPT_LOGGED_IN )
+			{
+				serv_got_crap( gc, "Successfully transferred to different server" );
+				g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
+				return( msn_write( gc, buf, strlen( buf ) ) );
+			}
+			else
+			{
+				msn_logged_in( gc );
+			}
+		}
+	}
+	else if( strcmp( cmd[0], "BPR" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "CHG" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "CHL" ) == 0 )
+	{
+		md5_state_t state;
+		md5_byte_t digest[16];
+		int i;
+		
+		if( num_parts != 3 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		md5_init( &state );
+		md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) );
+		md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) );
+		md5_finish( &state, digest );
+		
+		g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 );
+		for( i = 0; i < 16; i ++ )
+			g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] );
+		
+		return( msn_write( gc, buf, strlen( buf ) ) );
+	}
+	else if( strcmp( cmd[0], "QRY" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "QNG" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "ILN" ) == 0 )
+	{
+		struct msn_away_state *st;
+		
+		if( num_parts != 6 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		http_decode( cmd[4] );
+		serv_buddy_rename( gc, cmd[3], cmd[4] );
+		
+		st = msn_away_state_by_code( cmd[2] );
+		if( !st )
+		{
+			/* FIXME: Warn/Bomb about unknown away state? */
+			st = msn_away_state_list;
+		}
+		
+		serv_got_update( gc, cmd[3], 1, 0, 0, 0, st->number, 0 );
+	}
+	else if( strcmp( cmd[0], "FLN" ) == 0 )
+	{
+		if( cmd[1] )
+			serv_got_update( gc, cmd[1], 0, 0, 0, 0, 0, 0 );
+	}
+	else if( strcmp( cmd[0], "NLN" ) == 0 )
+	{
+		struct msn_away_state *st;
+		
+		if( num_parts != 5 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		http_decode( cmd[3] );
+		serv_buddy_rename( gc, cmd[2], cmd[3] );
+		
+		st = msn_away_state_by_code( cmd[1] );
+		if( !st )
+		{
+			/* FIXME: Warn/Bomb about unknown away state? */
+			st = msn_away_state_list;
+		}
+		
+		serv_got_update( gc, cmd[2], 1, 0, 0, 0, st->number, 0 );
+	}
+	else if( strcmp( cmd[0], "RNG" ) == 0 )
+	{
+		struct msn_switchboard *sb;
+		char *server;
+		int session, port;
+		
+		if( num_parts != 7 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		session = atoi( cmd[1] );
+		
+		server = strchr( cmd[2], ':' );
+		if( !server )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		*server = 0;
+		port = atoi( server + 1 );
+		server = cmd[2];
+		
+		if( strcmp( cmd[3], "CKI" ) != 0 )
+		{
+			hide_login_progress_error( gc, "Unknown authentication method for switchboard" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] );
+		
+		sb = msn_sb_create( gc, server, port, cmd[4], session );
+		sb->who = g_strdup( cmd[5] );
+	}
+	else if( strcmp( cmd[0], "ADD" ) == 0 )
+	{
+		if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 )
+		{
+			GSList *l;
+			
+			http_decode( cmd[5] );
+			
+			if( strchr( cmd[4], '@' ) == NULL )
+			{
+				hide_login_progress_error( gc, "Syntax error" );
+				signoff( gc );
+				return( 0 );
+			}
+			
+			/* We got added by someone. If we don't have this person in permit/deny yet, inform the user. */
+			for( l = gc->permit; l; l = l->next )
+				if( g_strcasecmp( l->data, cmd[4] ) == 0 )
+					return( 1 );
+			
+			for( l = gc->deny; l; l = l->next )
+				if( g_strcasecmp( l->data, cmd[4] ) == 0 )
+					return( 1 );
+			
+			msn_buddy_ask( gc, cmd[4], cmd[5] );
+		}
+	}
+	else if( strcmp( cmd[0], "REM" ) == 0 )
+	{
+	}
+	else if( strcmp( cmd[0], "OUT" ) == 0 )
+	{
+		if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 )
+		{
+			hide_login_progress_error( gc, "Someone else logged in with your account" );
+			gc->wants_to_die = 1;
+		}
+		else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 )
+		{
+			hide_login_progress_error( gc, "Terminating session because of server shutdown" );
+		}
+		else
+		{
+			hide_login_progress_error( gc, "Session terminated by remote server (reason unknown)" );
+		}
+		
+		signoff( gc );
+		return( 0 );
+	}
+	else if( strcmp( cmd[0], "REA" ) == 0 )
+	{
+		if( num_parts != 5 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+		
+		if( g_strcasecmp( cmd[3], gc->username ) == 0 )
+		{
+			http_decode( cmd[4] );
+			strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) );
+			gc->displayname[sizeof(gc->displayname)-1] = 0;
+		}
+		else
+		{
+			/* This is not supposed to happen, but let's handle it anyway... */
+			http_decode( cmd[4] );
+			serv_buddy_rename( gc, cmd[3], cmd[4] );
+		}
+	}
+	else if( strcmp( cmd[0], "IPG" ) == 0 )
+	{
+		do_error_dialog( gc, "Received IPG command, we don't handle them yet.", "MSN" );
+		
+		md->handler->msglen = atoi( cmd[1] );
+		
+		if( md->handler->msglen <= 0 )
+		{
+			hide_login_progress_error( gc, "Syntax error" );
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else if( isdigit( cmd[0][0] ) )
+	{
+		int num = atoi( cmd[0] );
+		struct msn_status_code *err = msn_status_by_number( num );
+		
+		g_snprintf( buf, sizeof( buf ), "Error reported by MSN server: %s", err->text );
+		do_error_dialog( gc, buf, "MSN" );
+		
+		if( err->flags & STATUS_FATAL )
+		{
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else
+	{
+		debug( "Received unknown command from main server: %s", cmd[0] );
+	}
+	
+	return( 1 );
+}
+
+static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+{
+	struct gaim_connection *gc = data;
+	char *body;
+	int blen = 0;
+	
+	if( !num_parts )
+		return( 1 );
+	
+	if( ( body = strstr( msg, "\r\n\r\n" ) ) )
+	{
+		body += 4;
+		blen = msglen - ( body - msg );
+	}
+	
+	if( strcmp( cmd[0], "MSG" ) == 0 )
+	{
+		if( g_strcasecmp( cmd[1], "Hotmail" ) == 0 )
+		{
+			char *ct = msn_findheader( msg, "Content-Type:", msglen );
+			
+			if( !ct )
+				return( 1 );
+			
+			if( g_strncasecmp( ct, "application/x-msmsgssystemmessage", 33 ) == 0 )
+			{
+				char *mtype;
+				char *arg1;
+				
+				if( !body )
+					return( 1 );
+				
+				mtype = msn_findheader( body, "Type:", blen );
+				arg1 = msn_findheader( body, "Arg1:", blen );
+				
+				if( mtype && strcmp( mtype, "1" ) == 0 )
+				{
+					if( arg1 )
+						serv_got_crap( gc, "The server is going down for maintenance in %s minutes.", arg1 );
+				}
+				
+				if( arg1 ) g_free( arg1 );
+				if( mtype ) g_free( mtype );
+			}
+			else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 )
+			{
+				/* We don't care about this profile for now... */
+			}
+			else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 )
+			{
+				char *inbox = msn_findheader( body, "Inbox-Unread:", blen );
+				char *folders = msn_findheader( body, "Folders-Unread:", blen );
+				
+				if( inbox && folders )
+				{
+					serv_got_crap( gc, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders );
+				}
+			}
+			else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 )
+			{
+				char *from = msn_findheader( body, "From-Addr:", blen );
+				char *fromname = msn_findheader( body, "From:", blen );
+				
+				if( from && fromname )
+				{
+					serv_got_crap( gc, "Received an e-mail message from %s <%s>.", fromname, from );
+				}
+			}
+			else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 )
+			{
+				/* Sorry, but this one really is *USELESS* */
+			}
+			else
+			{
+				debug( "Can't handle %s packet from notification server", ct );
+			}
+			
+			g_free( ct );
+		}
+	}
+	
+	return( 1 );
+}
+
+static void msn_auth_got_passport_id( struct passport_reply *rep )
+{
+	struct gaim_connection *gc = rep->data;
+	struct msn_data *md = gc->proto_data;
+	char *key = rep->result;
+	char buf[1024];
+	
+	if( key == NULL )
+	{
+		hide_login_progress( gc, "Error during Passport authentication" );
+		signoff( gc );
+	}
+	else
+	{
+		g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
+		msn_write( gc, buf, strlen( buf ) );
+	}
+}
Index: protocols/msn/passport.c
===================================================================
--- protocols/msn/passport.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/passport.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,313 @@
+/* passport.c
+ *
+ * Functions to login to microsoft passport service for Messenger
+ * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>
+ * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify             
+ * it under the terms of the GNU General Public License version 2                   
+ * as published by the Free Software Foundation                                     
+ *                                                                                   
+ * This program is distributed in the hope that is will be useful,                  
+ * bit WITHOU ANY WARRANTY; without even the implied warranty of                   
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    
+ * GNU General Public License for more details.                                     
+ *                                                                                   
+ * You should have received a copy of the GNU General Public License                
+ * along with this program; if not, write to the Free Software                      
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
+ *
+ */
+
+#include "ssl_client.h"
+#include "passport.h"
+#include "msn.h"
+#include "bitlbee.h"
+#include <ctype.h>
+#include <errno.h>
+
+#define MSN_BUF_LEN 8192
+
+static char *prd_cached = NULL;
+
+static char *passport_create_header( char *reply, char *email, char *pwd );
+static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
+static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond );
+static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url );
+static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond );
+static void destroy_reply( struct passport_reply *rep );
+
+
+int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func )
+{
+	char *header = passport_create_header( cookie, username, password );
+	
+	if( prd_cached )
+	{
+		int st;
+		
+		st = passport_get_id_from( data, func, header, prd_cached );
+		g_free( header );
+		return( st );
+	}
+	else
+	{
+		return( passport_retrieve_dalogin( data, func, header ) );
+	}
+}
+
+
+static char *passport_create_header( char *reply, char *email, char *pwd )
+{
+	char *buffer = g_new0( char, 2048 );
+	char *currenttoken;
+	char *email_enc, *pwd_enc;
+	
+	email_enc = g_new0( char, strlen( email ) * 3 + 1 );
+	strcpy( email_enc, email );
+	http_encode( email_enc );
+	
+	pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
+	strcpy( pwd_enc, pwd );
+	http_encode( pwd_enc );
+	
+	currenttoken = strstr( reply, "lc=" );
+	if( currenttoken == NULL )
+		return( NULL );
+	
+	g_snprintf( buffer, 2048,
+	            "Authorization: Passport1.4 OrgVerb=GET,"
+	            "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
+	            "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
+	            currenttoken );
+	
+	g_free( email_enc );
+	g_free( pwd_enc );
+	
+	return( buffer );
+}
+
+
+static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header )
+{
+	struct passport_reply *rep = g_new0( struct passport_reply, 1 );
+	void *ssl;
+	
+	rep->data = data;
+	rep->func = func;
+	rep->header = header;
+	
+	ssl = ssl_connect( "nexus.passport.com", 443, passport_retrieve_dalogin_connected, rep );
+	
+	if( !ssl )
+		destroy_reply( rep );
+	
+	return( ssl != NULL );
+}
+
+#define PPR_BUFFERSIZE 2048
+#define PPR_REQUEST "GET /rdr/pprdr.asp HTTP/1.0\r\n\r\n"
+static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond )
+{
+	int ret;
+	char buffer[PPR_BUFFERSIZE+1];
+	struct passport_reply *rep = data;
+	
+	if( !g_slist_find( msn_connections, rep->data ) )
+	{
+		if( ssl ) ssl_disconnect( ssl );
+		destroy_reply( rep );
+		return;
+	}
+	
+	if( !ssl )
+	{
+		rep->func( rep );
+		destroy_reply( rep );
+		return;
+	}
+	
+	ssl_write( ssl, PPR_REQUEST, strlen( PPR_REQUEST ) );
+	
+	if( ( ret = ssl_read( ssl, buffer, PPR_BUFFERSIZE ) ) <= 0 )
+	{
+		goto failure;
+	}
+
+	{
+		char *dalogin = strstr( buffer, "DALogin=" );
+		char *urlend;
+		
+		if( !dalogin )
+			goto failure;
+		
+		dalogin += strlen( "DALogin=" );
+		urlend = strchr( dalogin, ',' );
+		if( urlend )
+			*urlend = 0;
+		
+		/* strip the http(s):// part from the url */
+		urlend = strstr( urlend, "://" );
+		if( urlend )
+			dalogin = urlend + strlen( "://" );
+		
+		if( prd_cached == NULL )
+			prd_cached = g_strdup( dalogin );
+	}
+	
+	if( passport_get_id_from( rep->data, rep->func, rep->header, prd_cached ) )
+	{
+		ssl_disconnect( ssl );
+		destroy_reply( rep );
+		return;
+	}
+	
+failure:	
+	ssl_disconnect( ssl );
+	rep->func( rep );
+	destroy_reply( rep );
+}
+
+
+static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url )
+{
+	struct passport_reply *rep = g_new0( struct passport_reply, 1 );
+	char server[512], *dummy;
+	void *ssl;
+	
+	rep->data = data;
+	rep->func = func;
+	rep->redirects = 4;
+	
+	strncpy( server, url, 512 );
+	dummy = strchr( server, '/' );
+	if( dummy )
+		*dummy = 0;
+	
+	ssl = ssl_connect( server, 443, passport_get_id_connected, rep );
+	
+	if( ssl )
+	{
+		rep->header = g_strdup( header_i );
+		rep->url = g_strdup( url );
+	}
+	else
+	{
+		destroy_reply( rep );
+	}
+	
+	return( ssl != NULL );
+}
+
+#define PPG_BUFFERSIZE 4096
+static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond )
+{
+	struct passport_reply *rep = data;
+	char server[512], buffer[PPG_BUFFERSIZE+1], *dummy;
+	int ret;
+	
+	if( !g_slist_find( msn_connections, rep->data ) )
+	{
+		if( ssl ) ssl_disconnect( ssl );
+		destroy_reply( rep );
+		return;
+	}
+	
+	if( !ssl )
+	{
+		rep->func( rep );
+		destroy_reply( rep );
+		return;
+	}
+	
+	memset( buffer, 0, PPG_BUFFERSIZE + 1 );
+	
+	strncpy( server, rep->url, 512 );
+	dummy = strchr( server, '/' );
+	if( dummy == NULL )
+		goto end;
+	
+	g_snprintf( buffer, PPG_BUFFERSIZE - 1, "GET %s HTTP/1.0\r\n"
+	            "%s\r\n\r\n", dummy, rep->header );
+	
+	ssl_write( ssl, buffer, strlen( buffer ) );
+	memset( buffer, 0, PPG_BUFFERSIZE + 1 );
+	
+	{
+		char *buffer2 = buffer;
+		
+		while( ( ( ret = ssl_read( ssl, buffer2, 512 ) ) > 0 ) &&
+		       ( buffer + PPG_BUFFERSIZE - buffer2 - ret - 512 >= 0 ) )
+		{
+			buffer2 += ret;
+		}
+	}
+	
+	if( *buffer == 0 )
+		goto end;
+	
+	if( ( dummy = strstr( buffer, "Location:" ) ) )
+	{
+		char *urlend;
+		
+		rep->redirects --;
+		if( rep->redirects == 0 )
+			goto end;
+		
+		dummy += strlen( "Location:" );
+		while( isspace( *dummy ) ) dummy ++;
+		urlend = dummy;
+		while( !isspace( *urlend ) ) urlend ++;
+		*urlend = 0;
+		if( ( urlend = strstr( dummy, "://" ) ) )
+			dummy = urlend + strlen( "://" );
+		
+		g_free( rep->url );
+		rep->url = g_strdup( dummy );
+		
+		strncpy( server, dummy, sizeof( server ) - 1 );
+		dummy = strchr( server, '/' );
+		if( dummy ) *dummy = 0;
+		
+		ssl_disconnect( ssl );
+		
+		if( ssl_connect( server, 443, passport_get_id_connected, rep ) )
+		{
+			return;
+		}
+		else
+		{
+			rep->func( rep );
+			destroy_reply( rep );
+			return;
+		}
+	}
+	else if( strstr( buffer, "200 OK" ) )
+	{
+		if( ( dummy = strstr( buffer, "from-PP='" ) ) )
+		{
+			char *responseend;
+			
+			dummy += strlen( "from-PP='" );
+			responseend = strchr( dummy, '\'' );
+			if( responseend )
+				*responseend = 0;
+			
+			rep->result = g_strdup( dummy );
+		}
+	}
+	
+end:
+	ssl_disconnect( ssl );
+	rep->func( rep );
+	destroy_reply( rep );
+}
+
+
+static void destroy_reply( struct passport_reply *rep )
+{
+	if( rep->result ) g_free( rep->result );
+	if( rep->url ) g_free( rep->url );
+	if( rep->header ) g_free( rep->header );
+	if( rep ) g_free( rep );
+}
Index: protocols/msn/passport.h
===================================================================
--- protocols/msn/passport.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/passport.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,47 @@
+#ifndef __PASSPORT_H__
+#define __PASSPORT_H__
+/* passport.h
+ *
+ * Functions to login to Microsoft Passport Service for Messenger
+ * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>,
+ *                    Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify             
+ * it under the terms of the GNU General Public License version 2                   
+ * as published by the Free Software Foundation                                     
+ *                                                                                   
+ * This program is distributed in the hope that is will be useful,                  
+ * bit WITHOU ANY WARRANTY; without even the implied warranty of                   
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    
+ * GNU General Public License for more details.                                     
+ *                                                                                   
+ * You should have received a copy of the GNU General Public License                
+ * along with this program; if not, write to the Free Software                      
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA          
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#endif
+#include "nogaim.h"
+
+struct passport_reply
+{
+	void *data;
+	char *result;
+	void (*func)( struct passport_reply * );
+	char *url;
+	char *header;
+	int redirects;
+};
+
+int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func );
+
+#endif /* __PASSPORT_H__ */
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/sb.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,659 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - Switchboard server callbacks and utilities              */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <ctype.h>
+#include "nogaim.h"
+#include "msn.h"
+#include "passport.h"
+#include "md5.h"
+
+static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond );
+static int msn_sb_command( gpointer data, char **cmd, int num_parts );
+static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts );
+
+int msn_sb_write( struct msn_switchboard *sb, char *s, int len )
+{
+	int st;
+	
+	st = write( sb->fd, s, len );
+	if( st != len )
+	{
+		msn_sb_destroy( sb );
+		return( 0 );
+	}
+	
+	return( 1 );
+}
+
+struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session )
+{
+	struct msn_data *md = gc->proto_data;
+	struct msn_switchboard *sb = g_new0( struct msn_switchboard, 1 );
+	
+	sb->fd = proxy_connect( host, port, msn_sb_connected, sb );
+	if( sb->fd < 0 )
+	{
+		g_free( sb );
+		return( NULL );
+	}
+	
+	sb->gc = gc;
+	sb->key = g_strdup( key );
+	sb->session = session;
+	
+	msn_switchboards = g_slist_append( msn_switchboards, sb );
+	md->switchboards = g_slist_append( md->switchboards, sb );
+	
+	return( sb );
+}
+
+struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle )
+{
+	struct msn_data *md = gc->proto_data;
+	struct msn_switchboard *sb;
+	GSList *l;
+	
+	for( l = md->switchboards; l; l = l->next )
+	{
+		sb = l->data;
+		if( sb->who && strcmp( sb->who, handle ) == 0 )
+			return( sb );
+	}
+	
+	return( NULL );
+}
+
+struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id )
+{
+	struct msn_data *md = gc->proto_data;
+	struct msn_switchboard *sb;
+	GSList *l;
+	
+	for( l = md->switchboards; l; l = l->next )
+	{
+		sb = l->data;
+		if( sb->chat && sb->chat->id == id )
+			return( sb );
+	}
+	
+	return( NULL );
+}
+
+struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc )
+{
+	struct msn_data *md = gc->proto_data;
+	struct msn_switchboard *sb;
+	GSList *l;
+	
+	for( l = md->switchboards; l; l = l->next )
+	{
+		sb = l->data;
+		if( !sb->who && !sb->chat )
+			return( sb );
+	}
+	
+	return( NULL );
+}
+
+int msn_sb_sendmessage( struct msn_switchboard *sb, char *text )
+{
+	if( sb->ready )
+	{
+		char cmd[1024], *buf;
+		int i, j;
+		
+		if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 )
+		{
+			buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 );
+			i = strlen( MSN_MESSAGE_HEADERS );
+			
+			strcpy( buf, MSN_MESSAGE_HEADERS );
+			for( j = 0; text[j]; j ++ )
+			{
+				if( text[j] == '\n' )
+					buf[i++] = '\r';
+				
+				buf[i++] = text[j];
+			}
+		}
+		else
+		{
+			i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username );
+			buf = g_new0( char, strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username ) );
+			i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->gc->username );
+		}
+		
+		g_snprintf( cmd, sizeof( cmd ), "MSG %d N %d\r\n", ++sb->trId, i );
+		if( msn_sb_write( sb, cmd, strlen( cmd ) ) && msn_sb_write( sb, buf, i ) )
+		{
+			g_free( buf );
+			return( 1 );
+		}
+		else
+		{
+			g_free( buf );
+			return( 0 );
+		}
+	}
+	else if( sb->who )
+	{
+		struct msn_message *m = g_new0( struct msn_message, 1 );
+		
+		m->who = g_strdup( "" );
+		m->text = g_strdup( text );
+		sb->msgq = g_slist_append( sb->msgq, m );
+		
+		return( 1 );
+	}
+	else
+	{
+		return( 0 );
+	}
+}
+
+void msn_sb_to_chat( struct msn_switchboard *sb )
+{
+	struct gaim_connection *gc = sb->gc;
+	char buf[1024];
+	
+	/* Create the groupchat structure. */
+	g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
+	sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf );
+	
+	/* Populate the channel. */
+	if( sb->who ) add_chat_buddy( sb->chat, sb->who );
+	add_chat_buddy( sb->chat, gc->username );
+	
+	/* And make sure the switchboard doesn't look like a regular chat anymore. */
+	if( sb->who )
+	{
+		g_free( sb->who );
+		sb->who = NULL;
+	}
+}
+
+void msn_sb_destroy( struct msn_switchboard *sb )
+{
+	struct gaim_connection *gc = sb->gc;
+	struct msn_data *md = gc->proto_data;
+	
+	debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" );
+	
+	if( sb->key ) g_free( sb->key );
+	if( sb->who ) g_free( sb->who );
+	
+	if( sb->msgq )
+	{
+		struct msn_message *m;
+		GSList *l;
+		
+		for( l = sb->msgq; l; l = l->next )
+		{
+			m = l->data;
+			g_free( m->who );
+			g_free( m->text );
+			g_free( m );
+		}
+		g_slist_free( sb->msgq );
+		
+		serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with unsent message(s), you'll have to resend them." );
+	}
+	
+	if( sb->chat )
+	{
+		serv_got_chat_left( gc, sb->chat->id );
+	}
+	
+	if( sb->handler )
+	{
+		if( sb->handler->rxq ) g_free( sb->handler->rxq );
+		if( sb->handler->cmd_text ) g_free( sb->handler->cmd_text );
+		g_free( sb->handler );
+	}
+	
+	if( sb->inp ) gaim_input_remove( sb->inp );
+	closesocket( sb->fd );
+	
+	msn_switchboards = g_slist_remove( msn_switchboards, sb );
+	md->switchboards = g_slist_remove( md->switchboards, sb );
+	g_free( sb );
+}
+
+void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct msn_switchboard *sb = data;
+	struct gaim_connection *gc;
+	struct msn_data *md;
+	char buf[1024];
+	
+	/* Are we still alive? */
+	if( !g_slist_find( msn_switchboards, sb ) )
+		return;
+	
+	gc = sb->gc;
+	md = gc->proto_data;
+	
+	if( source != sb->fd )
+	{
+		debug( "ERROR %d while connecting to switchboard server", 1 );
+		msn_sb_destroy( sb );
+		return;
+	}
+	
+	/* Prepare the callback */
+	sb->handler = g_new0( struct msn_handler_data, 1 );
+	sb->handler->fd = sb->fd;
+	sb->handler->rxq = g_new0( char, 1 );
+	sb->handler->data = sb;
+	sb->handler->exec_command = msn_sb_command;
+	sb->handler->exec_message = msn_sb_message;
+	
+	if( sb->session == MSN_SB_NEW )
+		g_snprintf( buf, sizeof( buf ), "USR %d %s %s\r\n", ++sb->trId, gc->username, sb->key );
+	else
+		g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, gc->username, sb->key, sb->session );
+	
+	if( msn_sb_write( sb, buf, strlen( buf ) ) )
+		sb->inp = gaim_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb );
+	else
+		debug( "ERROR %d while connecting to switchboard server", 2 );
+}
+
+static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct msn_switchboard *sb = data;
+	
+	if( msn_handler( sb->handler ) == -1 )
+	{
+		debug( "ERROR: Switchboard died" );
+		msn_sb_destroy( sb );
+	}
+}
+
+static int msn_sb_command( gpointer data, char **cmd, int num_parts )
+{
+	struct msn_switchboard *sb = data;
+	struct gaim_connection *gc = sb->gc;
+	char buf[1024];
+	
+	if( !num_parts )
+	{
+		/* Hrrm... Empty command...? Ignore? */
+		return( 1 );
+	}
+	
+	if( strcmp( cmd[0], "XFR" ) == 0 )
+	{
+		hide_login_progress_error( gc, "Received an XFR from a switchboard server, unable to comply! This is likely to be a bug, please report it!" );
+		signoff( gc );
+		return( 0 );
+	}
+	else if( strcmp( cmd[0], "USR" ) == 0 )
+	{
+		if( num_parts != 5 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		if( strcmp( cmd[2], "OK" ) != 0 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		if( sb->who )
+		{
+			g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who );
+			return( msn_sb_write( sb, buf, strlen( buf ) ) );
+		}
+		else
+		{
+			debug( "Just created a switchboard, but I don't know what to do with it." );
+		}
+	}
+	else if( strcmp( cmd[0], "IRO" ) == 0 )
+	{
+		int num, tot;
+		
+		if( num_parts != 6 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		num = atoi( cmd[2] );
+		tot = atoi( cmd[3] );
+		
+		if( tot <= 0 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		else if( tot > 1 )
+		{
+			char buf[1024];
+			
+			if( num == 1 )
+			{
+				g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session );
+				sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf );
+				
+				g_free( sb->who );
+				sb->who = NULL;
+			}
+			
+			add_chat_buddy( sb->chat, cmd[4] );
+			
+			if( num == tot )
+			{
+				add_chat_buddy( sb->chat, gc->username );
+			}
+		}
+	}
+	else if( strcmp( cmd[0], "ANS" ) == 0 )
+	{
+		if( num_parts != 3 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		if( strcmp( cmd[2], "OK" ) != 0 )
+		{
+			debug( "Switchboard server sent a negative ANS reply" );
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		sb->ready = 1;
+	}
+	else if( strcmp( cmd[0], "CAL" ) == 0 )
+	{
+		if( num_parts != 4 || !isdigit( cmd[3][0] ) )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		sb->session = atoi( cmd[3] );
+	}
+	else if( strcmp( cmd[0], "JOI" ) == 0 )
+	{
+		if( num_parts != 3 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		if( sb->who && g_strcasecmp( cmd[1], sb->who ) == 0 )
+		{
+			/* The user we wanted to talk to is finally there, let's send the queued messages then. */
+			struct msn_message *m;
+			GSList *l;
+			int st = 1;
+			
+			debug( "%s arrived in the switchboard session, now sending queued message(s)", cmd[1] );
+			
+			/* Without this, sendmessage() will put everything back on the queue... */
+			sb->ready = 1;
+			
+			while( ( l = sb->msgq ) )
+			{
+				m = l->data;
+				if( st )
+				{
+					/* This hack is meant to convert a regular new chat into a groupchat */
+					if( strcmp( m->text, GROUPCHAT_SWITCHBOARD_MESSAGE ) == 0 )
+						msn_sb_to_chat( sb );
+					else
+						st = msn_sb_sendmessage( sb, m->text );
+				}
+				g_free( m->text );
+				g_free( m->who );
+				g_free( m );
+				
+				sb->msgq = g_slist_remove( sb->msgq, m );
+			}
+			
+			return( st );
+		}
+		else if( sb->who )
+		{
+			debug( "Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1] );
+			
+			/* This SB is a one-to-one chat right now, but someone else is joining. */
+			msn_sb_to_chat( sb );
+			
+			add_chat_buddy( sb->chat, cmd[1] );
+		}
+		else if( sb->chat )
+		{
+			add_chat_buddy( sb->chat, cmd[1] );
+			sb->ready = 1;
+		}
+		else
+		{
+			/* PANIC! */
+		}
+	}
+	else if( strcmp( cmd[0], "MSG" ) == 0 )
+	{
+		if( num_parts != 4 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		sb->handler->msglen = atoi( cmd[3] );
+		
+		if( sb->handler->msglen <= 0 )
+		{
+			debug( "Received a corrupted message on the switchboard, the switchboard will be closed" );
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+	}
+	else if( strcmp( cmd[0], "BYE" ) == 0 )
+	{
+		if( num_parts < 2 )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		
+		/* if( cmd[2] && *cmd[2] == '1' ) -=> Chat is being cleaned up because of idleness */
+		
+		if( sb->who )
+		{
+			/* This is a single-person chat, and the other person is leaving. */
+			g_free( sb->who );
+			sb->who = NULL;
+			sb->ready = 0;
+			
+			debug( "Person %s left the one-to-one switchboard connection. Keeping it around as a spare...", cmd[1] );
+			
+			/* We could clean up the switchboard now, but keeping it around
+			   as a spare for a next conversation sounds more sane to me.
+			   The server will clean it up when it's idle for too long. */
+		}
+		else if( sb->chat )
+		{
+			remove_chat_buddy( sb->chat, cmd[1], "" );
+		}
+		else
+		{
+			/* PANIC! */
+		}
+	}
+	else if( isdigit( cmd[0][0] ) )
+	{
+		int num = atoi( cmd[0] );
+		struct msn_status_code *err = msn_status_by_number( num );
+		
+		g_snprintf( buf, sizeof( buf ), "Error reported by switchboard server: %s", err->text );
+		do_error_dialog( gc, buf, "MSN" );
+		
+		if( err->flags & STATUS_SB_FATAL )
+		{
+			msn_sb_destroy( sb );
+			return( 0 );
+		}
+		else if( err->flags & STATUS_FATAL )
+		{
+			signoff( gc );
+			return( 0 );
+		}
+	}
+	else
+	{
+		debug( "Received unknown command from switchboard server: %s", cmd[0] );
+	}
+	
+	return( 1 );
+}
+
+static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts )
+{
+	struct msn_switchboard *sb = data;
+	struct gaim_connection *gc = sb->gc;
+	char *body;
+	int blen = 0;
+	
+	if( !num_parts )
+		return( 1 );
+	
+	if( ( body = strstr( msg, "\r\n\r\n" ) ) )
+	{
+		body += 4;
+		blen = msglen - ( body - msg );
+	}
+	
+	if( strcmp( cmd[0], "MSG" ) == 0 )
+	{
+		char *ct = msn_findheader( msg, "Content-Type:", msglen );
+		
+		if( !ct )
+			return( 1 );
+		
+		if( g_strncasecmp( ct, "text/plain", 10 ) == 0 )
+		{
+			g_free( ct );
+			
+			if( !body )
+				return( 1 );
+			
+			if( sb->who )
+			{
+				serv_got_im( gc, cmd[1], body, 0, 0, blen );
+			}
+			else if( sb->chat )
+			{
+				serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, body, 0 );
+			}
+			else
+			{
+				/* PANIC! */
+			}
+		}
+		else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 )
+		{
+			char *itype = msn_findheader( body, "Application-GUID:", blen );
+			char buf[1024];
+			
+			g_free( ct );
+			
+			*buf = 0;
+			
+			if( !itype )
+				return( 1 );
+			
+			/* File transfer. */
+			if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 )
+			{
+				char *name = msn_findheader( body, "Application-File:", blen );
+				char *size = msn_findheader( body, "Application-FileSize:", blen );
+				
+				if( name && size )
+				{
+					g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n"
+					            "Filetransfers are not supported by BitlBee for now...", name, size );
+				}
+				else
+				{
+					strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" );
+				}
+				
+				if( name ) g_free( name );
+				if( size ) g_free( size );
+			}
+			else
+			{
+				char *iname = msn_findheader( body, "Application-Name:", blen );
+				
+				g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>",
+				                                itype, iname ? iname : "no name" );
+				
+				if( iname ) g_free( iname );
+			}
+			
+			g_free( itype );
+			
+			if( !*buf )
+				return( 1 );
+			
+			if( sb->who )
+			{
+				serv_got_im( gc, cmd[1], buf, 0, 0, strlen( buf ) );
+			}
+			else if( sb->chat )
+			{
+				serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, buf, 0 );
+			}
+			else
+			{
+				/* PANIC! */
+			}
+		}
+		else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 )
+		{
+			char *who = msn_findheader( msg, "TypingUser:", msglen );
+			
+			if( who )
+			{
+				serv_got_typing( gc, who, 5 );
+				g_free( who );
+			}
+			
+			g_free( ct );
+		}
+		else
+		{
+			g_free( ct );
+		}
+	}
+	
+	return( 1 );
+}
Index: protocols/msn/tables.c
===================================================================
--- protocols/msn/tables.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/tables.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,166 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* MSN module - Some tables with useful data                            */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "nogaim.h"
+#include "msn.h"
+
+struct msn_away_state msn_away_state_list[] =
+{
+	{  0, "NLN", "Available" },
+	{  1, "BSY", "Busy" },
+	{  3, "IDL", "Idle" },
+	{  5, "BRB", "Be Right Back" },
+	{  7, "AWY", "Away" },
+	{  9, "PHN", "On the Phone" },
+	{ 11, "LUN", "Out to Lunch" },
+	{ 13, "HDN", "Hidden" },
+	{ -1, "",    "" }
+};
+
+struct msn_away_state *msn_away_state_by_number( int number )
+{
+	int i;
+	
+	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+		if( msn_away_state_list[i].number == number )
+			return( msn_away_state_list + i );
+	
+	return( NULL );
+}
+
+struct msn_away_state *msn_away_state_by_code( char *code )
+{
+	int i;
+	
+	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+		if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 )
+			return( msn_away_state_list + i );
+	
+	return( NULL );
+}
+
+struct msn_away_state *msn_away_state_by_name( char *name )
+{
+	int i;
+	
+	for( i = 0; msn_away_state_list[i].number > -1; i ++ )
+		if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 )
+			return( msn_away_state_list + i );
+	
+	return( NULL );
+}
+
+struct msn_status_code msn_status_code_list[] =
+{
+	{ 200, "Invalid syntax",                                        0 },
+	{ 201, "Invalid parameter",                                     0 },
+	{ 205, "Invalid (non-existent) handle",                         0 },
+	{ 206, "Domain name missing",                                   0 },
+	{ 207, "Already logged in",                                     0 },
+	{ 208, "Invalid handle",                                        0 },
+	{ 209, "Forbidden nickname",                                    0 },
+	{ 210, "Buddy list too long",                                   0 },
+	{ 215, "Handle is already in list",                             0 },
+	{ 216, "Handle is not in list",                                 0 },
+	{ 217, "Person is off-line or non-existent",                    0 },
+	{ 218, "Already in that mode",                                  0 },
+	{ 219, "Handle is already in opposite list",                    0 },
+	{ 223, "Too many groups",                                       0 },
+	{ 224, "Invalid group or already in list",                      0 },
+	{ 225, "Handle is not in that group",                           0 },
+	{ 229, "Group name too long",                                   0 },
+	{ 230, "Cannot remove that group",                              0 },
+	{ 231, "Invalid group",                                         0 },
+	{ 280, "Switchboard failed",                                    STATUS_SB_FATAL },
+	{ 281, "Transfer to switchboard failed",                        0 },
+	
+	{ 300, "Required field missing",                                0 },
+	{ 302, "Not logged in",                                         0 },
+	
+	{ 500, "Internal server error/Account banned",                  STATUS_FATAL },
+	{ 501, "Database server error",                                 STATUS_FATAL },
+	{ 502, "Command disabled",                                      0 },
+	{ 510, "File operation failed",                                 STATUS_FATAL },
+	{ 520, "Memory allocation failed",                              STATUS_FATAL },
+	{ 540, "Challenge response invalid",                            STATUS_FATAL },
+	
+	{ 600, "Server is busy",                                        STATUS_FATAL },
+	{ 601, "Server is unavailable",                                 STATUS_FATAL },
+	{ 602, "Peer nameserver is down",                               STATUS_FATAL },
+	{ 603, "Database connection failed",                            STATUS_FATAL },
+	{ 604, "Server is going down",                                  STATUS_FATAL },
+	{ 605, "Server is unavailable",                                 STATUS_FATAL },
+	
+	{ 700, "Could not create connection",                           STATUS_FATAL },
+	{ 710, "Invalid CVR parameters",                                STATUS_FATAL },
+	{ 711, "Write is blocking",                                     STATUS_FATAL },
+	{ 712, "Session is overloaded",                                 STATUS_FATAL },
+	{ 713, "Calling too rapidly",                                   0 },
+	{ 714, "Too many sessions",                                     STATUS_FATAL },
+	{ 715, "Not expected/Invalid argument/action",                  0 },
+	{ 717, "Bad friend file",                                       STATUS_FATAL },
+	{ 731, "Not expected/Invalid argument",                         0 },
+	
+	{ 800, "Changing too rapidly",                                  0 },
+	
+	{ 910, "Server is busy",                                        STATUS_FATAL },
+	{ 911, "Authentication failed",                                 STATUS_FATAL },
+	{ 912, "Server is busy",                                        STATUS_FATAL },
+	{ 913, "Not allowed when hiding",                               0 },
+	{ 914, "Server is unavailable",                                 STATUS_FATAL },
+	{ 915, "Server is unavailable",                                 STATUS_FATAL },
+	{ 916, "Server is unavailable",                                 STATUS_FATAL },
+	{ 917, "Authentication failed",                                 STATUS_FATAL },
+	{ 918, "Server is busy",                                        STATUS_FATAL },
+	{ 919, "Server is busy",                                        STATUS_FATAL },
+	{ 920, "Not accepting new principals",                          0 }, /* When a sb is full? */
+	{ 922, "Server is busy",                                        STATUS_FATAL },
+	{ 923, "Kids Passport without parental consent",                STATUS_FATAL },
+	{ 924, "Passport account not yet verified",                     STATUS_FATAL },
+	{ 928, "Bad ticket",                                            STATUS_FATAL },
+	{  -1, NULL,                                                    0 }
+};
+
+struct msn_status_code *msn_status_by_number( int number )
+{
+	static struct msn_status_code *unknown = NULL;
+	int i;
+	
+	for( i = 0; msn_status_code_list[i].number >= 0; i ++ )
+		if( msn_status_code_list[i].number == number )
+			return( msn_status_code_list + i );
+	
+	if( unknown == NULL )
+	{
+		unknown = g_new0( struct msn_status_code, 1 );
+		unknown->text = g_new0( char, 128 );
+	}
+	
+	unknown->number = number;
+	unknown->flags = 0;
+	g_snprintf( unknown->text, 128, "Unknown error (%d)", number );
+	
+	return( unknown );
+}
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/nogaim.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -13,4 +13,6 @@
  * from scratch for BitlBee and doesn't contain any code from Gaim anymore
  * (except for the function names).
+ *
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
  */
 
@@ -28,762 +30,17 @@
   You should have received a copy of the GNU General Public License with
   the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
 #define BITLBEE_CORE
+#include "nogaim.h"
 #include <ctype.h>
-
-#include "nogaim.h"
-
-GSList *connections;
-
-#ifdef WITH_PLUGINS
-GList *plugins = NULL;
-
-static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data)
-{
-	const struct plugin_info *ia = a;
-	const struct plugin_info *ib = b;
-
-	return g_strcasecmp(ia->name, ib->name);
-}
-
-/* semi-private */
-gboolean plugin_info_validate(struct plugin_info *info, const char *path)
-{
-	GList *l;
-	gboolean loaded = FALSE;
-
-	if (!path) {
-		path = "(null)";
-	}
-
-	if (info->abiver != BITLBEE_ABI_VERSION_CODE) {
-		log_message(LOGLVL_ERROR,
-			    "`%s' uses ABI %u but %u is required\n",
-			    path, info->abiver,
-			    BITLBEE_ABI_VERSION_CODE);
-		return FALSE;
-	}
-
-	if (!info->name || !info->version) {
-		log_message(LOGLVL_ERROR,
-			    "Name or version missing from the "
-			    "plugin info in `%s'\n", path);
-		return FALSE;
-	}
-
-	for (l = plugins; l; l = l->next) {
-		struct plugin_info *i = l->data;
-
-		if (g_strcasecmp(i->name, info->name) == 0) {
-			loaded = TRUE;
-			break;
-		}
-	}
-
-	if (loaded) {
-		log_message(LOGLVL_WARNING,
-			    "%s plugin already loaded\n",
-			    info->name);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-/* semi-private */
-gboolean plugin_info_add(struct plugin_info *info)
-{
-	plugins = g_list_insert_sorted_with_data(plugins, info, pluginscmp, NULL);
-	return TRUE;
-}
-
-gboolean load_plugin(char *path)
-{
-	struct plugin_info *info = NULL;
-	struct plugin_info * (*info_function) (void) = NULL;
-	void (*init_function) (void);
-
-	GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
-
-	if (!mod) {
-		log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error());
-		return FALSE;
-	}
-
-	if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) {
-		info = info_function();
-
-		if (!plugin_info_validate(info, path)) {
-			g_module_close(mod);
-			return FALSE;
-		}
-	} else {
-		log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path);
-	}
-
-	if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) {
-		log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
-		g_module_close(mod);
-		return FALSE;
-	}
-
-	if (info_function) {
-		plugin_info_add(info);
-	}
-
-	init_function();
-	return TRUE;
-}
-
-void load_plugins(void)
-{
-	GDir *dir;
-	GError *error = NULL;
-
-	dir = g_dir_open(global.conf->plugindir, 0, &error);
-
-	if (dir) {
-		const gchar *entry;
-		char *path;
-
-		while ((entry = g_dir_read_name(dir))) {
-			if (!g_str_has_suffix(entry, "." G_MODULE_SUFFIX)) {
-				continue;
-			}
-
-			path = g_build_filename(global.conf->plugindir, entry, NULL);
-			if (!path) {
-				log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry);
-				continue;
-			}
-
-			load_plugin(path);
-
-			g_free(path);
-		}
-
-		g_dir_close(dir);
-	}
-}
-
-GList *get_plugins()
-{
-	return plugins;
-}
-#endif
-
-GList *protocols = NULL;
-GList *disabled_protocols = NULL;
-
-void register_protocol(struct prpl *p)
-{
-	int i;
-	gboolean refused = global.conf->protocols != NULL;
-
-	for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++) {
-		if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0) {
-			refused = FALSE;
-		}
-	}
-
-	if (refused) {
-		disabled_protocols = g_list_append(disabled_protocols, p);
-	} else {
-		protocols = g_list_append(protocols, p);
-	}
-}
-
-static int proto_name_cmp(const void *proto_, const void *name)
-{
-	const struct prpl *proto = proto_;
-	return g_strcasecmp(proto->name, name);
-}
-
-struct prpl *find_protocol(const char *name)
-{
-	GList *gl = g_list_find_custom(protocols, name, proto_name_cmp);
-	return gl ? gl->data: NULL;
-}
-
-gboolean is_protocol_disabled(const char *name)
-{
-	return g_list_find_custom(disabled_protocols, name, proto_name_cmp) != NULL;
-}
-
-/* Returns heap allocated string with text attempting to explain why a protocol is missing 
- * Free the return value with g_free() */
-char *explain_unknown_protocol(const char *name)
-{
-	char *extramsg = NULL;
-
-	if (is_protocol_disabled(name)) {
-		return g_strdup("Protocol disabled in the global config (bitlbee.conf)");
-	}
-
-	if (strcmp(name, "yahoo") == 0) {
-		return g_strdup("The old yahoo protocol is gone, try the funyahoo++ libpurple plugin instead.");
-	}
-
-#ifdef WITH_PURPLE
-	if ((strcmp(name, "msn") == 0) ||
-	    (strcmp(name, "loubserp-mxit") == 0) ||
-	    (strcmp(name, "myspace") == 0)) {
-		return g_strdup("This protocol has been removed from your libpurple version.");
-	}
-
-	if (strcmp(name, "hipchat") == 0) {
-		return g_strdup("This account type isn't supported by libpurple's jabber.");
-	}
-
-#else
-	extramsg = "If this is a libpurple plugin, you might need to install bitlbee-libpurple instead.";
-#endif
-	return g_strconcat("The protocol plugin is not installed or could not be loaded. "
-	                   "Use the `plugins' command to list available protocols. ",
-	                   extramsg, NULL);
-}
-
-void nogaim_init()
-{
-	extern void jabber_initmodule();
-	extern void twitter_initmodule();
-	extern void purple_initmodule();
-
-#ifdef WITH_JABBER
-	jabber_initmodule();
-#endif
-
-#ifdef WITH_TWITTER
-	twitter_initmodule();
-#endif
-
-#ifdef WITH_PURPLE
-	purple_initmodule();
-#endif
-
-#ifdef WITH_PLUGINS
-	load_plugins();
-#endif
-}
-
-GList *get_protocols()
-{
-	return protocols;
-}
-
-GList *get_protocols_disabled()
-{
-	return disabled_protocols;
-}
-
-GSList *get_connections()
-{
-	return connections;
-}
-
-struct im_connection *imcb_new(account_t *acc)
-{
-	struct im_connection *ic;
-	GHashFunc fn_hash = NULL;
-	GEqualFunc fn_equal = NULL;
-
-	ic = g_new0(struct im_connection, 1);
-
-	ic->bee = acc->bee;
-	ic->acc = acc;
-	acc->ic = ic;
-
-	/* figure out if we have hashing functions compatible with handle_cmp */
-	if (acc->prpl->handle_cmp == g_ascii_strcasecmp) {
-		fn_hash = b_istr_hash;
-		fn_equal = b_istr_equal;
-	} else if (acc->prpl->handle_cmp == g_strcmp0 || acc->prpl->handle_cmp == strcmp) {
-		fn_hash = g_str_hash;
-		fn_equal = g_str_equal;
-	}
-
-	/* only create the hash table if we found them */
-	if (fn_hash && fn_equal) {
-		ic->bee_users = g_hash_table_new_full(fn_hash, fn_equal, NULL, NULL);
-	}
-
-	connections = g_slist_append(connections, ic);
-
-	return(ic);
-}
-
-void imc_free(struct im_connection *ic)
-{
-	account_t *a;
-
-	/* Destroy the pointer to this connection from the account list */
-	for (a = ic->bee->accounts; a; a = a->next) {
-		if (a->ic == ic) {
-			a->ic = NULL;
-			break;
-		}
-	}
-
-	if (ic->bee_users) {
-		g_hash_table_destroy(ic->bee_users);
-	}
-
-	connections = g_slist_remove(connections, ic);
-	g_free(ic);
-}
-
-static void serv_got_crap(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-	account_t *a;
-
-	if (!ic->bee->ui->log) {
-		return;
-	}
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if ((g_strcasecmp(set_getstr(&ic->bee->set, "strip_html"), "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html"))) {
-		strip_html(text);
-	}
-
-	/* Try to find a different connection on the same protocol. */
-	for (a = ic->bee->accounts; a; a = a->next) {
-		if (a->prpl == ic->acc->prpl && a->ic != ic) {
-			break;
-		}
-	}
-
-	/* If we found one, include the screenname in the message. */
-	if (a) {
-		ic->bee->ui->log(ic->bee, ic->acc->tag, text);
-	} else {
-		ic->bee->ui->log(ic->bee, ic->acc->prpl->name, text);
-	}
-
-	g_free(text);
-}
-
-void imcb_log(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (ic->flags & OPT_LOGGED_IN) {
-		serv_got_crap(ic, "%s", text);
-	} else {
-		serv_got_crap(ic, "Logging in: %s", text);
-	}
-
-	g_free(text);
-}
-
-void imcb_error(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (ic->flags & OPT_LOGGED_IN) {
-		serv_got_crap(ic, "Error: %s", text);
-	} else {
-		serv_got_crap(ic, "Login error: %s", text);
-	}
-
-	g_free(text);
-}
-
-static gboolean send_keepalive(gpointer d, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = d;
-
-	if ((ic->flags & OPT_PONGS) && !(ic->flags & OPT_PONGED)) {
-		/* This protocol is expected to ack keepalives and hasn't
-		   since the last time we were here. */
-		imcb_error(ic, "Connection timeout");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-	ic->flags &= ~OPT_PONGED;
-
-	if (ic->acc->prpl->keepalive) {
-		ic->acc->prpl->keepalive(ic);
-	}
-
-	return TRUE;
-}
-
-void start_keepalives(struct im_connection *ic, int interval)
-{
-	b_event_remove(ic->keepalive);
-	ic->keepalive = b_timeout_add(interval, send_keepalive, ic);
-
-	/* Connecting successfully counts as a first successful pong. */
-	if (ic->flags & OPT_PONGS) {
-		ic->flags |= OPT_PONGED;
-	}
-}
-
-void imcb_connected(struct im_connection *ic)
-{
-	/* MSN servers sometimes redirect you to a different server and do
-	   the whole login sequence again, so these "late" calls to this
-	   function should be handled correctly. (IOW, ignored) */
-	if (ic->flags & OPT_LOGGED_IN) {
-		return;
-	}
-
-	if (ic->acc->flags & ACC_FLAG_LOCAL) {
-		GHashTableIter nicks;
-		gpointer k, v;
-		g_hash_table_iter_init(&nicks, ic->acc->nicks);
-		while (g_hash_table_iter_next(&nicks, &k, &v)) {
-			ic->acc->prpl->add_buddy(ic, (char *) k, NULL);
-		}
-	}
-
-	imcb_log(ic, "Logged in");
-
-	ic->flags |= OPT_LOGGED_IN;
-	start_keepalives(ic, 60000);
-
-	/* Necessary to send initial presence status, even if we're not away. */
-	imc_away_send_update(ic);
-
-	/* Apparently we're connected successfully, so reset the
-	   exponential backoff timer. */
-	ic->acc->auto_reconnect_delay = 0;
-
-	if (ic->bee->ui->imc_connected) {
-		ic->bee->ui->imc_connected(ic);
-	}
-}
-
-gboolean auto_reconnect(gpointer data, gint fd, b_input_condition cond)
-{
-	account_t *a = data;
-
-	a->reconnect = 0;
-	account_on(a->bee, a);
-
-	return(FALSE);          /* Only have to run the timeout once */
-}
-
-void cancel_auto_reconnect(account_t *a)
-{
-	b_event_remove(a->reconnect);
-	a->reconnect = 0;
-}
-
-void imc_logout(struct im_connection *ic, int allow_reconnect)
-{
-	bee_t *bee = ic->bee;
-	account_t *a;
-	GSList *l;
-	int delay;
-
-	/* Nested calls might happen sometimes, this is probably the best
-	   place to catch them. */
-	if (ic->flags & OPT_LOGGING_OUT) {
-		return;
-	} else {
-		ic->flags |= OPT_LOGGING_OUT;
-	}
-
-	if (ic->bee->ui->imc_disconnected) {
-		ic->bee->ui->imc_disconnected(ic);
-	}
-
-	imcb_log(ic, "Signing off..");
-
-	/* TBH I don't remember anymore why I didn't just use ic->acc... */
-	for (a = bee->accounts; a; a = a->next) {
-		if (a->ic == ic) {
-			break;
-		}
-	}
-
-	if (a && !allow_reconnect && !(ic->flags & OPT_LOGGED_IN) &&
-	    set_getbool(&a->set, "oauth")) {
-		/* If this account supports OAuth, we're not logged in yet and
-		   not allowed to retry, assume there were auth issues. Give a
-		   helpful message on what might be necessary to fix this. */
-		imcb_log(ic, "If you're having problems logging in, try re-requesting "
-		         "an OAuth token: account %s set password \"\"", a->tag);
-	}
-
-	for (l = bee->users; l; ) {
-		bee_user_t *bu = l->data;
-		GSList *next = l->next;
-
-		if (bu->ic == ic) {
-			bee_user_free(bee, bu);
-		}
-
-		l = next;
-	}
-
-	b_event_remove(ic->keepalive);
-	ic->keepalive = 0;
-	ic->acc->prpl->logout(ic);
-	b_event_remove(ic->inpa);
-
-	g_free(ic->away);
-	ic->away = NULL;
-
-	query_del_by_conn((irc_t *) ic->bee->ui_data, ic);
-
-	if (!a) {
-		/* Uhm... This is very sick. */
-	} else if (allow_reconnect && set_getbool(&bee->set, "auto_reconnect") &&
-	           set_getbool(&a->set, "auto_reconnect") &&
-	           (delay = account_reconnect_delay(a)) > 0) {
-		imcb_log(ic, "Reconnecting in %d seconds..", delay);
-		a->reconnect = b_timeout_add(delay * 1000, auto_reconnect, a);
-	}
-
-	imc_free(ic);
-}
-
-void imcb_ask(struct im_connection *ic, char *msg, void *data,
-              query_callback doit, query_callback dont)
-{
-	query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data);
-}
-
-void imcb_ask_with_free(struct im_connection *ic, char *msg, void *data,
-                        query_callback doit, query_callback dont, query_callback myfree)
-{
-	query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, myfree, data);
-}
-
-void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group)
-{
-	bee_user_t *bu;
-	bee_t *bee = ic->bee;
-	bee_group_t *oldg;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		bu = bee_user_new(bee, ic, handle, 0);
-	}
-
-	oldg = bu->group;
-	bu->group = bee_group_by_name(bee, group, TRUE);
-
-	if (bee->ui->user_group && bu->group != oldg) {
-		bee->ui->user_group(bee, bu);
-	}
-}
-
-void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *fullname)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu || !fullname) {
-		return;
-	}
-
-	if (!bu->fullname || strcmp(bu->fullname, fullname) != 0) {
-		g_free(bu->fullname);
-		bu->fullname = g_strdup(fullname);
-
-		if (bee->ui->user_fullname) {
-			bee->ui->user_fullname(bee, bu);
-		}
-	}
-}
-
-void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group)
-{
-	bee_user_free(ic->bee, bee_user_by_handle(ic->bee, ic, handle));
-}
-
-/* Implements either imcb_buddy_nick_hint() or imcb_buddy_nick_change() depending on the value of 'change' */
-static void buddy_nick_hint_or_change(struct im_connection *ic, const char *handle, const char *nick, gboolean change)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu || !nick) {
-		return;
-	}
-
-	g_free(bu->nick);
-	bu->nick = g_strdup(nick);
-
-	if (change && bee->ui->user_nick_change) {
-		bee->ui->user_nick_change(bee, bu, nick);
-	} else if (!change && bee->ui->user_nick_hint) {
-		bee->ui->user_nick_hint(bee, bu, nick);
-	}
-}
-
-/* Soft variant, for newly created users. Does nothing if it's already online */
-void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
-{
-	buddy_nick_hint_or_change(ic, handle, nick, FALSE);
-}
-
-/* Hard variant, always changes the nick */
-void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick)
-{
-	buddy_nick_hint_or_change(ic, handle, nick, TRUE);
-}
-
-struct imcb_ask_cb_data {
-	struct im_connection *ic;
-	char *handle;
-};
-
-static void imcb_ask_cb_free(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	g_free(cbd->handle);
-	g_free(cbd);
-}
-
-static void imcb_ask_auth_cb_no(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->auth_deny(cbd->ic, cbd->handle);
-
-	imcb_ask_cb_free(cbd);
-}
-
-static void imcb_ask_auth_cb_yes(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->auth_allow(cbd->ic, cbd->handle);
-
-	imcb_ask_cb_free(cbd);
-}
-
-void imcb_ask_auth(struct im_connection *ic, const char *handle, const char *realname)
-{
-	struct imcb_ask_cb_data *data = g_new0(struct imcb_ask_cb_data, 1);
-	char *s, *realname_ = NULL;
-
-	if (realname != NULL) {
-		realname_ = g_strdup_printf(" (%s)", realname);
-	}
-
-	s = g_strdup_printf("The user %s%s wants to add you to his/her buddy list.",
-	                    handle, realname_ ? realname_ : "");
-
-	g_free(realname_);
-
-	data->ic = ic;
-	data->handle = g_strdup(handle);
-	query_add((irc_t *) ic->bee->ui_data, ic, s,
-	          imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, imcb_ask_cb_free, data);
-
-	g_free(s);
-}
-
-static void imcb_ask_add_cb_yes(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->add_buddy(cbd->ic, cbd->handle, NULL);
-
-	imcb_ask_cb_free(data);
-}
-
-void imcb_ask_add(struct im_connection *ic, const char *handle, const char *realname)
-{
-	struct imcb_ask_cb_data *data;
-	char *s;
-
-	/* TODO: Make a setting for this! */
-	if (bee_user_by_handle(ic->bee, ic, handle) != NULL) {
-		return;
-	}
-
-	data = g_new0(struct imcb_ask_cb_data, 1);
-
-	s = g_strdup_printf("The user %s is not in your buddy list yet. Do you want to add him/her now?", handle);
-
-	data->ic = ic;
-	data->handle = g_strdup(handle);
-	query_add((irc_t *) ic->bee->ui_data, ic, s,
-	          imcb_ask_add_cb_yes, imcb_ask_cb_free, imcb_ask_cb_free, data);
-
-	g_free(s);
-}
-
-struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle)
-{
-	return bee_user_by_handle(ic->bee, ic, handle);
-}
-
-/* The plan is to not allow straight calls to prpl functions anymore, but do
-   them all from some wrappers. We'll start to define some down here: */
-
-int imc_chat_msg(struct groupchat *c, char *msg, int flags)
-{
-	char *buf = NULL;
-
-	if ((c->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	}
-
-	c->ic->acc->prpl->chat_msg(c, msg, flags);
-	g_free(buf);
-
-	return 1;
-}
-
-static char *imc_away_state_find(GList *gcm, char *away, char **message);
-
-int imc_away_send_update(struct im_connection *ic)
-{
-	char *away, *msg = NULL;
-
-	if (ic->acc->prpl->away_states == NULL ||
-	    ic->acc->prpl->set_away == NULL) {
-		return 0;
-	}
-
-	away = set_getstr(&ic->acc->set, "away") ?
-	       : set_getstr(&ic->bee->set, "away");
-	if (away && *away) {
-		GList *m = ic->acc->prpl->away_states(ic);
-		if (m == NULL) {
-			return 0;
-		}
-		msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
-		away = imc_away_state_find(m, away, &msg) ? :
-		       (imc_away_state_find(m, "away", NULL) ? : m->data);
-	} else if (ic->acc->flags & ACC_FLAG_STATUS_MESSAGE) {
-		away = NULL;
-		msg = set_getstr(&ic->acc->set, "status") ?
-		      : set_getstr(&ic->bee->set, "status");
-	}
-
-	ic->acc->prpl->set_away(ic, away, msg);
-
-	return 1;
-}
-
-static char *imc_away_alias_list[8][5] =
+#include <iconv.h>
+
+struct prpl *proto_prpl[PROTO_MAX];
+char proto_name[PROTO_MAX][8] = { "TOC", "OSCAR", "YAHOO", "ICQ", "MSN", "", "", "", "JABBER", "", "", "", "", "", "", "" };
+
+static char *proto_away_alias[7][5] =
 {
 	{ "Away from computer", "Away", "Extended away", NULL },
@@ -793,106 +50,1033 @@
 	{ "On the phone", "Phone", "On phone", NULL },
 	{ "Out to lunch", "Lunch", "Food", NULL },
-	{ "Invisible", "Hidden" },
 	{ NULL }
 };
-
-static char *imc_away_state_find(GList *gcm, char *away, char **message)
+static char *proto_away_alias_find( GList *gcm, char *away );
+
+static int remove_chat_buddy_silent( struct conversation *b, char *handle );
+
+GSList *connections;
+
+
+/* nogaim.c */
+
+void nogaim_init()
+{
+	proto_prpl[PROTO_MSN] = g_new0 ( struct prpl, 1 );
+#ifdef WITH_MSN
+	msn_init( proto_prpl[PROTO_MSN] );
+#endif
+
+	proto_prpl[PROTO_OSCAR] = g_new0( struct prpl, 1 );
+#ifdef WITH_OSCAR
+	oscar_init( proto_prpl[PROTO_OSCAR] );
+#endif
+	
+	proto_prpl[PROTO_YAHOO] = g_new0( struct prpl, 1 );
+#ifdef WITH_YAHOO
+	byahoo_init( proto_prpl[PROTO_YAHOO] );
+#endif
+	
+	proto_prpl[PROTO_JABBER] = g_new0( struct prpl, 1 );
+#ifdef WITH_JABBER
+	jabber_init( proto_prpl[PROTO_JABBER] );
+#endif
+}
+
+GSList *get_connections() { return connections; }
+
+int proto_away( struct gaim_connection *gc, char *away )
+{
+	GList *m, *ms;
+	char *s;
+	
+	if( !away ) away = "";
+	ms = m = gc->prpl->away_states( gc );
+	
+	while( m )
+	{
+		if( *away )
+		{
+			if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 )
+				break;
+		}
+		else
+		{
+			if( g_strcasecmp( m->data, "Available" ) == 0 )
+				break;
+			if( g_strcasecmp( m->data, "Online" ) == 0 )
+				break;
+		}
+		m = m->next;
+	}
+	
+	if( m )
+	{
+		gc->prpl->set_away( gc, m->data, *away ? away : NULL );
+	}
+	else
+	{
+		s = proto_away_alias_find( ms, away );
+		if( s )
+		{
+			gc->prpl->set_away( gc, s, away );
+			if( set_getint( gc->irc, "debug" ) )
+				irc_usermsg( gc->irc, "Setting away state for %s to %s", proto_name[gc->protocol], s );
+		}
+		else
+			gc->prpl->set_away( gc, GAIM_AWAY_CUSTOM, away );
+	}
+	
+	g_list_free( ms );
+	
+	return( 1 );
+}
+
+static char *proto_away_alias_find( GList *gcm, char *away )
 {
 	GList *m;
 	int i, j;
-
-	for (m = gcm; m; m = m->next) {
-		if (g_strncasecmp(m->data, away, strlen(m->data)) == 0) {
-			/* At least the Yahoo! module works better if message
-			   contains no data unless it adds something to what
-			   we have in state already. */
-			if (message && strlen(m->data) == strlen(away)) {
-				*message = NULL;
+	
+	for( i = 0; *proto_away_alias[i]; i ++ )
+	{
+		for( j = 0; proto_away_alias[i][j]; j ++ )
+			if( g_strncasecmp( away, proto_away_alias[i][j], strlen( proto_away_alias[i][j] ) ) == 0 )
+				break;
+		
+		if( !proto_away_alias[i][j] )	/* If we reach the end, this row */
+			continue;		/* is not what we want. Next!    */
+		
+		/* Now find an entry in this row which exists in gcm */
+		for( j = 0; proto_away_alias[i][j]; j ++ )
+		{
+			m = gcm;
+			while( m )
+			{
+				if( g_strcasecmp( proto_away_alias[i][j], m->data ) == 0 )
+					return( proto_away_alias[i][j] );
+				m = m->next;
 			}
-
-			return m->data;
-		}
-	}
-
-	for (i = 0; *imc_away_alias_list[i]; i++) {
-		int keep_message;
-
-		for (j = 0; imc_away_alias_list[i][j]; j++) {
-			if (g_strncasecmp(away, imc_away_alias_list[i][j], strlen(imc_away_alias_list[i][j])) == 0) {
-				keep_message = strlen(away) != strlen(imc_away_alias_list[i][j]);
+		}
+	}
+	
+	return( NULL );
+}
+
+/* Compare two handles for a specific protocol. For most protocols,
+   g_strcasecmp is okay, but for AIM, for example, it's not. This really
+   should be a compare function inside the PRPL module, but I do it this
+   way for now because I don't want to touch the Gaim code too much since
+   it's not going to be here for too long anymore. */
+int handle_cmp( char *a, char *b, int protocol )
+{
+	if( protocol == PROTO_TOC || protocol == PROTO_ICQ )
+	{
+		/* AIM, being teh evil, thinks it's cool that users can put
+		   random spaces in screennames. But "A B" and "AB" are
+		   equal. Hrmm, okay. */
+		while( 1 )
+		{
+			while( *a == ' ' ) a ++;
+			while( *b == ' ' ) b ++;
+			
+			if( *a && *b )
+			{
+				if( tolower( *a ) != tolower( *b ) )
+					return( 1 );
+			}
+			else if( *a || *b )
+				return( 1 );
+			else
+				return( 0 );
+			
+			a ++;
+			b ++;
+		}
+	}
+	else
+	{
+		return( g_strcasecmp( a, b ) );
+	}
+}
+
+
+/* multi.c */
+
+struct gaim_connection *new_gaim_conn( struct aim_user *user )
+{
+	struct gaim_connection *gc;
+	account_t *a;
+	
+	gc = g_new0( struct gaim_connection, 1 );
+	
+	gc->protocol = user->protocol;
+	gc->prpl = proto_prpl[gc->protocol];
+	g_snprintf( gc->username, sizeof( gc->username ), "%s", user->username );
+	g_snprintf( gc->password, sizeof( gc->password ), "%s", user->password );
+	/* [MD]	BUGFIX: don't set gc->irc to the global IRC, but use the one from the struct aim_user.
+	 * This fixes daemon mode breakage where IRC doesn't point to the currently active connection.
+	 */
+	gc->irc=user->irc;
+	
+	connections = g_slist_append( connections, gc );
+	
+	user->gc = gc;
+	gc->user = user;
+	
+	// Find the account_t so we can set its gc pointer
+	for( a = gc->irc->accounts; a; a = a->next )
+		if( ( struct aim_user * ) a->gc == user )
+		{
+			a->gc = gc;
+			break;
+		}
+	
+	return( gc );
+}
+
+void destroy_gaim_conn( struct gaim_connection *gc )
+{
+	account_t *a;
+	
+	/* Destroy the pointer to this connection from the account list */
+	for( a = gc->irc->accounts; a; a = a->next )
+		if( a->gc == gc )
+		{
+			a->gc = NULL;
+			break;
+		}
+	
+	connections = g_slist_remove( connections, gc );
+	g_free( gc->user );
+	g_free( gc );
+}
+
+void set_login_progress( struct gaim_connection *gc, int step, char *msg )
+{
+	irc_usermsg( gc->irc, "%s(%s) - Logging in: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+/* Errors *while* logging in */
+void hide_login_progress( struct gaim_connection *gc, char *msg )
+{
+	irc_usermsg( gc->irc, "%s(%s) - Login error: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+/* Errors *after* logging in */
+void hide_login_progress_error( struct gaim_connection *gc, char *msg )
+{
+	irc_usermsg( gc->irc, "%s(%s) - Logged out: %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+void serv_got_crap( struct gaim_connection *gc, char *format, ... )
+{
+	va_list params;
+	char text[1024], buf[1024];
+	char *msg;
+	
+	va_start( params, format );
+	g_vsnprintf( text, sizeof( text ), format, params );
+	va_end( params );
+
+	if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( "UTF8", set_getstr( gc->irc, "charset" ), text, buf, 0, 1024 ) != -1 )
+		msg = buf;
+	else
+		msg = text;
+	
+	/* if( g_strcasecmp( set_getstr(gc->irc, "html" ), "strip" ) == 0 ) */
+	if( gc->flags & OPT_CONN_HTML )
+		strip_html( msg );
+	
+	irc_usermsg( gc->irc, "%s(%s) - %s", proto_name[gc->protocol], gc->username, msg );
+}
+
+static gboolean send_keepalive( gpointer d )
+{
+	struct gaim_connection *gc = d;
+	
+	if( gc->prpl && gc->prpl->keepalive )
+		gc->prpl->keepalive( gc );
+	
+	return TRUE;
+}
+
+void account_online( struct gaim_connection *gc )
+{
+	user_t *u;
+	
+	/* MSN servers sometimes redirect you to a different server and do
+	   the whole login sequence again, so subsequent calls to this
+	   function should be handled correctly. (IOW, ignored) */
+	if( gc->flags & OPT_LOGGED_IN )
+		return;
+	
+	u = user_find( gc->irc, gc->irc->nick );
+	
+	irc_usermsg( gc->irc, "%s(%s) - Logged in", proto_name[gc->protocol], gc->username );
+	
+	gc->keepalive = g_timeout_add( 60000, send_keepalive, gc );
+	gc->flags |= OPT_LOGGED_IN;
+	
+	if( u && u->away ) proto_away( gc, u->away );
+	
+	if( gc->protocol == PROTO_ICQ )
+	{
+		for( u = gc->irc->users; u; u = u->next )
+			if( u->gc == gc )
 				break;
+		
+		if( u == NULL )
+			irc_usermsg( gc->irc, "\x02""***\x02"" BitlBee now supports ICQ server-side contact lists. "
+			                      "See \x02""help import_buddies\x02"" for more information." );
+	}
+}
+
+gboolean auto_reconnect( gpointer data )
+{
+	account_t *a = data;
+	
+	a->reconnect = 0;
+	account_on( a->irc, a );
+	
+	return( FALSE );	/* Only have to run the timeout once */
+}
+
+void cancel_auto_reconnect( account_t *a )
+{
+	while( g_source_remove_by_user_data( (gpointer) a ) );
+	a->reconnect = 0;
+}
+
+void account_offline( struct gaim_connection *gc )
+{
+	gc->wants_to_die = TRUE;
+	signoff( gc );
+}
+
+void signoff( struct gaim_connection *gc )
+{
+	irc_t *irc = gc->irc;
+	user_t *t, *u = irc->users;
+	account_t *a;
+	
+	irc_usermsg( gc->irc, "%s(%s) - Signing off..", proto_name[gc->protocol], gc->username );
+
+	gaim_input_remove( gc->keepalive );
+	gc->keepalive = 0;
+	gc->prpl->close( gc );
+	gaim_input_remove( gc->inpa );
+	
+	while( u )
+	{
+		if( u->gc == gc )
+		{
+			t = u->next;
+			user_del( irc, u->nick );
+			u = t;
+		}
+		else
+			u = u->next;
+	}
+	
+	query_del_by_gc( gc->irc, gc );
+	
+	for( a = irc->accounts; a; a = a->next )
+		if( a->gc == gc )
+			break;
+	
+	if( !a )
+	{
+		/* Uhm... This is very sick. */
+	}
+	else if( !gc->wants_to_die && set_getint( irc, "auto_reconnect" ) )
+	{
+		int delay = set_getint( irc, "auto_reconnect_delay" );
+		irc_usermsg( gc->irc, "%s(%s) - Reconnecting in %d seconds..", proto_name[gc->protocol], gc->username, delay);
+		
+		a->reconnect = 1;
+		g_timeout_add( delay * 1000, auto_reconnect, a );
+	}
+	
+	destroy_gaim_conn( gc );
+}
+
+
+/* dialogs.c */
+
+void do_error_dialog( struct gaim_connection *gc, char *msg, char *title )
+{
+	irc_usermsg( gc->irc, "%s(%s) - Error: %s", gc->username, title, msg );
+}
+
+void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont )
+{
+	query_add( gc->irc, gc, msg, doit, dont, data );
+}
+
+
+/* list.c */
+
+int bud_list_cache_exists( struct gaim_connection *gc )
+{
+	return( 0 );
+}
+
+void do_import( struct gaim_connection *gc, void *null )
+{
+	return;
+}
+
+void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname )
+{
+	user_t *u;
+	char nick[MAX_NICK_LENGTH+1];
+	char *s;
+	irc_t *irc = gc->irc;
+	
+	if( set_getint( irc, "debug" ) && 0 ) /* This message is too useless */
+		irc_usermsg( irc, "Receiving user add from protocol: %s", handle );
+	
+	if( user_findhandle( gc, handle ) )
+	{
+		if( set_getint( irc, "debug" ) )
+			irc_usermsg( irc, "User already exists, ignoring add request: %s", handle );
+		
+		return;
+		
+		/* Buddy seems to exist already. Let's ignore this request then... */
+	}
+	
+	memset( nick, 0, MAX_NICK_LENGTH + 1 );
+	strcpy( nick, nick_get( gc->irc, handle, gc->protocol, realname ) );
+	
+	u = user_add( gc->irc, nick );
+	
+	if( !realname || !*realname ) realname = nick;
+	u->realname = g_strdup( realname );
+	
+	if( ( s = strchr( handle, '@' ) ) )
+	{
+		u->host = g_strdup( s + 1 );
+		u->user = g_strndup( handle, s - handle );
+	}
+	else if( gc->user->proto_opt[0] && *gc->user->proto_opt[0] )
+	{
+		u->host = g_strdup( gc->user->proto_opt[0] );
+		u->user = g_strdup( handle );
+		
+		/* s/ /_/ ... important for AOL screennames */
+		for( s = u->user; *s; s ++ )
+			if( *s == ' ' )
+				*s = '_';
+	}
+	else
+	{
+		u->host = g_strdup( proto_name[gc->user->protocol] );
+		u->user = g_strdup( handle );
+	}
+	
+	u->gc = gc;
+	u->handle = g_strdup( handle );
+	u->send_handler = buddy_send_handler;
+	u->last_typing_notice = 0;
+}
+
+struct buddy *find_buddy( struct gaim_connection *gc, char *handle )
+{
+	static struct buddy b[1];
+	user_t *u;
+	
+	u = user_findhandle( gc, handle );
+	
+	if( !u )
+		return( NULL );
+
+	memset( b, 0, sizeof( b ) );
+	strncpy( b->name, handle, 80 );
+	strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN );
+	b->present = u->online;
+	b->gc = u->gc;
+	
+	return( b );
+}
+
+void do_export( struct gaim_connection *gc )
+{
+	return;
+}
+
+void signoff_blocked( struct gaim_connection *gc )
+{
+	return; /* Make all blocked users look invisible (TODO?) */
+}
+
+
+void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname )
+{
+	user_t *u = user_findhandle( gc, handle );
+	char *name, buf[1024];
+	
+	if( !u ) return;
+	
+	/* Convert all UTF-8 */
+	if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), realname, buf, 0, sizeof( buf ) ) != -1 )
+		name = buf;
+	else
+		name = realname;
+	
+	if( g_strcasecmp( u->realname, name ) != 0 )
+	{
+		if( u->realname != u->nick ) g_free( u->realname );
+		
+		u->realname = g_strdup( name );
+		
+		if( ( gc->flags & OPT_LOGGED_IN ) && set_getint( gc->irc, "display_namechanges" ) )
+			irc_usermsg( gc->irc, "User `%s' changed name to `%s'", u->nick, u->realname );
+	}
+}
+
+
+/* prpl.c */
+
+void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg )
+{
+	return;
+}
+
+
+/* server.c */                    
+
+void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps )
+{
+	user_t *u;
+	int oa, oo;
+	
+	u = user_findhandle( gc, handle );
+	
+	if( !u )
+	{
+		if( g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "add" ) == 0 )
+		{
+			add_buddy( gc, NULL, handle, NULL );
+			u = user_findhandle( gc, handle );
+		}
+		else
+		{
+			if( set_getint( gc->irc, "debug" ) || g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "ignore" ) != 0 )
+			{
+				irc_usermsg( gc->irc, "serv_got_update() for handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username );
+				irc_usermsg( gc->irc, "loggedin = %d, type = %d", loggedin, type );
 			}
-		}
-
-		if (!imc_away_alias_list[i][j]) {       /* If we reach the end, this row */
-			continue;                       /* is not what we want. Next!    */
-
-		}
-		/* Now find an entry in this row which exists in gcm */
-		for (j = 0; imc_away_alias_list[i][j]; j++) {
-			for (m = gcm; m; m = m->next) {
-				if (g_strcasecmp(imc_away_alias_list[i][j], m->data) == 0) {
-					if (!keep_message && message) {
-						*message = NULL;
-					}
-
-					return m->data;
+			
+			return;
+		}
+		return;
+	}
+	
+	oa = u->away != NULL;
+	oo = u->online;
+	
+	if( u->away )
+	{
+		g_free( u->away );
+		u->away = NULL;
+	}
+	
+	if( loggedin && !u->online )
+	{
+		irc_spawn( gc->irc, u );
+		u->online = 1;
+	}
+	else if( !loggedin && u->online )
+	{
+		struct conversation *c;
+		
+		irc_kill( gc->irc, u );
+		u->online = 0;
+		
+		/* Remove him/her from the conversations to prevent PART messages after he/she QUIT already */
+		for( c = gc->conversations; c; c = c->next )
+			remove_chat_buddy_silent( c, handle );
+	}
+	
+	if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_OSCAR || gc->protocol == PROTO_TOC ) )
+	{
+		u->away = g_strdup( "Away" );
+	}
+	else if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_JABBER ) )
+	{
+		if( type & UC_DND )
+			u->away = g_strdup( "Do Not Disturb" );
+		else if( type & UC_XA )
+			u->away = g_strdup( "Extended Away" );
+		else // if( type & UC_AWAY )
+			u->away = g_strdup( "Away" );
+	}
+	else if( ( type & UC_UNAVAILABLE ) && gc->prpl->get_status_string )
+	{
+		u->away = g_strdup( gc->prpl->get_status_string( gc, type ) );
+	}
+	else
+		u->away = NULL;
+	
+	/* LISPy... */
+	if( ( set_getint( gc->irc, "away_devoice" ) ) &&		/* Don't do a thing when user doesn't want it */
+	    ( u->online ) &&						/* Don't touch offline people */
+	    ( ( ( u->online != oo ) && !u->away ) ||			/* Voice joining people */
+	      ( ( u->online == oo ) && ( oa == !u->away ) ) ) )		/* (De)voice people changing state */
+	{
+		irc_write( gc->irc, ":%s!%s@%s MODE %s %cv %s", gc->irc->mynick, gc->irc->mynick, gc->irc->myhost,
+		                                                gc->irc->channel, u->away?'-':'+', u->nick );
+	}
+}
+
+void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len )
+{
+	irc_t *irc = gc->irc;
+	user_t *u;
+	char buf[8192];
+	
+	u = user_findhandle( gc, handle );
+	
+	if( !u )
+	{
+		char *h = set_getstr( irc, "handle_unknown" );
+		
+		if( g_strcasecmp( h, "ignore" ) == 0 )
+		{
+			if( set_getint( irc, "debug" ) )
+				irc_usermsg( irc, "Ignoring message from unknown handle %s on connection %s(%s)", handle, proto_name[gc->protocol], gc->username );
+			
+			return;
+		}
+		else if( g_strncasecmp( h, "add", 3 ) == 0 )
+		{
+			int private = set_getint( irc, "private" );
+			
+			if( h[3] )
+			{
+				if( g_strcasecmp( h + 3, "_private" ) == 0 )
+					private = 1;
+				else if( g_strcasecmp( h + 3, "_channel" ) == 0 )
+					private = 0;
+			}
+			
+			add_buddy( gc, NULL, handle, NULL );
+			u = user_findhandle( gc, handle );
+			u->is_private = private;
+		}
+		else
+		{
+			irc_usermsg( irc, "Message from unknown handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username );
+			u = user_find( irc, irc->mynick );
+		}
+	}
+	
+	/* if( g_strcasecmp( set_getstr( irc, "html" ), "strip" ) == 0 ) */
+	if( gc->flags & OPT_CONN_HTML )
+		strip_html( msg );
+
+	if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( "UTF-8", set_getstr( irc, "charset" ), msg, buf, 0, 8192 ) != -1 )
+		msg = buf;
+	
+	while( strlen( msg ) > 450 )
+	{
+		char tmp, *nl;
+		
+		tmp = msg[450];
+		msg[450] = 0;
+		
+		/* If there's a newline in this string, split up there so we're not
+		   going to split up lines. If there isn't a newline, well, too bad. */
+		if( ( nl = strrchr( msg, '\n' ) ) )
+			*nl = 0;
+		
+		irc_msgfrom( irc, u->nick, msg );
+		
+		msg[450] = tmp;
+		
+		/* Move on. */
+		if( nl )
+		{
+			*nl = '\n';
+			msg = nl + 1;
+		}
+		else
+		{
+			msg += 450;
+		}
+	}
+	irc_msgfrom( irc, u->nick, msg );
+}
+
+void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout )
+{
+	user_t *u;
+	
+	if( !set_getint( gc->irc, "typing_notice" ) )
+		return;
+	
+	if( ( u = user_findhandle( gc, handle ) ) )
+		irc_noticefrom( gc->irc, u->nick, "* Typing a message *" );
+}
+
+void serv_got_chat_left( struct gaim_connection *gc, int id )
+{
+	struct conversation *c, *l = NULL;
+	GList *ir;
+	
+	if( set_getint( gc->irc, "debug" ) )
+		irc_usermsg( gc->irc, "You were removed from conversation %d", (int) id );
+	
+	for( c = gc->conversations; c && c->id != id; c = (l=c)->next );
+	
+	if( c )
+	{
+		if( c->joined )
+		{
+			user_t *u, *r;
+			
+			r = user_find( gc->irc, gc->irc->mynick );
+			irc_privmsg( gc->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" );
+			
+			u = user_find( gc->irc, gc->irc->nick );
+			irc_kick( gc->irc, u, c->channel, r );
+			/* irc_part( gc->irc, u, c->channel ); */
+		}
+		
+		if( l )
+			l->next = c->next;
+		else
+			gc->conversations = c->next;
+		
+		for( ir = c->in_room; ir; ir = ir->next )
+			g_free( ir->data );
+		g_list_free( c->in_room );
+		g_free( c->channel );
+		g_free( c->title );
+		g_free( c );
+	}
+}
+
+void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime )
+{
+	struct conversation *c;
+	user_t *u;
+	char buf[8192];
+	
+	/* Gaim sends own messages through this too. IRC doesn't want this, so kill them */
+	if( g_strcasecmp( who, gc->user->username ) == 0 )
+		return;
+	
+	u = user_findhandle( gc, who );
+	for( c = gc->conversations; c && c->id != id; c = c->next );
+	
+	/* if( g_strcasecmp( set_getstr( gc->irc, "html" ), "strip" ) == 0 ) */
+	if( gc->flags & OPT_CONN_HTML )
+		strip_html( msg );
+	
+	if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), msg, buf, 0, 8192 ) != -1 )
+		msg = buf;
+	
+	if( c && u )
+		irc_privmsg( gc->irc, u, "PRIVMSG", c->channel, "", msg );
+	else
+		irc_usermsg( gc->irc, "Message from/to conversation %s@%d (unknown conv/user): %s", who, id, msg );
+}
+
+struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle )
+{
+	struct conversation *c;
+	char *s;
+	
+	/* This one just creates the conversation structure, user won't see anything yet */
+	
+	if( gc->conversations )
+	{
+		for( c = gc->conversations; c->next; c = c->next );
+		c = c->next = g_new0( struct conversation, 1 );
+	}
+	else
+		gc->conversations = c = g_new0( struct conversation, 1);
+	
+	c->id = id;
+	c->gc = gc;
+	c->title = g_strdup( handle );
+	
+	s = g_new( char, 16 );
+	sprintf( s, "#chat_%03d", gc->irc->c_id++ );
+	c->channel = g_strdup( s );
+	g_free( s );
+	
+	if( set_getint( gc->irc, "debug" ) )
+		irc_usermsg( gc->irc, "Creating new conversation: (id=%d,handle=%s)", id, handle );
+	
+	return( c );
+}
+
+void serv_finish_login( struct gaim_connection *gc )
+{
+	return;
+}
+
+
+/* buddy_chat.c */
+
+void add_chat_buddy( struct conversation *b, char *handle )
+{
+	user_t *u = user_findhandle( b->gc, handle );
+	int me = 0;
+	
+	if( set_getint( b->gc->irc, "debug" ) )
+		irc_usermsg( b->gc->irc, "User %s added to conversation %d", handle, b->id );
+	
+	/* It might be yourself! */
+	if( handle_cmp ( handle, b->gc->user->username, b->gc->protocol ) == 0 )
+	{
+		u = user_find( b->gc->irc, b->gc->irc->nick );
+		if( !b->joined )
+			irc_join( b->gc->irc, u, b->channel );
+		b->joined = me = 1;
+	}
+	
+	/* Most protocols allow people to join, even when they're not in
+	   your contact list. Try to handle that here */
+	if( !u )
+	{
+		add_buddy( b->gc, NULL, handle, NULL );
+		u = user_findhandle( b->gc, handle );
+	}
+	
+	/* Add the handle to the room userlist, if it's not 'me' */
+	if( !me )
+	{
+		if( b->joined )
+			irc_join( b->gc->irc, u, b->channel );
+		b->in_room = g_list_append( b->in_room, g_strdup( handle ) );
+	}
+}
+
+void remove_chat_buddy( struct conversation *b, char *handle, char *reason )
+{
+	user_t *u;
+	int me = 0;
+	
+	if( set_getint( b->gc->irc, "debug" ) )
+		irc_usermsg( b->gc->irc, "User %s removed from conversation %d (%s)", handle, b->id, reason ? reason : "" );
+	
+	/* It might be yourself! */
+	if( g_strcasecmp( handle, b->gc->user->username ) == 0 )
+	{
+		u = user_find( b->gc->irc, b->gc->irc->nick );
+		b->joined = 0;
+		me = 1;
+	}
+	else
+	{
+		u = user_findhandle( b->gc, handle );
+	}
+	
+	if( remove_chat_buddy_silent( b, handle ) )
+		if( ( b->joined || me ) && u )
+			irc_part( b->gc->irc, u, b->channel );
+}
+
+static int remove_chat_buddy_silent( struct conversation *b, char *handle )
+{
+	GList *i;
+	
+	/* Find the handle in the room userlist and shoot it */
+	i = b->in_room;
+	while( i )
+	{
+		if( g_strcasecmp( handle, i->data ) == 0 )
+		{
+			g_free( i->data );
+			b->in_room = g_list_remove( b->in_room, i->data );
+			return( 1 );
+		}
+		
+		i = i->next;
+	}
+	
+	return( 0 );
+}
+
+
+/* prefs.c */
+
+/* Necessary? */
+void build_block_list()
+{
+	return;
+}
+
+void build_allow_list()
+{
+	return;
+}
+
+
+/* Misc. BitlBee stuff which shouldn't really be here */
+
+struct conversation *conv_findchannel( char *channel )
+{
+	struct gaim_connection *gc;
+	struct conversation *c;
+	GSList *l;
+	
+	/* This finds the connection which has a conversation which belongs to this channel */
+	for( l = connections; l; l = l->next )
+	{
+		gc = l->data;
+		for( c = gc->conversations; c && g_strcasecmp( c->channel, channel ) != 0; c = c->next );
+		if( c )
+			return( c );
+	}
+	
+	return( NULL );
+}
+
+char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value )
+{
+	int st;
+	
+	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+		st = 1;
+	else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+		st = 0;
+	else if( sscanf( value, "%d", &st ) != 1 )
+		return( NULL );
+	
+	st = st != 0;
+	
+	/* Horror.... */
+	
+	if( st != set_getint( irc, "away_devoice" ) )
+	{
+		char list[80] = "";
+		user_t *u = irc->users;
+		int i = 0, count = 0;
+		char pm;
+		char v[80];
+		
+		if( st )
+			pm = '+';
+		else
+			pm = '-';
+		
+		while( u )
+		{
+			if( u->gc && u->online && !u->away )
+			{
+				if( ( strlen( list ) + strlen( u->nick ) ) >= 79 )
+				{
+					for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
+					irc_write( irc, ":%s!%s@%s MODE %s %c%s%s",
+					           irc->mynick, irc->mynick, irc->myhost,
+		        			   irc->channel, pm, v, list );
+					
+					*list = 0;
+					count = 0;
 				}
+				
+				sprintf( list + strlen( list ), " %s", u->nick );
+				count ++;
 			}
-		}
-
-		/* No need to look further, apparently this state doesn't
-		   have any good alias for this protocol. */
-		break;
-	}
-
-	return NULL;
-}
-
-void imc_add_allow(struct im_connection *ic, char *handle)
-{
-	if (g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
-		ic->permit = g_slist_prepend(ic->permit, g_strdup(handle));
-	}
-
-	ic->acc->prpl->add_permit(ic, handle);
-}
-
-void imc_rem_allow(struct im_connection *ic, char *handle)
-{
-	GSList *l;
-
-	if ((l = g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
-		g_free(l->data);
-		ic->permit = g_slist_delete_link(ic->permit, l);
-	}
-
-	ic->acc->prpl->rem_permit(ic, handle);
-}
-
-void imc_add_block(struct im_connection *ic, char *handle)
-{
-	if (g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
-		ic->deny = g_slist_prepend(ic->deny, g_strdup(handle));
-	}
-
-	ic->acc->prpl->add_deny(ic, handle);
-}
-
-void imc_rem_block(struct im_connection *ic, char *handle)
-{
-	GSList *l;
-
-	if ((l = g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
-		g_free(l->data);
-		ic->deny = g_slist_delete_link(ic->deny, l);
-	}
-
-	ic->acc->prpl->rem_deny(ic, handle);
-}
-
-/* Deprecated: using this function resulted in merging several handles accidentally
- * Also the irc layer handles this decently nowadays */
-void imcb_clean_handle(struct im_connection *ic, char *handle)
-{
-}
+			u = u->next;
+		}
+		
+		/* $v = 'v' x $i */
+		for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0;
+		irc_write( irc, ":%s!%s@%s MODE %s %c%s%s", irc->mynick, irc->mynick, irc->myhost,
+		                                            irc->channel, pm, v, list );
+	}
+	
+	return( set_eval_bool( irc, set, value ) );
+}
+
+int serv_send_im( irc_t *irc, user_t *u, char *msg, int flags )
+{
+	char buf[8192];
+	
+	if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 )
+		msg = buf;
+
+	if( u->gc->flags & OPT_CONN_HTML) {
+		char * html = escape_html(msg);
+		strncpy(buf, html, 8192);
+		g_free(html);
+	}
+	
+	return( ((struct gaim_connection *)u->gc)->prpl->send_im( u->gc, u->handle, msg, strlen( msg ), flags ) );
+}
+
+int serv_send_chat( irc_t *irc, struct gaim_connection *gc, int id, char *msg )
+{
+	char buf[8192];
+	
+	if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 &&
+	    do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 )
+		msg = buf;
+
+	if( gc->flags & OPT_CONN_HTML) {
+		char * html = escape_html(msg);
+		strncpy(buf, html, 8192);
+		g_free(html);
+	}
+	
+	return( gc->prpl->chat_send( gc, id, msg ) );
+}
+
+/* Convert from one charset to another.
+   
+   from_cs, to_cs: Source and destination charsets
+   src, dst: Source and destination strings
+   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
+   maxbuf: Maximum number of bytes to write to dst
+   
+   Returns the number of bytes written to maxbuf or -1 on an error.
+*/
+signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
+{
+	iconv_t cd;
+	size_t res;
+	size_t inbytesleft, outbytesleft;
+	char *inbuf = src;
+	char *outbuf = dst;
+	
+	cd = iconv_open( to_cs, from_cs );
+	if( cd == (iconv_t) -1 )
+		return( -1 );
+	
+	inbytesleft = size ? size : strlen( src );
+	outbytesleft = maxbuf - 1;
+	res = iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+	*outbuf = '\0';
+	iconv_close( cd );
+	
+	if( res == (size_t) -1 )
+		return( -1 );
+	else
+		return( outbuf - dst );
+}
+
+char *set_eval_charset( irc_t *irc, set_t *set, char *value )
+{
+	iconv_t cd;
+
+	if ( g_strncasecmp( value, "none", 4 ) == 0 )
+		return( value );
+
+	cd = iconv_open( "UTF-8", value );
+	if( cd == (iconv_t) -1 )
+		return( NULL );
+
+	iconv_close( cd );
+	return( value );
+}
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ protocols/nogaim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,11 +1,10 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
 /*
- * nogaim, soon to be known as im_api. Not a separate product (unless
- * someone would be interested in such a thing), just a new name.
+ * nogaim
  *
  * Gaim without gaim - for BitlBee
@@ -16,5 +15,5 @@
  * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
  *                          (and possibly other members of the Gaim team)
- * Copyright 2002-2012 Wilmer van der Gaast <wilmer@gaast.net>
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
  */
 
@@ -32,6 +31,6 @@
   You should have received a copy of the GNU General Public License with
   the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -39,49 +38,45 @@
 #define _NOGAIM_H
 
-#if (__sun)
-#include <inttypes.h>
-#else
-#include <stdint.h>
-#endif
-
 #include "bitlbee.h"
-#include "account.h"
 #include "proxy.h"
-#include "query.h"
 #include "md5.h"
-#include "ft.h"
-
+#include "sha.h"
+
+
+#define BUF_LEN MSG_LEN
+#define BUF_LONG ( BUF_LEN * 2 )
+#define MSG_LEN 2048
+#define BUF_LEN MSG_LEN
+
+#define SELF_ALIAS_LEN 400
 #define BUDDY_ALIAS_MAXLEN 388   /* because MSN names can be 387 characters */
 
-#define WEBSITE "https://www.bitlbee.org/"
-
-/* Sharing flags between all kinds of things. I just hope I won't hit any
-   limits before 32-bit machines become extinct. ;-) */
-#define OPT_LOGGED_IN   0x00000001
-#define OPT_LOGGING_OUT 0x00000002
-#define OPT_AWAY        0x00000004
-#define OPT_MOBILE      0x00000008
-#define OPT_DOES_HTML   0x00000010
-#define OPT_LOCALBUDDY  0x00000020 /* For nicks local to one groupchat */
-#define OPT_SLOW_LOGIN  0x00000040 /* I.e. Twitter Oauth @ login time */
-#define OPT_TYPING      0x00000100 /* Some pieces of code make assumptions */
-#define OPT_THINKING    0x00000200 /* about these values... Stupid me! */
-#define OPT_NOOTR       0x00001000 /* protocol not suitable for OTR */
-#define OPT_PONGS       0x00010000 /* Service sends us keep-alives */
-#define OPT_PONGED      0x00020000 /* Received a keep-alive during last interval */
-#define OPT_SELFMESSAGE 0x00080000 /* A message sent by self from another location */
+#define PERMIT_ALL      1
+#define PERMIT_NONE     2
+#define PERMIT_SOME     3
+#define DENY_SOME       4
+
+#define WEBSITE "http://www.bitlee.org/"
+#define IM_FLAG_AWAY 0x0020
+#define OPT_CONN_HTML 0x00000001
+#define OPT_LOGGED_IN 0x00010000
+#define GAIM_AWAY_CUSTOM "Custom"
+
+#define GAIM_LOGO	0
+#define GAIM_ERROR	1
+#define GAIM_WARNING	2
+#define GAIM_INFO	3
 
 /* ok. now the fun begins. first we create a connection structure */
-struct im_connection {
-	account_t *acc;
-	uint32_t flags;
-
-	/* each connection then can have its own protocol-specific data */
-	void *proto_data;
-
+struct gaim_connection {
+	/* we need to do either oscar or TOC */
+	/* we make this as an int in case if we want to add more protocols later */
+	int protocol;
+	struct prpl *prpl;
+	guint32 flags;
+	
 	/* all connections need an input watcher */
 	int inpa;
-	guint keepalive;
-
+	
 	/* buddy list stuff. there is still a global groups for the buddy list, but
 	 * we need to maintain our own set of buddies, and our own permit/deny lists */
@@ -89,37 +84,51 @@
 	GSList *deny;
 	int permdeny;
-
+	
+	/* all connections need a list of chats, even if they don't have chat */
+	GSList *buddy_chats;
+	
+	/* each connection then can have its own protocol-specific data */
+	void *proto_data;
+	
+	struct aim_user *user;
+	
+	char username[64];
+	char displayname[128];
+	char password[32];
+	guint keepalive;
+	/* stuff needed for per-connection idle times */
+	guint idle_timer;
+	time_t login_time;
+	time_t lastsent;
+	int is_idle;
+	
 	char *away;
-
+	int is_auto_away;
+	
+	int evil;
+	gboolean wants_to_die; /* defaults to FALSE */
+	
 	/* BitlBee */
-	bee_t *bee;
-
-	GSList *groupchats;
-	GSList *chatlist;
-	GHashTable *bee_users;
-};
-
-struct groupchat {
-	struct im_connection *ic;
+	irc_t *irc;
+	int lstitems;  /* added for msnP8 */
+	
+	struct conversation *conversations;
+};
+
+/* struct buddy_chat went away and got merged with this. */
+struct conversation {
+	struct gaim_connection *gc;
 
 	/* stuff used just for chat */
-	/* The in_room variable is a list of handles (not nicks!), kind of
-	 * "nick list". This is how you can check who is in the group chat
-	 * already, for example to avoid adding somebody two times. */
-	GList *in_room;
-	//GList *ignored;
-
-	//struct groupchat *next;
-	/* The title variable contains the ID you gave when you created the
-	 * chat using imcb_chat_new(). */
-	char *title;
-	/* Use imcb_chat_topic() to change this variable otherwise the user
-	 * won't notice the topic change. */
-	char *topic;
-	char joined;
-	/* This is for you, you can add your own structure here to extend this
-	 * structure for your protocol's needs. */
-	void *data;
-	void *ui_data;
+        GList *in_room;
+        GList *ignored;
+        int id;
+        
+        /* BitlBee */
+        struct conversation *next;
+        char *channel;
+        char *title;
+        char joined;
+        void *data;
 };
 
@@ -127,269 +136,212 @@
 	char name[80];
 	char show[BUDDY_ALIAS_MAXLEN];
-	int present;
+        int present;
+	int evil;
 	time_t signon;
 	time_t idle;
-	int uc;
+        int uc;
 	guint caps; /* woohoo! */
 	void *proto_data; /* what a hack */
-	struct im_connection *ic; /* the connection it belongs to */
-};
-
-struct buddy_action {
-	char *name;
-	char *description;
-};
-
-/* This enum takes a few things from libpurple and a few things from old OPT_ flags.
- * The only flag that was used before this struct was PRPL_OPT_NOOTR.
- *
- * The libpurple ones only use the same values as the PurpleProtocolOptions
- * enum for convenience, but there's no promise of direct compatibility with
- * those values. As of libpurple 2.8.0 they use up to 0x800 (1 << 11), which is
- * a nice coincidence.
- */
-typedef enum {
-	/* The protocol doesn't use passwords
-	 * Mirrors libpurple's OPT_PROTO_NO_PASSWORD */
-	PRPL_OPT_NO_PASSWORD = 1 << 4,
-
-	/* The protocol doesn't require passwords, but may use them
-	 * Mirrors libpurple's OPT_PROTO_PASSWORD_OPTIONAL */
-	PRPL_OPT_PASSWORD_OPTIONAL = 1 << 7,
-
-	/* The protocol is not suitable for OTR, see OPT_NOOTR */
-	PRPL_OPT_NOOTR = 1 << 12,
-} prpl_options_t;
+	struct gaim_connection *gc; /* the connection it belongs to */
+};
+
+struct aim_user {
+	char username[64];
+	char alias[SELF_ALIAS_LEN]; 
+	char password[32];
+	char user_info[2048];
+	int options;
+	int protocol;
+	/* prpls can use this to save information about the user,
+	 * like which server to connect to, etc */
+	char proto_opt[7][256];
+
+	struct gaim_connection *gc;
+	irc_t *irc;
+};
 
 struct prpl {
+	int protocol;
 	int options;
-	/* You should set this to the name of your protocol.
-	 * - The user sees this name ie. when imcb_log() is used. */
-	const char *name;
-	void *data;
-	/* Maximum Message Size of this protocol.
-	 * - Introduced for OTR, in order to fragment large protocol messages.
-	 * - 0 means "unlimited". */
-	unsigned int mms;
-
-	/* Added this one to be able to add per-account settings, don't think
-	 * it should be used for anything else. You are supposed to use the
-	 * set_add() function to add new settings. */
-	void (* init)           (account_t *);
-	/* The typical usage of the login() function:
-	 * - Create an im_connection using imcb_new() from the account_t parameter.
-	 * - Initialize your myproto_data struct - you should store all your protocol-specific data there.
-	 * - Save your custom structure to im_connection->proto_data.
-	 * - Use proxy_connect() to connect to the server.
-	 */
-	void (* login)          (account_t *);
-	/* Implementing this function is optional. */
-	void (* keepalive)      (struct im_connection *);
-	/* In this function you should:
-	 * - Tell the server about you are logging out.
-	 * - g_free() your myproto_data struct as BitlBee does not know how to
-	 *   properly do so.
-	 */
-	void (* logout)         (struct im_connection *);
-
-	/* This function is called when the user wants to send a message to a handle.
-	 * - 'to' is a handle, not a nick
-	 * - 'flags' may be ignored
-	 */
-	int (* buddy_msg)      (struct im_connection *, char *to, char *message, int flags);
-	/* This function is called then the user uses the /away IRC command.
-	 * - 'state' contains the away reason.
-	 * - 'message' may be ignored if your protocol does not support it.
-	 */
-	void (* set_away)       (struct im_connection *, char *state, char *message);
-	/* Implementing this function is optional. */
-	int (* send_typing)    (struct im_connection *, char *who, int flags);
-
-	/* 'name' is a handle to add/remove. For now BitlBee doesn't really
-	 * handle groups, just set it to NULL, so you can ignore that
-	 * parameter. */
-	void (* add_buddy)      (struct im_connection *, char *name, char *group);
-	void (* remove_buddy)   (struct im_connection *, char *name, char *group);
-
-	/* Block list stuff. Implementing these are optional. */
-	void (* add_permit)     (struct im_connection *, char *who);
-	void (* add_deny)       (struct im_connection *, char *who);
-	void (* rem_permit)     (struct im_connection *, char *who);
-	void (* rem_deny)       (struct im_connection *, char *who);
-
-	/* Request profile info. Free-formatted stuff, the IM module gives back
-	   this info via imcb_log(). Implementing these are optional. */
-	void (* get_info)       (struct im_connection *, char *who);
-
-	/* Group chat stuff. */
-	/* This is called when the user uses the /invite IRC command.
-	 * - 'who' may be ignored
-	 * - 'message' is a handle to invite
-	 */
-	void (* chat_invite)    (struct groupchat *, char *who, char *message);
-	/* This is called when the user uses the /kick IRC command.
-	 * - 'who' is a handle to kick
-	 * - 'message' is a kick message or NULL
-	 */
-	void (* chat_kick)      (struct groupchat *, char *who, const char *message);
-	/* This is called when the user uses the /part IRC command in a group
-	 * chat. You just should tell the user about it, nothing more. */
-	void (* chat_leave)     (struct groupchat *);
-	/* This is called when the user sends a message to the groupchat.
-	 * 'flags' may be ignored. */
-	void (* chat_msg)       (struct groupchat *, char *message, int flags);
-	/* This is called when the user uses the /join #nick IRC command.
-	 * - 'who' is the handle of the nick
-	 */
-	struct groupchat *
-	(* chat_with)      (struct im_connection *, char *who);
-	/* This is used when the user uses the /join #channel IRC command.  If
-	 * your protocol does not support publicly named group chats, then do
-	 * not implement this. */
-	struct groupchat *
-	(* chat_join)      (struct im_connection *, const char *room,
-	                    const char *nick, const char *password, set_t **sets);
-	/* Change the topic, if supported. Note that BitlBee expects the IM
-	   server to confirm the topic change with a regular topic change
-	   event. If it doesn't do that, you have to fake it to make it
-	   visible to the user. */
-	void (* chat_topic)     (struct groupchat *, char *topic);
-
-	/* If your protocol module needs any special info for joining chatrooms
-	   other than a roomname + nickname, add them here. */
-	void (* chat_add_settings)      (account_t *acc, set_t **head);
-	void (* chat_free_settings)     (account_t *acc, set_t **head);
-
-	/* You can tell what away states your protocol supports, so that
-	 * BitlBee will try to map the IRC away reasons to them. If your
-	 * protocol doesn't have any, just return one generic "Away". */
-	GList *(* away_states)(struct im_connection *ic);
-
-	/* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh*
-	 * - Most protocols will just want to set this to g_strcasecmp().*/
-	int (* handle_cmp) (const char *who1, const char *who2);
-
-	/* Implement these callbacks if you want to use imcb_ask_auth() */
-	void (* auth_allow)     (struct im_connection *, const char *who);
-	void (* auth_deny)      (struct im_connection *, const char *who);
-
-	/* Incoming transfer request */
-	void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle);
-
-	void (* buddy_data_add) (struct bee_user *bu);
-	void (* buddy_data_free) (struct bee_user *bu);
-
-	GList *(* buddy_action_list) (struct bee_user *bu);
-	void *(* buddy_action) (struct bee_user *bu, const char *action, char * const args[], void *data);
-
-	/* If null, equivalent to handle_cmp( ic->acc->user, who ) */
-	gboolean (* handle_is_self) (struct im_connection *, const char *who);
-
-	/* This sets/updates the im_connection->chatlist field with a
-	 * bee_chat_info_t GSList. This function should ensure the
-	 * bee_chat_list_finish() function gets called at some point
-	 * after the chat list is completely updated.
-	 */
-	void (* chat_list) (struct im_connection *, const char *server);
-
-	/* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */
-	void *resv1;
-	void *resv2;
-	void *resv3;
-	void *resv4;
-	void *resv5;
-};
-
-struct plugin_info
-{
-	guint abiver;
-	const char *name;
-	const char *version;
-	const char *description;
-	const char *author;
-	const char *url;
-};
-
-#ifdef WITH_PLUGINS
-G_MODULE_EXPORT GList *get_plugins();
-#endif
-
-/* im_api core stuff. */
+	char *(* name)();
+
+	/* for ICQ and Yahoo, who have off/on per-conversation options */
+	/* char *checkbox; this should be per-connection */
+
+	GList *(* away_states)(struct gaim_connection *gc);
+	GList *(* actions)();
+	void   (* do_action)(struct gaim_connection *, char *);
+	/* user_opts returns a GList* of g_malloc'd struct proto_user_opts */
+	GList *(* user_opts)();
+	GList *(* chat_info)(struct gaim_connection *);
+
+	/* all the server-related functions */
+
+	/* a lot of these (like get_dir) are protocol-dependent and should be removed. ones like
+	 * set_dir (which is also protocol-dependent) can stay though because there's a dialog
+	 * (i.e. the prpl says you can set your dir info, the ui shows a dialog and needs to call
+	 * set_dir in order to set it) */
+
+	void (* login)		(struct aim_user *);
+	void (* close)		(struct gaim_connection *);
+	int  (* send_im)	(struct gaim_connection *, char *who, char *message, int len, int away);
+	int  (* send_typing)	(struct gaim_connection *, char *who, int typing);
+	void (* set_info)	(struct gaim_connection *, char *info);
+	void (* get_info)	(struct gaim_connection *, char *who);
+	void (* set_away)	(struct gaim_connection *, char *state, char *message);
+	void (* get_away)       (struct gaim_connection *, char *who);
+	void (* set_idle)	(struct gaim_connection *, int idletime);
+	void (* add_buddy)	(struct gaim_connection *, char *name);
+	void (* remove_buddy)	(struct gaim_connection *, char *name, char *group);
+	void (* add_permit)	(struct gaim_connection *, char *name);
+	void (* add_deny)	(struct gaim_connection *, char *name);
+	void (* rem_permit)	(struct gaim_connection *, char *name);
+	void (* rem_deny)	(struct gaim_connection *, char *name);
+	void (* set_permit_deny)(struct gaim_connection *);
+	void (* join_chat)	(struct gaim_connection *, GList *data);
+	void (* chat_invite)	(struct gaim_connection *, int id, char *who, char *message);
+	void (* chat_leave)	(struct gaim_connection *, int id);
+	void (* chat_whisper)	(struct gaim_connection *, int id, char *who, char *message);
+	int  (* chat_send)	(struct gaim_connection *, int id, char *message);
+	int  (* chat_open)	(struct gaim_connection *, char *who);
+	void (* keepalive)	(struct gaim_connection *);
+
+	/* get "chat buddy" info and away message */
+	void (* get_cb_info)	(struct gaim_connection *, int, char *who);
+	void (* get_cb_away)	(struct gaim_connection *, int, char *who);
+
+	/* save/store buddy's alias on server list/roster */
+	void (* alias_buddy)	(struct gaim_connection *, char *who);
+
+	/* change a buddy's group on a server list/roster */
+	void (* group_buddy)	(struct gaim_connection *, char *who, char *old_group, char *new_group);
+
+	void (* buddy_free)	(struct buddy *);
+
+	char *(* get_status_string) (struct gaim_connection *gc, int stat);
+};
+
+#define PROTO_TOC	0
+#define PROTO_OSCAR	1
+#define PROTO_YAHOO	2
+#define PROTO_ICQ	3
+#define PROTO_MSN	4
+#define PROTO_IRC	5
+#define PROTO_FTP	6
+#define PROTO_VGATE	7
+#define PROTO_JABBER	8
+#define PROTO_NAPSTER	9
+#define PROTO_ZEPHYR	10
+#define PROTO_GADUGADU	11
+#define PROTO_MAX	16
+
+extern char proto_name[PROTO_MAX][8];
+
+#define UC_UNAVAILABLE  1
+
+/* JABBER */
+#define UC_AWAY (0x02 | UC_UNAVAILABLE)
+#define UC_CHAT  0x04
+#define UC_XA   (0x08 | UC_UNAVAILABLE)
+#define UC_DND  (0x10 | UC_UNAVAILABLE)
+
+G_MODULE_EXPORT GSList *get_connections();
+extern struct prpl *proto_prpl[16];
+
+/* nogaim.c */
+int serv_send_im(irc_t *irc, user_t *u, char *msg, int flags);
+int serv_send_chat(irc_t *irc, struct gaim_connection *gc, int id, char *msg );
+
+G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf );
+char *set_eval_charset( irc_t *irc, set_t *set, char *value );
+
 void nogaim_init();
-G_MODULE_EXPORT GList *get_protocols();
-G_MODULE_EXPORT GList *get_protocols_disabled();
-G_MODULE_EXPORT GSList *get_connections();
-G_MODULE_EXPORT struct prpl *find_protocol(const char *name);
-G_MODULE_EXPORT gboolean is_protocol_disabled(const char *name);
-G_MODULE_EXPORT char *explain_unknown_protocol(const char *name);
-/* When registering a new protocol, you should allocate space for a new prpl
- * struct, initialize it (set the function pointers to point to your
- * functions), finally call this function. */
-G_MODULE_EXPORT void register_protocol(struct prpl *);
-
-/* Connection management. */
-/* You will need this function in prpl->login() to get an im_connection from
- * the account_t parameter. */
-G_MODULE_EXPORT struct im_connection *imcb_new(account_t *acc);
-G_MODULE_EXPORT void imc_free(struct im_connection *ic);
-/* Once you're connected, you should call this function, so that the user will
- * see the success. */
-G_MODULE_EXPORT void imcb_connected(struct im_connection *ic);
-/* This can be used to disconnect when something went wrong (ie. read error
- * from the server). You probably want to set the second parameter to TRUE. */
-G_MODULE_EXPORT void imc_logout(struct im_connection *ic, int allow_reconnect);
-
-/* Communicating with the user. */
-/* A printf()-like function to tell the user anything you want. */
-G_MODULE_EXPORT void imcb_log(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-/* To tell the user an error, ie. before logging out when an error occurs. */
-G_MODULE_EXPORT void imcb_error(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-
-/* To ask a your about something.
- * - 'msg' is the question.
- * - 'data' can be your custom struct - it will be passed to the callbacks.
- * - 'doit' or 'dont' will be called depending of the answer of the user.
- */
-G_MODULE_EXPORT void imcb_ask(struct im_connection *ic, char *msg, void *data, query_callback doit,
-                              query_callback dont);
-G_MODULE_EXPORT void imcb_ask_with_free(struct im_connection *ic, char *msg, void *data, query_callback doit,
-                                        query_callback dont, query_callback myfree);
-
-/* Two common questions you may want to ask:
- * - X added you to his contact list, allow?
- * - X is not in your contact list, want to add?
- */
-G_MODULE_EXPORT void imcb_ask_auth(struct im_connection *ic, const char *handle, const char *realname);
-G_MODULE_EXPORT void imcb_ask_add(struct im_connection *ic, const char *handle, const char *realname);
-
-/* Buddy management */
-/* This function should be called for each handle which are visible to the
- * user, usually after a login, or if the user added a buddy and the IM
- * server confirms that the add was successful. Don't forget to do this! */
-G_MODULE_EXPORT void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group);
-G_MODULE_EXPORT void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group);
-G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname);
-G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick);
-G_MODULE_EXPORT void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick);
-G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data);
-
-G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags);
-G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle);
-
-G_GNUC_DEPRECATED G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle);
-
-/* Actions, or whatever. */
-int imc_away_send_update(struct im_connection *ic);
-int imc_chat_msg(struct groupchat *c, char *msg, int flags);
-
-void imc_add_allow(struct im_connection *ic, char *handle);
-void imc_rem_allow(struct im_connection *ic, char *handle);
-void imc_add_block(struct im_connection *ic, char *handle);
-void imc_rem_block(struct im_connection *ic, char *handle);
-
-/* Misc. stuff */
-char *set_eval_timezone(set_t *set, char *value);
-gboolean auto_reconnect(gpointer data, gint fd, b_input_condition cond);
-void cancel_auto_reconnect(struct account *a);
-
-#endif
+int proto_away( struct gaim_connection *gc, char *away );
+char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value );
+int handle_cmp( char *a, char *b, int protocol );
+
+gboolean auto_reconnect( gpointer data );
+void cancel_auto_reconnect( struct account *a );
+
+/* multi.c */
+G_MODULE_EXPORT struct gaim_connection *new_gaim_conn( struct aim_user *user );
+G_MODULE_EXPORT void destroy_gaim_conn( struct gaim_connection *gc );
+G_MODULE_EXPORT void set_login_progress( struct gaim_connection *gc, int step, char *msg );
+G_MODULE_EXPORT void hide_login_progress( struct gaim_connection *gc, char *msg );
+G_MODULE_EXPORT void hide_login_progress_error( struct gaim_connection *gc, char *msg );
+G_MODULE_EXPORT void serv_got_crap( struct gaim_connection *gc, char *format, ... );
+G_MODULE_EXPORT void account_online( struct gaim_connection *gc );
+G_MODULE_EXPORT void account_offline( struct gaim_connection *gc );
+G_MODULE_EXPORT void signoff( struct gaim_connection *gc );
+
+/* dialogs.c */
+G_MODULE_EXPORT void do_error_dialog( struct gaim_connection *gc, char *msg, char *title );
+G_MODULE_EXPORT void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont );
+
+/* list.c */
+G_MODULE_EXPORT int bud_list_cache_exists( struct gaim_connection *gc );
+G_MODULE_EXPORT void do_import( struct gaim_connection *gc, void *null );
+G_MODULE_EXPORT void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname );
+G_MODULE_EXPORT struct buddy *find_buddy( struct gaim_connection *gc, char *handle );
+G_MODULE_EXPORT void do_export( struct gaim_connection *gc );
+G_MODULE_EXPORT void signoff_blocked( struct gaim_connection *gc );
+
+G_MODULE_EXPORT void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname );
+
+/* buddy_chat.c */
+G_MODULE_EXPORT void add_chat_buddy( struct conversation *b, char *handle );
+G_MODULE_EXPORT void remove_chat_buddy( struct conversation *b, char *handle, char *reason );
+
+/* prpl.c */
+G_MODULE_EXPORT void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg );
+
+/* server.c */                    
+G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps );
+G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len );
+G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout );
+G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data );
+G_MODULE_EXPORT struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle );
+G_MODULE_EXPORT void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime );
+G_MODULE_EXPORT void serv_got_chat_left( struct gaim_connection *gc, int id );
+/* void serv_finish_login( struct gaim_connection *gc ); */
+
+/* util.c */
+G_MODULE_EXPORT char *utf8_to_str( const char *in );
+G_MODULE_EXPORT char *str_to_utf8( const char *in );
+G_MODULE_EXPORT void strip_linefeed( gchar *text );
+G_MODULE_EXPORT char *add_cr( char *text );
+G_MODULE_EXPORT char *tobase64( const char *text );
+G_MODULE_EXPORT char *normalize( const char *s );
+G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
+G_MODULE_EXPORT void strip_html( char *msg );
+G_MODULE_EXPORT char * escape_html(const char *html);
+G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value);
+
+#ifdef WITH_MSN
+/* msn.c */
+G_MODULE_EXPORT void msn_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_OSCAR
+/* oscar.c */
+G_MODULE_EXPORT void oscar_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_JABBER
+/* jabber.c */
+G_MODULE_EXPORT void jabber_init( struct prpl *ret );
+#endif
+
+#ifdef WITH_YAHOO
+/* yahoo.c */
+G_MODULE_EXPORT void byahoo_init( struct prpl *ret );
+#endif
+
+/* prefs.c */
+G_MODULE_EXPORT void build_block_list();
+G_MODULE_EXPORT void build_allow_list();
+
+struct conversation *conv_findchannel( char *channel );
+
+
+#endif
Index: protocols/oscar/AUTHORS
===================================================================
--- protocols/oscar/AUTHORS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/AUTHORS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,31 @@
+
+Anyone else want to be in here?
+
+---
+
+N: Adam Fritzler
+H: mid
+E: mid@auk.cx
+W: http://www.auk.cx/~mid,http://www.auk.cx/faim
+D: Wrote most of the wap of crap that you see before you.
+
+N: Josh Myer
+E: josh@joshisanerd.com
+D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings.
+
+N: Daniel Reed
+H: n, linuxkitty
+E: n@ml.org
+W: http://users.n.ml.org/n/
+D: Fixed aim_snac.c
+
+N: Eric Warmenhoven
+E: warmenhoven@linux.com
+D: Some OFT info, author of the faim interface for gaim
+
+N: Brock Wilcox
+H: awwaiid
+E: awwaiid@auk.cx
+D: Figured out original password roasting
+
+
Index: protocols/oscar/COPYING
===================================================================
--- protocols/oscar/COPYING	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/COPYING	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,513 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+
Index: protocols/oscar/Makefile
===================================================================
--- protocols/oscar/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee  ##
+##                       ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o ft.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: oscarr.o
+
+.PHONY: all clean distclean
+
+clean:
+	rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+	@echo '*' Compiling $<
+	@$(CC) -c $(CFLAGS) $< -o $@
+
+oscarr.o: $(objects)
+	@echo '*' Linking oscarr.o
+	@$(LD) $(LFLAGS) $(objects) -o oscarr.o
Index: protocols/oscar/admin.c
===================================================================
--- protocols/oscar/admin.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/admin.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,196 @@
+#include <aim.h>
+#include "admin.h"
+
+/* called for both reply and change-reply */
+static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	/*
+	 * struct {
+	 *    guint16 perms;
+	 *    guint16 tlvcount;
+	 *    aim_tlv_t tlvs[tlvcount];
+	 *  } admin_info[n];
+	 */
+	while (aim_bstream_empty(bs)) {
+		guint16 perms, tlvcount;
+
+		perms = aimbs_get16(bs);
+		tlvcount = aimbs_get16(bs);
+
+		while (tlvcount && aim_bstream_empty(bs)) {
+			aim_rxcallback_t userfunc;
+			guint16 type, len;
+			guint8 *val;
+			int str = 0;
+
+			type = aimbs_get16(bs);
+			len = aimbs_get16(bs);
+
+			if ((type == 0x0011) || (type == 0x0004))
+				str = 1;
+
+			if (str)
+				val = (guint8 *)aimbs_getstr(bs, len);
+			else
+				val = aimbs_getraw(bs, len);
+
+			/* XXX fix so its only called once for the entire packet */
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, type, len, val, str);
+
+			g_free(val);
+
+			tlvcount--;
+		}
+	}
+
+	return 1;
+}
+
+static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint16 status;
+
+	status = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, status);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005))
+		return infochange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return accountconfirm(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int admin_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = AIM_CB_FAM_ADM;
+	mod->version = 0x0001;
+	mod->toolid = AIM_TOOL_NEWWIN;
+	mod->toolversion = 0x0629; 
+	mod->flags = 0;
+	strncpy(mod->name, "admin", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw)
+{
+	aim_frame_t *tx;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
+
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	/* new password TLV t(0002) */
+	aim_addtlvtochain_raw(&tl, 0x0002, strlen(newpw), (guint8 *)newpw);
+
+	/* current password TLV t(0012) */
+	aim_addtlvtochain_raw(&tl, 0x0012, strlen(curpw), (guint8 *)curpw);
+
+	aim_writetlvchain(&tx->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, tx);
+
+	return 0;
+}
+
+/*
+ * Request account confirmation. 
+ *
+ * This will cause an email to be sent to the address associated with
+ * the account.  By following the instructions in the mail, you can
+ * get the TRIAL flag removed from your account.
+ *
+ */
+int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0007, 0x0006);
+}
+
+/*
+ * Request a bit of account info.
+ *
+ * The only known valid tag is 0x0011 (email address).
+ *
+ */ 
+int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info)
+{
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
+
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0002, 0x0000, snacid);
+
+	aimbs_put16(&tx->data, info);
+	aimbs_put16(&tx->data, 0x0000);
+
+	aim_tx_enqueue(sess, tx);
+
+	return 0;
+}
+
+int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail)
+{
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	aim_addtlvtochain_raw(&tl, 0x0011, strlen(newemail), (guint8 *)newemail);
+	
+	aim_writetlvchain(&tx->data, &tl);
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, tx);
+
+	return 0;
+}
+
+int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick)
+{
+	aim_frame_t *tx;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid);
+
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(newnick), (guint8 *)newnick);
+	
+	aim_writetlvchain(&tx->data, &tl);
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, tx);
+
+
+	return 0;
+}
Index: protocols/oscar/admin.h
===================================================================
--- protocols/oscar/admin.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/admin.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,13 @@
+#ifndef __OSCAR_ADMIN_H__
+#define __OSCAR_ADMIN_H__
+
+#define AIM_CB_FAM_ADM 0x0007
+
+/*
+ * SNAC Family: Administrative Services.
+ */ 
+#define AIM_CB_ADM_ERROR 0x0001
+#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005
+#define AIM_CB_ADM_DEFAULT 0xffff
+
+#endif /* __OSCAR_ADMIN_H__ */
Index: protocols/oscar/aim.h
===================================================================
--- protocols/oscar/aim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/aim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,921 @@
+/* 
+ * Main libfaim header.  Must be included in client for prototypes/macros.
+ *
+ * "come on, i turned a chick lesbian; i think this is the hackish equivalent"
+ *                                                -- Josh Meyer
+ *
+ */
+
+#ifndef __AIM_H__
+#define __AIM_H__
+
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <time.h>
+#include <gmodule.h>
+
+#include "bitlbee.h"
+
+/* XXX adjust these based on autoconf-detected platform */
+typedef guint32 aim_snacid_t;
+typedef guint16 flap_seqnum_t;
+
+/* Portability stuff (DMP) */
+
+#if defined(mach) && defined(__APPLE__)
+#define gethostbyname(x) gethostbyname2(x, AF_INET) 
+#endif
+
+/* 
+ * Current Maximum Length for Screen Names (not including NULL) 
+ *
+ * Currently only names up to 16 characters can be registered
+ * however it is aparently legal for them to be larger.
+ */
+#define MAXSNLEN 32
+
+/*
+ * Current Maximum Length for Instant Messages
+ *
+ * This was found basically by experiment, but not wholly
+ * accurate experiment.  It should not be regarded
+ * as completely correct.  But its a decent approximation.
+ *
+ * Note that although we can send this much, its impossible
+ * for WinAIM clients (up through the latest (4.0.1957)) to
+ * send any more than 1kb.  Amaze all your windows friends
+ * with utterly oversized instant messages!
+ *
+ * XXX: the real limit is the total SNAC size at 8192. Fix this.
+ * 
+ */
+#define MAXMSGLEN 7987
+
+/*
+ * Maximum size of a Buddy Icon.
+ */
+#define MAXICONLEN 7168
+#define AIM_ICONIDENT "AVT1picture.id"
+
+/*
+ * Current Maximum Length for Chat Room Messages
+ *
+ * This is actually defined by the protocol to be
+ * dynamic, but I have yet to see due cause to 
+ * define it dynamically here.  Maybe later.
+ *
+ */
+#define MAXCHATMSGLEN 512
+
+/*
+ * Standard size of an AIM authorization cookie
+ */
+#define AIM_COOKIELEN            0x100
+
+#define AIM_MD5_STRING "AOL Instant Messenger (SM)"
+
+/*
+ * Default Authorizer server name and TCP port for the OSCAR farm.  
+ *
+ * You shouldn't need to change this unless you're writing
+ * your own server. 
+ *
+ * Note that only one server is needed to start the whole
+ * AIM process.  The later server addresses come from
+ * the authorizer service.
+ *
+ * This is only here for convenience.  Its still up to
+ * the client to connect to it.
+ *
+ */
+#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com"
+#define AIM_LOGIN_PORT 5190
+
+/*
+ * Size of the SNAC caching hash.
+ *
+ * Default: 16
+ *
+ */
+#define AIM_SNAC_HASH_SIZE 16
+
+/*
+ * Client info.  Filled in by the client and passed in to 
+ * aim_send_login().  The information ends up getting passed to OSCAR
+ * through the initial login command.
+ *
+ */
+struct client_info_s {
+	const char *clientstring;
+	guint16 clientid;
+	int major;
+	int minor;
+	int point;
+	int build;
+	const char *country; /* two-letter abbrev */
+	const char *lang; /* two-letter abbrev */
+};
+
+#define AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 { \
+	"AOL Instant Messenger (SM), version 3.5.1670/WIN32", \
+	0x0004, \
+	0x0003, \
+	0x0005, \
+	0x0000, \
+	0x0686, \
+	"us", \
+	"en", \
+}
+
+#define AIM_CLIENTINFO_KNOWNGOOD_4_1_2010 { \
+	  "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \
+	  0x0004, \
+	  0x0004, \
+	  0x0001, \
+	  0x0000, \
+	  0x07da, \
+	  "us", \
+	  "en", \
+}
+
+/*
+ * I would make 4.1.2010 the default, but they seem to have found
+ * an alternate way of breaking that one. 
+ *
+ * 3.5.1670 should work fine, however, you will be subjected to the
+ * memory test, which may require you to have a WinAIM binary laying 
+ * around. (see login.c::memrequest())
+ */
+#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* 
+ * These could be arbitrary, but its easier to use the actual AIM values 
+ */
+#define AIM_CONN_TYPE_AUTH          0x0007
+#define AIM_CONN_TYPE_ADS           0x0005
+#define AIM_CONN_TYPE_BOS           0x0002
+#define AIM_CONN_TYPE_CHAT          0x000e
+#define AIM_CONN_TYPE_CHATNAV       0x000d
+
+/* they start getting arbitrary in rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS    0x0101 /* these do not speak FLAP! */
+#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */
+
+/*
+ * Subtypes, we need these for OFT stuff.
+ */
+#define AIM_CONN_SUBTYPE_OFT_DIRECTIM  0x0001
+#define AIM_CONN_SUBTYPE_OFT_GETFILE   0x0002
+#define AIM_CONN_SUBTYPE_OFT_SENDFILE  0x0003
+#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004
+#define AIM_CONN_SUBTYPE_OFT_VOICE     0x0005
+
+/*
+ * Status values returned from aim_conn_new().  ORed together.
+ */
+#define AIM_CONN_STATUS_READY       0x0001
+#define AIM_CONN_STATUS_INTERNALERR 0x0002
+#define AIM_CONN_STATUS_RESOLVERR   0x0040
+#define AIM_CONN_STATUS_CONNERR     0x0080
+#define AIM_CONN_STATUS_INPROGRESS  0x0100
+
+#define AIM_FRAMETYPE_FLAP 0x0000
+#define AIM_FRAMETYPE_OFT  0x0001
+
+/*
+ * message type flags
+ */
+#define AIM_MTYPE_PLAIN     0x01
+#define AIM_MTYPE_CHAT      0x02
+#define AIM_MTYPE_FILEREQ   0x03
+#define AIM_MTYPE_URL       0x04
+#define AIM_MTYPE_AUTHREQ   0x06
+#define AIM_MTYPE_AUTHDENY  0x07
+#define AIM_MTYPE_AUTHOK    0x08
+#define AIM_MTYPE_SERVER    0x09
+#define AIM_MTYPE_ADDED     0x0C
+#define AIM_MTYPE_WWP       0x0D
+#define AIM_MTYPE_EEXPRESS  0x0E
+#define AIM_MTYPE_CONTACTS  0x13
+#define AIM_MTYPE_PLUGIN    0x1A
+#define AIM_MTYPE_AUTOAWAY	0xE8
+#define AIM_MTYPE_AUTOBUSY	0xE9
+#define AIM_MTYPE_AUTONA	0xEA
+#define AIM_MTYPE_AUTODND	0xEB
+#define AIM_MTYPE_AUTOFFC	0xEC
+
+typedef struct aim_conn_s {
+	int fd;
+	guint16 type;
+	guint16 subtype;
+	flap_seqnum_t seqnum;
+	guint32 status;
+	void *priv; /* misc data the client may want to store */
+	void *internal; /* internal conn-specific libfaim data */
+	time_t lastactivity; /* time of last transmit */
+	int forcedlatency; 
+	void *handlerlist;
+	void *sessv; /* pointer to parent session */
+	void *inside; /* only accessible from inside libfaim */
+	struct aim_conn_s *next;
+} aim_conn_t;
+
+/*
+ * Byte Stream type. Sort of.
+ *
+ * Use of this type serves a couple purposes:
+ *   - Buffer/buflen pairs are passed all around everywhere. This turns
+ *     that into one value, as well as abstracting it slightly.
+ *   - Through the abstraction, it is possible to enable bounds checking
+ *     for robustness at the cost of performance.  But a clean failure on
+ *     weird packets is much better than a segfault.
+ *   - I like having variables named "bs".
+ *
+ * Don't touch the insides of this struct.  Or I'll have to kill you.
+ *
+ */
+typedef struct aim_bstream_s {
+	guint8 *data;
+	guint32 len;
+	guint32 offset;
+} aim_bstream_t;
+
+typedef struct aim_frame_s {
+	guint8 hdrtype; /* defines which piece of the union to use */
+	union {
+		struct { 
+			guint8 type;
+			flap_seqnum_t seqnum;     
+		} flap;
+		struct {
+			guint16 type;
+			guint8 magic[4]; /* ODC2 OFT2 */
+			guint16 hdr2len;
+			guint8 *hdr2; /* rest of bloated header */
+		} oft;
+	} hdr;
+	aim_bstream_t data;	/* payload stream */
+	guint8 handled;		/* 0 = new, !0 = been handled */
+	guint8 nofree;		/* 0 = free data on purge, 1 = only unlink */
+	aim_conn_t *conn;  /* the connection it came in on... */
+	struct aim_frame_s *next;
+} aim_frame_t;
+
+typedef struct aim_msgcookie_s {
+	unsigned char cookie[8];
+	int type;
+	void *data;
+	time_t addtime;
+	struct aim_msgcookie_s *next;
+} aim_msgcookie_t;
+
+/*
+ * AIM Session: The main client-data interface.  
+ *
+ */
+typedef struct aim_session_s {
+
+	/* ---- Client Accessible ------------------------ */
+
+	/* Our screen name. */
+	char sn[MAXSNLEN+1];
+
+	/*
+	 * Pointer to anything the client wants to 
+	 * explicitly associate with this session.
+	 *
+	 * This is for use in the callbacks mainly. In any
+	 * callback, you can access this with sess->aux_data.
+	 *
+	 */
+	void *aux_data;
+
+	/* ---- Internal Use Only ------------------------ */
+
+	/* Server-stored information (ssi) */
+	struct {
+		int received_data;
+		guint16 revision;
+		struct aim_ssi_item *items;
+		time_t timestamp;
+		int waiting_for_ack;
+		aim_frame_t *holding_queue;
+	} ssi;
+
+	/* Connection information */
+	aim_conn_t *connlist;
+
+	/*
+	 * Transmit/receive queues.
+	 *
+	 * These are only used when you don't use your own lowlevel
+	 * I/O.  I don't suggest that you use libfaim's internal I/O.
+	 * Its really bad and the API/event model is quirky at best.
+	 *  
+	 */
+	aim_frame_t *queue_outgoing;   
+	aim_frame_t *queue_incoming; 
+
+	/*
+	 * Tx Enqueuing function.
+	 *
+	 * This is how you override the transmit direction of libfaim's
+	 * internal I/O.  This function will be called whenever it needs
+	 * to send something.
+	 *
+	 */
+	int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *);
+
+	/*
+	 * Outstanding snac handling 
+	 *
+	 * XXX: Should these be per-connection? -mid
+	 */
+	void *snac_hash[AIM_SNAC_HASH_SIZE];
+	aim_snacid_t snacid_next;
+
+	struct aim_icq_info *icq_info;
+	struct aim_oft_info *oft_info;
+	struct aim_authresp_info *authinfo;
+	struct aim_emailinfo *emailinfo;
+
+	struct {
+		struct aim_userinfo_s *userinfo;
+		struct userinfo_node *torequest;
+		struct userinfo_node *requested;
+		int waiting_for_response;
+	} locate;
+
+	guint32 flags; /* AIM_SESS_FLAGS_ */
+
+	aim_msgcookie_t *msgcookies;
+
+	void *modlistv;
+
+	guint8 aim_icq_state;  /* ICQ representation of away state */
+} aim_session_t;
+
+/* Values for sess->flags */
+#define AIM_SESS_FLAGS_SNACLOGIN         0x00000001
+#define AIM_SESS_FLAGS_XORLOGIN          0x00000002
+#define AIM_SESS_FLAGS_NONBLOCKCONNECT   0x00000004
+#define AIM_SESS_FLAGS_DONTTIMEOUTONICBM 0x00000008
+
+/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
+#define AIM_ICQ_STATE_NORMAL    0x00000000
+#define AIM_ICQ_STATE_AWAY      0x00000001
+#define AIM_ICQ_STATE_DND       0x00000002
+#define AIM_ICQ_STATE_OUT       0x00000004
+#define AIM_ICQ_STATE_BUSY      0x00000010
+#define AIM_ICQ_STATE_CHAT      0x00000020
+#define AIM_ICQ_STATE_INVISIBLE 0x00000100
+#define AIM_ICQ_STATE_WEBAWARE  0x00010000
+#define AIM_ICQ_STATE_HIDEIP            0x00020000
+#define AIM_ICQ_STATE_BIRTHDAY          0x00080000
+#define AIM_ICQ_STATE_DIRECTDISABLED    0x00100000
+#define AIM_ICQ_STATE_ICQHOMEPAGE       0x00200000
+#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
+#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000
+
+/*
+ * AIM User Info, Standard Form.
+ */
+typedef struct {
+	char sn[MAXSNLEN+1];
+	guint16 warnlevel;
+	guint16 idletime;
+	guint16 flags;
+	guint32 membersince;
+	guint32 onlinesince;
+	guint32 sessionlen; 
+	guint32 capabilities;
+	struct {
+		guint32 status;
+		guint32 ipaddr;
+		guint8 crap[0x25]; /* until we figure it out... */
+	} icqinfo;
+	guint32 present;
+} aim_userinfo_t;
+
+#define AIM_USERINFO_PRESENT_FLAGS        0x00000001
+#define AIM_USERINFO_PRESENT_MEMBERSINCE  0x00000002
+#define AIM_USERINFO_PRESENT_ONLINESINCE  0x00000004
+#define AIM_USERINFO_PRESENT_IDLE         0x00000008
+#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010
+#define AIM_USERINFO_PRESENT_ICQIPADDR    0x00000020
+#define AIM_USERINFO_PRESENT_ICQDATA      0x00000040
+#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080
+#define AIM_USERINFO_PRESENT_SESSIONLEN   0x00000100
+
+const char *aim_userinfo_sn(aim_userinfo_t *ui);
+guint16 aim_userinfo_flags(aim_userinfo_t *ui);
+guint16 aim_userinfo_idle(aim_userinfo_t *ui);
+float aim_userinfo_warnlevel(aim_userinfo_t *ui);
+time_t aim_userinfo_membersince(aim_userinfo_t *ui);
+time_t aim_userinfo_onlinesince(aim_userinfo_t *ui);
+guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui);
+int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap);
+
+#define AIM_FLAG_UNCONFIRMED 	0x0001 /* "damned transients" */
+#define AIM_FLAG_ADMINISTRATOR	0x0002
+#define AIM_FLAG_AOL		0x0004
+#define AIM_FLAG_OSCAR_PAY	0x0008
+#define AIM_FLAG_FREE 		0x0010
+#define AIM_FLAG_AWAY		0x0020
+#define AIM_FLAG_ICQ		0x0040
+#define AIM_FLAG_WIRELESS	0x0080
+#define AIM_FLAG_UNKNOWN100	0x0100
+#define AIM_FLAG_UNKNOWN200	0x0200
+#define AIM_FLAG_ACTIVEBUDDY    0x0400
+#define AIM_FLAG_UNKNOWN800	0x0800
+#define AIM_FLAG_ABINTERNAL     0x1000
+
+#define AIM_FLAG_ALLUSERS	0x001f
+
+/*
+ * TLV handling
+ */
+
+/* Generic TLV structure. */
+typedef struct aim_tlv_s {
+	guint16 type;
+	guint16 length;
+	guint8 *value;
+} aim_tlv_t;
+
+/* List of above. */
+typedef struct aim_tlvlist_s {
+	aim_tlv_t *tlv;
+	struct aim_tlvlist_s *next;
+} aim_tlvlist_t;
+
+/* TLV-handling functions */
+
+#if 0
+/* Very, very raw TLV handling. */
+int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v);
+int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v);
+int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v);
+int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v);
+#endif
+
+/* TLV list handling. */
+aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs);
+void aim_freetlvchain(aim_tlvlist_t **list);
+aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, guint16 t, const int n);
+char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n);
+guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 type, const int num);
+guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n);
+guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n);
+int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list);
+int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v);
+int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v);
+int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 type, const guint32 v);
+int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 type, const char *msg);
+int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v);
+int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps);
+int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type);
+int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui);
+int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl);
+int aim_counttlvchain(aim_tlvlist_t **list);
+int aim_sizetlvchain(aim_tlvlist_t **list);
+
+
+/*
+ * Get command from connections
+ *
+ * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction.
+ * XXX Make this easily overridable.
+ *
+ */
+int aim_get_command(aim_session_t *, aim_conn_t *);
+
+/*
+ * Dispatch commands that are in the rx queue.
+ */
+void aim_rxdispatch(aim_session_t *);
+
+int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn);
+
+typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...);
+
+struct aim_clientrelease {
+	char *name;
+	guint32 build;
+	char *url;
+	char *info;
+};
+
+struct aim_authresp_info {
+	char *sn;
+	guint16 errorcode;
+	char *errorurl;
+	guint16 regstatus;
+	char *email;
+	char *bosip;
+	guint8 *cookie;
+	struct aim_clientrelease latestrelease;
+	struct aim_clientrelease latestbeta;
+};
+
+/* Callback data for redirect. */
+struct aim_redirect_data {
+	guint16 group;
+	const char *ip;
+	const guint8 *cookie;
+	struct { /* group == AIM_CONN_TYPE_CHAT */
+		guint16 exchange;
+		const char *room;
+		guint16 instance;
+	} chat;
+};
+
+int aim_clientready(aim_session_t *sess, aim_conn_t *conn);
+int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn);
+int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key);
+int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest);
+void aim_purge_rxqueue(aim_session_t *);
+
+#define AIM_TX_QUEUED    0 /* default */
+#define AIM_TX_IMMEDIATE 1
+#define AIM_TX_USER      2
+int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *));
+
+int aim_tx_flushqueue(aim_session_t *);
+void aim_tx_purgequeue(aim_session_t *);
+
+int aim_conn_setlatency(aim_conn_t *conn, int newval);
+
+void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn);
+
+int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, u_short family, u_short type, aim_rxcallback_t newhandler, u_short flags);
+int aim_clearhandlers(aim_conn_t *conn);
+
+aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group);
+aim_session_t *aim_conn_getsess(aim_conn_t *conn);
+void aim_conn_close(aim_conn_t *deadconn);
+aim_conn_t *aim_newconn(aim_session_t *, int type, const char *dest);
+int aim_conngetmaxfd(aim_session_t *);
+aim_conn_t *aim_select(aim_session_t *, struct timeval *, int *);
+int aim_conn_isready(aim_conn_t *);
+int aim_conn_setstatus(aim_conn_t *, int);
+int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn);
+int aim_conn_isconnecting(aim_conn_t *conn);
+
+typedef void (*faim_debugging_callback_t)(aim_session_t *sess, int level, const char *format, va_list va);
+int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t);
+void aim_session_init(aim_session_t *, guint32 flags, int debuglevel);
+void aim_session_kill(aim_session_t *);
+void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password);
+aim_conn_t *aim_getconn_type(aim_session_t *, int type);
+aim_conn_t *aim_getconn_type_all(aim_session_t *, int type);
+aim_conn_t *aim_getconn_fd(aim_session_t *, int fd);
+
+/* aim_misc.c */
+
+
+struct aim_chat_roominfo {
+	unsigned short exchange;
+	char *name;
+	unsigned short instance;
+};
+
+
+#define AIM_VISIBILITYCHANGE_PERMITADD    0x05
+#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06
+#define AIM_VISIBILITYCHANGE_DENYADD      0x07
+#define AIM_VISIBILITYCHANGE_DENYREMOVE   0x08
+
+#define AIM_PRIVFLAGS_ALLOWIDLE           0x01
+#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE    0x02
+
+#define AIM_WARN_ANON                     0x01
+
+int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn);
+int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags);
+int aim_nop(aim_session_t *, aim_conn_t *);
+int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn);
+int aim_bos_setidle(aim_session_t *, aim_conn_t *, guint32);
+int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *);
+int aim_bos_setbuddylist(aim_session_t *, aim_conn_t *, const char *);
+int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps);
+int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, guint32 mask);
+int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, guint32);
+int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *);
+int aim_reqservice(aim_session_t *, aim_conn_t *, guint16);
+int aim_bos_reqrights(aim_session_t *, aim_conn_t *);
+int aim_bos_reqbuddyrights(aim_session_t *, aim_conn_t *);
+int aim_bos_reqlocaterights(aim_session_t *, aim_conn_t *);
+int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy);
+int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy);
+int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status);
+
+struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *);
+
+#define AIM_CLIENTTYPE_UNKNOWN  0x0000
+#define AIM_CLIENTTYPE_MC       0x0001
+#define AIM_CLIENTTYPE_WINAIM   0x0002
+#define AIM_CLIENTTYPE_WINAIM41 0x0003
+#define AIM_CLIENTTYPE_AOL_TOC  0x0004
+unsigned short aim_fingerprintclient(unsigned char *msghdr, int len);
+
+#define AIM_RATE_CODE_CHANGE     0x0001
+#define AIM_RATE_CODE_WARNING    0x0002
+#define AIM_RATE_CODE_LIMIT      0x0003
+#define AIM_RATE_CODE_CLEARLIMIT 0x0004
+int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn);
+
+/* aim_im.c */
+
+aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize);
+
+aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn);
+int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size);
+int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn);
+int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn);
+
+#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST  0
+#define AIM_SENDMEMBLOCK_FLAG_ISHASH     1
+
+int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag);
+
+#define AIM_GETINFO_GENERALINFO 0x00001
+#define AIM_GETINFO_AWAYMESSAGE 0x00003
+#define AIM_GETINFO_CAPABILITIES 0x0004
+
+struct aim_invite_priv {
+	char *sn;
+	char *roomname;
+	guint16 exchange;
+	guint16 instance;
+};
+
+#define AIM_COOKIETYPE_UNKNOWN  0x00
+#define AIM_COOKIETYPE_ICBM     0x01
+#define AIM_COOKIETYPE_ADS      0x02
+#define AIM_COOKIETYPE_BOS      0x03
+#define AIM_COOKIETYPE_IM       0x04
+#define AIM_COOKIETYPE_CHAT     0x05
+#define AIM_COOKIETYPE_CHATNAV  0x06
+#define AIM_COOKIETYPE_INVITE   0x07
+/* we'll move OFT up a bit to give breathing room.  not like it really
+ * matters. */
+#define AIM_COOKIETYPE_OFTIM    0x10
+#define AIM_COOKIETYPE_OFTGET   0x11
+#define AIM_COOKIETYPE_OFTSEND  0x12
+#define AIM_COOKIETYPE_OFTVOICE 0x13
+#define AIM_COOKIETYPE_OFTIMAGE 0x14
+#define AIM_COOKIETYPE_OFTICON  0x15
+
+int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur);
+
+#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000
+#define AIM_TRANSFER_DENY_DECLINE 0x0001
+#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002
+int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, unsigned short code);
+aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, const guint8 *cookie, const guint8 *ip, guint16 listingfiles, guint16 listingtotsize, guint16 listingsize, guint32 listingchecksum, guint16 rendid);
+
+int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, unsigned short);
+int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info);
+int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+
+#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED	0x00000001
+#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED	0x00000002
+
+/* This is what the server will give you if you don't set them yourself. */
+#define AIM_IMPARAM_DEFAULTS { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	512, /* !! Note how small this is. */ \
+	(99.9)*10, (99.9)*10, \
+	1000 /* !! And how large this is. */ \
+}
+
+/* This is what most AIM versions use. */
+#define AIM_IMPARAM_REASONABLE { \
+	0, \
+	AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \
+	8000, \
+	(99.9)*10, (99.9)*10, \
+	0 \
+}
+
+
+struct aim_icbmparameters {
+	guint16 maxchan;
+	guint32 flags; /* AIM_IMPARAM_FLAG_ */
+	guint16 maxmsglen; /* message size that you will accept */
+	guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */
+	guint16 maxrecverwarn;
+	guint32 minmsginterval; /* in milliseconds? */
+};
+
+int aim_reqicbmparams(aim_session_t *sess);
+int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params);
+
+/* auth.c */
+int aim_sendcookie(aim_session_t *, aim_conn_t *, const guint8 *);
+
+int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw);
+int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn);
+int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info);
+int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail);
+int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick);
+
+/* These apply to exchanges as well. */
+#define AIM_CHATROOM_FLAG_EVILABLE 0x0001
+#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002
+#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004
+#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008
+
+struct aim_chat_exchangeinfo {
+	guint16 number;
+	guint16 flags;
+	char *name;
+	char *charset1;
+	char *lang1;
+	char *charset2;
+	char *lang2;
+};
+
+#define AIM_CHATFLAGS_NOREFLECT 0x0001
+#define AIM_CHATFLAGS_AWAY      0x0002
+int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen);
+int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance);
+char *aim_chat_getname(aim_conn_t *conn);
+aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name);
+
+int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn);
+
+int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance);
+
+int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange);
+int aim_chat_leaveroom(aim_session_t *sess, const char *name);
+
+/* aim_util.c */
+/*
+ * These are really ugly.  You'd think this was LISP.  I wish it was.
+ *
+ * XXX With the advent of bstream's, these should be removed to enforce
+ * their use.
+ *
+ */
+#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1)
+#define aimutil_get8(buf) ((*(buf))&0xff)
+#define aimutil_put16(buf, data) ( \
+		(*(buf) = (u_char)((data)>>8)&0xff), \
+		(*((buf)+1) = (u_char)(data)&0xff),  \
+		2)
+#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff))
+#define aimutil_put32(buf, data) ( \
+		(*((buf)) = (u_char)((data)>>24)&0xff), \
+		(*((buf)+1) = (u_char)((data)>>16)&0xff), \
+		(*((buf)+2) = (u_char)((data)>>8)&0xff), \
+		(*((buf)+3) = (u_char)(data)&0xff), \
+		4)
+#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \
+		(((*((buf)+1))<<16)&0x00ff0000) + \
+		(((*((buf)+2))<< 8)&0x0000ff00) + \
+		(((*((buf)+3)    )&0x000000ff)))
+
+/* Little-endian versions (damn ICQ) */
+#define aimutil_putle8(buf, data) ( \
+		(*(buf) = (unsigned char)(data) & 0xff), \
+		1)
+#define aimutil_getle8(buf) ( \
+		(*(buf)) & 0xff \
+		)
+#define aimutil_putle16(buf, data) ( \
+		(*((buf)+0) = (unsigned char)((data) >> 0) & 0xff),  \
+		(*((buf)+1) = (unsigned char)((data) >> 8) & 0xff), \
+		2)
+#define aimutil_getle16(buf) ( \
+		(((*((buf)+0)) << 0) & 0x00ff) + \
+		(((*((buf)+1)) << 8) & 0xff00) \
+		)
+#define aimutil_putle32(buf, data) ( \
+		(*((buf)+0) = (unsigned char)((data) >>  0) & 0xff), \
+		(*((buf)+1) = (unsigned char)((data) >>  8) & 0xff), \
+		(*((buf)+2) = (unsigned char)((data) >> 16) & 0xff), \
+		(*((buf)+3) = (unsigned char)((data) >> 24) & 0xff), \
+		4)
+#define aimutil_getle32(buf) ( \
+		(((*((buf)+0)) <<  0) & 0x000000ff) + \
+		(((*((buf)+1)) <<  8) & 0x0000ff00) + \
+		(((*((buf)+2)) << 16) & 0x00ff0000) + \
+		(((*((buf)+3)) << 24) & 0xff000000))
+
+
+int aimutil_putstr(u_char *, const char *, int);
+int aimutil_tokslen(char *toSearch, int index, char dl);
+int aimutil_itemcnt(char *toSearch, char dl);
+char *aimutil_itemidx(char *toSearch, int index, char dl);
+int aim_sncmp(const char *a, const char *b);
+
+#include <aim_internal.h>
+
+/*
+ * SNAC Families.
+ */
+#define AIM_CB_FAM_ACK 0x0000
+#define AIM_CB_FAM_GEN 0x0001
+#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */
+
+/*
+ * SNAC Family: Ack.
+ * 
+ * Not really a family, but treating it as one really
+ * helps it fit into the libfaim callback structure better.
+ *
+ */
+#define AIM_CB_ACK_ACK 0x0001
+
+/*
+ * SNAC Family: General.
+ */ 
+#define AIM_CB_GEN_ERROR 0x0001
+#define AIM_CB_GEN_CLIENTREADY 0x0002
+#define AIM_CB_GEN_SERVERREADY 0x0003
+#define AIM_CB_GEN_SERVICEREQ 0x0004
+#define AIM_CB_GEN_REDIRECT 0x0005
+#define AIM_CB_GEN_RATEINFOREQ 0x0006
+#define AIM_CB_GEN_RATEINFO 0x0007
+#define AIM_CB_GEN_RATEINFOACK 0x0008
+#define AIM_CB_GEN_RATECHANGE 0x000a
+#define AIM_CB_GEN_SERVERPAUSE 0x000b
+#define AIM_CB_GEN_SERVERRESUME 0x000d
+#define AIM_CB_GEN_REQSELFINFO 0x000e
+#define AIM_CB_GEN_SELFINFO 0x000f
+#define AIM_CB_GEN_EVIL 0x0010
+#define AIM_CB_GEN_SETIDLE 0x0011
+#define AIM_CB_GEN_MIGRATIONREQ 0x0012
+#define AIM_CB_GEN_MOTD 0x0013
+#define AIM_CB_GEN_SETPRIVFLAGS 0x0014
+#define AIM_CB_GEN_WELLKNOWNURL 0x0015
+#define AIM_CB_GEN_NOP 0x0016
+#define AIM_CB_GEN_DEFAULT 0xffff
+
+/*
+ * SNAC Family: Advertisement Services
+ */ 
+#define AIM_CB_ADS_ERROR 0x0001
+#define AIM_CB_ADS_DEFAULT 0xffff
+
+/*
+ * OFT Services
+ *
+ * See non-SNAC note below.
+ */
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
+#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+
+#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */
+#define AIM_CB_OFT_GETFILEFILEREQ 0x0008    /* received file request */
+#define AIM_CB_OFT_GETFILEFILESEND 0x0009   /* received file request confirm -- send data */
+#define AIM_CB_OFT_GETFILECOMPLETE 0x000a   /* received file send complete*/
+#define AIM_CB_OFT_GETFILEINITIATE 0x000b   /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c   /* OFT connection disconnected.*/
+#define AIM_CB_OFT_GETFILELISTING 0x000d   /* OFT listing.txt received.*/
+#define AIM_CB_OFT_GETFILERECEIVE 0x000e   /* OFT file incoming.*/
+#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f
+#define AIM_CB_OFT_GETFILESTATE4 0x0010
+
+#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020   /* OFT connection disconnected.*/
+
+
+
+/*
+ * SNAC Family: Internal Messages
+ *
+ * This isn't truely a SNAC family either, but using
+ * these, we can integrated non-SNAC services into
+ * the SNAC-centered libfaim callback structure.
+ *
+ */ 
+#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001
+#define AIM_CB_SPECIAL_AUTHOTHER 0x0002
+#define AIM_CB_SPECIAL_CONNERR 0x0003
+#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004
+#define AIM_CB_SPECIAL_FLAPVER 0x0005
+#define AIM_CB_SPECIAL_CONNINITDONE 0x0006
+#define AIM_CB_SPECIAL_IMAGETRANSFER 0x007
+#define AIM_CB_SPECIAL_UNKNOWN 0xffff
+#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN
+
+#endif /* __AIM_H__ */
Index: protocols/oscar/aim_internal.h
===================================================================
--- protocols/oscar/aim_internal.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/aim_internal.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,216 @@
+/*
+ * aim_internal.h -- prototypes/structs for the guts of libfaim
+ *
+ */
+
+#ifndef __AIM_INTERNAL_H__
+#define __AIM_INTERNAL_H__ 1
+
+typedef struct {
+	guint16 family;
+	guint16 subtype;
+	guint16 flags;
+	guint32 id;
+} aim_modsnac_t;
+
+#define AIM_MODULENAME_MAXLEN 16
+#define AIM_MODFLAG_MULTIFAMILY 0x0001
+typedef struct aim_module_s {
+	guint16 family;
+	guint16 version;
+	guint16 toolid;
+	guint16 toolversion;
+	guint16 flags;
+	char name[AIM_MODULENAME_MAXLEN+1];
+	int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs);
+	void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod);
+	void *priv;
+	struct aim_module_s *next;
+} aim_module_t;
+
+int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *));
+void aim__shutdownmodules(aim_session_t *sess);
+aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group);
+
+int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod);
+int admin_modfirst(aim_session_t *sess, aim_module_t *mod);
+int bos_modfirst(aim_session_t *sess, aim_module_t *mod);
+int search_modfirst(aim_session_t *sess, aim_module_t *mod);
+int stats_modfirst(aim_session_t *sess, aim_module_t *mod);
+int auth_modfirst(aim_session_t *sess, aim_module_t *mod);
+int msg_modfirst(aim_session_t *sess, aim_module_t *mod);
+int misc_modfirst(aim_session_t *sess, aim_module_t *mod);
+int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod);
+int chat_modfirst(aim_session_t *sess, aim_module_t *mod);
+int locate_modfirst(aim_session_t *sess, aim_module_t *mod);
+int general_modfirst(aim_session_t *sess, aim_module_t *mod);
+int ssi_modfirst(aim_session_t *sess, aim_module_t *mod);
+int icq_modfirst(aim_session_t *sess, aim_module_t *mod);
+
+int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype);
+int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *);
+int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *);
+
+#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset)
+
+void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn);
+int aim_recv(int fd, void *buf, size_t count);
+int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len);
+int aim_bstream_empty(aim_bstream_t *bs);
+int aim_bstream_curpos(aim_bstream_t *bs);
+int aim_bstream_setpos(aim_bstream_t *bs, int off);
+void aim_bstream_rewind(aim_bstream_t *bs);
+int aim_bstream_advance(aim_bstream_t *bs, int n);
+guint8 aimbs_get8(aim_bstream_t *bs);
+guint16 aimbs_get16(aim_bstream_t *bs);
+guint32 aimbs_get32(aim_bstream_t *bs);
+guint8 aimbs_getle8(aim_bstream_t *bs);
+guint16 aimbs_getle16(aim_bstream_t *bs);
+guint32 aimbs_getle32(aim_bstream_t *bs);
+int aimbs_put8(aim_bstream_t *bs, guint8 v);
+int aimbs_put16(aim_bstream_t *bs, guint16 v);
+int aimbs_put32(aim_bstream_t *bs, guint32 v);
+int aimbs_putle8(aim_bstream_t *bs, guint8 v);
+int aimbs_putle16(aim_bstream_t *bs, guint16 v);
+int aimbs_putle32(aim_bstream_t *bs, guint32 v);
+int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len);
+guint8 *aimbs_getraw(aim_bstream_t *bs, int len);
+char *aimbs_getstr(aim_bstream_t *bs, int len);
+int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len);
+int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len);
+
+int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn);
+
+int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur);
+flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *);
+aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen);
+void aim_frame_destroy(aim_frame_t *);
+int aim_tx_enqueue(aim_session_t *, aim_frame_t *);
+int aim_tx_printqueue(aim_session_t *);
+void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *);
+
+aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, u_short family, u_short type);
+
+/*
+ * Generic SNAC structure.  Rarely if ever used.
+ */
+typedef struct aim_snac_s {
+	aim_snacid_t id;
+	guint16 family;
+	guint16 type;
+	guint16 flags;
+	void *data;
+	time_t issuetime;
+	struct aim_snac_s *next;
+} aim_snac_t;
+
+void aim_initsnachash(aim_session_t *sess);
+aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac);
+aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen);
+aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id);
+void aim_cleansnacs(aim_session_t *, int maxage);
+int aim_putsnac(aim_bstream_t *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id);
+
+aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src);
+void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src);
+
+int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *);
+
+int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...);
+
+/* Stored in ->priv of the service request SNAC for chats. */
+struct chatsnacinfo {
+	guint16 exchange;
+	char name[128];
+	guint16 instance;
+};
+
+/* these are used by aim_*_clientready */
+#define AIM_TOOL_JAVA   0x0001
+#define AIM_TOOL_MAC    0x0002
+#define AIM_TOOL_WIN16  0x0003
+#define AIM_TOOL_WIN32  0x0004
+#define AIM_TOOL_MAC68K 0x0005
+#define AIM_TOOL_MACPPC 0x0006
+#define AIM_TOOL_NEWWIN 0x0010
+struct aim_tool_version {
+	guint16 group;
+	guint16 version;
+	guint16 tool;
+	guint16 toolversion;
+};
+
+/* 
+ * In SNACland, the terms 'family' and 'group' are synonymous -- the former
+ * is my term, the latter is AOL's.
+ */
+struct snacgroup {
+	guint16 group;
+	struct snacgroup *next;
+};
+
+struct snacpair {
+	guint16 group;
+	guint16 subtype;
+	struct snacpair *next;
+};
+
+struct rateclass {
+	guint16 classid;
+	guint32 windowsize;
+	guint32 clear;
+	guint32 alert;
+	guint32 limit;
+	guint32 disconnect;
+	guint32 current;
+	guint32 max;
+	guint8 unknown[5]; /* only present in versions >= 3 */
+	struct snacpair *members;
+	struct rateclass *next;
+};
+
+/*
+ * This is inside every connection.  But it is a void * to anything
+ * outside of libfaim.  It should remain that way.  It's called data
+ * abstraction.  Maybe you've heard of it.  (Probably not if you're a 
+ * libfaim user.)
+ * 
+ */
+typedef struct aim_conn_inside_s {
+	struct snacgroup *groups;
+	struct rateclass *rates;
+} aim_conn_inside_t;
+
+void aim_conn_addgroup(aim_conn_t *conn, guint16 group);
+
+guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len);
+int aim_putcap(aim_bstream_t *bs, guint32 caps);
+
+int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type);
+aim_msgcookie_t *aim_mkcookie(guint8 *, int, void *);
+aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const unsigned char *, const int);
+int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie);
+int aim_msgcookie_gettype(int reqclass);
+int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie);
+
+int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *);
+int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info);
+
+int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo);
+
+int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret);
+int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret);
+void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn);
+void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn);
+
+void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn);
+
+/* These are all handled internally now. */
+int aim_setversions(aim_session_t *sess, aim_conn_t *conn);
+int aim_reqrates(aim_session_t *, aim_conn_t *);
+int aim_rates_addparam(aim_session_t *, aim_conn_t *);
+int aim_rates_delparam(aim_session_t *, aim_conn_t *);
+
+#endif /* __AIM_INTERNAL_H__ */
Index: protocols/oscar/auth.c
===================================================================
--- protocols/oscar/auth.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/auth.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,543 @@
+/*
+ * Deals with the authorizer (group 0x0017=23, and old-style non-SNAC login).
+ *
+ */
+
+#include <aim.h> 
+
+#include "md5.h"
+
+static int aim_encode_password(const char *password, unsigned char *encoded);
+
+/* 
+ * This just pushes the passed cookie onto the passed connection, without
+ * the SNAC header or any of that.
+ *
+ * Very commonly used, as every connection except auth will require this to
+ * be the first thing you send.
+ *
+ */
+int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const guint8 *chipsahoy)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0001, 4+2+2+AIM_COOKIELEN)))
+		return -ENOMEM;
+
+	aimbs_put32(&fr->data, 0x00000001);
+	aim_addtlvtochain_raw(&tl, 0x0006, AIM_COOKIELEN, chipsahoy);	
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Normally the FLAP version is sent as the first few bytes of the cookie,
+ * meaning you generally never call this.
+ *
+ * But there are times when something might want it seperate. Specifically,
+ * libfaim sends this internally when doing SNAC login.
+ *
+ */
+int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4)))
+		return -ENOMEM;
+
+	aimbs_put32(&fr->data, 0x00000001);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * This is a bit confusing.
+ *
+ * Normal SNAC login goes like this:
+ *   - connect
+ *   - server sends flap version
+ *   - client sends flap version
+ *   - client sends screen name (17/6)
+ *   - server sends hash key (17/7)
+ *   - client sends auth request (17/2 -- aim_send_login)
+ *   - server yells
+ *
+ * XOR login (for ICQ) goes like this:
+ *   - connect
+ *   - server sends flap version
+ *   - client sends auth request which contains flap version (aim_send_login)
+ *   - server yells
+ *
+ * For the client API, we make them implement the most complicated version,
+ * and for the simpler version, we fake it and make it look like the more
+ * complicated process.
+ *
+ * This is done by giving the client a faked key, just so we can convince
+ * them to call aim_send_login right away, which will detect the session
+ * flag that says this is XOR login and ignore the key, sending an ICQ
+ * login request instead of the normal SNAC one.
+ *
+ * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/.
+ *
+ * XXX This may cause problems if the client relies on callbacks only
+ * being called from the context of aim_rxdispatch()...
+ *
+ */
+static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t fr;
+	aim_rxcallback_t userfunc;
+	
+	sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN;
+	sess->flags |= AIM_SESS_FLAGS_XORLOGIN;
+
+	fr.conn = conn;
+	
+	if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007)))
+		userfunc(sess, &fr, "");
+
+	return 0;
+}
+
+/*
+ * In AIM 3.5 protocol, the first stage of login is to request login from the 
+ * Authorizer, passing it the screen name for verification.  If the name is 
+ * invalid, a 0017/0003 is spit back, with the standard error contents.  If 
+ * valid, a 0017/0007 comes back, which is the signal to send it the main 
+ * login command (0017/0002). 
+ *
+ */
+int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if ((sn[0] >= '0') && (sn[0] <= '9'))
+		return goddamnicq(sess, conn, sn);
+
+	sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+
+	aim_sendflapver(sess, conn);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid);
+
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Part two of the ICQ hack.  Note the ignoring of the key and clientinfo.
+ */
+static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password)
+{
+	static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2001b.5.17.1.3642.85"};
+	static const char lang[] = {"en"};
+	static const char country[] = {"us"};
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	guint8 *password_encoded;
+
+	if (!(password_encoded = (guint8 *) g_malloc(strlen(password))))
+		return -ENOMEM;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) {
+		g_free(password_encoded);
+		return -ENOMEM;
+	}
+
+	aim_encode_password(password, password_encoded);
+
+	aimbs_put32(&fr->data, 0x00000001);
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+	aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded);
+	aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), (guint8 *)clientstr);
+	aim_addtlvtochain16(&tl, 0x0016, 0x010a); /* cliend ID */
+	aim_addtlvtochain16(&tl, 0x0017, 0x0005); /* major version */
+	aim_addtlvtochain16(&tl, 0x0018, 0x0011); /* minor version */
+	aim_addtlvtochain16(&tl, 0x0019, 0x0001); /* point version */
+	aim_addtlvtochain16(&tl, 0x001a, 0x0e3a); /* build */
+	aim_addtlvtochain32(&tl, 0x0014, 0x00000055); /* distribution chan */
+	aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), (guint8 *)lang);
+	aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), (guint8 *)country);
+
+	aim_writetlvchain(&fr->data, &tl);
+
+	g_free(password_encoded);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * send_login(int socket, char *sn, char *password)
+ *  
+ * This is the initial login request packet.
+ *
+ * NOTE!! If you want/need to make use of the aim_sendmemblock() function,
+ * then the client information you send here must exactly match the
+ * executable that you're pulling the data from.
+ *
+ * WinAIM 4.8.2540
+ *   clientstring = "AOL Instant Messenger (SM), version 4.8.2540/WIN32"
+ *   clientid = 0x0109
+ *   major = 0x0004
+ *   minor = 0x0008
+ *   point = 0x0000
+ *   build = 0x09ec
+ *   t(0x0014) = 0x000000af
+ *   t(0x004a) = 0x01
+ *
+ * WinAIM 4.3.2188:
+ *   clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32"
+ *   clientid = 0x0109
+ *   major = 0x0400
+ *   minor = 0x0003
+ *   point = 0x0000
+ *   build = 0x088c
+ *   unknown = 0x00000086
+ *   lang = "en"
+ *   country = "us"
+ *   unknown4a = 0x01
+ *
+ * Latest WinAIM that libfaim can emulate without server-side buddylists:
+ *   clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32"
+ *   clientid = 0x0004
+ *   major  = 0x0004
+ *   minor  = 0x0001
+ *   point = 0x0000
+ *   build  = 0x07da
+ *   unknown= 0x0000004b
+ *
+ * WinAIM 3.5.1670:
+ *   clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32"
+ *   clientid = 0x0004
+ *   major =  0x0003
+ *   minor =  0x0005
+ *   point = 0x0000
+ *   build =  0x0686
+ *   unknown =0x0000002a
+ *
+ * Java AIM 1.1.19:
+ *   clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001"
+ *   clientid = 0x0001
+ *   major  = 0x0001
+ *   minor  = 0x0001
+ *   point = (not sent)
+ *   build  = 0x0013
+ *   unknown= (not sent)
+ *   
+ * AIM for Linux 1.1.112:
+ *   clientstring = "AOL Instant Messenger (SM)"
+ *   clientid = 0x1d09
+ *   major  = 0x0001
+ *   minor  = 0x0001
+ *   point = 0x0001
+ *   build  = 0x0070
+ *   unknown= 0x0000008b
+ *   serverstore = 0x01
+ *
+ */
+int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key)
+{
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	guint8 digest[16];
+	aim_snacid_t snacid;
+
+	if (!ci || !sn || !password)
+		return -EINVAL;
+
+	/*
+	 * What the XORLOGIN flag _really_ means is that its an ICQ login,
+	 * which is really stupid and painful, so its not done here.
+	 *
+	 */
+	if (sess->flags & AIM_SESS_FLAGS_XORLOGIN)
+		return goddamnicq2(sess, conn, sn, password);
+
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid);
+
+	aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn);
+
+	aim_encode_password_md5(password, key, digest);
+	aim_addtlvtochain_raw(&tl, 0x0025, 16, digest);
+
+	/*
+	 * Newer versions of winaim have an empty type x004c TLV here.
+	 */
+
+	if (ci->clientstring)
+		aim_addtlvtochain_raw(&tl, 0x0003, strlen(ci->clientstring), (guint8 *)ci->clientstring);
+	aim_addtlvtochain16(&tl, 0x0016, (guint16)ci->clientid);
+	aim_addtlvtochain16(&tl, 0x0017, (guint16)ci->major);
+	aim_addtlvtochain16(&tl, 0x0018, (guint16)ci->minor);
+	aim_addtlvtochain16(&tl, 0x0019, (guint16)ci->point);
+	aim_addtlvtochain16(&tl, 0x001a, (guint16)ci->build);
+	aim_addtlvtochain_raw(&tl, 0x000e, strlen(ci->country), (guint8 *)ci->country);
+	aim_addtlvtochain_raw(&tl, 0x000f, strlen(ci->lang), (guint8 *)ci->lang);
+
+	/*
+	 * If set, old-fashioned buddy lists will not work. You will need
+	 * to use SSI.
+	 */
+	aim_addtlvtochain8(&tl, 0x004a, 0x01);
+
+	aim_writetlvchain(&fr->data, &tl);
+
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_encode_password_md5(const char *password, const char *key, guint8 *digest)
+{
+	md5_state_t state;
+
+	md5_init(&state);	
+	md5_append(&state, (const md5_byte_t *)key, strlen(key));
+	md5_append(&state, (const md5_byte_t *)password, strlen(password));
+	md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING));
+	md5_finish(&state, (md5_byte_t *)digest);
+
+	return 0;
+}
+
+/**
+ * aim_encode_password - Encode a password using old XOR method
+ * @password: incoming password
+ * @encoded: buffer to put encoded password
+ *
+ * This takes a const pointer to a (null terminated) string
+ * containing the unencoded password.  It also gets passed
+ * an already allocated buffer to store the encoded password.
+ * This buffer should be the exact length of the password without
+ * the null.  The encoded password buffer /is not %NULL terminated/.
+ *
+ * The encoding_table seems to be a fixed set of values.  We'll
+ * hope it doesn't change over time!  
+ *
+ * This is only used for the XOR method, not the better MD5 method.
+ *
+ */
+static int aim_encode_password(const char *password, guint8 *encoded)
+{
+	guint8 encoding_table[] = {
+#if 0 /* old v1 table */
+		0xf3, 0xb3, 0x6c, 0x99,
+		0x95, 0x3f, 0xac, 0xb6,
+		0xc5, 0xfa, 0x6b, 0x63,
+		0x69, 0x6c, 0xc3, 0x9f
+#else /* v2.1 table, also works for ICQ */
+		0xf3, 0x26, 0x81, 0xc4,
+		0x39, 0x86, 0xdb, 0x92,
+		0x71, 0xa3, 0xb9, 0xe6,
+		0x53, 0x7a, 0x95, 0x7c
+#endif
+	};
+	int i;
+
+	for (i = 0; i < strlen(password); i++)
+		encoded[i] = (password[i] ^ encoding_table[i]);
+
+	return 0;
+}
+
+/*
+ * This is sent back as a general response to the login command.
+ * It can be either an error or a success, depending on the
+ * precense of certain TLVs.  
+ *
+ * The client should check the value passed as errorcode. If
+ * its nonzero, there was an error.
+ *
+ */
+static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_tlvlist_t *tlvlist;
+	aim_rxcallback_t userfunc;
+	struct aim_authresp_info info;
+	int ret = 0;
+
+	memset(&info, 0, sizeof(info));
+
+	/*
+	 * Read block of TLVs.  All further data is derived
+	 * from what is parsed here.
+	 */
+	tlvlist = aim_readtlvchain(bs);
+
+	/*
+	 * No matter what, we should have a screen name.
+	 */
+	memset(sess->sn, 0, sizeof(sess->sn));
+	if (aim_gettlv(tlvlist, 0x0001, 1)) {
+		info.sn = aim_gettlv_str(tlvlist, 0x0001, 1);
+		strncpy(sess->sn, info.sn, sizeof(sess->sn));
+	}
+
+	/*
+	 * Check for an error code.  If so, we should also
+	 * have an error url.
+	 */
+	if (aim_gettlv(tlvlist, 0x0008, 1)) 
+		info.errorcode = aim_gettlv16(tlvlist, 0x0008, 1);
+	if (aim_gettlv(tlvlist, 0x0004, 1))
+		info.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1);
+
+	/*
+	 * BOS server address.
+	 */
+	if (aim_gettlv(tlvlist, 0x0005, 1))
+		info.bosip = aim_gettlv_str(tlvlist, 0x0005, 1);
+
+	/*
+	 * Authorization cookie.
+	 */
+	if (aim_gettlv(tlvlist, 0x0006, 1)) {
+		aim_tlv_t *tmptlv;
+
+		tmptlv = aim_gettlv(tlvlist, 0x0006, 1);
+
+		info.cookie = tmptlv->value;
+	}
+
+	/*
+	 * The email address attached to this account
+	 *   Not available for ICQ logins.
+	 */
+	if (aim_gettlv(tlvlist, 0x0011, 1))
+		info.email = aim_gettlv_str(tlvlist, 0x0011, 1);
+
+	/*
+	 * The registration status.  (Not real sure what it means.)
+	 *   Not available for ICQ logins.
+	 *
+	 *   1 = No disclosure
+	 *   2 = Limited disclosure
+	 *   3 = Full disclosure
+	 *
+	 * This has to do with whether your email address is available
+	 * to other users or not.  AFAIK, this feature is no longer used.
+	 *
+	 */
+	if (aim_gettlv(tlvlist, 0x0013, 1))
+		info.regstatus = aim_gettlv16(tlvlist, 0x0013, 1);
+
+	if (aim_gettlv(tlvlist, 0x0040, 1))
+		info.latestbeta.build = aim_gettlv32(tlvlist, 0x0040, 1);
+	if (aim_gettlv(tlvlist, 0x0041, 1))
+		info.latestbeta.url = aim_gettlv_str(tlvlist, 0x0041, 1);
+	if (aim_gettlv(tlvlist, 0x0042, 1))
+		info.latestbeta.info = aim_gettlv_str(tlvlist, 0x0042, 1);
+	if (aim_gettlv(tlvlist, 0x0043, 1))
+		info.latestbeta.name = aim_gettlv_str(tlvlist, 0x0043, 1);
+	if (aim_gettlv(tlvlist, 0x0048, 1))
+		; /* no idea what this is */
+
+	if (aim_gettlv(tlvlist, 0x0044, 1))
+		info.latestrelease.build = aim_gettlv32(tlvlist, 0x0044, 1);
+	if (aim_gettlv(tlvlist, 0x0045, 1))
+		info.latestrelease.url = aim_gettlv_str(tlvlist, 0x0045, 1);
+	if (aim_gettlv(tlvlist, 0x0046, 1))
+		info.latestrelease.info = aim_gettlv_str(tlvlist, 0x0046, 1);
+	if (aim_gettlv(tlvlist, 0x0047, 1))
+		info.latestrelease.name = aim_gettlv_str(tlvlist, 0x0047, 1);
+	if (aim_gettlv(tlvlist, 0x0049, 1))
+		; /* no idea what this is */
+
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003)))
+		ret = userfunc(sess, rx, &info);
+
+	g_free(info.sn);
+	g_free(info.bosip);
+	g_free(info.errorurl);
+	g_free(info.email);
+	g_free(info.latestrelease.name);
+	g_free(info.latestrelease.url);
+	g_free(info.latestrelease.info);
+	g_free(info.latestbeta.name);
+	g_free(info.latestbeta.url);
+	g_free(info.latestbeta.info);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Middle handler for 0017/0007 SNACs.  Contains the auth key prefixed
+ * by only its length in a two byte word.
+ *
+ * Calls the client, which should then use the value to call aim_send_login.
+ *
+ */
+static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int keylen, ret = 1;
+	aim_rxcallback_t userfunc;
+	char *keystr;
+
+	keylen = aimbs_get16(bs);
+	keystr = aimbs_getstr(bs, keylen);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, keystr);
+
+	g_free(keystr); 
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return parse(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return keyparse(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int auth_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0017;
+	mod->version = 0x0000;
+	mod->flags = 0;
+	strncpy(mod->name, "auth", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
Index: protocols/oscar/bos.c
===================================================================
--- protocols/oscar/bos.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/bos.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,161 @@
+#include <aim.h>
+#include "bos.h"
+
+/* Request BOS rights (group 9, type 2) */
+int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0009, 0x0002);
+}
+
+/* BOS Rights (group 9, type 3) */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	guint16 maxpermits = 0, maxdenies = 0;
+	int ret = 0;
+
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_readtlvchain(bs);
+
+	/*
+	 * TLV type 0x0001: Maximum number of buddies on permit list.
+	 */
+	if (aim_gettlv(tlvlist, 0x0001, 1))
+		maxpermits = aim_gettlv16(tlvlist, 0x0001, 1);
+
+	/*
+	 * TLV type 0x0002: Maximum number of buddies on deny list.
+	 */
+	if (aim_gettlv(tlvlist, 0x0002, 1)) 
+		maxdenies = aim_gettlv16(tlvlist, 0x0002, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxpermits, maxdenies);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;  
+}
+
+/* 
+ * Set group permisson mask (group 9, type 4)
+ *
+ * Normally 0x1f (all classes).
+ *
+ * The group permission mask allows you to keep users of a certain
+ * class or classes from talking to you.  The mask should be
+ * a bitwise OR of all the user classes you want to see you.
+ *
+ */
+int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, guint32 mask)
+{
+	return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask);
+}
+
+/*
+ * Modify permit/deny lists (group 9, types 5, 6, 7, and 8)
+ *
+ * Changes your visibility depending on changetype:
+ *
+ *  AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you
+ *  AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list
+ *  AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names
+ *  AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again
+ *
+ * list should be a list of 
+ * screen names in the form "Screen Name One&ScreenNameTwo&" etc.
+ *
+ * Equivelents to options in WinAIM:
+ *   - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD
+ *      with only your name on it.
+ *   - Allow only users on my Buddy List: Send an 
+ *      AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your
+ *      buddy list
+ *   - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD 
+ *      with everyone listed that you want to see you.
+ *   - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only 
+ *      yourself in the list
+ *   - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with
+ *      the list of users to be blocked
+ *
+ * XXX ye gods.
+ */
+int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist)
+{
+	aim_frame_t *fr;
+	int packlen = 0;
+	guint16 subtype;
+	char *localcpy = NULL, *tmpptr = NULL;
+	int i;
+	int listcount;
+	aim_snacid_t snacid;
+
+	if (!denylist)
+		return -EINVAL;
+
+	if (changetype == AIM_VISIBILITYCHANGE_PERMITADD)
+		subtype = 0x05;
+	else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE)
+		subtype = 0x06;
+	else if (changetype == AIM_VISIBILITYCHANGE_DENYADD)
+		subtype = 0x07;
+	else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE)
+		subtype = 0x08;
+	else
+		return -EINVAL;
+
+	localcpy = g_strdup(denylist);
+
+	listcount = aimutil_itemcnt(localcpy, '&');
+	packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) {
+		g_free(localcpy);
+		return -ENOMEM;
+	}
+
+	snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid);
+
+	for (i = 0; (i < (listcount - 1)) && (i < 99); i++) {
+		tmpptr = aimutil_itemidx(localcpy, i, '&');
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr));
+
+		g_free(tmpptr);
+	}
+	g_free(localcpy);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int bos_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0009;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "bos", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+
Index: protocols/oscar/bos.h
===================================================================
--- protocols/oscar/bos.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/bos.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,14 @@
+#ifndef __OSCAR_BOS_H__
+#define __OSCAR_BOS_H__
+
+#define AIM_CB_FAM_BOS 0x0009
+
+/*
+ * SNAC Family: Misc BOS Services.
+ */ 
+#define AIM_CB_BOS_ERROR 0x0001
+#define AIM_CB_BOS_RIGHTSQUERY 0x0002
+#define AIM_CB_BOS_RIGHTS 0x0003
+#define AIM_CB_BOS_DEFAULT 0xffff
+
+#endif /* __OSCAR_BOS_H__ */
Index: protocols/oscar/buddylist.c
===================================================================
--- protocols/oscar/buddylist.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/buddylist.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,150 @@
+#include <aim.h>
+#include "buddylist.h"
+
+/*
+ * Oncoming Buddy notifications contain a subset of the
+ * user information structure.  Its close enough to run
+ * through aim_extractuserinfo() however.
+ *
+ * Although the offgoing notification contains no information,
+ * it is still in a format parsable by extractuserinfo.
+ *
+ */
+static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t userinfo;
+	aim_rxcallback_t userfunc;
+
+	aim_extractuserinfo(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &userinfo);
+
+	return 0;
+}
+
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	guint16 maxbuddies = 0, maxwatchers = 0;
+	int ret = 0;
+
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_readtlvchain(bs);
+
+	/*
+	 * TLV type 0x0001: Maximum number of buddies.
+	 */
+	if (aim_gettlv(tlvlist, 0x0001, 1))
+		maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1);
+
+	/*
+	 * TLV type 0x0002: Maximum number of watchers.
+	 *
+	 * Watchers are other users who have you on their buddy
+	 * list.  (This is called the "reverse list" by a certain
+	 * other IM protocol.)
+	 * 
+	 */
+	if (aim_gettlv(tlvlist, 0x0002, 1))
+		maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1);
+
+	/*
+	 * TLV type 0x0003: Unknown.
+	 *
+	 * ICQ only?
+	 */
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxbuddies, maxwatchers);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;  
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+	else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c))
+		return buddychange(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0003;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "buddylist", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+/*
+ * aim_add_buddy()
+ *
+ * Adds a single buddy to your buddy list after login.
+ *
+ * XXX this should just be an extension of setbuddylist()
+ *
+ */
+int aim_add_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sn || !strlen(sn))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * XXX generalise to support removing multiple buddies (basically, its
+ * the same as setbuddylist() but with a different snac subtype).
+ *
+ */
+int aim_remove_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sn || !strlen(sn))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid);
+
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
Index: protocols/oscar/buddylist.h
===================================================================
--- protocols/oscar/buddylist.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/buddylist.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,23 @@
+#ifndef __OSCAR_BUDDYLIST_H__
+#define __OSCAR_BUDDYLIST_H__
+
+#define AIM_CB_FAM_BUD 0x0003
+
+/*
+ * SNAC Family: Buddy List Management Services.
+ */ 
+#define AIM_CB_BUD_ERROR 0x0001
+#define AIM_CB_BUD_REQRIGHTS 0x0002
+#define AIM_CB_BUD_RIGHTSINFO 0x0003
+#define AIM_CB_BUD_ADDBUDDY 0x0004
+#define AIM_CB_BUD_REMBUDDY 0x0005
+#define AIM_CB_BUD_REJECT 0x000a
+#define AIM_CB_BUD_ONCOMING 0x000b
+#define AIM_CB_BUD_OFFGOING 0x000c
+#define AIM_CB_BUD_DEFAULT 0xffff
+
+/* aim_buddylist.c */
+int aim_add_buddy(aim_session_t *, aim_conn_t *, const char *);
+int aim_remove_buddy(aim_session_t *, aim_conn_t *, const char *);
+
+#endif /* __OSCAR_BUDDYLIST_H__ */
Index: protocols/oscar/chat.c
===================================================================
--- protocols/oscar/chat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,713 @@
+/*
+ * aim_chat.c
+ *
+ * Routines for the Chat service.
+ *
+ */
+
+#include <aim.h> 
+#include <glib.h>
+#include "info.h"
+
+/* Stored in the ->priv of chat connections */
+struct chatconnpriv {
+	guint16 exchange;
+	char *name;
+	guint16 instance;
+};
+
+void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv;
+
+	if (ccp)
+		g_free(ccp->name);
+	g_free(ccp);
+
+	return;
+}
+
+char *aim_chat_getname(aim_conn_t *conn)
+{
+	struct chatconnpriv *ccp;
+
+	if (!conn)
+		return NULL;
+
+	if (conn->type != AIM_CONN_TYPE_CHAT)
+		return NULL;
+
+	ccp = (struct chatconnpriv *)conn->priv;
+
+	return ccp->name;
+}
+
+/* XXX get this into conn.c -- evil!! */
+aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
+{
+	aim_conn_t *cur;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv;
+
+		if (cur->type != AIM_CONN_TYPE_CHAT)
+			continue;
+		if (!cur->priv) {
+			do_error_dialog(sess->aux_data, "chat connection with no name!", "Gaim");
+			continue;
+		}
+
+		if (strcmp(ccp->name, name) == 0)
+			break;
+	}
+
+	return cur;
+}
+
+int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+	struct chatconnpriv *ccp;
+
+	if (!conn || !roomname)
+		return -EINVAL;
+
+	if (conn->priv)
+		g_free(conn->priv);
+
+	if (!(ccp = g_malloc(sizeof(struct chatconnpriv))))
+		return -ENOMEM;
+
+	ccp->exchange = exchange;
+	ccp->name = g_strdup(roomname);
+	ccp->instance = instance;
+
+	conn->priv = (void *)ccp;
+
+	return 0;
+}
+
+/*
+ * Send a Chat Message.
+ *
+ * Possible flags:
+ *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
+ *                                 should be sent to their sender.
+ *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
+ *                                 (Note that WinAIM does not honor this,
+ *                                 and displays the message as normal.)
+ *
+ * XXX convert this to use tlvchains 
+ */
+int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen)
+{   
+	int i;
+	aim_frame_t *fr;
+	aim_msgcookie_t *cookie;
+	aim_snacid_t snacid;
+	guint8 ckstr[8];
+	aim_tlvlist_t *otl = NULL, *itl = NULL;
+
+	if (!sess || !conn || !msg || (msglen <= 0))
+		return 0;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
+
+
+	/* 
+	 * Generate a random message cookie.
+	 *
+	 * XXX mkcookie should generate the cookie and cache it in one
+	 * operation to preserve uniqueness.
+	 *
+	 */
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimutil_put8(ckstr+i, (guint8) rand());
+
+	cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
+	cookie->data = NULL; /* XXX store something useful here */
+
+	aim_cachecookie(sess, cookie);
+
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimbs_put8(&fr->data, ckstr[i]);
+
+
+	/*
+	 * Channel ID. 
+	 */
+	aimbs_put16(&fr->data, 0x0003);
+
+
+	/*
+	 * Type 1: Flag meaning this message is destined to the room.
+	 */
+	aim_addtlvtochain_noval(&otl, 0x0001);
+
+	/*
+	 * Type 6: Reflect
+	 */
+	if (!(flags & AIM_CHATFLAGS_NOREFLECT))
+		aim_addtlvtochain_noval(&otl, 0x0006);
+
+	/*
+	 * Type 7: Autoresponse
+	 */
+	if (flags & AIM_CHATFLAGS_AWAY)
+		aim_addtlvtochain_noval(&otl, 0x0007);
+
+	/*
+	 * SubTLV: Type 1: Message
+	 */
+	aim_addtlvtochain_raw(&itl, 0x0001, strlen(msg), (guint8 *)msg);
+
+	/*
+	 * Type 5: Message block.  Contains more TLVs.
+	 *
+	 * This could include other information... We just
+	 * put in a message TLV however.  
+	 * 
+	 */
+	aim_addtlvtochain_frozentlvlist(&otl, 0x0005, &itl);
+
+	aim_writetlvchain(&fr->data, &otl);
+	
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&otl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance)
+{
+	guint8 *buf;
+	int buflen;
+	aim_bstream_t bs;
+
+	buflen = 2 + 1 + strlen(roomname) + 2;
+	
+	if (!(buf = g_malloc(buflen)))
+		return 0;
+
+	aim_bstream_init(&bs, buf, buflen);
+
+	aimbs_put16(&bs, exchange);
+	aimbs_put8(&bs, strlen(roomname));
+	aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname));
+	aimbs_put16(&bs, instance);
+
+	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+	g_free(buf);
+
+	return 0;
+}
+
+/*
+ * Join a room of name roomname.  This is the first step to joining an 
+ * already created room.  It's basically a Service Request for 
+ * family 0x000e, with a little added on to specify the exchange and room 
+ * name.
+ */
+int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	struct chatsnacinfo csi;
+	
+	if (!sess || !conn || !roomname || !strlen(roomname))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM;
+
+	memset(&csi, 0, sizeof(csi));
+	csi.exchange = exchange;
+	strncpy(csi.name, roomname, sizeof(csi.name));
+	csi.instance = instance;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
+	aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
+
+	/*
+	 * Requesting service chat (0x000e)
+	 */
+	aimbs_put16(&fr->data, 0x000e);
+
+	aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0; 
+}
+
+int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
+{
+	int namelen;
+
+	if (!bs || !outinfo)
+		return 0;
+
+	outinfo->exchange = aimbs_get16(bs);
+	namelen = aimbs_get8(bs);
+	outinfo->name = aimbs_getstr(bs, namelen);
+	outinfo->instance = aimbs_get16(bs);
+
+	return 0;
+}
+
+int aim_chat_leaveroom(aim_session_t *sess, const char *name)
+{
+	aim_conn_t *conn;
+
+	if (!(conn = aim_chat_getconn(sess, name)))
+		return -ENOENT;
+
+	aim_conn_close(conn);
+
+	return 0;
+}
+
+/*
+ * conn must be a BOS connection!
+ */
+int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
+{
+	int i;
+	aim_frame_t *fr;
+	aim_msgcookie_t *cookie;
+	struct aim_invite_priv *priv;
+	guint8 ckstr[8];
+	aim_snacid_t snacid;
+	aim_tlvlist_t *otl = NULL, *itl = NULL;
+	guint8 *hdr;
+	int hdrlen;
+	aim_bstream_t hdrbs;
+	
+	if (!sess || !conn || !sn || !msg || !roomname)
+		return -EINVAL;
+
+	if (conn->type != AIM_CONN_TYPE_BOS)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+
+	/*
+	 * Cookie
+	 */
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimutil_put8(ckstr, (guint8) rand());
+
+	/* XXX should be uncached by an unwritten 'invite accept' handler */
+	if ((priv = g_malloc(sizeof(struct aim_invite_priv)))) {
+		priv->sn = g_strdup(sn);
+		priv->roomname = g_strdup(roomname);
+		priv->exchange = exchange;
+		priv->instance = instance;
+	}
+
+	if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv)))
+		aim_cachecookie(sess, cookie);
+	else
+		g_free(priv);
+
+	for (i = 0; i < sizeof(ckstr); i++)
+		aimbs_put8(&fr->data, ckstr[i]);
+
+
+	/*
+	 * Channel (2)
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Everything else is inside this TLV.
+	 *
+	 * Sigh.  AOL was rather inconsistent right here.  So we have
+	 * to play some minor tricks.  Right inside the type 5 is some
+	 * raw data, followed by a series of TLVs.  
+	 *
+	 */
+	hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2;
+	hdr = g_malloc(hdrlen);
+	aim_bstream_init(&hdrbs, hdr, hdrlen);
+	
+	aimbs_put16(&hdrbs, 0x0000); /* Unknown! */
+	aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */
+	aim_putcap(&hdrbs, AIM_CAPS_CHAT);
+
+	aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+	aim_addtlvtochain_noval(&itl, 0x000f);
+	aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), (guint8 *)msg);
+	aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance);
+	aim_writetlvchain(&hdrbs, &itl);
+	
+	aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+	aim_writetlvchain(&fr->data, &otl);
+
+	g_free(hdr);
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&otl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * General room information.  Lots of stuff.
+ *
+ * Values I know are in here but I havent attached
+ * them to any of the 'Unknown's:
+ *	- Language (English)
+ *
+ * SNAC 000e/0002
+ */
+static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t *userinfo = NULL;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	int usercount = 0;
+	guint8 detaillevel = 0;
+	char *roomname = NULL;
+	struct aim_chat_roominfo roominfo;
+	guint16 tlvcount = 0;
+	aim_tlvlist_t *tlvlist;
+	char *roomdesc = NULL;
+	guint16 flags = 0;
+	guint32 creationtime = 0;
+	guint16 maxmsglen = 0, maxvisiblemsglen = 0;
+	guint16 unknown_d2 = 0, unknown_d5 = 0;
+
+	aim_chat_readroominfo(bs, &roominfo);
+
+	detaillevel = aimbs_get8(bs);
+
+	if (detaillevel != 0x02) {
+		do_error_dialog(sess->aux_data, "Only detaillevel 0x2 is support at the moment", "Gaim");
+		return 1;
+	}
+
+	tlvcount = aimbs_get16(bs);
+
+	/*
+	 * Everything else are TLVs.
+	 */ 
+	tlvlist = aim_readtlvchain(bs);
+
+	/*
+	 * TLV type 0x006a is the room name in Human Readable Form.
+	 */
+	if (aim_gettlv(tlvlist, 0x006a, 1))
+		roomname = aim_gettlv_str(tlvlist, 0x006a, 1);
+
+	/*
+	 * Type 0x006f: Number of occupants.
+	 */
+	if (aim_gettlv(tlvlist, 0x006f, 1))
+		usercount = aim_gettlv16(tlvlist, 0x006f, 1);
+
+	/*
+	 * Type 0x0073:  Occupant list.
+	 */
+	if (aim_gettlv(tlvlist, 0x0073, 1)) {	
+		int curoccupant = 0;
+		aim_tlv_t *tmptlv;
+		aim_bstream_t occbs;
+
+		tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
+
+		/* Allocate enough userinfo structs for all occupants */
+		userinfo = g_new0(aim_userinfo_t, usercount);
+
+		aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
+
+		while (curoccupant < usercount)
+			aim_extractuserinfo(sess, &occbs, &userinfo[curoccupant++]);
+	}
+
+	/* 
+	 * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
+	 */
+	if (aim_gettlv(tlvlist, 0x00c9, 1))
+		flags = aim_gettlv16(tlvlist, 0x00c9, 1);
+
+	/* 
+	 * Type 0x00ca: Creation time (4 bytes)
+	 */
+	if (aim_gettlv(tlvlist, 0x00ca, 1))
+		creationtime = aim_gettlv32(tlvlist, 0x00ca, 1);
+
+	/* 
+	 * Type 0x00d1: Maximum Message Length
+	 */
+	if (aim_gettlv(tlvlist, 0x00d1, 1))
+		maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1);
+
+	/* 
+	 * Type 0x00d2: Unknown. (2 bytes)
+	 */
+	if (aim_gettlv(tlvlist, 0x00d2, 1))
+		unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1);
+
+	/* 
+	 * Type 0x00d3: Room Description
+	 */
+	if (aim_gettlv(tlvlist, 0x00d3, 1))
+		roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
+
+	/*
+	 * Type 0x000d4: Unknown (flag only)
+	 */
+	if (aim_gettlv(tlvlist, 0x000d4, 1))
+		;
+
+	/* 
+	 * Type 0x00d5: Unknown. (1 byte)
+	 */
+	if (aim_gettlv(tlvlist, 0x00d5, 1))
+		unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1);
+
+
+	/*
+	 * Type 0x00d6: Encoding 1 ("us-ascii")
+	 */
+	if (aim_gettlv(tlvlist, 0x000d6, 1))
+		;
+	
+	/*
+	 * Type 0x00d7: Language 1 ("en")
+	 */
+	if (aim_gettlv(tlvlist, 0x000d7, 1))
+		;
+
+	/*
+	 * Type 0x00d8: Encoding 2 ("us-ascii")
+	 */
+	if (aim_gettlv(tlvlist, 0x000d8, 1))
+		;
+	
+	/*
+	 * Type 0x00d9: Language 2 ("en")
+	 */
+	if (aim_gettlv(tlvlist, 0x000d9, 1))
+		;
+
+	/*
+	 * Type 0x00da: Maximum visible message length
+	 */
+	if (aim_gettlv(tlvlist, 0x000da, 1))
+		maxvisiblemsglen = aim_gettlv16(tlvlist, 0x00da, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+		ret = userfunc(sess,
+				rx, 
+				&roominfo,
+				roomname,
+				usercount,
+				userinfo,	
+				roomdesc,
+				flags,
+				creationtime,
+				maxmsglen,
+				unknown_d2,
+				unknown_d5,
+				maxvisiblemsglen);
+	}
+
+	g_free(roominfo.name);
+	g_free(userinfo);
+	g_free(roomname);
+	g_free(roomdesc);
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t *userinfo = NULL;
+	aim_rxcallback_t userfunc;
+	int curcount = 0, ret = 0;
+
+	while (aim_bstream_empty(bs)) {
+		curcount++;
+		userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t));
+		aim_extractuserinfo(sess, bs, &userinfo[curcount-1]);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, curcount, userinfo);
+
+	g_free(userinfo);
+
+	return ret;
+}
+
+/*
+ * We could probably include this in the normal ICBM parsing 
+ * code as channel 0x0003, however, since only the start
+ * would be the same, we might as well do it here.
+ *
+ * General outline of this SNAC:
+ *   snac
+ *   cookie
+ *   channel id
+ *   tlvlist
+ *     unknown
+ *     source user info
+ *       name
+ *       evility
+ *       userinfo tlvs
+ *         online time
+ *         etc
+ *     message metatlv
+ *       message tlv
+ *         message string
+ *       possibly others
+ *  
+ */
+static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t userinfo;
+	aim_rxcallback_t userfunc;	
+	int ret = 0;
+	guint8 *cookie;
+	guint16 channel;
+	aim_tlvlist_t *otl;
+	char *msg = NULL;
+	aim_msgcookie_t *ck;
+
+	memset(&userinfo, 0, sizeof(aim_userinfo_t));
+
+	/*
+	 * ICBM Cookie.  Uncache it.
+	 */
+	cookie = aimbs_getraw(bs, 8);
+
+	if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
+		g_free(ck->data);
+		g_free(ck);
+	}
+
+	/*
+	 * Channel ID
+	 *
+	 * Channels 1 and 2 are implemented in the normal ICBM
+	 * parser.
+	 *
+	 * We only do channel 3 here.
+	 *
+	 */
+	channel = aimbs_get16(bs);
+
+	if (channel != 0x0003) {
+		do_error_dialog(sess->aux_data, "unknown channel!", "Gaim");
+		return 0;
+	}
+
+	/*
+	 * Start parsing TLVs right away. 
+	 */
+	otl = aim_readtlvchain(bs);
+
+	/*
+	 * Type 0x0003: Source User Information
+	 */
+	if (aim_gettlv(otl, 0x0003, 1)) {
+		aim_tlv_t *userinfotlv;
+		aim_bstream_t tbs;
+
+		userinfotlv = aim_gettlv(otl, 0x0003, 1);
+
+		aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
+		aim_extractuserinfo(sess, &tbs, &userinfo);
+	}
+
+	/*
+	 * Type 0x0001: If present, it means it was a message to the 
+	 * room (as opposed to a whisper).
+	 */
+	if (aim_gettlv(otl, 0x0001, 1))
+		;
+
+	/*
+	 * Type 0x0005: Message Block.  Conains more TLVs.
+	 */
+	if (aim_gettlv(otl, 0x0005, 1)) {
+		aim_tlvlist_t *itl;
+		aim_tlv_t *msgblock;
+		aim_bstream_t tbs;
+
+		msgblock = aim_gettlv(otl, 0x0005, 1);
+		aim_bstream_init(&tbs, msgblock->value, msgblock->length);
+		itl = aim_readtlvchain(&tbs);
+
+		/* 
+		 * Type 0x0001: Message.
+		 */	
+		if (aim_gettlv(itl, 0x0001, 1))
+			msg = aim_gettlv_str(itl, 0x0001, 1);
+
+		aim_freetlvchain(&itl); 
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo, msg);
+
+	g_free(cookie);
+	g_free(msg);
+	aim_freetlvchain(&otl);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0002)
+		return infoupdate(sess, mod, rx, snac, bs);
+	else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
+		return userlistchange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return incomingmsg(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000e;
+	mod->version = 0x0001;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "chat", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
Index: protocols/oscar/chat.h
===================================================================
--- protocols/oscar/chat.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chat.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,17 @@
+#ifndef __OSCAR_CHAT_H__
+#define __OSCAR_CHAT_H__
+
+#define AIM_CB_FAM_CHT 0x000e /* Chat */
+
+/*
+ * SNAC Family: Chat Services
+ */ 
+#define AIM_CB_CHT_ERROR 0x0001
+#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002
+#define AIM_CB_CHT_USERJOIN 0x0003
+#define AIM_CB_CHT_USERLEAVE 0x0004
+#define AIM_CB_CHT_OUTGOINGMSG 0x0005
+#define AIM_CB_CHT_INCOMINGMSG 0x0006
+#define AIM_CB_CHT_DEFAULT 0xffff
+
+#endif /* __OSCAR_CHAT_H__ */
Index: protocols/oscar/chatnav.c
===================================================================
--- protocols/oscar/chatnav.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chatnav.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,421 @@
+/*
+ * Handle ChatNav.
+ *
+ * [The ChatNav(igation) service does various things to keep chat
+ *  alive.  It provides room information, room searching and creating, 
+ *  as well as giving users the right ("permission") to use chat.]
+ *
+ */
+
+#include <aim.h>
+#include "chatnav.h"
+
+/*
+ * conn must be a chatnav connection!
+ */
+int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002);
+}
+
+int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange)
+{
+	static const char ck[] = {"create"};
+	static const char lang[] = {"en"};
+	static const char charset[] = {"us-ascii"};
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid);
+
+	/* exchange */
+	aimbs_put16(&fr->data, exchange);
+
+	/*
+	 * This looks to be a big hack.  You'll note that this entire
+	 * SNAC is just a room info structure, but the hard room name,
+	 * here, is set to "create".  
+	 *
+	 * Either this goes on the "list of questions concerning
+	 * why-the-hell-did-you-do-that", or this value is completly
+	 * ignored.  Without experimental evidence, but a good knowledge of
+	 * AOL style, I'm going to guess that it is the latter, and that
+	 * the value of the room name in create requests is ignored.
+	 */
+	aimbs_put8(&fr->data, strlen(ck));
+	aimbs_putraw(&fr->data, (guint8 *)ck, strlen(ck));
+
+	/* 
+	 * instance
+	 * 
+	 * Setting this to 0xffff apparently assigns the last instance.
+	 *
+	 */
+	aimbs_put16(&fr->data, 0xffff);
+
+	/* detail level */
+	aimbs_put8(&fr->data, 0x01);
+
+	aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), (guint8 *)name);
+	aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), (guint8 *)charset);
+	aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), (guint8 *)lang);
+
+	/* tlvcount */
+	aimbs_put16(&fr->data, aim_counttlvchain(&tl));
+	aim_writetlvchain(&fr->data, &tl);
+
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	struct aim_chat_exchangeinfo *exchanges = NULL;
+	int curexchange;
+	aim_tlv_t *exchangetlv;
+	guint8 maxrooms = 0;
+	aim_tlvlist_t *tlvlist, *innerlist;
+
+	tlvlist = aim_readtlvchain(bs);
+
+	/* 
+	 * Type 0x0002: Maximum concurrent rooms.
+	 */ 
+	if (aim_gettlv(tlvlist, 0x0002, 1))
+		maxrooms = aim_gettlv8(tlvlist, 0x0002, 1);
+
+	/* 
+	 * Type 0x0003: Exchange information
+	 *
+	 * There can be any number of these, each one
+	 * representing another exchange.  
+	 * 
+	 */
+	for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))); ) {
+		aim_bstream_t tbs;
+
+		aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length);
+
+		curexchange++;
+
+		exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo));
+
+		/* exchange number */
+		exchanges[curexchange-1].number = aimbs_get16(&tbs);
+		innerlist = aim_readtlvchain(&tbs);
+
+		/* 
+		 * Type 0x000a: Unknown.
+		 *
+		 * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others).
+		 *
+		 */
+		if (aim_gettlv(innerlist, 0x000a, 1))
+			;
+
+		/* 
+		 * Type 0x000d: Unknown.
+		 */
+		if (aim_gettlv(innerlist, 0x000d, 1))
+			;
+
+		/* 
+		 * Type 0x0004: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x0004, 1))
+			;
+
+		/* 
+		 * Type 0x0002: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x0002, 1)) {
+			guint16 classperms;
+
+			classperms = aim_gettlv16(innerlist, 0x0002, 1);
+			
+		}
+
+		/*
+		 * Type 0x00c9: Flags
+		 *
+		 * 1 Evilable
+		 * 2 Nav Only
+		 * 4 Instancing Allowed
+		 * 8 Occupant Peek Allowed
+		 *
+		 */ 
+		if (aim_gettlv(innerlist, 0x00c9, 1))
+			exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1);
+		      
+		/*
+		 * Type 0x00ca: Creation Date 
+		 */
+		if (aim_gettlv(innerlist, 0x00ca, 1))
+			;
+		      
+		/*
+		 * Type 0x00d0: Mandatory Channels?
+		 */
+		if (aim_gettlv(innerlist, 0x00d0, 1))
+			;
+
+		/*
+		 * Type 0x00d1: Maximum Message length
+		 */
+		if (aim_gettlv(innerlist, 0x00d1, 1))
+			;
+
+		/*
+		 * Type 0x00d2: Maximum Occupancy?
+		 */
+		if (aim_gettlv(innerlist, 0x00d2, 1))	
+			;
+
+		/*
+		 * Type 0x00d3: Exchange Description
+		 */
+		if (aim_gettlv(innerlist, 0x00d3, 1))	
+			exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1);
+		else
+			exchanges[curexchange-1].name = NULL;
+
+		/*
+		 * Type 0x00d4: Exchange Description URL
+		 */
+		if (aim_gettlv(innerlist, 0x00d4, 1))	
+			;
+
+		/*
+		 * Type 0x00d5: Creation Permissions
+		 *
+		 * 0  Creation not allowed
+		 * 1  Room creation allowed
+		 * 2  Exchange creation allowed
+		 * 
+		 */
+		if (aim_gettlv(innerlist, 0x00d5, 1)) {
+			guint8 createperms;
+
+			createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+		}
+
+		/*
+		 * Type 0x00d6: Character Set (First Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d6, 1))	
+			exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1);
+		else
+			exchanges[curexchange-1].charset1 = NULL;
+		      
+		/*
+		 * Type 0x00d7: Language (First Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d7, 1))	
+			exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1);
+		else
+			exchanges[curexchange-1].lang1 = NULL;
+
+		/*
+		 * Type 0x00d8: Character Set (Second Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d8, 1))	
+			exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1);
+		else
+			exchanges[curexchange-1].charset2 = NULL;
+
+		/*
+		 * Type 0x00d9: Language (Second Time)
+		 */	      
+		if (aim_gettlv(innerlist, 0x00d9, 1))	
+			exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1);
+		else
+			exchanges[curexchange-1].lang2 = NULL;
+		      
+		/*
+		 * Type 0x00da: Unknown
+		 */
+		if (aim_gettlv(innerlist, 0x00da, 1))	
+			;
+
+		aim_freetlvchain(&innerlist);
+	}
+
+	/*
+	 * Call client.
+	 */
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges);
+
+	for (curexchange--; curexchange >= 0; curexchange--) {
+		g_free(exchanges[curexchange].name);
+		g_free(exchanges[curexchange].charset1);
+		g_free(exchanges[curexchange].lang1);
+		g_free(exchanges[curexchange].charset2);
+		g_free(exchanges[curexchange].lang2);
+	}
+	g_free(exchanges);
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist, *innerlist;
+	char *ck = NULL, *fqcn = NULL, *name = NULL;
+	guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0;
+	guint32 createtime = 0;
+	guint8 createperms = 0, detaillevel;
+	int cklen;
+	aim_tlv_t *bigblock;
+	int ret = 0;
+	aim_bstream_t bbbs;
+
+	tlvlist = aim_readtlvchain(bs);
+
+	if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) {
+		do_error_dialog(sess->aux_data, "no bigblock in top tlv in create room response", "Gaim");
+	
+		aim_freetlvchain(&tlvlist);
+		return 0;
+	}
+
+	aim_bstream_init(&bbbs, bigblock->value, bigblock->length);
+
+	exchange = aimbs_get16(&bbbs);
+	cklen = aimbs_get8(&bbbs);
+	ck = aimbs_getstr(&bbbs, cklen);
+	instance = aimbs_get16(&bbbs);
+	detaillevel = aimbs_get8(&bbbs);
+
+	if (detaillevel != 0x02) {
+		do_error_dialog(sess->aux_data, "unknown detaillevel in create room response", "Gaim");
+		aim_freetlvchain(&tlvlist);
+		g_free(ck);
+		return 0;
+	}
+
+	unknown = aimbs_get16(&bbbs);
+
+	innerlist = aim_readtlvchain(&bbbs);
+
+	if (aim_gettlv(innerlist, 0x006a, 1))
+		fqcn = aim_gettlv_str(innerlist, 0x006a, 1);
+
+	if (aim_gettlv(innerlist, 0x00c9, 1))
+		flags = aim_gettlv16(innerlist, 0x00c9, 1);
+
+	if (aim_gettlv(innerlist, 0x00ca, 1))
+		createtime = aim_gettlv32(innerlist, 0x00ca, 1);
+
+	if (aim_gettlv(innerlist, 0x00d1, 1))
+		maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1);
+
+	if (aim_gettlv(innerlist, 0x00d2, 1))
+		maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1);
+
+	if (aim_gettlv(innerlist, 0x00d3, 1))
+		name = aim_gettlv_str(innerlist, 0x00d3, 1);
+
+	if (aim_gettlv(innerlist, 0x00d5, 1))
+		createperms = aim_gettlv8(innerlist, 0x00d5, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
+		ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck);
+	}
+
+	g_free(ck);
+	g_free(name);
+	g_free(fqcn);
+	aim_freetlvchain(&innerlist);
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Since multiple things can trigger this callback, we must lookup the 
+ * snacid to determine the original snac subtype that was called.
+ *
+ * XXX This isn't really how this works.  But this is:  Every d/9 response
+ * has a 16bit value at the beginning. That matches to:
+ *    Short Desc = 1
+ *    Full Desc = 2
+ *    Instance Info = 4
+ *    Nav Short Desc = 8
+ *    Nav Instance Info = 16
+ * And then everything is really asynchronous.  There is no specific 
+ * attachment of a response to a create room request, for example.  Creating
+ * the room yields no different a response than requesting the room's info.
+ *
+ */
+static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_snac_t *snac2;
+	int ret = 0;
+
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		do_error_dialog(sess->aux_data, "received response to unknown request!", "Gaim");
+		return 0;
+	}
+
+	if (snac2->family != 0x000d) {
+		do_error_dialog(sess->aux_data, "recieved response that maps to corrupt request!", "Gaim");
+		return 0;
+	}
+
+	/*
+	 * We now know what the original SNAC subtype was.
+	 */
+	if (snac2->type == 0x0002) /* request chat rights */
+		ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2);
+	else if (snac2->type == 0x0003) {} /* request exchange info */
+	else if (snac2->type == 0x0004) {} /* request room info */
+	else if (snac2->type == 0x0005) {} /* request more room info */
+	else if (snac2->type == 0x0006) {} /* request occupant list */
+	else if (snac2->type == 0x0007) {} /* search for a room */
+	else if (snac2->type == 0x0008) /* create room */
+		ret = parseinfo_create(sess, mod, rx, snac, bs, snac2);
+	else
+		do_error_dialog(sess->aux_data, "unknown request subtype", "Gaim");
+
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0009)
+		return parseinfo(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000d;
+	mod->version = 0x0003;
+	mod->toolid = 0x0010;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "chatnav", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
Index: protocols/oscar/chatnav.h
===================================================================
--- protocols/oscar/chatnav.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chatnav.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,14 @@
+#ifndef __OSCAR_CHATNAV_H__
+#define __OSCAR_CHATNAV_H__
+
+#define AIM_CB_FAM_CTN 0x000d /* ChatNav */
+
+/*
+ * SNAC Family: Chat Navigation Services
+ */ 
+#define AIM_CB_CTN_ERROR 0x0001
+#define AIM_CB_CTN_CREATE 0x0008
+#define AIM_CB_CTN_INFO 0x0009
+#define AIM_CB_CTN_DEFAULT 0xffff
+
+#endif /* __OSCAR_CHATNAV_H__ */
Index: protocols/oscar/conn.c
===================================================================
--- protocols/oscar/conn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/conn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,690 @@
+
+/*
+ * conn.c
+ *
+ * Does all this gloriously nifty connection handling stuff...
+ *
+ */
+
+#include <aim.h> 
+#include "sock.h"
+
+static int aim_logoff(aim_session_t *sess);
+
+/*
+ * In OSCAR, every connection has a set of SNAC groups associated
+ * with it.  These are the groups that you can send over this connection
+ * without being guarenteed a "Not supported" SNAC error.  
+ *
+ * The grand theory of things says that these associations transcend 
+ * what libfaim calls "connection types" (conn->type).  You can probably
+ * see the elegance here, but since I want to revel in it for a bit, you 
+ * get to hear it all spelled out.
+ *
+ * So let us say that you have your core BOS connection running.  One
+ * of your modules has just given you a SNAC of the group 0x0004 to send
+ * you.  Maybe an IM destined for some twit in Greenland.  So you start
+ * at the top of your connection list, looking for a connection that 
+ * claims to support group 0x0004.  You find one.  Why, that neat BOS
+ * connection of yours can do that.  So you send it on its way.
+ *
+ * Now, say, that fellow from Greenland has friends and they all want to
+ * meet up with you in a lame chat room.  This has landed you a SNAC
+ * in the family 0x000e and you have to admit you're a bit lost.  You've
+ * searched your connection list for someone who wants to make your life
+ * easy and deliver this SNAC for you, but there isn't one there.
+ *
+ * Here comes the good bit.  Without even letting anyone know, particularly
+ * the module that decided to send this SNAC, and definitly not that twit
+ * in Greenland, you send out a service request.  In this request, you have
+ * marked the need for a connection supporting group 0x000e.  A few seconds
+ * later, you receive a service redirect with an IP address and a cookie in
+ * it.  Great, you say.  Now I have something to do.  Off you go, making
+ * that connection.  One of the first things you get from this new server
+ * is a message saying that indeed it does support the group you were looking
+ * for.  So you continue and send rate confirmation and all that.  
+ * 
+ * Then you remember you had that SNAC to send, and now you have a means to
+ * do it, and you do, and everyone is happy.  Except the Greenlander, who is
+ * still stuck in the bitter cold.
+ *
+ * Oh, and this is useful for building the Migration SNACs, too.  In the
+ * future, this may help convince me to implement rate limit mitigation
+ * for real.  We'll see.
+ *
+ * Just to make me look better, I'll say that I've known about this great
+ * scheme for quite some time now.  But I still haven't convinced myself
+ * to make libfaim work that way.  It would take a fair amount of effort,
+ * and probably some client API changes as well.  (Whenever I don't want
+ * to do something, I just say it would change the client API.  Then I 
+ * instantly have a couple of supporters of not doing it.)
+ *
+ * Generally, addgroup is only called by the internal handling of the
+ * server ready SNAC.  So if you want to do something before that, you'll
+ * have to be more creative.  That is done rather early, though, so I don't
+ * think you have to worry about it.  Unless you're me.  I care deeply
+ * about such inane things.
+ *
+ */
+void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+
+	if (!(sg = g_malloc(sizeof(struct snacgroup))))
+		return;
+
+	sg->group = group;
+
+	sg->next = ins->groups;
+	ins->groups = sg;
+
+	return;
+}
+
+aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
+{
+	aim_conn_t *cur;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside;
+		struct snacgroup *sg;
+
+		for (sg = ins->groups; sg; sg = sg->next) {
+			if (sg->group == group)
+				return cur;
+		}
+	}
+
+	return NULL;
+}
+
+static void connkill_snacgroups(struct snacgroup **head)
+{
+	struct snacgroup *sg;
+
+	for (sg = *head; sg; ) {
+		struct snacgroup *tmp;
+
+		tmp = sg->next;
+		g_free(sg);
+		sg = tmp;
+	}
+
+	*head = NULL;
+
+	return;
+}
+
+static void connkill_rates(struct rateclass **head)
+{
+	struct rateclass *rc;
+
+	for (rc = *head; rc; ) {
+		struct rateclass *tmp;
+		struct snacpair *sp;
+
+		tmp = rc->next;
+
+		for (sp = rc->members; sp; ) {
+			struct snacpair *tmpsp;
+
+			tmpsp = sp->next;
+			g_free(sp);
+			sp = tmpsp;
+		}
+		g_free(rc);
+
+		rc = tmp;
+	}
+
+	*head = NULL;
+
+	return;
+}
+
+static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
+{
+
+	aim_rxqueue_cleanbyconn(sess, *deadconn);
+	aim_tx_cleanqueue(sess, *deadconn);
+
+	if ((*deadconn)->fd != -1) 
+		aim_conn_close(*deadconn);
+
+	/*
+	 * XXX ->priv should never be touched by the library. I know
+	 * it used to be, but I'm getting rid of all that.  Use
+	 * ->internal instead.
+	 */
+	if ((*deadconn)->priv)
+		g_free((*deadconn)->priv);
+
+	/*
+	 * This will free ->internal if it necessary...
+	 */
+	if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
+		aim_conn_kill_rend(sess, *deadconn);
+	else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
+		aim_conn_kill_chat(sess, *deadconn);
+
+	if ((*deadconn)->inside) {
+		aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;
+
+		connkill_snacgroups(&inside->groups);
+		connkill_rates(&inside->rates);
+
+		g_free(inside);
+	}
+
+	g_free(*deadconn);
+	*deadconn = NULL;
+
+	return;
+}
+
+/**
+ * aim_connrst - Clears out connection list, killing remaining connections.
+ * @sess: Session to be cleared
+ *
+ * Clears out the connection list and kills any connections left.
+ *
+ */
+static void aim_connrst(aim_session_t *sess)
+{
+
+	if (sess->connlist) {
+		aim_conn_t *cur = sess->connlist, *tmp;
+
+		while (cur) {
+			tmp = cur->next;
+			aim_conn_close(cur);
+			connkill_real(sess, &cur);
+			cur = tmp;
+		}
+	}
+
+	sess->connlist = NULL;
+
+	return;
+}
+
+/**
+ * aim_conn_init - Reset a connection to default values.
+ * @deadconn: Connection to be reset
+ *
+ * Initializes and/or resets a connection structure.
+ *
+ */
+static void aim_conn_init(aim_conn_t *deadconn)
+{
+
+	if (!deadconn)
+		return;
+
+	deadconn->fd = -1;
+	deadconn->subtype = -1;
+	deadconn->type = -1;
+	deadconn->seqnum = 0;
+	deadconn->lastactivity = 0;
+	deadconn->forcedlatency = 0;
+	deadconn->handlerlist = NULL;
+	deadconn->priv = NULL;
+	memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));
+
+	return;
+}
+
+/**
+ * aim_conn_getnext - Gets a new connection structure.
+ * @sess: Session
+ *
+ * Allocate a new empty connection structure.
+ *
+ */
+static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
+{
+	aim_conn_t *newconn;
+
+	if (!(newconn = g_new0(aim_conn_t,1))) 	
+		return NULL;
+
+	if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) {
+		g_free(newconn);
+		return NULL;
+	}
+
+	aim_conn_init(newconn);
+
+	newconn->next = sess->connlist;
+	sess->connlist = newconn;
+
+	return newconn;
+}
+
+/**
+ * aim_conn_kill - Close and free a connection.
+ * @sess: Session for the connection
+ * @deadconn: Connection to be freed
+ *
+ * Close, clear, and free a connection structure. Should never be
+ * called from within libfaim.
+ *
+ */
+void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
+{
+	aim_conn_t *cur, **prev;
+
+	if (!deadconn || !*deadconn)	
+		return;
+
+	for (prev = &sess->connlist; (cur = *prev); ) {
+		if (cur == *deadconn) {
+			*prev = cur->next;
+			break;
+		}
+		prev = &cur->next;
+	}
+
+	if (!cur)
+		return; /* oops */
+
+	connkill_real(sess, &cur);
+
+	return;
+}
+
+/**
+ * aim_conn_close - Close a connection
+ * @deadconn: Connection to close
+ *
+ * Close (but not free) a connection.
+ *
+ * This leaves everything untouched except for clearing the 
+ * handler list and setting the fd to -1 (used to recognize
+ * dead connections).  It will also remove cookies if necessary.
+ *
+ */
+void aim_conn_close(aim_conn_t *deadconn)
+{
+
+	if (deadconn->fd >= 3)
+		closesocket(deadconn->fd);
+	deadconn->fd = -1;
+	if (deadconn->handlerlist)
+		aim_clearhandlers(deadconn);
+	if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS)
+		aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn);
+
+	return;
+}
+
+/**
+ * aim_getconn_type - Find a connection of a specific type
+ * @sess: Session to search
+ * @type: Type of connection to look for
+ *
+ * Searches for a connection of the specified type in the 
+ * specified session.  Returns the first connection of that
+ * type found.
+ *
+ * XXX except for RENDEZVOUS, all uses of this should be removed and
+ * use aim_conn_findbygroup() instead.
+ */
+aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
+{
+	aim_conn_t *cur;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if ((cur->type == type) && 
+				!(cur->status & AIM_CONN_STATUS_INPROGRESS))
+			break;
+	}
+
+	return cur;
+}
+
+aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
+{
+	aim_conn_t *cur;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		if (cur->type == type)
+			break;
+	}
+
+	return cur;
+}
+
+/**
+ * aim_cloneconn - clone an aim_conn_t
+ * @sess: session containing parent
+ * @src: connection to clone
+ *
+ * A new connection is allocated, and the values are filled in
+ * appropriately. Note that this function sets the new connnection's
+ * ->priv pointer to be equal to that of its parent: only the pointer
+ * is copied, not the data it points to.
+ *
+ * This function returns a pointer to the new aim_conn_t, or %NULL on
+ * error
+ */
+aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
+{
+	aim_conn_t *conn;
+
+	if (!(conn = aim_conn_getnext(sess)))
+		return NULL;
+
+	conn->fd = src->fd;
+	conn->type = src->type;
+	conn->subtype = src->subtype;
+	conn->seqnum = src->seqnum;
+	conn->priv = src->priv;
+	conn->internal = src->internal;
+	conn->lastactivity = src->lastactivity;
+	conn->forcedlatency = src->forcedlatency;
+	conn->sessv = src->sessv;
+	aim_clonehandlers(sess, conn, src);
+
+	if (src->inside) {
+		/*
+		 * XXX should clone this section as well, but since currently
+		 * this function only gets called for some of that rendezvous
+		 * crap, and not on SNAC connections, its probably okay for
+		 * now. 
+		 *
+		 */
+	}
+
+	return conn;
+}
+
+/**
+ * aim_newconn - Open a new connection
+ * @sess: Session to create connection in
+ * @type: Type of connection to create
+ * @dest: Host to connect to (in "host:port" syntax)
+ *
+ * Opens a new connection to the specified dest host of specified
+ * type, using the proxy settings if available.  If @host is %NULL,
+ * the connection is allocated and returned, but no connection 
+ * is made.
+ *
+ * FIXME: Return errors in a more sane way.
+ *
+ */
+aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
+{
+	aim_conn_t *connstruct;
+	guint16 port = AIM_LOGIN_PORT;
+	char *host;
+	int i;
+
+	if (!(connstruct = aim_conn_getnext(sess)))
+		return NULL;
+
+	connstruct->sessv = (void *)sess;
+	connstruct->type = type;
+
+	if (!dest) { /* just allocate a struct */
+		connstruct->fd = -1;
+		connstruct->status = 0;
+		return connstruct;
+	}
+
+	/* 
+	 * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
+	 * colon, in the BOS redirect.  This fatally breaks all previous 
+	 * libfaims.  Bad, bad AOL.
+	 *
+	 * We put this here to catch every case. 
+	 *
+	 */
+
+	for(i = 0; i < (int)strlen(dest); i++) {
+		if (dest[i] == ':') {
+			port = atoi(&(dest[i+1]));
+			break;
+		}
+	}
+
+	host = (char *)g_malloc(i+1);
+	strncpy(host, dest, i);
+	host[i] = '\0';
+
+	connstruct->fd = proxy_connect(host, port, NULL, NULL);
+
+	g_free(host);
+
+	return connstruct;
+}
+
+/**
+ * aim_conn_setlatency - Set a forced latency value for connection
+ * @conn: Conn to set latency for
+ * @newval: Number of seconds to force between transmits
+ *
+ * Causes @newval seconds to be spent between transmits on a connection.
+ *
+ * This is my lame attempt at overcoming not understanding the rate
+ * limiting. 
+ *
+ * XXX: This should really be replaced with something that scales and
+ * backs off like the real rate limiting does.
+ *
+ */
+int aim_conn_setlatency(aim_conn_t *conn, int newval)
+{
+
+	if (!conn)
+		return -1;
+
+	conn->forcedlatency = newval;
+	conn->lastactivity = 0; /* reset this just to make sure */
+
+	return 0;
+}
+
+/**
+ * aim_session_init - Initializes a session structure
+ * @sess: Session to initialize
+ * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
+ * @debuglevel: Level of debugging output (zero is least)
+ *
+ * Sets up the initial values for a session.
+ *
+ */
+void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
+{
+
+	if (!sess)
+		return;
+
+	memset(sess, 0, sizeof(aim_session_t));
+	aim_connrst(sess);
+	sess->queue_outgoing = NULL;
+	sess->queue_incoming = NULL;
+	aim_initsnachash(sess);
+	sess->msgcookies = NULL;
+	sess->snacid_next = 0x00000001;
+
+	sess->flags = 0;
+
+	sess->modlistv = NULL;
+
+	sess->ssi.received_data = 0;
+	sess->ssi.waiting_for_ack = 0;
+	sess->ssi.holding_queue = NULL;
+	sess->ssi.revision = 0;
+	sess->ssi.items = NULL;
+	sess->ssi.timestamp = (time_t)0;
+
+	sess->locate.userinfo = NULL;
+	sess->locate.torequest = NULL;
+	sess->locate.requested = NULL;
+	sess->locate.waiting_for_response = FALSE;
+
+	sess->icq_info = NULL;
+	sess->authinfo = NULL;
+	sess->emailinfo = NULL;
+	sess->oft_info = NULL;
+
+
+	/*
+	 * Default to SNAC login unless XORLOGIN is explicitly set.
+	 */
+	if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
+		sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+	sess->flags |= flags;
+
+	/*
+	 * This must always be set.  Default to the queue-based
+	 * version for back-compatibility.  
+	 */
+	aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
+
+
+	/*
+	 * Register all the modules for this session...
+	 */
+	aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
+	aim__registermodule(sess, general_modfirst);
+	aim__registermodule(sess, locate_modfirst);
+	aim__registermodule(sess, buddylist_modfirst);
+	aim__registermodule(sess, msg_modfirst);
+	aim__registermodule(sess, admin_modfirst);
+	aim__registermodule(sess, bos_modfirst);
+	aim__registermodule(sess, search_modfirst);
+	aim__registermodule(sess, stats_modfirst);
+	aim__registermodule(sess, chatnav_modfirst);
+	aim__registermodule(sess, chat_modfirst);
+	/* missing 0x0f - 0x12 */
+	aim__registermodule(sess, ssi_modfirst);
+	/* missing 0x14 */
+	aim__registermodule(sess, icq_modfirst);
+	/* missing 0x16 */
+	aim__registermodule(sess, auth_modfirst);
+
+	return;
+}
+
+/**
+ * aim_session_kill - Deallocate a session
+ * @sess: Session to kill
+ *
+ */
+void aim_session_kill(aim_session_t *sess)
+{
+	aim_cleansnacs(sess, -1);
+
+	aim_logoff(sess);
+
+	aim__shutdownmodules(sess);
+
+	return;
+}
+
+/*
+ * XXX this is nearly as ugly as proxyconnect().
+ */
+int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
+{
+	fd_set fds, wfds;
+	struct timeval tv;
+	int res, error = ETIMEDOUT;
+	aim_rxcallback_t userfunc;
+
+	if (!conn || (conn->fd == -1))
+		return -1;
+
+	if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
+		return -1;
+
+	FD_ZERO(&fds);
+	FD_SET(conn->fd, &fds);
+	FD_ZERO(&wfds);
+	FD_SET(conn->fd, &wfds);
+	tv.tv_sec = 0;
+	tv.tv_usec = 0;
+
+	if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
+		error = errno;
+		aim_conn_close(conn);
+		errno = error;
+		return -1;
+	} else if (res == 0) {
+		return 0; /* hasn't really completed yet... */
+	} 
+
+	if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
+		unsigned int len = sizeof(error);
+
+		if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+			error = errno;
+	}
+
+	if (error) {
+		aim_conn_close(conn);
+		errno = error;
+		return -1;
+	}
+
+#ifndef _WIN32
+	fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
+#endif
+
+	conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
+
+	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
+		userfunc(sess, NULL, conn);
+
+	/* Flush out the queues if there was something waiting for this conn  */
+	aim_tx_flushqueue(sess);
+
+	return 0;
+}
+
+aim_session_t *aim_conn_getsess(aim_conn_t *conn)
+{
+
+	if (!conn)
+		return NULL;
+
+	return (aim_session_t *)conn->sessv;
+}
+
+/*
+ * aim_logoff()
+ *
+ * Closes -ALL- open connections.
+ *
+ */
+static int aim_logoff(aim_session_t *sess)
+{
+
+	aim_connrst(sess);  /* in case we want to connect again */
+
+	return 0;
+
+}
+
+/*
+ * aim_flap_nop()
+ *
+ * No-op.  WinAIM 4.x sends these _every minute_ to keep
+ * the connection alive.  
+ */
+int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
+		return -ENOMEM;
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+
Index: protocols/oscar/ft.c
===================================================================
--- protocols/oscar/ft.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ft.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,2005 @@
+/*
+ * File transfer (OFT) and DirectIM (ODC).
+ * (OSCAR File Transfer, Oscar Direct Connect(ion?)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <aim.h>
+#include <glib.h>
+#include "ft.h"
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/utsname.h> /* for aim_directim_initiate */
+#include <arpa/inet.h> /* for inet_ntoa */
+#endif
+
+/* TODO: 
+   o look for memory leaks.. there's going to be shitloads, i'm sure. 
+*/
+
+struct aim_directim_intdata {
+	guint8 cookie[8];
+	char sn[MAXSNLEN+1];
+	char ip[22];
+};
+
+static int listenestablish(guint16 portnum);
+ 
+/**
+ * aim_handlerendconnect - call this to accept OFT connections and set up the required structures
+ * @sess: the session
+ * @cur: the conn the incoming connection is on
+ *
+ * call this when you get an outstanding read on a conn with subtype
+ * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current
+ * &aim_conn_t and tweak things as appropriate. the new conn and the
+ * listener conn are both returned to the client in the
+ * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback.
+ */
+int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur)
+{ 
+	int acceptfd = 0;
+	struct sockaddr cliaddr;
+	unsigned int clilen = sizeof(cliaddr);
+	int ret = 0;
+	aim_conn_t *newconn;
+
+	if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1)
+		return 0; /* not an error */
+
+	if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */
+		closesocket(acceptfd);
+		aim_conn_close(cur);
+		return -1;
+	} 
+
+	if (!(newconn = aim_cloneconn(sess, cur))) {
+		closesocket(acceptfd);
+		aim_conn_close(cur);
+		return -1;
+	}
+
+	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;
+	newconn->fd = acceptfd;
+
+	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { 
+		struct aim_directim_intdata *priv;
+		aim_rxcallback_t userfunc;
+
+		priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal);
+		cur->internal = NULL;
+
+		g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u", 
+				inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), 
+				ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE)))
+			ret = userfunc(sess, NULL, newconn, cur);
+
+	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {
+#if 0
+		struct aim_filetransfer_priv *priv;
+		aim_rxcallback_t userfunc;
+
+
+		newconn->priv = cur->priv;
+		cur->priv = NULL;
+		priv = (struct aim_filetransfer_priv *)newconn->priv;
+
+		g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port));
+
+		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE)))
+			ret = userfunc(sess, NULL, newconn, cur);
+#endif
+	} else { 
+		do_error_dialog(sess->aux_data, "Got a Connection on a listener that's not Rendezvous Closing conn.", "Gaim");
+		aim_conn_close(newconn);
+		ret = -1;
+	}
+
+	return ret;
+}
+
+/**
+ * aim_send_typing - send client-to-client typing notification over established connection
+ * @sess: session to conn
+ * @conn: directim connection
+ * @typing: If true, notify user has started typing; if false, notify user has stopped.
+ *
+ * The connection must have been previously established.
+ */
+int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing)
+{
+	
+struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	aim_frame_t *fr;
+	aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */
+
+	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
+		return -EINVAL; 
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))
+	       return -ENOMEM;	
+
+	memcpy(fr->hdr.oft.magic, "ODC2", 4);
+	
+	fr->hdr.oft.hdr2len = 0x44;
+	
+	if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) { 
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+	memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len);
+	
+	aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+	aimbs_put16(&hdrbs, 0x0006);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, intdata->cookie, 8);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put32(&hdrbs, 0x00000000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+
+	/* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */
+	aimbs_put16(&hdrbs, ( typing ? 0x000e : 0x0002));
+
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn));
+	
+	aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
+
+	aimbs_put8(&hdrbs, 0x00);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+
+	/* end of hdr2 */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+} 
+
+/**
+ * aim_send_im_direct - send IM client-to-client over established connection
+ * @sess: session to conn
+ * @conn: directim connection
+ * @msg:  null-terminated string to send. 
+ * len:   The length of the message to send, including binary data.
+ * 
+ * Call this just like you would aim_send_im, to send a directim. You
+ * _must_ have previously established the directim connection.
+ */
+int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len)
+{
+	struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	aim_frame_t *fr;
+	aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */
+
+	if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) 
+		return -EINVAL; 
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len)))
+		return -ENOMEM;	
+
+	memcpy(fr->hdr.oft.magic, "ODC2", 4);
+	
+	fr->hdr.oft.hdr2len = 0x44;
+	
+	if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) { 
+		aim_frame_destroy(fr);
+		return -ENOMEM;
+	}
+	memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len);
+	
+	aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+	
+	aimbs_put16(&hdrbs, 0x0006);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, intdata->cookie, 8);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put32(&hdrbs, len);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	
+	/* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */
+	aimbs_put16(&hdrbs, 0x0000);
+	
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn));
+	
+	aim_bstream_setpos(&hdrbs, 52); /* bleeehh */
+	
+	aimbs_put8(&hdrbs, 0x00);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_put16(&hdrbs, 0x0000);
+	
+	/* end of hdr2 */
+	
+#if 0 /* XXX this is how you send buddy icon info... */	
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e);
+	i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8);
+#endif
+	aimbs_putraw(&fr->data, (guint8 *)msg, len);
+	
+	aim_tx_enqueue(sess, fr);
+	
+	return 0;
+} 
+
+static int getlocalip(guint8 *ip)
+{
+	struct hostent *hptr;
+	char localhost[129];
+
+	/* XXX if available, use getaddrinfo() */
+	/* XXX allow client to specify which IP to use for multihomed boxes */
+
+	if (gethostname(localhost, 128) < 0)
+		return -1;
+
+	if (!(hptr = gethostbyname(localhost)))
+		return -1;
+
+	memcpy(ip, hptr->h_addr_list[0], 4);
+
+	return 0;
+}
+
+/**
+ * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one)
+ * @destsn: the SN to connect to.
+ *
+ */
+aim_conn_t *aim_directim_initiate(aim_session_t *sess, const char *destsn)
+{ 
+	aim_conn_t *newconn;
+	aim_msgcookie_t *cookie;
+	struct aim_directim_intdata *priv;
+	int listenfd;
+	guint16 port = 4443;
+	guint8 localip[4];
+	guint8 ck[8];
+
+	if (getlocalip(localip) == -1)
+		return NULL;
+
+	if ((listenfd = listenestablish(port)) == -1)
+		return NULL;
+
+	aim_request_directim(sess, destsn, localip, port, ck);
+
+	cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1);
+	memcpy(cookie->cookie, ck, 8);
+	cookie->type = AIM_COOKIETYPE_OFTIM;
+
+	/* this one is for the cookie */
+	priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
+	cookie->data = priv;
+	aim_cachecookie(sess, cookie);
+
+	/* XXX switch to aim_cloneconn()? */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
+		closesocket(listenfd);
+		return NULL;
+	}
+
+	/* this one is for the conn */
+	priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata, 1);
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
+
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = priv;
+	newconn->lastactivity = time(NULL);
+
+	return newconn;
+}
+
+/**
+ * aim_sendfile_intitiate - For those times when we want to send the file ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn: the SN to connect to.
+ * @filename: the name of the files you want to send
+ *
+ */
+aim_conn_t *aim_sendfile_initiate(aim_session_t *sess, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize)
+{ 
+	aim_conn_t *newconn;
+	aim_msgcookie_t *cookie;
+	struct aim_directim_intdata *priv;
+	int listenfd;
+	guint16 port = 4443;
+	guint8 localip[4];
+	guint8 ck[8];
+
+	if (getlocalip(localip) == -1)
+		return NULL;
+
+	if ((listenfd = listenestablish(port)) == -1)
+		return NULL;
+
+	aim_request_sendfile(sess, destsn, filename, numfiles, totsize, localip, port, ck);
+
+	cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1);
+	memcpy(cookie->cookie, ck, 8);
+	cookie->type = AIM_COOKIETYPE_OFTIM;
+
+	/* this one is for the cookie */
+	priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
+	cookie->data = priv;
+	aim_cachecookie(sess, cookie);
+
+	/* XXX switch to aim_cloneconn()? */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) {
+		closesocket(listenfd);
+		return NULL;
+	}
+
+	/* this one is for the conn */
+	priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1);
+
+	memcpy(priv->cookie, ck, 8);
+	strncpy(priv->sn, destsn, sizeof(priv->sn));
+
+	newconn->fd = listenfd;
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
+	newconn->internal = priv;
+	newconn->lastactivity = time(NULL);
+
+	return newconn;
+}
+
+#if 0
+/**
+ * unsigned int aim_oft_listener_clean - close up old listeners
+ * @sess: session to clean up in
+ * @age: maximum age in seconds 
+ *
+ * returns number closed, -1 on error.
+ */
+unsigned int aim_oft_listener_clean(struct aim_session_t *sess, time_t age) 
+{ 
+  struct aim_conn_t *cur;
+  time_t now;
+  unsigned int hit = 0;
+  
+  if (!sess)
+    return -1;
+  now = time(NULL);
+  faim_mutex_lock(&sess->connlistlock);
+  for(cur = sess->connlist;cur; cur = cur->next)
+    if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { 
+      faim_mutex_lock(&cur->active);
+      if (cur->lastactivity < (now - age) ) { 
+	faim_mutex_unlock(&cur->active);
+	aim_conn_close(cur);
+	hit++;
+      } else 
+	faim_mutex_unlock(&cur->active);
+    } 
+  faim_mutex_unlock(&sess->connlistlock);
+  return hit;
+} 
+#endif 
+
+const char *aim_directim_getsn(aim_conn_t *conn)
+{
+	struct aim_directim_intdata *intdata;
+
+	if (!conn)
+	       return NULL;
+
+	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || 
+			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+	       return NULL;
+
+	if (!conn->internal)
+		return NULL;
+
+	intdata = (struct aim_directim_intdata *)conn->internal;
+
+	return intdata->sn;
+}
+
+/**
+ * aim_directim_connect - connect to buddy for directim
+ * @sess: the session to append the conn to,
+ * @sn: the SN we're connecting to
+ * @addr: address to connect to
+ *
+ * This is a wrapper for aim_newconn.
+ *
+ * If addr is NULL, the socket is not created, but the connection is 
+ * allocated and setup to connect.
+ *
+ */
+aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const guint8 *cookie)
+{ 
+	aim_conn_t *newconn;
+	struct aim_directim_intdata *intdata;
+
+	if (!sess || !sn)
+		return NULL;
+
+	if (!(intdata = g_new0(struct aim_directim_intdata, 1)))
+		return NULL;
+
+	memcpy(intdata->cookie, cookie, 8);
+	strncpy(intdata->sn, sn, sizeof(intdata->sn));
+	if (addr)
+		strncpy(intdata->ip, addr, sizeof(intdata->ip));
+
+	/* XXX verify that non-blocking connects actually work */
+	if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) {
+		g_free(intdata);
+		return NULL;
+	}
+
+	if (!newconn) {
+		g_free(intdata);
+		return newconn;
+	}
+
+	newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+	newconn->internal = intdata;
+
+	return newconn;
+} 
+
+/**
+ * aim_directim_getconn - find a directim conn for buddy name
+ * @sess: your session,
+ * @name: the name to get,  
+ *
+ * returns conn for directim with name, %NULL if none found. 
+ *
+ */
+aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name)
+{
+	aim_conn_t *cur;
+
+	if (!sess || !name || !strlen(name))
+		return NULL;
+
+	for (cur = sess->connlist; cur; cur = cur->next) {
+		struct aim_directim_intdata *intdata;
+		
+		if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))
+			continue;
+
+		intdata = cur->internal;
+
+		if (aim_sncmp(intdata->sn, name) == 0)
+			break;
+	}
+
+	return cur;
+} 
+
+/**
+ * aim_accepttransfer - accept a file transfer request
+ * @sess: the session,
+ * @conn: the BOS conn for the CAP reply
+ * @sn: the screenname to send it to,
+ * @cookie: the cookie used
+ * @ip: the ip to connect to
+ * @listingfiles: number of files to share
+ * @listingtotsize: total size of shared files
+ * @listingsize: length of the listing file(buffer)
+ * @listingchecksum: checksum of the listing
+ * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE)  
+ *
+ * Returns new connection or %NULL on error.
+ *
+ * XXX this should take a struct.
+ */
+aim_conn_t *aim_accepttransfer(aim_session_t *sess, 
+						  aim_conn_t *conn, 
+						  const char *sn, const guint8 *cookie, 
+						  const guint8 *ip, 
+						  guint16 listingfiles, 
+						  guint16 listingtotsize, 
+						  guint16 listingsize, 
+						  guint32 listingchecksum, 
+						  guint16 rendid)
+{
+       return NULL;	
+#if 0
+  struct command_tx_struct *newpacket, *newoft;
+  struct aim_conn_t *newconn;
+  struct aim_fileheader_t *fh;
+  struct aim_filetransfer_priv *priv;
+  struct aim_msgcookie_t *cachedcook;
+  int curbyte, i;
+
+  if (!sess || !conn || !sn || !cookie || !ip) {
+    return NULL;
+  }
+
+  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip);
+
+  if (!newconn || (newconn->fd == -1)) {
+    perror("aim_newconn");
+    faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0);
+    return newconn;
+  } else {
+    priv = (struct aim_filetransfer_priv *)g_new0(struct aim_filetransfer_priv,1);
+
+    memcpy(priv->cookie, cookie, 8);
+    priv->state = 0;
+    strncpy(priv->sn, sn, MAXSNLEN);
+    strncpy(priv->ip, ip, sizeof(priv->ip));
+    newconn->priv = (void *)priv;
+
+    faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd);
+  }
+
+  if (rendid == AIM_CAPS_GETFILE)  {
+    newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+
+      faimdprintf(sess, 2, "faim: getfile request accept\n");
+
+      if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { 
+	faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+	/* XXX: conn leak here */
+	return NULL;
+      } 
+
+      newoft->lock = 1;
+      memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+      newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+      if (!(fh = (struct aim_fileheader_t*)g_new0(struct aim_fileheader_t,1))) {
+	/* XXX: conn leak here */
+	perror("calloc");
+	return NULL;
+      }
+
+      fh->encrypt = 0x0000;
+      fh->compress = 0x0000;
+      fh->totfiles = listingfiles;
+      fh->filesleft = listingfiles;      /* is this right -- total parts and parts left?*/
+      fh->totparts = 0x0001;
+      fh->partsleft = 0x0001;
+      fh->totsize = listingtotsize;
+      fh->size = listingsize;      /* ls -l listing.txt */
+      fh->modtime = (int)time(NULL); /* we'll go with current time for now */
+      fh->checksum = listingchecksum;
+      fh->rfcsum = 0x00000000;
+      fh->rfsize = 0x00000000;
+      fh->cretime = 0x00000000;
+      fh->rfcsum = 0x00000000;
+      fh->nrecvd = 0x00000000;
+      fh->recvcsum = 0x00000000;
+      memset(fh->idstring, 0, sizeof(fh->idstring));
+      memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+      fh->flags = 0x02;
+      fh->lnameoffset = 0x1a;
+      fh->lsizeoffset = 0x10;
+      memset(fh->dummy, 0, sizeof(fh->dummy));
+      memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+      /* we need to figure out these encodings for filenames */
+      fh->nencode = 0x0000;
+      fh->nlanguage = 0x0000;
+      memset(fh->name, 0, sizeof(fh->name));
+      memcpy(fh->name, "listing.txt", sizeof(fh->name));
+
+      if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
+	newoft->lock = 0;
+	aim_frame_destroy(newoft);
+	/* XXX: conn leak */
+	perror("calloc (1)");
+	return NULL;
+      } 
+
+      memcpy(fh->bcookie, cookie, 8);
+
+      if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh)))
+	faimdprintf(sess, 1, "eek, bh fail!\n");
+
+      newoft->lock = 0;
+      aim_tx_enqueue(sess, newoft);
+   
+      if (!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) { 
+	faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n");
+	/* XXX: more cleanup, conn leak */
+	perror("calloc (2)");
+	return NULL;
+      }
+
+      memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t));
+      memcpy(cachedcook->cookie, cookie, 8);
+
+      cachedcook->type = AIM_COOKIETYPE_OFTGET;
+      cachedcook->data = (void *)priv;
+
+      if (aim_cachecookie(sess, cachedcook) == -1)
+	faimdprintf(sess, 1, "faim: ERROR caching message cookie\n");
+
+      g_free(fh);     
+ 
+      /* OSCAR CAP accept packet */
+   
+      if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) {
+	return NULL;
+      }
+  } else {
+    return NULL;
+  }
+  
+  newpacket->lock = 1;
+  curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  for (i = 0; i < 8; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept*/);
+
+  for (i = 0;i < 8; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
+
+  curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid);
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  return newconn;
+#endif
+}
+
+/**
+ * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE*
+ *  @file is an opened listing file
+ * 
+ * returns a pointer to the filled-in fileheader_t
+ *
+ * Currently omits checksum. we'll fix this when AOL breaks us, i
+ * guess.
+ *
+ */
+struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file) 
+{
+	return NULL;
+#if 0
+  struct aim_fileheader_t *fh;
+  u_long totsize = 0, size = 0, checksum = 0xffff0000;
+  short totfiles = 0;
+  char *linebuf, sizebuf[9];
+  
+  int linelength = 1024;
+
+  /* XXX: if we have a line longer than 1024chars, God help us. */
+  if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) {
+    faimdprintf(sess, 2, "linebuf calloc failed\n");
+    return NULL;
+  }  
+
+  if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */
+    perror("getlisting END1 fseek:");
+    faimdprintf(sess, 2, "getlising fseek END1 error\n");
+  }
+
+  if ((size = ftell(file)) == -1) {
+    perror("getlisting END1 getpos:");
+    faimdprintf(sess, 2, "getlising getpos END1 error\n");
+  }
+
+  if (fseek(file, 0, SEEK_SET) != 0) {
+    perror("getlesting fseek(SET):");
+    faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n");
+  }
+
+  memset(linebuf, 0, linelength);
+
+  size = 0;
+
+  while(fgets(linebuf,  linelength, file)) {
+    totfiles++;
+    memset(sizebuf, 0, 9);
+
+    size += strlen(linebuf);
+    
+    if (strlen(linebuf) < 23) {
+      faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf);
+      continue;
+    }
+    if (linebuf[strlen(linebuf)-1] != '\n') {
+      faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n");
+    }
+
+    memcpy(sizebuf, linebuf+17, 8);
+
+    totsize += strtol(sizebuf, NULL, 10);
+    memset(linebuf, 0, linelength);
+  }   
+
+  if (fseek(file, 0, SEEK_SET) == -1) {
+    perror("getlisting END2 fseek:");
+    faimdprintf(sess, 2, "getlising fseek END2 error\n");
+  }  
+
+  g_free(linebuf);
+
+  /* we're going to ignore checksumming the data for now -- that
+   * requires walking the whole listing.txt. it should probably be
+   * done at register time and cached, but, eh. */  
+
+  if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t))))
+    return NULL;
+
+  fh->encrypt     = 0x0000;
+  fh->compress    = 0x0000; 
+  fh->totfiles    = totfiles;
+  fh->filesleft   = totfiles; /* is this right ?*/
+  fh->totparts    = 0x0001;
+  fh->partsleft   = 0x0001;
+  fh->totsize     = totsize;
+  fh->size        = size; /* ls -l listing.txt */
+  fh->modtime     = (int)time(NULL); /* we'll go with current time for now */
+  fh->checksum    = checksum; /* XXX: checksum ! */
+  fh->rfcsum      = 0x00000000;
+  fh->rfsize      = 0x00000000;
+  fh->cretime     = 0x00000000;
+  fh->rfcsum      = 0x00000000;
+  fh->nrecvd      = 0x00000000;
+  fh->recvcsum    = 0x00000000;
+
+  /*  memset(fh->idstring, 0, sizeof(fh->idstring)); */
+  memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring));
+  memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring));
+
+  fh->flags       = 0x02;
+  fh->lnameoffset = 0x1a;
+  fh->lsizeoffset = 0x10;
+
+  /*  memset(fh->dummy, 0, sizeof(fh->dummy)); */
+  memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo));
+
+  fh->nencode     = 0x0000; /* we need to figure out these encodings for filenames */
+  fh->nlanguage   = 0x0000;
+
+  /*  memset(fh->name, 0, sizeof(fh->name)); */
+  memcpy(fh->name, "listing.txt", sizeof(fh->name));
+  memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name));
+
+  faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name))));
+  return fh;
+#endif
+}
+
+/**
+ * aim_listenestablish - create a listening socket on a port.
+ * @portnum: the port number to bind to.  
+ *
+ * you need to call accept() when it's connected. returns your fd 
+ *
+ * XXX: give the client author the responsibility of setting up a
+ * listener, then we no longer have a libfaim problem with broken
+ * solaris *innocent smile* -jbm
+ */
+static int listenestablish(guint16 portnum)
+{
+#if HAVE_GETADDRINFO
+	int listenfd;
+	const int on = 1;
+	struct addrinfo hints, *res, *ressave;
+	char serv[5];
+
+	g_snprintf(serv, sizeof(serv), "%d", portnum);
+	memset(&hints, 0, sizeof(struct addrinfo));
+	hints.ai_flags = AI_PASSIVE;
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) {
+		perror("getaddrinfo");
+		return -1;
+	} 
+	ressave = res;
+	do { 
+		listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);    
+		if (listenfd < 0)
+			continue;
+		setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+		if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
+			break;
+		/* success */
+		closesocket(listenfd);
+	} while ( (res = res->ai_next) );
+
+	if (!res)
+		return -1;
+
+	if (listen(listenfd, 1024)!=0) { 
+		perror("listen");
+		return -1;
+	} 
+
+	freeaddrinfo(ressave);
+	return listenfd;
+#else
+	int listenfd;
+	const int on = 1;
+	struct sockaddr_in sockin;
+
+	if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		perror("socket(listenfd)");
+		return -1;
+	}
+
+	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) {
+		perror("setsockopt(listenfd)");
+		closesocket(listenfd);
+		return -1;
+	} 
+
+	memset(&sockin, 0, sizeof(struct sockaddr_in));
+	sockin.sin_family = AF_INET;
+	sockin.sin_port = htons(portnum);
+
+	if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
+		perror("bind(listenfd)");
+		closesocket(listenfd);
+		return -1;
+	}
+	if (listen(listenfd, 4) != 0) {
+		perror("listen(listenfd)");
+		closesocket(listenfd);
+		return -1;
+	}
+	return listenfd;
+#endif
+} 
+
+static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	aim_rxcallback_t userfunc;
+
+	ft = conn->priv;
+	if (ft->state == 2) {
+		/* waiting on listing data */
+		int ret = 0;
+		char *listing;
+		struct command_tx_struct *newoft;
+
+		if (!(listing = g_malloc(ft->fh.size)))
+			return -1;
+
+		ft->state = 0;
+		if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size)	
+			faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size);
+
+		if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) {
+			faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n");
+			faim_mutex_unlock(&conn->active);
+			g_free(listing);
+			aim_conn_close(conn);
+			return -1;
+		}
+
+		memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+		newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+		/* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */
+
+		ft->fh.nrecvd = ft->fh.size;
+		ft->fh.recvcsum = ft->fh.checksum;
+		ft->fh.flags = 0;
+
+		if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+			aim_frame_destroy(newoft);
+			g_free(listing);
+			return -1;
+		}
+
+		if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh))))
+			faimdprintf(sess, 2, "eek! bh fail listing\n");
+
+		/* send the 120b */
+		aim_tx_enqueue(sess, newoft);
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) )
+			ret = userfunc(sess, NULL, conn, ft, listing);
+
+		g_free(listing);
+		return ret;
+	}
+
+	if (ft->state == 3) { 
+		/* waiting on file data */
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) 
+			return userfunc(sess, NULL, conn, ft);
+		return 0;
+	}
+
+	if (ft->state == 4) {
+		if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) )
+			return userfunc(sess, NULL, conn);
+		aim_conn_close(conn);
+		return 0;
+	}	
+
+	return 0;
+#else
+	return -1;
+#endif
+}
+
+static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_msgcookie_t *cook;
+	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+	cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTSEND);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	g_free(conn->internal);
+
+	return;
+}
+
+static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_msgcookie_t *cook;
+	struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv;
+
+	cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTGET);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	g_free(conn->internal);
+
+	return;
+}
+
+static void connclose_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal;
+	aim_msgcookie_t *cook;
+
+	cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM);
+	aim_cookie_free(sess, cook);
+
+	return;
+}
+
+static void connkill_directim(aim_session_t *sess, aim_conn_t *conn)
+{
+	
+	g_free(conn->internal);
+
+	return;
+}
+
+void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+		return;
+
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+		connclose_sendfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		connclose_getfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+		connclose_directim(sess, conn);
+
+	return;
+}
+
+void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn)
+{
+
+	if (conn->type != AIM_CONN_TYPE_RENDEZVOUS)
+		return;
+
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE)
+		connkill_sendfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		connkill_getfile(sess, conn);
+	else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)
+		connkill_directim(sess, conn);
+
+	return;
+}
+
+static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+	aim_frame_t fr;
+	aim_rxcallback_t userfunc;
+	guint32 payloadlength;
+	guint16 flags;
+	char *snptr = NULL;
+
+	fr.conn = conn;
+
+	payloadlength = aimutil_get32(hdr+22);
+	flags = aimutil_get16(hdr+32);
+	snptr = (char *)hdr+38;
+
+	if (flags == 0x000e) { 
+		int ret = 0;
+
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr, 1);
+
+		return ret;
+	
+	} else if (flags == 0x0002) {
+		int ret = 0;
+		
+		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING)))
+			ret = userfunc(sess, &fr, snptr, 0);
+
+		return ret;
+
+	} else if ((flags == 0x0000) && payloadlength) { 
+		char *msg, *msg2;
+		int ret = 0;
+		int recvd = 0;
+		int i;
+
+		if (!(msg = g_malloc(payloadlength+1)))
+			return -1;
+		memset(msg, 0, payloadlength+1);
+		msg2 = msg;
+		
+		while (payloadlength - recvd) {
+			if (payloadlength - recvd >= 1024)
+				i = aim_recv(conn->fd, msg2, 1024);
+			else 
+				i = aim_recv(conn->fd, msg2, payloadlength - recvd);
+			if (i <= 0) {
+				g_free(msg);
+				return -1;
+			}
+			recvd = recvd + i;
+			msg2 = msg2 + i;
+			if ((userfunc=aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
+				userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
+		}
+		
+		if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) )
+			ret = userfunc(sess, &fr, snptr, msg, payloadlength);
+
+		g_free(msg);
+
+		return ret;
+	}
+
+	return 0;
+}
+
+static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_fileheader_t *fh;
+	struct aim_msgcookie_t *cook;
+	struct command_tx_struct *newoft;
+	aim_rxcallback_t userfunc;
+
+	faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n");
+	fh = aim_oft_getfh(hdr);
+
+	faim_mutex_unlock(&conn->active);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		g_free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+
+	/* we're waaaaiiiting.. for listing.txt */
+	ft->state = 2;
+
+	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+	g_free(fh);
+
+	if(aim_cachecookie(sess, cook) == -1) {
+		faimdprintf(sess, 1, "error caching cookie\n");
+		return -1;
+	}     
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) {
+		aim_conn_close(conn);
+		return -1;
+	}
+
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+		newoft->lock = 0;
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+		newoft->lock = 0;
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+#endif
+	return -1;
+}
+
+static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_fileheader_t *fh;
+	struct aim_msgcookie_t *cook;
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET)))
+		faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n",  AIM_COOKIETYPE_OFTGET, fh->bcookie);
+
+	ft = cook->data;
+
+	if (ft->fh.size != fh->size)
+		faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size);
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ)))
+		ret = userfunc(sess, NULL, conn, fh);
+
+	faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n");
+
+	g_free(fh);
+
+	return ret;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct aim_fileheader_t *fh;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		g_free(fh);
+		return -1;
+	}
+
+	g_free(fh);
+
+	ft = cook->data;
+
+	if (aim_cachecookie(sess, cook) == -1)
+		return -1;
+
+	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM)))
+		return userfunc(sess, NULL, conn);
+#endif
+	return -1;
+}
+
+static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct aim_fileheader_t *fh;
+	struct command_tx_struct *newoft;
+	int i = 0;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		g_free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+	memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t));
+	g_free(fh);
+
+	aim_cachecookie(sess, cook);
+
+	faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name);
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) )
+		i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie);
+
+	if (i < 0)
+		return i;
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) {
+		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+		return -1;
+	}
+
+	newoft->lock = 1;
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	} 
+
+	/* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */
+	ft->fh.nrecvd = 0;
+	ft->fh.recvcsum = 0;
+	ft->fh.flags = 0x20;
+
+	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+
+	faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n");
+
+	return i;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	struct command_tx_struct *newoft;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		g_free(fh);
+		return -1;
+	}
+
+	g_free(fh);
+
+	ft = cook->data;
+
+	ft->state = 3;
+
+	if (aim_cachecookie(sess, cook) == -1)
+		return -1;
+
+	faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name);
+
+	if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+		faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n");
+		return -1;
+	}
+
+	newoft->lock = 1;
+	memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+
+	newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+	if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) {
+		aim_frame_destroy(newoft);
+		return -1;
+	}
+
+	aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh));
+
+	newoft->lock = 0;
+	aim_tx_enqueue(sess, newoft);
+
+	faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL)
+		return 1;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	struct aim_filetransfer_priv *ft;
+	struct aim_msgcookie_t *cook;
+	int ret = 1;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) {
+		g_free(fh);
+		return -1;
+	}
+
+	ft = cook->data;
+
+	faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) )
+		ret = userfunc(sess, NULL, conn, fh);
+
+	g_free(fh);
+
+	return ret;
+#else
+	return -1;
+#endif
+}
+
+static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr)
+{
+#if 0
+	struct aim_fileheader_t *fh;
+	aim_rxcallback_t userfunc;
+
+	fh = aim_oft_getfh(hdr);
+
+	faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n");
+
+	if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) )
+		userfunc(sess, NULL, conn, fh);
+
+	g_free(fh);
+#endif
+
+	return -1;
+}
+
+/**
+ * aim_get_command_rendezvous - OFT equivalent of aim_get_command
+ * @sess: session to work on
+ * @conn: conn to pull data from 
+ *
+ * this reads and handles data from conn->fd. currently a little rough
+ * around the edges
+ */
+int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn)
+{
+	guint8 hdrbuf1[6];
+	guint8 *hdr = NULL;
+	int hdrlen, hdrtype;
+	int ret = -1;
+
+	if (!sess || !conn)
+		return -1;
+
+	memset(hdrbuf1, 0, sizeof(hdrbuf1));
+
+	/* I guess? I didn't understand any of that mess... */
+	if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE)
+		return getcommand_getfile(sess, conn);
+
+	/* XXX fix all the error cases here */
+	if (aim_recv(conn->fd, hdrbuf1, 6) < 6) {
+
+		do_error_dialog(sess->aux_data, "read error", "Gaim");
+
+		aim_conn_close(conn);
+
+		return -1;
+	}
+
+	hdrlen = aimutil_get16(hdrbuf1+4);
+	hdrlen -= 6;
+
+	hdr = g_malloc(hdrlen);
+
+	if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) {
+		do_error_dialog(sess->aux_data, "read error", "Gaim");
+		g_free(hdr);
+		aim_conn_close(conn);
+		return -1;
+	}
+
+	hdrtype = aimutil_get16(hdr);
+
+	if (hdrtype == 0x0001)
+		ret = handlehdr_directim(sess, conn, hdr);
+	else if (hdrtype == 0x1108) /* getfile listing.txt incoming tx->rx */
+		ret = handlehdr_getfile_listing(sess, conn, hdr);
+	else if (hdrtype == 0x1209) /* get file listing ack rx->tx */
+		ret = handlehdr_getfile_listing2(sess, conn, hdr);
+	else if (hdrtype == 0x120b) /* get file listing rx confirm */
+		ret = handlehdr_getfile_listing3(sess, conn, hdr);
+	else if (hdrtype == 0x120c) /* getfile request */
+		ret = handlehdr_getfile_request(sess, conn, hdr);
+	else if (hdrtype == 0x0101) /* getfile sending data */
+		ret = handlehdr_getfile_sending(sess, conn, hdr);
+	else if (hdrtype == 0x0202) /* getfile recv data */
+		ret = handlehdr_getfile_recv(sess, conn, hdr);
+	else if (hdrtype == 0x0204) /* getfile finished */
+		ret = handlehdr_getfile_finish(sess, conn, hdr);
+	else {
+		ret = -1;
+	}
+	
+	g_free(hdr);
+
+	if (ret == -1)
+		aim_conn_close(conn);
+
+	return ret;
+}
+
+#if 0
+/**
+ * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr.
+ * @hdr: buffer to extract header from  
+ *
+ * returns pointer to new struct on success; %NULL on error.  
+ *
+ */
+static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) 
+{
+  struct aim_fileheader_t *fh;
+  int i, j;
+  if (!(fh = calloc(1, sizeof(struct aim_fileheader_t))))
+    return NULL;
+  
+  /* [0] and [1] are the type. we can ignore those here. */
+  i = 2;
+  for(j = 0; j < 8; j++, i++)
+    fh->bcookie[j] = hdr[i];
+  fh->encrypt = aimutil_get16(hdr+i);
+  i += 2;
+  fh->compress = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totfiles = aimutil_get16(hdr+i);
+  i += 2;
+  fh->filesleft = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totparts = aimutil_get16(hdr+i);
+  i += 2;
+  fh->partsleft = aimutil_get16(hdr+i);
+  i += 2;
+  fh->totsize = aimutil_get32(hdr+i);
+  i += 4;
+  fh->size = aimutil_get32(hdr+i);
+  i += 4;
+  fh->modtime = aimutil_get32(hdr+i);
+  i += 4;
+  fh->checksum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfrcsum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfsize = aimutil_get32(hdr+i);
+  i += 4;
+  fh->cretime = aimutil_get32(hdr+i);
+  i += 4;
+  fh->rfcsum = aimutil_get32(hdr+i);
+  i += 4;
+  fh->nrecvd = aimutil_get32(hdr+i);
+  i += 4;
+  fh->recvcsum = aimutil_get32(hdr+i);
+  i += 4;
+  memcpy(fh->idstring, hdr+i, 32);
+  i += 32;
+  fh->flags = aimutil_get8(hdr+i);
+  i += 1;
+  fh->lnameoffset = aimutil_get8(hdr+i);
+  i += 1;
+  fh->lsizeoffset = aimutil_get8(hdr+i);
+  i += 1;
+  memcpy(fh->dummy, hdr+i, 69);
+  i += 69;
+  memcpy(fh->macfileinfo, hdr+i, 16);
+  i += 16;
+  fh->nencode = aimutil_get16(hdr+i);
+  i += 2;
+  fh->nlanguage = aimutil_get16(hdr+i);
+  i += 2;
+  memcpy(fh->name, hdr+i, 64);
+  i += 64;
+  return fh;
+} 
+#endif
+
+/**
+ * aim_oft_checksum - calculate oft checksum of buffer
+ * @buffer: buffer of data to checksum
+ * @bufsize: size of buffer
+ * @checksum: pointer to integer to place result in (pointer!) 
+ *
+ *
+ * Note that checksum is a pointer. Checksum should be filled with
+ * 0xFFFF0000 for each new file; you can have this checksum chunks of
+ * files in series if you just call it repeatedly in a for(; ; ) loop
+ * and don't reset the checksum between each call. And you thought we
+ * didn't care about you and your pathetic client's meomry footprint
+ * ;^) 
+ *
+ *
+ * Also, it's been said that this is incorrect as currently
+ * written. You were warned.
+ */
+guint32 aim_oft_checksum(aim_session_t *sess, const char *buffer, int bufsize, guint32 *checksum)
+{
+	return 0xdeadbeef;
+#if 0
+  guint16 check0, check1;
+  int i;
+
+  check0 = ((*checksum & 0xFF000000) >> 16);
+  check1 = ((*checksum & 0x00ff0000) >> 16);
+  for(i = 0; i < bufsize; i++) {
+    if (i % 2) { /* use check1 -- second byte */
+      if ( (short)buffer[i] > check1 ) { /* wrapping */
+	check1 += 0x100;  /* this is a cheap way to wrap */
+
+	/* if we're wrapping, decrement the other one */
+	/* XXX: check this corner case */
+	if (check0 == 0) 
+	  check0 = 0x00ff;
+	else 
+	  check0--;
+      } 
+      check1 -= buffer[i];
+    } else { /* use check0 -- first byte  */
+      if ( (short)buffer[i] > check0 ) { /* wrapping */
+	check0 += 0x100;       /* this is a cheap way to wrap */
+  
+	/* if we're wrapping, decrement the other one */
+	/* XXX: check this corner case */
+	if (check1 == 0) 
+	  check1 = 0x00ff;
+	else 
+	  check1--;
+      } 
+      check0 -= buffer[i];
+    } 
+  }
+
+  if (check0 > 0xff || check1 > 0xff)  { 
+    /* they shouldn't be able to do this. error! */
+    faimdprintf(sess, 2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
+    return -1;
+  } 
+
+  /* grab just the lowest byte; this should be clean, but just in
+     case */
+  check0 &= 0xff;
+  check1 &= 0xff;
+
+  *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
+  return *checksum;
+#endif
+} 
+
+#if 0
+/**
+ * aim_oft_buildheader - fills a buffer with network-order fh data
+ * @dest: buffer to fill -- pre-alloced
+ * @fh: fh to get data from  
+ *
+ * returns length written; -1 on error.
+ * DOES NOT DO BOUNDS CHECKING!
+ *
+ */
+static int oft_buildheader(unsigned char *dest, struct aim_fileheader_t *fh) 
+{ 
+  int i, curbyte;
+  if (!dest || !fh)
+    return -1;
+  curbyte = 0;
+  for(i = 0; i < 8; i++) 
+    curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]);
+  curbyte += aimutil_put16(dest+curbyte, fh->encrypt);
+  curbyte += aimutil_put16(dest+curbyte, fh->compress);
+  curbyte += aimutil_put16(dest+curbyte, fh->totfiles);
+  curbyte += aimutil_put16(dest+curbyte, fh->filesleft);
+  curbyte += aimutil_put16(dest+curbyte, fh->totparts);
+  curbyte += aimutil_put16(dest+curbyte, fh->partsleft);
+  curbyte += aimutil_put32(dest+curbyte, fh->totsize);
+  curbyte += aimutil_put32(dest+curbyte, fh->size);
+  curbyte += aimutil_put32(dest+curbyte, fh->modtime);
+  curbyte += aimutil_put32(dest+curbyte, fh->checksum);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfsize);
+  curbyte += aimutil_put32(dest+curbyte, fh->cretime);
+  curbyte += aimutil_put32(dest+curbyte, fh->rfcsum);
+  curbyte += aimutil_put32(dest+curbyte, fh->nrecvd);
+  curbyte += aimutil_put32(dest+curbyte, fh->recvcsum);
+  memcpy(dest+curbyte, fh->idstring, 32);
+  curbyte += 32;
+  curbyte += aimutil_put8(dest+curbyte, fh->flags);
+  curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset);
+  curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset);
+  memcpy(dest+curbyte, fh->dummy, 69);
+  curbyte += 69;
+  memcpy(dest+curbyte, fh->macfileinfo, 16);
+  curbyte += 16;
+  curbyte += aimutil_put16(dest+curbyte, fh->nencode);
+  curbyte += aimutil_put16(dest+curbyte, fh->nlanguage);
+  memset(dest+curbyte, 0x00, 64);
+  memcpy(dest+curbyte, fh->name, 64);
+
+  /* XXX: Filenames longer than 64B  */
+  curbyte += 64;
+  return curbyte;
+}
+#endif
+
+/**
+ * aim_getfile_intitiate - Request an OFT getfile session
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn is the SN to connect to.
+ * 
+ * returns a new &aim_conn_t on success, %NULL on error
+ */
+aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn)
+{ 
+	return NULL;
+#if 0
+  struct command_tx_struct *newpacket;
+  struct aim_conn_t *newconn;
+  struct aim_filetransfer_priv *priv;
+  struct aim_msgcookie_t *cookie;
+  int curbyte, i, listenfd;
+  short port = 4443;
+  struct hostent *hptr;
+  struct utsname myname;
+  char cap[16];
+  char d[4];
+ 
+  /* Open our socket */
+
+  if ( (listenfd = aim_listenestablish(port)) == -1)
+    return NULL;
+
+  /* get our local IP */
+
+  if (uname(&myname) < 0)
+    return NULL;
+  if ( (hptr = gethostbyname(myname.nodename)) == NULL)
+    return NULL;
+  memcpy(&d, hptr->h_addr_list[0], 4);
+
+  aim_putcap(cap, 16, AIM_CAPS_GETFILE);
+
+  /* create the OSCAR packet */
+
+  if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42)))
+    return NULL;
+  newpacket->lock = 1;
+
+  /* lock struct */
+  curbyte = 0;
+  curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+  /* XXX: check the cookie before commiting to using it */
+
+  /* Generate a random message cookie
+   * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
+  for (i=0; i<7; i++) 
+    curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10));
+
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* grab all the data for cookie caching. */
+ 
+  if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t))))
+    return NULL;
+  memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+  cookie->type = AIM_COOKIETYPE_OFTGET;
+
+  if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv))))
+    return NULL;
+  memcpy(priv->cookie, cookie, 8);
+  memcpy(priv->sn, destsn, sizeof(priv->sn));
+  memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
+  priv->state = 1;
+
+  cookie->data = priv;
+
+  aim_cachecookie(sess, cookie);
+
+  /* Channel ID */
+  curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+
+  /* Destination SN (prepended with byte length) */
+  curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+  curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /* enTLV start */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042);
+
+  /* Flag data / ICBM Parameters? */
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+  curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  /* Cookie */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
+
+  /* Capability String */
+  curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+
+  /* 000a/0002 : 0001 */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+  /* 0003/0004: IP address */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+  for(i = 0; i < 4; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
+
+  /* already in network byte order  */
+ 
+  /* 0005/0002: Port */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+  curbyte += aimutil_put16(newpacket->data+curbyte, port);
+
+  /* 000f/0000: ?? */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+  /* 2711/000c: ?? */
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+  curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
+  curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
+
+  for(i = 0; i < 0x000c - 4; i++)
+    curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+  newpacket->commandlen = curbyte;
+  newpacket->lock = 0;
+  aim_tx_enqueue(sess, newpacket);
+
+  /* allocate and set up our connection */
+
+  i = fcntl(listenfd, F_GETFL, 0);
+  fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
+  newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
+
+  if (!newconn){ 
+    perror("aim_newconn");
+    return NULL;
+  }
+
+  newconn->fd = listenfd;
+  newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+  newconn->priv = priv;
+
+  return newconn;
+#endif
+}
+ 
+/**
+ * aim_oft_getfile_request - request a particular file over an established getfile connection
+ * @sess: your session
+ * @conn: the established OFT getfile connection
+ * @name: filename to request
+ * @size: size of the file 
+ *
+ *
+ * returns -1 on error, 0 on successful enqueuing
+ */
+int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size)
+{
+	return -EINVAL;
+#if 0
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+  if (!sess || !conn || !conn->priv || !name)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  }
+
+  newoft->lock = 1;
+
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+  ft = (struct aim_filetransfer_priv *)conn->priv;
+  ft->fh.filesleft = 1;
+  ft->fh.totfiles = 1;
+  ft->fh.totparts = 1;
+  ft->fh.partsleft = 1;
+  ft->fh.totsize = size;
+  ft->fh.size = size;
+  ft->fh.checksum = 0;
+  memcpy(ft->fh.name, name, strlen(name));
+  memset(ft->fh.name+strlen(name), 0, 1);
+
+  if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+    newoft->lock = 0;
+    aim_frame_destroy(newoft);
+    return -1;
+  }
+
+  if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) {
+    newoft->lock = 0;
+    aim_frame_destroy(newoft);
+    return -1;
+  }
+
+  newoft->lock = 0;
+
+  aim_tx_enqueue(sess, newoft);
+  return 0;
+#endif
+}
+ 
+/**
+ * aim_oft_getfile_ack - acknowledge a getfile download as complete
+ * @sess: your session
+ * @conn: the getfile conn to send the ack over 
+ *
+ * Call this function after you have read all the data in a particular
+ * filetransfer. Returns -1 on error, 0 on apparent success
+ *
+ */
+int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn) 
+{
+	return -EINVAL;
+#if 0
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+
+  if (!sess || !conn || !conn->priv)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  } 
+
+  newoft->lock = 1;
+
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100-8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { 
+   newoft->lock = 0;
+   aim_frame_destroy(newoft);
+   return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+
+ if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+   newoft->lock = 0;
+   aim_frame_destroy(newoft);
+   return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+#endif
+}
+ 
+/**
+ * aim_oft_getfile_end - end a getfile.
+ * @sess: your session
+ * @conn: the getfile connection 
+ *
+ * call this before you close the getfile connection if you're on the
+ * receiving/requesting end.
+ */
+int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn)
+{
+	return -EINVAL;
+#if 0
+  struct command_tx_struct *newoft;
+  struct aim_filetransfer_priv *ft;
+  
+  if (!sess || !conn || !conn->priv)
+    return -1;
+
+  if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0204, 0))) {
+    faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+    return -1;
+  }
+  
+  newoft->lock = 1;
+  
+  memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+  newoft->hdr.oft.hdr2len = 0x100 - 8;
+  
+  if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+    newoft->lock = 0;
+    aim_frame_destroy(newoft);
+    return -1;
+  }
+  
+  ft = (struct aim_filetransfer_priv *)conn->priv;
+  ft->state = 4; /* no longer wanting data */
+  ft->fh.nrecvd = ft->fh.size;
+  ft->fh.recvcsum = ft->fh.checksum;
+  ft->fh.flags = 0x21;
+  
+  if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+    newoft->lock = 0;
+    aim_frame_destroy(newoft);
+    return -1;
+  }
+  
+  newoft->lock = 0;
+  aim_tx_enqueue(sess, newoft);
+  
+  return 0;
+#endif /* 0 */
+}
+
Index: protocols/oscar/ft.h
===================================================================
--- protocols/oscar/ft.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ft.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,78 @@
+#ifndef __OSCAR_FT_H__
+#define __OSCAR_FT_H__
+
+#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
+
+/*
+ * OFT Services
+ *
+ * See non-SNAC note below.
+ */
+#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002
+#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003
+#define AIM_CB_OFT_DIRECTIMTYPING 0x0004
+#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005
+
+#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/
+#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */
+#define AIM_CB_OFT_GETFILEFILEREQ 0x0008    /* received file request */
+#define AIM_CB_OFT_GETFILEFILESEND 0x0009   /* received file request confirm -- send data */
+#define AIM_CB_OFT_GETFILECOMPLETE 0x000a   /* received file send complete*/
+#define AIM_CB_OFT_GETFILEINITIATE 0x000b   /* request for file get acknowledge */
+#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c   /* OFT connection disconnected.*/
+#define AIM_CB_OFT_GETFILELISTING 0x000d   /* OFT listing.txt received.*/
+#define AIM_CB_OFT_GETFILERECEIVE 0x000e   /* OFT file incoming.*/
+#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f
+#define AIM_CB_OFT_GETFILESTATE4 0x0010
+
+#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020   /* OFT connection disconnected.*/
+
+struct aim_fileheader_t {
+#if 0
+	char  magic[4];		/* 0 */
+	short hdrlen; 		/* 4 */
+	short hdrtype;		/* 6 */
+#endif
+	char  bcookie[8];       /* 8 */
+	short encrypt;          /* 16 */
+	short compress;         /* 18 */
+	short totfiles;         /* 20 */
+	short filesleft;        /* 22 */
+	short totparts;         /* 24 */
+	short partsleft;        /* 26 */
+	long  totsize;          /* 28 */
+	long  size;             /* 32 */
+	long  modtime;          /* 36 */
+	long  checksum;         /* 40 */
+	long  rfrcsum;          /* 44 */
+	long  rfsize;           /* 48 */
+	long  cretime;          /* 52 */
+	long  rfcsum;           /* 56 */
+	long  nrecvd;           /* 60 */
+	long  recvcsum;         /* 64 */
+	char  idstring[32];     /* 68 */
+	char  flags;            /* 100 */
+	char  lnameoffset;      /* 101 */
+	char  lsizeoffset;      /* 102 */
+	char  dummy[69];        /* 103 */
+	char  macfileinfo[16];  /* 172 */
+	short nencode;          /* 188 */
+	short nlanguage;        /* 190 */
+	char  name[64];         /* 192 */
+				/* 256 */
+};
+
+
+
+struct aim_filetransfer_priv {
+	char sn[MAXSNLEN];
+	guint8 cookie[8];
+	char ip[30];
+	int state;
+	struct aim_fileheader_t fh;
+};
+
+#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */
+
+#endif /* __OSCAR_FT_H__ */
Index: protocols/oscar/icq.c
===================================================================
--- protocols/oscar/icq.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/icq.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,441 @@
+/*
+ * Encapsulated ICQ.
+ *
+ */
+
+#include <aim.h>
+#include "icq.h"
+
+int aim_icq_reqofflinemsgs(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x003c); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_icq_ackofflinemsgs(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x003e); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!xml || !strlen(xml))
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 10 + 2 + strlen(xml) + 1;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x0998); /* shrug. */
+	aimbs_putle16(&fr->data, strlen(xml) + 1);
+	aimbs_putraw(&fr->data, (guint8 *)xml, strlen(xml) + 1);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_icq_getallinfo(aim_session_t *sess, const char *uin)
+{
+        aim_conn_t *conn;
+        aim_frame_t *fr;
+        aim_snacid_t snacid;
+        int bslen;
+        struct aim_icq_info *info;
+
+        if (!uin || uin[0] < '0' || uin[0] > '9')
+                return -EINVAL;
+
+        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+                return -EINVAL;
+
+        bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+                return -ENOMEM;
+
+        snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+        aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+        /* For simplicity, don't bother using a tlvlist */
+        aimbs_put16(&fr->data, 0x0001);
+        aimbs_put16(&fr->data, bslen);
+
+        aimbs_putle16(&fr->data, bslen - 2);
+        aimbs_putle32(&fr->data, atoi(sess->sn));
+        aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+        aimbs_putle16(&fr->data, snacid); /* eh. */
+        aimbs_putle16(&fr->data, 0x04b2); /* shrug. */
+        aimbs_putle32(&fr->data, atoi(uin));
+
+        aim_tx_enqueue(sess, fr);
+
+        /* Keep track of this request and the ICQ number and request ID */
+        info = g_new0(struct aim_icq_info, 1);
+        info->reqid = snacid;
+        info->uin = atoi(uin);
+        info->next = sess->icq_info;
+        sess->icq_info = info;
+
+        return 0;
+}
+
+int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int bslen;
+
+	if (!uin || uin[0] < '0' || uin[0] > '9')
+		return -EINVAL;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015)))
+		return -EINVAL;
+
+	bslen = 2 + 4 + 2 + 2 + 2 + 4;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid);
+
+	/* For simplicity, don't bother using a tlvlist */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, bslen);
+
+	aimbs_putle16(&fr->data, bslen - 2);
+	aimbs_putle32(&fr->data, atoi(sess->sn));
+	aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */
+	aimbs_putle16(&fr->data, snacid); /* eh. */
+	aimbs_putle16(&fr->data, 0x051f); /* shrug. */
+	aimbs_putle32(&fr->data, atoi(uin));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static void aim_icq_freeinfo(struct aim_icq_info *info) {
+        int i;
+
+        if (!info)
+                return;
+        g_free(info->nick);
+        g_free(info->first);
+        g_free(info->last);
+        g_free(info->email);
+        g_free(info->homecity);
+        g_free(info->homestate);
+        g_free(info->homephone);
+        g_free(info->homefax);
+        g_free(info->homeaddr);
+        g_free(info->mobile);
+        g_free(info->homezip);
+        g_free(info->personalwebpage);
+        if (info->email2)
+                for (i = 0; i < info->numaddresses; i++)
+                        g_free(info->email2[i]);
+        g_free(info->email2);
+        g_free(info->workcity);
+        g_free(info->workstate);
+        g_free(info->workphone);
+        g_free(info->workfax);
+        g_free(info->workaddr);
+        g_free(info->workzip);
+        g_free(info->workcompany);
+        g_free(info->workdivision);
+        g_free(info->workposition);
+        g_free(info->workwebpage);
+        g_free(info->info);
+        g_free(info);
+}
+
+/**
+ * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet.
+ */
+static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_tlvlist_t *tl;
+	aim_tlv_t *datatlv;
+	aim_bstream_t qbs;
+	guint32 ouruin;
+	guint16 cmdlen, cmd, reqid;
+
+	if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) {
+		aim_freetlvchain(&tl);
+		do_error_dialog(sess->aux_data, "corrupt ICQ response\n", "Gaim");
+		return 0;
+	}
+
+	aim_bstream_init(&qbs, datatlv->value, datatlv->length);
+
+	cmdlen = aimbs_getle16(&qbs);
+	ouruin = aimbs_getle32(&qbs);
+	cmd = aimbs_getle16(&qbs);
+	reqid = aimbs_getle16(&qbs);
+
+	if (cmd == 0x0041) { /* offline message */
+		guint16 msglen;
+		struct aim_icq_offlinemsg msg;
+		aim_rxcallback_t userfunc;
+
+		memset(&msg, 0, sizeof(msg));
+
+		msg.sender = aimbs_getle32(&qbs);
+		msg.year = aimbs_getle16(&qbs);
+		msg.month = aimbs_getle8(&qbs);
+		msg.day = aimbs_getle8(&qbs);
+		msg.hour = aimbs_getle8(&qbs);
+		msg.minute = aimbs_getle8(&qbs);
+		msg.type = aimbs_getle16(&qbs);
+		msglen = aimbs_getle16(&qbs);
+		msg.msg = aimbs_getstr(&qbs, msglen);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG)))
+			ret = userfunc(sess, rx, &msg);
+
+		g_free(msg.msg);
+
+	} else if (cmd == 0x0042) {
+		aim_rxcallback_t userfunc;
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE)))
+			ret = userfunc(sess, rx);
+	} else if (cmd == 0x07da) { /* information */
+		guint16 subtype;
+		struct aim_icq_info *info;
+		aim_rxcallback_t userfunc;
+
+		subtype = aimbs_getle16(&qbs);
+		aim_bstream_advance(&qbs, 1); /* 0x0a */
+
+		/* find another data from the same request */
+		for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next);
+
+		if (!info) {
+			info = g_new0(struct aim_icq_info, 1);
+			info->reqid = reqid;
+			info->next = sess->icq_info;
+			sess->icq_info = info;
+		}
+
+		switch (subtype) {
+			case 0x00a0: { /* hide ip status */
+							 /* nothing */
+						 } break;
+			case 0x00aa: { /* password change status */
+							 /* nothing */
+						 } break;
+			case 0x00c8: { /* general and "home" information */
+							 info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->homecountry = aimbs_getle16(&qbs);
+							 /* 0x0a 00 02 00 */
+							 /* 1 byte timezone? */
+							 /* 1 byte hide email flag? */
+						 } break;
+			case 0x00dc: { /* personal information */
+							 info->age = aimbs_getle8(&qbs);
+							 info->unknown = aimbs_getle8(&qbs);
+							 info->gender = aimbs_getle8(&qbs);
+							 info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->birthyear = aimbs_getle16(&qbs);
+							 info->birthmonth = aimbs_getle8(&qbs);
+							 info->birthday = aimbs_getle8(&qbs);
+							 info->language1 = aimbs_getle8(&qbs);
+							 info->language2 = aimbs_getle8(&qbs);
+							 info->language3 = aimbs_getle8(&qbs);
+							 /* 0x00 00 01 00 00 01 00 00 00 00 00 */
+						 } break;
+			case 0x00d2: { /* work information */
+							 info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workcountry = aimbs_getle16(&qbs);
+							 info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 aim_bstream_advance(&qbs, 2); /* 0x01 00 */
+							 info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+						 } break;
+			case 0x00e6: { /* additional personal information */
+							 info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1);
+						 } break;
+			case 0x00eb: { /* email address(es) */
+							 int i;
+							 info->numaddresses = aimbs_getle16(&qbs);
+							 info->email2 = g_new0(char *, info->numaddresses);
+							 for (i = 0; i < info->numaddresses; i++) {
+								 info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+								 if (i+1 != info->numaddresses)
+									 aim_bstream_advance(&qbs, 1); /* 0x00 */
+							 }
+						 } break;
+			case 0x00f0: { /* personal interests */
+						 } break;
+			case 0x00fa: { /* past background and current organizations */
+						 } break;
+			case 0x0104: { /* alias info */
+							 info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 aim_bstream_advance(&qbs, aimbs_getle16(&qbs));
+							 /* email address? */
+							 /* Then 0x00 02 00 */
+						 } break;
+			case 0x010e: { /* unknown */
+							 /* 0x00 00 */
+						 } break;
+
+			case 0x019a: { /* simple info */
+							 aim_bstream_advance(&qbs, 2);
+							 info->uin = aimbs_getle32(&qbs);
+							 info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs));
+							 /* Then 0x00 02 00 00 00 00 00 */
+						 } break;
+		} /* End switch statement */
+
+
+		if (!(snac->flags & 0x0001)) {
+			if (subtype != 0x0104)
+				if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO)))
+					ret = userfunc(sess, rx, info);
+
+			/* Bitlbee - not supported, yet 
+			if (info->uin && info->nick)
+				if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS)))
+					ret = userfunc(sess, rx, info);
+			*/
+
+			if (sess->icq_info == info) {
+				sess->icq_info = info->next;
+			} else {
+				struct aim_icq_info *cur;
+				for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
+				if (cur->next)
+					cur->next = cur->next->next;
+			}
+			aim_icq_freeinfo(info);
+		}
+	}
+
+	aim_freetlvchain(&tl);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return icqresponse(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int icq_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0015;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x047c;
+	mod->flags = 0;
+	strncpy(mod->name, "icq", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+
Index: protocols/oscar/icq.h
===================================================================
--- protocols/oscar/icq.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/icq.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,98 @@
+#ifndef __OSCAR_ICQ_H__
+#define __OSCAR_ICQ_H__
+
+#define AIM_CB_FAM_ICQ 0x0015
+
+/*
+ * SNAC Family: ICQ
+ *
+ * Most of these are actually special.
+ */ 
+#define AIM_CB_ICQ_ERROR 0x0001
+#define AIM_CB_ICQ_OFFLINEMSG 0x00f0
+#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1
+#define AIM_CB_ICQ_SIMPLEINFO 0x00f2
+#define AIM_CB_ICQ_INFO 0x00f2 /* just transitional */
+#define AIM_CB_ICQ_DEFAULT 0xffff
+
+struct aim_icq_offlinemsg {
+	guint32 sender;
+	guint16 year;
+	guint8 month, day, hour, minute;
+	guint16 type;
+	char *msg;
+};
+
+struct aim_icq_simpleinfo {
+	guint32 uin;
+	char *nick;
+	char *first;
+	char *last;
+	char *email;
+};
+
+struct aim_icq_info {
+        gushort reqid;
+
+        /* simple */
+        guint32 uin;
+
+        /* general and "home" information (0x00c8) */
+        char *nick;
+        char *first;
+        char *last;
+        char *email;
+        char *homecity;
+        char *homestate;
+        char *homephone;
+        char *homefax;
+        char *homeaddr;
+        char *mobile;
+        char *homezip;
+        gushort homecountry;
+/*      guchar timezone;
+        guchar hideemail; */
+
+        /* personal (0x00dc) */
+        guchar age;
+        guchar unknown;
+        guchar gender;
+        char *personalwebpage;
+        gushort birthyear;
+        guchar birthmonth;
+        guchar birthday;
+        guchar language1;
+        guchar language2;
+        guchar language3;
+
+        /* work (0x00d2) */
+        char *workcity;
+        char *workstate;
+        char *workphone;
+        char *workfax;
+        char *workaddr;
+        char *workzip;
+        gushort workcountry;
+        char *workcompany;
+        char *workdivision;
+        char *workposition;
+        char *workwebpage;
+
+        /* additional personal information (0x00e6) */
+        char *info;
+
+        /* email (0x00eb) */
+        gushort numaddresses;
+        char **email2;
+
+        /* we keep track of these in a linked list because we're 1337 */
+        struct aim_icq_info *next;
+};
+
+
+int aim_icq_reqofflinemsgs(aim_session_t *sess);
+int aim_icq_ackofflinemsgs(aim_session_t *sess);
+int aim_icq_getallinfo(aim_session_t *sess, const char *uin);
+int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin);
+
+#endif /* __OSCAR_ICQ_H__ */
Index: protocols/oscar/im.c
===================================================================
--- protocols/oscar/im.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/im.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,2029 @@
+/*
+ *  aim_im.c
+ *
+ *  The routines for sending/receiving Instant Messages.
+ *
+ *  Note the term ICBM (Inter-Client Basic Message) which blankets
+ *  all types of genericly routed through-server messages.  Within
+ *  the ICBM types (family 4), a channel is defined.  Each channel
+ *  represents a different type of message.  Channel 1 is used for
+ *  what would commonly be called an "instant message".  Channel 2
+ *  is used for negotiating "rendezvous".  These transactions end in
+ *  something more complex happening, such as a chat invitation, or
+ *  a file transfer.
+ *
+ *  In addition to the channel, every ICBM contains a cookie.  For
+ *  standard IMs, these are only used for error messages.  However,
+ *  the more complex rendezvous messages make suitably more complex
+ *  use of this field.
+ *
+ */
+
+#include <aim.h>
+#include "im.h"
+#include "info.h"
+
+/*
+ * Takes a msghdr (and a length) and returns a client type
+ * code.  Note that this is *only a guess* and has a low likelihood
+ * of actually being accurate.
+ *
+ * Its based on experimental data, with the help of Eric Warmenhoven
+ * who seems to have collected a wide variety of different AIM clients.
+ *
+ *
+ * Heres the current collection:
+ *  0501 0003 0101 0101 01       AOL Mobile Communicator, WinAIM 1.0.414
+ *  0501 0003 0101 0201 01       WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
+ *                                      4.3.2229, 4.4.2286
+ *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
+ *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
+ *                                      TOC client
+ *
+ * Note that in this function, only the feature bytes are tested, since
+ * the rest will always be the same.
+ *
+ */
+guint16 aim_fingerprintclient(guint8 *msghdr, int len)
+{
+	static const struct {
+		guint16 clientid;
+		int len;
+		guint8 data[10];
+	} fingerprints[] = {
+		/* AOL Mobile Communicator, WinAIM 1.0.414 */
+		{ AIM_CLIENTTYPE_MC, 
+		  3, {0x01, 0x01, 0x01}},
+
+		/* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
+		{ AIM_CLIENTTYPE_WINAIM, 
+		  3, {0x01, 0x01, 0x02}},
+
+		/* WinAIM 4.1.2010, libfaim */
+		{ AIM_CLIENTTYPE_WINAIM41,
+		  4, {0x01, 0x01, 0x01, 0x02}},
+
+		/* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
+		{ AIM_CLIENTTYPE_AOL_TOC,
+		  1, {0x01}},
+
+		{ 0, 0}
+	};
+	int i;
+
+	if (!msghdr || (len <= 0))
+		return AIM_CLIENTTYPE_UNKNOWN;
+
+	for (i = 0; fingerprints[i].len; i++) {
+		if (fingerprints[i].len != len)
+			continue;
+		if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
+			return fingerprints[i].clientid;
+	}
+
+	return AIM_CLIENTTYPE_UNKNOWN;
+}
+
+/* This should be endian-safe now... but who knows... */
+guint16 aim_iconsum(const guint8 *buf, int buflen)
+{
+	guint32 sum;
+	int i;
+
+	for (i = 0, sum = 0; i + 1 < buflen; i += 2)
+		sum += (buf[i+1] << 8) + buf[i];
+	if (i < buflen)
+		sum += buf[i];
+
+	sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
+
+	return (guint16)sum;
+}
+
+/*
+ * Send an ICBM (instant message).  
+ *
+ *
+ * Possible flags:
+ *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
+ *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
+ *                        when the message is received (of type 0x0004/0x000c)
+ *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
+ *                        online (probably ICQ only).
+ *   AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is
+ *                        made up of UNICODE duples.  If you set
+ *                        this, you'd better be damn sure you know
+ *                        what you're doing.
+ *   AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset
+ *                        known as ISO-8859-1.  
+ *
+ * Generally, you should use the lowest encoding possible to send
+ * your message.  If you only use basic punctuation and the generic
+ * Latin alphabet, use ASCII7 (no flags).  If you happen to use non-ASCII7
+ * characters, but they are all clearly defined in ISO-8859-1, then 
+ * use that.  Keep in mind that not all characters in the PC ASCII8
+ * character set are defined in the ISO standard. For those cases (most
+ * notably when the (r) symbol is used), you must use the full UNICODE
+ * encoding for your message.  In UNICODE mode, _all_ characters must
+ * occupy 16bits, including ones that are not special.  (Remember that
+ * the first 128 UNICODE symbols are equivelent to ASCII7, however they
+ * must be prefixed with a zero high order byte.)
+ *
+ * I strongly discourage the use of UNICODE mode, mainly because none
+ * of the clients I use can parse those messages (and besides that,
+ * wchars are difficult and non-portable to handle in most UNIX environments).
+ * If you really need to include special characters, use the HTML UNICODE 
+ * entities.  These are of the form &#2026; where 2026 is the hex 
+ * representation of the UNICODE index (in this case, UNICODE 
+ * "Horizontal Ellipsis", or 133 in in ASCII8).
+ *
+ * Implementation note:  Since this is one of the most-used functions
+ * in all of libfaim, it is written with performance in mind.  As such,
+ * it is not as clear as it could be in respect to how this message is
+ * supposed to be layed out. Most obviously, tlvlists should be used 
+ * instead of writing out the bytes manually. 
+ *
+ * XXX more precise verification that we never send SNACs larger than 8192
+ * XXX check SNAC size for multipart
+ *
+ */
+int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args)
+{
+	static const guint8 deffeatures[] = {
+		0x01, 0x01, 0x01, 0x02
+	};
+	aim_conn_t *conn;
+	int i, msgtlvlen;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!args)
+		return -EINVAL;
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		if (args->mpmsg->numparts <= 0)
+			return -EINVAL;
+	} else {
+		if (!args->msg || (args->msglen <= 0))
+			return -EINVAL;
+
+		if (args->msglen >= MAXMSGLEN)
+			return -E2BIG;
+	}
+
+	/* Painfully calculate the size of the message TLV */
+	msgtlvlen = 1 + 1; /* 0501 */
+
+	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
+		msgtlvlen += 2 + args->featureslen;
+	else
+		msgtlvlen += 2 + sizeof(deffeatures);
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		aim_mpmsg_section_t *sec;
+
+		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+			msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+			msgtlvlen += 4 /* charset */ + sec->datalen;
+		}
+
+	} else {
+		msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+		msgtlvlen += 4 /* charset */ + args->msglen;
+	}
+
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
+		return -ENOMEM;
+
+	/* XXX should be optional */	
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* 
+	 * Generate a random message cookie 
+	 *
+	 * We could cache these like we do SNAC IDs.  (In fact, it 
+	 * might be a good idea.)  In the message error functions, 
+	 * the 8byte message cookie is returned as well as the 
+	 * SNAC ID.
+	 *
+	 */
+	for (i = 0; i < 8; i++)
+		aimbs_put8(&fr->data, (guint8) rand());
+
+	/*
+	 * Channel ID
+	 */
+	aimbs_put16(&fr->data, 0x0001);
+
+	/*
+	 * Destination SN (prepended with byte length)
+	 */
+	aimbs_put8(&fr->data, strlen(args->destsn));
+	aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn));
+
+	/*
+	 * Message TLV (type 2).
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, msgtlvlen);
+
+	/*
+	 * Features 
+	 *
+	 */
+	aimbs_put8(&fr->data, 0x05);
+	aimbs_put8(&fr->data, 0x01);
+
+	if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
+		aimbs_put16(&fr->data, args->featureslen);
+		aimbs_putraw(&fr->data, args->features, args->featureslen);
+	} else {
+		aimbs_put16(&fr->data, sizeof(deffeatures));
+		aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
+	}
+
+	if (args->flags & AIM_IMFLAGS_MULTIPART) {
+		aim_mpmsg_section_t *sec;
+
+		for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+			aimbs_put16(&fr->data, 0x0101);
+			aimbs_put16(&fr->data, sec->datalen + 4);
+			aimbs_put16(&fr->data, sec->charset);
+			aimbs_put16(&fr->data, sec->charsubset);
+			aimbs_putraw(&fr->data, sec->data, sec->datalen);
+		}
+
+	} else {
+
+		aimbs_put16(&fr->data, 0x0101);
+
+		/* 
+		 * Message block length.
+		 */
+		aimbs_put16(&fr->data, args->msglen + 0x04);
+
+		/*
+		 * Character set.
+		 */
+		if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
+
+			aimbs_put16(&fr->data, args->charset);
+			aimbs_put16(&fr->data, args->charsubset);
+
+		} else {
+			if (args->flags & AIM_IMFLAGS_UNICODE)
+				aimbs_put16(&fr->data, 0x0002);
+			else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
+				aimbs_put16(&fr->data, 0x0003);
+			else
+				aimbs_put16(&fr->data, 0x0000);
+
+			aimbs_put16(&fr->data, 0x0000);
+		}
+
+		/*
+		 * Message.  Not terminated.
+		 */
+		aimbs_putraw(&fr->data, (guint8 *)args->msg, args->msglen);
+	}
+
+	/*
+	 * Set the Request Acknowledge flag.  
+	 */
+	if (args->flags & AIM_IMFLAGS_ACK) {
+		aimbs_put16(&fr->data, 0x0003);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	/*
+	 * Set the Autoresponse flag.
+	 */
+	if (args->flags & AIM_IMFLAGS_AWAY) {
+		aimbs_put16(&fr->data, 0x0004);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	if (args->flags & AIM_IMFLAGS_OFFLINE) {
+		aimbs_put16(&fr->data, 0x0006);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	/*
+	 * Set the I HAVE A REALLY PURTY ICON flag.
+	 */
+	if (args->flags & AIM_IMFLAGS_HASICON) {
+		aimbs_put16(&fr->data, 0x0008);
+		aimbs_put16(&fr->data, 0x000c);
+		aimbs_put32(&fr->data, args->iconlen);
+		aimbs_put16(&fr->data, 0x0001);
+		aimbs_put16(&fr->data, args->iconsum);
+		aimbs_put32(&fr->data, args->iconstamp);
+	}
+
+	/*
+	 * Set the Buddy Icon Requested flag.
+	 */
+	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
+		aimbs_put16(&fr->data, 0x0009);
+		aimbs_put16(&fr->data, 0x0000);
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM))
+		aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */
+
+	return 0;
+}
+
+/*
+ * Simple wrapper for aim_send_im_ext() 
+ *
+ * You cannot use aim_send_im if you need the HASICON flag.  You must
+ * use aim_send_im_ext directly for that.
+ *
+ * aim_send_im also cannot be used if you require UNICODE messages, because
+ * that requires an explicit message length.  Use aim_send_im_ext().
+ *
+ */
+int aim_send_im(aim_session_t *sess, const char *destsn, guint16 flags, const char *msg)
+{
+	struct aim_sendimext_args args;
+
+	args.destsn = destsn;
+	args.flags = flags;
+	args.msg = msg;
+	args.msglen = strlen(msg);
+
+	/* Make these don't get set by accident -- they need aim_send_im_ext */
+	args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
+
+	return aim_send_im_ext(sess, &args);
+}
+
+/*
+ * This is also performance sensitive. (If you can believe it...)
+ *
+ */
+int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
+{
+	aim_conn_t *conn;
+	int i;
+	guint8 ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
+		return -EINVAL;
+
+	for (i = 0; i < 8; i++)
+		aimutil_put8(ck+i, (guint8) rand());
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/*
+	 * Cookie
+	 */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/*
+	 * Channel (2)
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Encompasses everything below.
+	 */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, ck, 8);
+	aim_putcap(&fr->data, AIM_CAPS_BUDDYICON);
+
+	/* TLV t(000a) */
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+
+	/* TLV t(000f) */
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* TLV t(2711) */
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, iconsum);
+	aimbs_put32(&fr->data, iconlen);
+	aimbs_put32(&fr->data, stamp);
+	aimbs_putraw(&fr->data, icon, iconlen);
+	aimbs_putraw(&fr->data, (guint8 *)AIM_ICONIDENT, strlen(AIM_ICONIDENT));
+
+	/* TLV t(0003) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
+ * send it to clients advertising the RTF capability.  In fact, if you send
+ * it to a client that doesn't support that capability, the server will gladly
+ * bounce it back to you.
+ *
+ * You'd think this would be in icq.c, but, well, I'm trying to stick with
+ * the one-group-per-file scheme as much as possible.  This could easily
+ * be an exception, since Rendezvous IMs are external of the Oscar core, 
+ * and therefore are undefined.  Really I just need to think of a good way to
+ * make an interface similar to what AOL actually uses.  But I'm not using COM.
+ *
+ */
+int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
+{
+	const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
+	aim_conn_t *conn;
+	int i;
+	guint8 ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int servdatalen;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!args || !args->destsn || !args->rtfmsg)
+		return -EINVAL;
+
+	servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
+
+	for (i = 0; i < 8; i++)
+		aimutil_put8(ck+i, (guint8) rand());
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/*
+	 * Cookie
+	 */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/*
+	 * Channel (2)
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(args->destsn));
+	aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn));
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Encompasses everything below.
+	 */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, ck, 8);
+	aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+	/*
+	 * t(000a) l(0002) v(0001)
+	 */
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+
+	/*
+	 * t(000f) l(0000) v()
+	 */
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/*
+	 * Service Data TLV
+	 */
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, servdatalen);
+
+	aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */);
+	aimbs_putle16(&fr->data, 9);
+	aim_putcap(&fr->data, AIM_CAPS_EMPTY);
+	aimbs_putle16(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle8(&fr->data, 0);
+	aimbs_putle16(&fr->data, 0x03ea); /* trid1 */
+
+	aimbs_putle16(&fr->data, 14);
+	aimbs_putle16(&fr->data, 0x03eb); /* trid2 */
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle32(&fr->data, 0);
+
+	aimbs_putle16(&fr->data, 0x0001);
+	aimbs_putle32(&fr->data, 0);
+	aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1);
+	aimbs_putraw(&fr->data, (guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1);
+
+	aimbs_putle32(&fr->data, args->fgcolor);
+	aimbs_putle32(&fr->data, args->bgcolor);
+	aimbs_putle32(&fr->data, strlen(rtfcap)+1);
+	aimbs_putraw(&fr->data, (guint8 *)rtfcap, strlen(rtfcap)+1);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret)
+{
+	aim_conn_t *conn;
+	guint8 ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL, *itl = NULL;
+	int hdrlen, i;
+	guint8 *hdr;
+	aim_bstream_t hdrbs;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* 
+	 * Generate a random message cookie 
+	 *
+	 * This cookie needs to be alphanumeric and NULL-terminated to be 
+	 * TOC-compatible.
+	 *
+	 * XXX have I mentioned these should be generated in msgcookie.c?
+	 *
+	 */
+	for (i = 0; i < 7; i++)
+	       	ck[i] = 0x30 + ((guint8) rand() % 10);
+	ck[7] = '\0';
+
+	if (ckret)
+		memcpy(ckret, ck, 8);
+
+	/* Cookie */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/* Channel */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/* Destination SN */
+	aimbs_put8(&fr->data, strlen(destsn));
+	aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn));
+
+	aim_addtlvtochain_noval(&tl, 0x0003);
+
+	hdrlen = 2+8+16+6+8+6+4;
+	hdr = g_malloc(hdrlen);
+	aim_bstream_init(&hdrbs, hdr, hdrlen);
+
+	aimbs_put16(&hdrbs, 0x0000);
+	aimbs_putraw(&hdrbs, ck, 8);
+	aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE);
+
+	aim_addtlvtochain16(&itl, 0x000a, 0x0001);
+	aim_addtlvtochain_raw(&itl, 0x0003, 4, ip);
+	aim_addtlvtochain16(&itl, 0x0005, port);
+	aim_addtlvtochain_noval(&itl, 0x000f);
+	
+	aim_writetlvchain(&hdrbs, &itl);
+
+	aim_addtlvtochain_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
+
+	aim_writetlvchain(&fr->data, &tl);
+
+	g_free(hdr);
+	aim_freetlvchain(&itl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret)
+{
+	aim_conn_t *conn;
+	int i;
+	guint8 ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!sn || !filename)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	for (i = 0; i < 7; i++)
+		aimutil_put8(ck+i, 0x30 + ((guint8) rand() % 10));
+	ck[7] = '\0';
+
+	if (ckret)
+		memcpy(ckret, ck, 8);
+
+	/*
+	 * Cookie
+	 */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/*
+	 * Channel (2)
+	 */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	/*
+	 * TLV t(0005)
+	 *
+	 * Encompasses everything below. Gee.
+	 */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4);
+
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_putraw(&fr->data, ck, 8);
+	aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
+
+	/* TLV t(000a) */
+	aimbs_put16(&fr->data, 0x000a);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, 0x0001);
+
+	/* TLV t(0003) (IP) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0004);
+	aimbs_putraw(&fr->data, ip, 4);
+
+	/* TLV t(0005) (port) */
+	aimbs_put16(&fr->data, 0x0005);
+	aimbs_put16(&fr->data, 0x0002);
+	aimbs_put16(&fr->data, port);
+
+	/* TLV t(000f) */
+	aimbs_put16(&fr->data, 0x000f);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* TLV t(2711) */
+	aimbs_put16(&fr->data, 0x2711);
+	aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4);
+
+	/* ? */
+	aimbs_put16(&fr->data, 0x0001);
+	aimbs_put16(&fr->data, numfiles);
+	aimbs_put32(&fr->data, totsize);
+	aimbs_putraw(&fr->data, (guint8 *)filename, strlen(filename));
+
+	/* ? */
+	aimbs_put32(&fr->data, 0x00000000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * Request the status message of the given ICQ user.
+ *
+ * @param sess The oscar session.
+ * @param sn The UIN of the user of whom you wish to request info.
+ * @param type The type of info you wish to request.  This should be the current 
+ *        state of the user, as one of the AIM_ICQ_STATE_* defines.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type)
+{
+	aim_conn_t *conn;
+	int i;
+	guint8 ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
+		return -EINVAL;
+
+	for (i = 0; i < 8; i++)
+		aimutil_put8(ck+i, (guint8) rand());
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* Cookie */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/* Channel (2) */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/* Dest sn */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	/* TLV t(0005) - Encompasses almost everything below. */
+	aimbs_put16(&fr->data, 0x0005); /* T */
+	aimbs_put16(&fr->data, 0x005e); /* L */
+	{ /* V */
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* Cookie */
+		aimbs_putraw(&fr->data, ck, 8);
+
+		/* Put the 16 byte server relay capability */
+		aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+		/* TLV t(000a) */
+		aimbs_put16(&fr->data, 0x000a);
+		aimbs_put16(&fr->data, 0x0002);
+		aimbs_put16(&fr->data, 0x0001);
+
+		/* TLV t(000f) */
+		aimbs_put16(&fr->data, 0x000f);
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* TLV t(2711) */
+		aimbs_put16(&fr->data, 0x2711);
+		aimbs_put16(&fr->data, 0x0036);
+		{ /* V */
+			aimbs_putle16(&fr->data, 0x001b); /* L */
+			aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle16(&fr->data, 0x0003); /* Client features? */
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle8(&fr->data, 0x00); /* Unkizown */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+
+			aimbs_putle16(&fr->data, 0x000e); /* L */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+
+			/* The type of status message being requested */
+			if (type & AIM_ICQ_STATE_CHAT)
+				aimbs_putle16(&fr->data, 0x03ec);
+			else if(type & AIM_ICQ_STATE_DND)
+				aimbs_putle16(&fr->data, 0x03eb);
+			else if(type & AIM_ICQ_STATE_OUT)
+				aimbs_putle16(&fr->data, 0x03ea);
+			else if(type & AIM_ICQ_STATE_BUSY)
+				aimbs_putle16(&fr->data, 0x03e9);
+			else if(type & AIM_ICQ_STATE_AWAY)
+				aimbs_putle16(&fr->data, 0x03e8);
+
+			aimbs_putle16(&fr->data, 0x0000); /* Status? */
+			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
+			aimbs_putle16(&fr->data, 0x0001); /* L? */
+			aimbs_putle8(&fr->data, 0x00); /* Null termination? */
+		} /* End TLV t(2711) */
+	} /* End TLV t(0005) */
+
+	/* TLV t(0003) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
+ * answers status message requests
+ * @param sess the oscar session
+ * @param sender the guy whos asking
+ * @param cookie message id which we are answering for
+ * @param message away message
+ * @param state our current away state the way icq requests it (0xE8 for away, 0xE9 occupied, ...)
+ * @return 0 if no error
+ */
+int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie,
+        const char *message, const guint8 state, const guint16 dc)
+{
+    aim_conn_t *conn;
+    aim_frame_t *fr;
+    aim_snacid_t snacid;
+
+    if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+                return -EINVAL;
+        
+    if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 
+					10+8+2+1+strlen(sender)+2+0x1d+0x10+9+strlen(message)+1)))
+                return -ENOMEM;
+
+    snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+    aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+    
+    aimbs_putraw(&fr->data, cookie, 8);
+    
+    aimbs_put16(&fr->data, 0x0002); /* channel */
+    aimbs_put8(&fr->data, strlen(sender));
+    aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender));
+
+    aimbs_put16(&fr->data, 0x0003); /* reason: channel specific */
+
+    aimbs_putle16(&fr->data, 0x001b); /* length of data SEQ1 */
+    aimbs_putle16(&fr->data, 0x0008); /* protocol version */
+
+    aimbs_putle32(&fr->data, 0x0000); /* no plugin -> 16 times 0x00 */ 
+    aimbs_putle32(&fr->data, 0x0000); 
+    aimbs_putle32(&fr->data, 0x0000); 
+    aimbs_putle32(&fr->data, 0x0000);
+
+    aimbs_putle16(&fr->data, 0x0000); /* unknown */
+    aimbs_putle32(&fr->data, 0x0003); /* client features */
+    aimbs_putle8(&fr->data, 0x00); /* unknown */
+    aimbs_putle16(&fr->data, dc); /* Sequence number?  XXX - This should decrement by 1 with each request */
+    /* end of SEQ1 */
+
+    aimbs_putle16(&fr->data, 0x000e); /* Length of SEQ2 */
+    aimbs_putle16(&fr->data, dc); /* Sequence number? same as above
+                                       * XXX - This should decrement by 1 with each request */
+    aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+    aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+    aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+    /* end of SEQ2 */
+
+    /* now for the real fun */
+    aimbs_putle8(&fr->data, state); /* away state */
+    aimbs_putle8(&fr->data, 0x03); /* msg-flag: 03 for states */
+    aimbs_putle16(&fr->data, 0x0000); /* status code ? */
+    aimbs_putle16(&fr->data, 0x0000); /* priority code */
+    aimbs_putle16(&fr->data, strlen(message) + 1); /* message length + termination */
+    aimbs_putraw(&fr->data, (guint8 *) message, strlen(message) + 1); /* null terminated string */
+    
+    aim_tx_enqueue(sess, fr);
+
+
+    return 0;
+}
+
+
+static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int i, ret = 0;
+	aim_rxcallback_t userfunc;
+	guint8 cookie[8];
+	guint16 channel;
+	aim_tlvlist_t *tlvlist;
+	char *sn;
+	int snlen;
+	guint16 icbmflags = 0;
+	guint8 flag1 = 0, flag2 = 0;
+	char *msg = NULL;
+	aim_tlv_t *msgblock;
+
+	/* ICBM Cookie. */
+	for (i = 0; i < 8; i++)
+		cookie[i] = aimbs_get8(bs);
+
+	/* Channel ID */
+	channel = aimbs_get16(bs);
+
+	if (channel != 0x01) {
+		do_error_dialog(sess->aux_data, "icbm: ICBM recieved on unsupported channel.  Ignoring.", "Gaim");
+		return 0;
+	}
+
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+
+	tlvlist = aim_readtlvchain(bs);
+
+	if (aim_gettlv(tlvlist, 0x0003, 1))
+		icbmflags |= AIM_IMFLAGS_ACK;
+	if (aim_gettlv(tlvlist, 0x0004, 1))
+		icbmflags |= AIM_IMFLAGS_AWAY;
+
+	if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) {
+		aim_bstream_t mbs;
+		int featurelen, msglen;
+
+		aim_bstream_init(&mbs, msgblock->value, msgblock->length);
+
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
+			aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+		aimbs_get8(&mbs);
+
+		msglen = aimbs_get16(&mbs) - 4; /* final block length */
+
+		flag1 = aimbs_get16(&mbs);
+		flag2 = aimbs_get16(&mbs);
+
+		msg = aimbs_getstr(&mbs, msglen);
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2);
+
+	g_free(sn);
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+/*
+ * Ahh, the joys of nearly ridiculous over-engineering.
+ *
+ * Not only do AIM ICBM's support multiple channels.  Not only do they
+ * support multiple character sets.  But they support multiple character 
+ * sets / encodings within the same ICBM.
+ *
+ * These multipart messages allow for complex space savings techniques, which
+ * seem utterly unnecessary by today's standards.  In fact, there is only
+ * one client still in popular use that still uses this method: AOL for the
+ * Macintosh, Version 5.0.  Obscure, yes, I know.  
+ *
+ * In modern (non-"legacy") clients, if the user tries to send a character
+ * that is not ISO-8859-1 or ASCII, the client will send the entire message
+ * as UNICODE, meaning that every character in the message will occupy the
+ * full 16 bit UNICODE field, even if the high order byte would be zero.
+ * Multipart messages prevent this wasted space by allowing the client to
+ * only send the characters in UNICODE that need to be sent that way, and
+ * the rest of the message can be sent in whatever the native character 
+ * set is (probably ASCII).
+ *
+ * An important note is that sections will be displayed in the order that
+ * they appear in the ICBM.  There is no facility for merging or rearranging
+ * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
+ * you must supply two ASCII sections with a UNICODE in the middle, and incur
+ * the associated overhead.
+ *
+ * Normally I would have laughed and given a firm 'no' to supporting this
+ * seldom-used feature, but something is attracting me to it.  In the future,
+ * it may be possible to abuse this to send mixed-media messages to other
+ * open source clients (like encryption or something) -- see faimtest for
+ * examples of how to do this.
+ *
+ * I would definitly recommend avoiding this feature unless you really
+ * know what you are doing, and/or you have something neat to do with it.
+ *
+ */
+int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+
+	memset(mpm, 0, sizeof(aim_mpmsg_t));
+
+	return 0;
+}
+
+static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, guint8 *data, guint16 datalen)
+{
+	aim_mpmsg_section_t *sec; 
+	
+	if (!(sec = g_new0(aim_mpmsg_section_t,1)))
+		return -1;
+
+	sec->charset = charset;
+	sec->charsubset = charsubset;
+	sec->data = data;
+	sec->datalen = datalen;
+	sec->next = NULL;
+
+	if (!mpm->parts)
+		mpm->parts = sec;
+	else {
+		aim_mpmsg_section_t *cur;
+
+		for (cur = mpm->parts; cur->next; cur = cur->next)
+			;
+		cur->next = sec;
+	}
+
+	mpm->numparts++;
+
+	return 0;
+}
+
+int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen)
+{
+	guint8 *dup;
+
+	if (!(dup = g_malloc(datalen)))
+		return -1;
+	memcpy(dup, data, datalen);
+
+	if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
+		g_free(dup);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* XXX should provide a way of saying ISO-8859-1 specifically */
+int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
+{
+	char *dup;
+
+	if (!(dup = g_strdup(ascii))) 
+		return -1;
+
+	if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, (guint8 *)dup, (guint16) strlen(ascii)) == -1) {
+		g_free(dup);
+		return -1;
+	}
+
+	return 0;
+}
+
+int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen)
+{
+	guint8 *buf;
+	aim_bstream_t bs;
+	int i;
+
+	if (!(buf = g_malloc(unicodelen * 2)))
+		return -1;
+
+	aim_bstream_init(&bs, buf, unicodelen * 2);
+
+	/* We assume unicode is in /host/ byte order -- convert to network */
+	for (i = 0; i < unicodelen; i++)
+		aimbs_put16(&bs, unicode[i]);
+
+	if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
+		g_free(buf);
+		return -1;
+	}
+	
+	return 0;
+}
+
+void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+	aim_mpmsg_section_t *cur;
+
+	for (cur = mpm->parts; cur; ) {
+		aim_mpmsg_section_t *tmp;
+		
+		tmp = cur->next;
+		g_free(cur->data);
+		g_free(cur);
+		cur = tmp;
+	}
+	
+	mpm->numparts = 0;
+	mpm->parts = NULL;
+
+	return;
+}
+
+/*
+ * Start by building the multipart structures, then pick the first 
+ * human-readable section and stuff it into args->msg so no one gets
+ * suspicious.
+ *
+ */
+static int incomingim_ch1_parsemsgs(aim_session_t *sess, guint8 *data, int len, struct aim_incomingim_ch1_args *args)
+{
+	static const guint16 charsetpri[] = {
+		0x0000, /* ASCII first */
+		0x0003, /* then ISO-8859-1 */
+		0x0002, /* UNICODE as last resort */
+	};
+	static const int charsetpricount = 3;
+	int i;
+	aim_bstream_t mbs;
+	aim_mpmsg_section_t *sec;
+
+	aim_bstream_init(&mbs, data, len);
+
+	while (aim_bstream_empty(&mbs)) {
+		guint16 msglen, flag1, flag2;
+		char *msgbuf;
+
+		aimbs_get8(&mbs); /* 01 */
+		aimbs_get8(&mbs); /* 01 */
+
+		/* Message string length, including character set info. */
+		msglen = aimbs_get16(&mbs);
+
+		/* Character set info */
+		flag1 = aimbs_get16(&mbs);
+		flag2 = aimbs_get16(&mbs);
+
+		/* Message. */
+		msglen -= 4;
+
+		/*
+		 * For now, we don't care what the encoding is.  Just copy
+		 * it into a multipart struct and deal with it later. However,
+		 * always pad the ending with a NULL.  This makes it easier
+		 * to treat ASCII sections as strings.  It won't matter for
+		 * UNICODE or binary data, as you should never read past
+		 * the specified data length, which will not include the pad.
+		 *
+		 * XXX There's an API bug here.  For sending, the UNICODE is
+		 * given in host byte order (aim_mpmsg_addunicode), but here
+		 * the received messages are given in network byte order.
+		 *
+		 */
+		msgbuf = aimbs_getstr(&mbs, msglen);
+		mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, (guint8 *)msgbuf, (guint16) msglen);
+
+	} /* while */
+
+	args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
+
+	/*
+	 * Clients that support multiparts should never use args->msg, as it
+	 * will point to an arbitrary section.
+	 *
+	 * Here, we attempt to provide clients that do not support multipart
+	 * messages with something to look at -- hopefully a human-readable
+	 * string.  But, failing that, a UNICODE message, or nothing at all.
+	 *
+	 * Which means that even if args->msg is NULL, it does not mean the
+	 * message was blank.
+	 *
+	 */
+	for (i = 0; i < charsetpricount; i++) {
+		for (sec = args->mpmsg.parts; sec; sec = sec->next) {
+
+			if (sec->charset != charsetpri[i])
+				continue;
+
+			/* Great. We found one.  Fill it in. */
+			args->charset = sec->charset;
+			args->charsubset = sec->charsubset;
+			args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET;
+
+			/* Set up the simple flags */
+			if (args->charset == 0x0000)
+				; /* ASCII */
+			else if (args->charset == 0x0002)
+				args->icbmflags |= AIM_IMFLAGS_UNICODE;
+			else if (args->charset == 0x0003)
+				args->icbmflags |= AIM_IMFLAGS_ISO_8859_1;
+			else if (args->charset == 0xffff)
+				; /* no encoding (yeep!) */
+
+			if (args->charsubset == 0x0000)
+				; /* standard subencoding? */
+			else if (args->charsubset == 0x000b)
+				args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
+			else if (args->charsubset == 0xffff)
+				; /* no subencoding */
+#if 0
+			/* XXX this isn't really necesary... */	
+			if (	((args.flag1 != 0x0000) &&
+				 (args.flag1 != 0x0002) &&
+				 (args.flag1 != 0x0003) &&
+				 (args.flag1 != 0xffff)) ||
+				((args.flag2 != 0x0000) &&
+				 (args.flag2 != 0x000b) &&
+				 (args.flag2 != 0xffff))) {
+				faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
+			}
+#endif
+
+			args->msg = (char *)sec->data;
+			args->msglen = sec->datalen;
+
+			return 0;
+		}
+	}
+
+	/* No human-readable sections found.  Oh well. */
+	args->charset = args->charsubset = 0xffff;
+	args->msg = NULL;
+	args->msglen = 0;
+
+	return 0;
+}
+
+static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie)
+{
+	guint16 type, length;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	struct aim_incomingim_ch1_args args;
+	int endpos;
+
+	memset(&args, 0, sizeof(args));
+
+	aim_mpmsg_init(sess, &args.mpmsg);
+
+	/*
+	 * This used to be done using tlvchains.  For performance reasons,
+	 * I've changed it to process the TLVs in-place.  This avoids lots
+	 * of per-IM memory allocations.
+	 */
+	while (aim_bstream_empty(bs)) {
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		endpos = aim_bstream_curpos(bs) + length;
+
+		if (type == 0x0002) { /* Message Block */
+
+			/*
+			 * This TLV consists of the following:
+			 *   - 0501 -- Unknown
+			 *   - Features: Don't know how to interpret these
+			 *   - 0101 -- Unknown
+			 *   - Message
+			 *
+			 */
+
+			aimbs_get8(bs); /* 05 */
+			aimbs_get8(bs); /* 01 */
+
+			args.featureslen = aimbs_get16(bs);
+			/* XXX XXX this is all evil! */
+			args.features = bs->data + bs->offset;
+			aim_bstream_advance(bs, args.featureslen);
+			args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
+
+			/*
+			 * The rest of the TLV contains one or more message
+			 * blocks...
+			 */
+			incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
+
+		} else if (type == 0x0003) { /* Server Ack Requested */
+
+			args.icbmflags |= AIM_IMFLAGS_ACK;
+
+		} else if (type == 0x0004) { /* Message is Auto Response */
+
+			args.icbmflags |= AIM_IMFLAGS_AWAY;
+
+		} else if (type == 0x0006) { /* Message was received offline. */
+
+			/* XXX not sure if this actually gets sent. */
+			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
+
+		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
+
+			args.iconlen = aimbs_get32(bs);
+			aimbs_get16(bs); /* 0x0001 */
+			args.iconsum = aimbs_get16(bs);
+			args.iconstamp = aimbs_get32(bs);
+
+			/*
+			 * This looks to be a client bug.  MacAIM 4.3 will
+			 * send this tag, but with all zero values, in the
+			 * first message of a conversation. This makes no
+			 * sense whatsoever, so I'm going to say its a bug.
+			 *
+			 * You really shouldn't advertise a zero-length icon
+			 * anyway.
+			 * 
+			 */
+			if (args.iconlen)
+				args.icbmflags |= AIM_IMFLAGS_HASICON;
+
+		} else if (type == 0x0009) {
+
+			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
+
+		} else if (type == 0x0017) {
+
+			args.extdatalen = length;
+			args.extdata = aimbs_getraw(bs, args.extdatalen);
+
+		} else {
+			// do_error_dialog(sess->aux_data, "Unknown TLV encountered", "Gaim");
+		}
+
+		/*
+		 * This is here to protect ourselves from ourselves.  That
+		 * is, if something above doesn't completly parse its value
+		 * section, or, worse, overparses it, this will set the
+		 * stream where it needs to be in order to land on the next
+		 * TLV when the loop continues.
+		 *
+		 */
+		aim_bstream_setpos(bs, endpos);
+	}
+
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+	aim_mpmsg_free(sess, &args.mpmsg);
+	g_free(args.extdata);
+
+	return ret;
+}
+
+static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
+{
+
+	g_free((char *)args->info.rtfmsg.rtfmsg);
+
+	return;
+}
+
+/*
+ * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is 
+ * kind of odd. This sends the client ICQRTF since that is all that I've seen
+ * SERVERRELAY used for.
+ *
+ * Note that this is all little-endian.  Cringe.
+ *
+ * This cap is used for auto status message replies, too [ft]
+ *
+ */
+static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata)
+{
+	guint16 hdrlen, msglen, dc;
+	guint8 msgtype, msgflags;
+    guint8 *plugin;
+    int i = 0, tmp = 0;
+    struct gaim_connection *gc = sess->aux_data;
+
+    /* at the moment we just can deal with requests, not with cancel or accept */
+    if (args->status != 0) return;
+
+	hdrlen = aimbs_getle16(servdata);
+
+    aim_bstream_advance(servdata, 0x02); /* protocol version */
+    plugin = aimbs_getraw(servdata, 0x10); /* following data is a message or 
+                                              something plugin specific */
+    /* as there is no plugin handling, just skip the rest */
+    aim_bstream_advance(servdata, hdrlen - 0x12);
+
+	hdrlen = aimbs_getle16(servdata);
+    dc = aimbs_getle16(servdata); /* save the sequence number */
+	aim_bstream_advance(servdata, hdrlen - 0x02);
+
+    /* TODO is it a message or something for a plugin? */
+    for (i = 0; i < 0x10; i++) {
+        tmp |= plugin[i];
+    }
+
+    if (!tmp) { /* message follows */
+
+        msgtype = aimbs_getle8(servdata);
+        msgflags = aimbs_getle8(servdata);
+
+        aim_bstream_advance(servdata, 0x04); /* status code and priority code */
+
+        msglen = aimbs_getle16(servdata); /* message string length */
+	args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen);
+
+        switch(msgtype) {
+            case AIM_MTYPE_PLAIN:
+
+                args->info.rtfmsg.fgcolor = aimbs_getle32(servdata);
+                args->info.rtfmsg.bgcolor = aimbs_getle32(servdata);
+
+                hdrlen = aimbs_getle32(servdata);
+                aim_bstream_advance(servdata, hdrlen);
+
+                /* XXX This is such a hack. */
+                args->reqclass = AIM_CAPS_ICQRTF;
+                break;
+
+            case AIM_MTYPE_AUTOAWAY: 
+            case AIM_MTYPE_AUTOBUSY:
+            case AIM_MTYPE_AUTONA:
+            case AIM_MTYPE_AUTODND:
+            case AIM_MTYPE_AUTOFFC:
+	    case 0x9c:	/* ICQ 5 seems to send this */
+                aim_send_im_ch2_statusmessage(sess, userinfo->sn, args->cookie,
+                        gc->away, sess->aim_icq_state, dc);
+                break;
+
+        }
+    } /* message or plugin specific */
+
+    g_free(plugin);
+	args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
+
+	return;
+}
+
+typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args);
+
+static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+	aim_rxcallback_t userfunc;
+	aim_tlv_t *block1, *servdatatlv;
+	aim_tlvlist_t *list2;
+	struct aim_incomingim_ch2_args args;
+	aim_bstream_t bbs, sdbs, *sdbsptr = NULL;
+	guint8 *cookie2;
+	int ret = 0;
+
+	char clientip1[30] = {""};
+	char clientip2[30] = {""};
+	char verifiedip[30] = {""};
+
+	memset(&args, 0, sizeof(args));
+
+	/*
+	 * There's another block of TLVs embedded in the type 5 here. 
+	 */
+	block1 = aim_gettlv(tlvlist, 0x0005, 1);
+	aim_bstream_init(&bbs, block1->value, block1->length);
+
+	/*
+	 * First two bytes represent the status of the connection.
+	 *
+	 * 0 is a request, 1 is a deny (?), 2 is an accept
+	 */ 
+	args.status = aimbs_get16(&bbs);
+
+	/*
+	 * Next comes the cookie.  Should match the ICBM cookie.
+	 */
+	cookie2 = aimbs_getraw(&bbs, 8);
+	if (memcmp(cookie, cookie2, 8) != 0) 
+		do_error_dialog(sess->aux_data, "rend: warning cookies don't match!", "Gaim");
+	memcpy(args.cookie, cookie2, 8);
+	g_free(cookie2);
+
+	/*
+	 * The next 16bytes are a capability block so we can
+	 * identify what type of rendezvous this is.
+	 */
+	args.reqclass = aim_getcap(sess, &bbs, 0x10);
+
+	/* 
+	 * What follows may be TLVs or nothing, depending on the
+	 * purpose of the message.
+	 *
+	 * Ack packets for instance have nothing more to them.
+	 */
+	list2 = aim_readtlvchain(&bbs);
+
+	/*
+	 * IP address from the perspective of the client.
+	 */
+	if (aim_gettlv(list2, 0x0002, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_gettlv(list2, 0x0002, 1);
+
+		g_snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d",
+				aimutil_get8(iptlv->value+0),
+				aimutil_get8(iptlv->value+1),
+				aimutil_get8(iptlv->value+2),
+				aimutil_get8(iptlv->value+3));
+	}
+
+	/*
+	 * Secondary IP address from the perspective of the client.
+	 */
+	if (aim_gettlv(list2, 0x0003, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_gettlv(list2, 0x0003, 1);
+
+		g_snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d",
+				aimutil_get8(iptlv->value+0),
+				aimutil_get8(iptlv->value+1),
+				aimutil_get8(iptlv->value+2),
+				aimutil_get8(iptlv->value+3));
+	}
+
+	/*
+	 * Verified IP address (from the perspective of Oscar).
+	 *
+	 * This is added by the server.
+	 */
+	if (aim_gettlv(list2, 0x0004, 1)) {
+		aim_tlv_t *iptlv;
+
+		iptlv = aim_gettlv(list2, 0x0004, 1);
+
+		g_snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d",
+				aimutil_get8(iptlv->value+0),
+				aimutil_get8(iptlv->value+1),
+				aimutil_get8(iptlv->value+2),
+				aimutil_get8(iptlv->value+3));
+	}
+
+	/*
+	 * Port number for something.
+	 */
+	if (aim_gettlv(list2, 0x0005, 1))
+		args.port = aim_gettlv16(list2, 0x0005, 1);
+
+	/*
+	 * Error code.
+	 */
+	if (aim_gettlv(list2, 0x000b, 1))
+		args.errorcode = aim_gettlv16(list2, 0x000b, 1);
+
+	/*
+	 * Invitation message / chat description.
+	 */
+	if (aim_gettlv(list2, 0x000c, 1))
+		args.msg = aim_gettlv_str(list2, 0x000c, 1);
+
+	/*
+	 * Character set.
+	 */
+	if (aim_gettlv(list2, 0x000d, 1))
+		args.encoding = aim_gettlv_str(list2, 0x000d, 1);
+	
+	/*
+	 * Language.
+	 */
+	if (aim_gettlv(list2, 0x000e, 1))
+		args.language = aim_gettlv_str(list2, 0x000e, 1);
+
+	/* Unknown -- two bytes = 0x0001 */
+	if (aim_gettlv(list2, 0x000a, 1))
+		;
+
+	/* Unknown -- no value */
+	if (aim_gettlv(list2, 0x000f, 1))
+		;
+
+	if (strlen(clientip1))
+		args.clientip = (char *)clientip1;
+	if (strlen(clientip2))
+		args.clientip2 = (char *)clientip2;
+	if (strlen(verifiedip))
+		args.verifiedip = (char *)verifiedip;
+
+	/*
+	 * This is must be present in PROPOSALs, but will probably not
+	 * exist in CANCELs and ACCEPTs.
+	 *
+	 * Service Data blocks are module-specific in format.
+	 */
+	if ((servdatatlv = aim_gettlv(list2, 0x2711 /* 10001 */, 1))) {
+
+		aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length);
+		sdbsptr = &sdbs;
+	}
+
+	if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)
+		incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr);
+
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+
+	if (args.destructor)
+		((ch2_args_destructor_t)args.destructor)(sess, &args);
+
+	g_free((char *)args.msg);
+	g_free((char *)args.encoding);
+	g_free((char *)args.language);
+
+	aim_freetlvchain(&list2);
+
+	return ret;
+}
+
+static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie)
+{
+	aim_bstream_t meat;
+	aim_rxcallback_t userfunc;
+	aim_tlv_t *block;
+	struct aim_incomingim_ch4_args args;
+	int ret = 0;
+
+	/*
+	 * Make a bstream for the meaty part.  Yum.  Meat.
+	 */
+	if (!(block = aim_gettlv(tlvlist, 0x0005, 1)))
+		return -1;
+	aim_bstream_init(&meat, block->value, block->length);
+
+	args.uin = aimbs_getle32(&meat);
+	args.type = aimbs_getle16(&meat);
+	args.msg = (char *)aimbs_getraw(&meat, aimbs_getle16(&meat));
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, channel, userinfo, &args);
+
+	g_free(args.msg);
+
+	return ret;
+}
+
+/*
+ * It can easily be said that parsing ICBMs is THE single
+ * most difficult thing to do in the in AIM protocol.  In
+ * fact, I think I just did say that.
+ *
+ * Below is the best damned solution I've come up with
+ * over the past sixteen months of battling with it. This
+ * can parse both away and normal messages from every client
+ * I have access to.  Its not fast, its not clean.  But it works.
+ *
+ */
+static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int i, ret = 0;
+	guint8 cookie[8];
+	guint16 channel;
+	aim_userinfo_t userinfo;
+
+	memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
+
+	/*
+	 * Read ICBM Cookie.  And throw away.
+	 */
+	for (i = 0; i < 8; i++)
+		cookie[i] = aimbs_get8(bs);
+
+	/*
+	 * Channel ID.
+	 *
+	 * Channel 0x0001 is the message channel.  There are 
+	 * other channels for things called "rendevous"
+	 * which represent chat and some of the other new
+	 * features of AIM2/3/3.5. 
+	 *
+	 * Channel 0x0002 is the Rendevous channel, which
+	 * is where Chat Invitiations and various client-client
+	 * connection negotiations come from.
+	 *
+	 * Channel 0x0004 is used for ICQ authorization, or 
+	 * possibly any system notice.
+	 *
+	 */
+	channel = aimbs_get16(bs);
+
+	/*
+	 * Extract the standard user info block.
+	 *
+	 * Note that although this contains TLVs that appear contiguous
+	 * with the TLVs read below, they are two different pieces.  The
+	 * userinfo block contains the number of TLVs that contain user
+	 * information, the rest are not even though there is no seperation.
+	 * aim_extractuserinfo() returns the number of bytes used by the
+	 * userinfo tlvs, so you can start reading the rest of them right
+	 * afterward.  
+	 *
+	 * That also means that TLV types can be duplicated between the
+	 * userinfo block and the rest of the message, however there should
+	 * never be two TLVs of the same type in one block.
+	 * 
+	 */
+	aim_extractuserinfo(sess, bs, &userinfo);
+
+	/*
+	 * From here on, its depends on what channel we're on.
+	 *
+	 * Technically all channels have a TLV list have this, however,
+	 * for the common channel 1 case, in-place parsing is used for
+	 * performance reasons (less memory allocation).
+	 */
+	if (channel == 1) {
+
+		ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
+
+	} else if (channel == 2) {
+		aim_tlvlist_t *tlvlist;
+
+		/*
+		 * Read block of TLVs (not including the userinfo data).  All 
+		 * further data is derived from what is parsed here.
+		 */
+		tlvlist = aim_readtlvchain(bs);
+
+		ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+
+		aim_freetlvchain(&tlvlist);
+
+	} else if (channel == 4) {
+		aim_tlvlist_t *tlvlist;
+
+		tlvlist = aim_readtlvchain(bs);
+		ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
+		aim_freetlvchain(&tlvlist);
+
+	} else {
+
+		do_error_dialog(sess->aux_data, "ICBM received on an unsupported channel.  Ignoring.", "Gaim");
+
+		return 0;
+	}
+
+	return ret;
+}
+
+/*
+ * Possible codes:
+ *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
+ *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
+ *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
+ * 
+ */
+int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, guint16 code)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
+	
+	aimbs_putraw(&fr->data, cookie, 8);
+
+	aimbs_put16(&fr->data, 0x0002); /* channel */
+	aimbs_put8(&fr->data, strlen(sender));
+	aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender));
+
+	aim_addtlvtochain16(&tl, 0x0003, code);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * aim_reqicbmparaminfo()
+ *
+ * Request ICBM parameter information.
+ *
+ */
+int aim_reqicbmparams(aim_session_t *sess)
+{
+	aim_conn_t *conn;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
+}
+
+/*
+ *
+ * I definitly recommend sending this.  If you don't, you'll be stuck
+ * with the rather unreasonable defaults.  You don't want those.  Send this.
+ * 
+ */
+int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
+		return -EINVAL;
+
+	if (!params)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
+
+	/* This is read-only (see Parameter Reply). Must be set to zero here. */
+	aimbs_put16(&fr->data, 0x0000);
+
+	/* These are all read-write */
+	aimbs_put32(&fr->data, params->flags); 
+	aimbs_put16(&fr->data, params->maxmsglen);
+	aimbs_put16(&fr->data, params->maxsenderwarn); 
+	aimbs_put16(&fr->data, params->maxrecverwarn); 
+	aimbs_put32(&fr->data, params->minmsginterval);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	struct aim_icbmparameters params;
+	aim_rxcallback_t userfunc;
+
+	params.maxchan = aimbs_get16(bs);
+	params.flags = aimbs_get32(bs);
+	params.maxmsglen = aimbs_get16(bs);
+	params.maxsenderwarn = aimbs_get16(bs);
+	params.maxrecverwarn = aimbs_get16(bs);
+	params.minmsginterval = aimbs_get32(bs);
+	
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &params);
+
+	return 0;
+}
+
+static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 channel, nummissed, reason;
+	aim_userinfo_t userinfo;
+
+	while (aim_bstream_empty(bs)) {	
+
+		channel = aimbs_get16(bs);
+		aim_extractuserinfo(sess, bs, &userinfo);
+		nummissed = aimbs_get16(bs);
+		reason = aimbs_get16(bs);
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			 ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
+	}
+
+	return ret;
+}
+
+/*
+ * Receive the response from an ICQ status message request.  This contains the 
+ * ICQ status message.  Go figure.
+ */
+static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	guint16 channel, reason;
+	char *sn;
+	guint8 *ck, snlen;
+
+	ck = aimbs_getraw(bs, 8);
+	channel = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+	reason = aimbs_get16(bs);
+
+	switch (reason) {
+		case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
+			guint8 statusmsgtype, *msg;
+			guint16 len;
+			guint32 state;
+
+			len = aimbs_getle16(bs); /* Should be 0x001b */
+			aim_bstream_advance(bs, len); /* Unknown */
+
+			len = aimbs_getle16(bs); /* Should be 0x000e */
+			aim_bstream_advance(bs, len); /* Unknown */
+
+			statusmsgtype = aimbs_getle8(bs);
+			switch (statusmsgtype) {
+				case 0xe8:
+					state = AIM_ICQ_STATE_AWAY;
+					break;
+				case 0xe9:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
+					break;
+				case 0xea:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
+					break;
+				case 0xeb:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
+					break;
+				case 0xec:
+					state = AIM_ICQ_STATE_CHAT;
+					break;
+				default:
+					state = 0;
+					break;
+			}
+
+			aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
+			aimbs_getle16(bs); /* Unknown - 0x0000 */
+			aimbs_getle16(bs); /* Unknown - 0x0000 */
+
+			len = aimbs_getle16(bs);
+			msg = aimbs_getraw(bs, len);
+
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				ret = userfunc(sess, rx, channel, sn, reason, state, msg);
+
+			g_free(msg);
+		} break;
+
+		default: {
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				ret = userfunc(sess, rx, channel, sn, reason);
+		} break;
+	} /* end switch */
+
+	g_free(ck);
+	g_free(sn);
+
+	return ret;
+}
+
+static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint16 type;
+	guint8 snlen, *ck;
+	char *sn;
+	int ret = 0;
+
+	ck = aimbs_getraw(bs, 8);
+	type = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, type, sn);
+
+	g_free(sn);
+	g_free(ck);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0005)
+		return paraminfo(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return outgoingim(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return incomingim(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000a)
+		return missedcall(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000b)
+		return clientautoresp(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000c)
+		return msgack(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0004;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "messaging", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
Index: protocols/oscar/im.h
===================================================================
--- protocols/oscar/im.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/im.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,198 @@
+#ifndef __OSCAR_IM_H__
+#define __OSCAR_IM_H__
+
+#define AIM_CB_FAM_MSG 0x0004
+
+/*
+ * SNAC Family: Messaging Services.
+ */ 
+#define AIM_CB_MSG_ERROR 0x0001
+#define AIM_CB_MSG_PARAMINFO 0x0005
+#define AIM_CB_MSG_INCOMING 0x0007
+#define AIM_CB_MSG_EVIL 0x0009
+#define AIM_CB_MSG_MISSEDCALL 0x000a
+#define AIM_CB_MSG_CLIENTAUTORESP 0x000b
+#define AIM_CB_MSG_ACK 0x000c
+#define AIM_CB_MSG_DEFAULT 0xffff
+
+#define AIM_IMFLAGS_AWAY		0x0001 /* mark as an autoreply */
+#define AIM_IMFLAGS_ACK			0x0002 /* request a receipt notice */
+#define AIM_IMFLAGS_UNICODE		0x0004
+#define AIM_IMFLAGS_ISO_8859_1		0x0008
+#define AIM_IMFLAGS_BUDDYREQ		0x0010 /* buddy icon requested */
+#define AIM_IMFLAGS_HASICON		0x0020 /* already has icon */
+#define AIM_IMFLAGS_SUBENC_MACINTOSH	0x0040 /* damn that Steve Jobs! */
+#define AIM_IMFLAGS_CUSTOMFEATURES 	0x0080 /* features field present */
+#define AIM_IMFLAGS_EXTDATA		0x0100
+#define AIM_IMFLAGS_CUSTOMCHARSET	0x0200 /* charset fields set */
+#define AIM_IMFLAGS_MULTIPART		0x0400 /* ->mpmsg section valid */
+#define AIM_IMFLAGS_OFFLINE		0x0800 /* send to offline user */
+
+/*
+ * Multipart message structures.
+ */
+typedef struct aim_mpmsg_section_s {
+	guint16 charset;
+	guint16 charsubset;
+	guint8 *data;
+	guint16 datalen;
+	struct aim_mpmsg_section_s *next;
+} aim_mpmsg_section_t;
+
+typedef struct aim_mpmsg_s {
+	int numparts;
+	aim_mpmsg_section_t *parts;
+} aim_mpmsg_t;
+
+int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm);
+int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen);
+int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii);
+int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen);
+void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm);
+
+/*
+ * Arguments to aim_send_im_ext().
+ *
+ * This is really complicated.  But immensely versatile.
+ *
+ */
+struct aim_sendimext_args {
+
+	/* These are _required_ */
+	const char *destsn;
+	guint32 flags; /* often 0 */
+
+	/* Only required if not using multipart messages */
+	const char *msg;
+	int msglen;
+
+	/* Required if ->msg is not provided */
+	aim_mpmsg_t *mpmsg;
+
+	/* Only used if AIM_IMFLAGS_HASICON is set */
+	guint32 iconlen;
+	time_t iconstamp;
+	guint32 iconsum;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */
+	guint8 *features;
+	guint8 featureslen;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */
+	guint16 charset;
+	guint16 charsubset;
+};
+
+/*
+ * Arguments to aim_send_rtfmsg().
+ */
+struct aim_sendrtfmsg_args {
+	const char *destsn;
+	guint32 fgcolor;
+	guint32 bgcolor;
+	const char *rtfmsg; /* must be in RTF */
+};
+
+/*
+ * This information is provided in the Incoming ICBM callback for
+ * Channel 1 ICBM's.  
+ *
+ * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they
+ * are optional, both are always set by the current libfaim code.
+ * That may or may not change in the future.  It is mainly for
+ * consistency with aim_sendimext_args.
+ *
+ * Multipart messages require some explanation. If you want to use them,
+ * I suggest you read all the comments in im.c.
+ *
+ */
+struct aim_incomingim_ch1_args {
+
+	/* Always provided */
+	aim_mpmsg_t mpmsg;
+	guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */
+	
+	/* Only provided if message has a human-readable section */
+	char *msg;
+	int msglen;
+
+	/* Only provided if AIM_IMFLAGS_HASICON is set */
+	time_t iconstamp;
+	guint32 iconlen;
+	guint16 iconsum;
+
+	/* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */
+	guint8 *features;
+	guint8 featureslen;
+
+	/* Only provided if AIM_IMFLAGS_EXTDATA is set */
+	guint8 extdatalen;
+	guint8 *extdata;
+
+	/* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */
+	guint16 charset;
+	guint16 charsubset;
+};
+
+/* Valid values for channel 2 args->status */
+#define AIM_RENDEZVOUS_PROPOSE 0x0000
+#define AIM_RENDEZVOUS_CANCEL  0x0001
+#define AIM_RENDEZVOUS_ACCEPT  0x0002
+
+struct aim_incomingim_ch2_args {
+	guint8 cookie[8];
+	guint16 reqclass;
+	guint16 status;
+	guint16 errorcode;
+	const char *clientip;
+	const char *clientip2;
+	const char *verifiedip;
+	guint16 port;
+	const char *msg; /* invite message or file description */
+	const char *encoding;
+	const char *language;
+	union {
+		struct {
+			guint32 checksum;
+			guint32 length;
+			time_t timestamp;
+			guint8 *icon;
+		} icon;
+		struct {
+			struct aim_chat_roominfo roominfo;
+		} chat;
+		struct {
+			guint32 fgcolor;
+			guint32 bgcolor;
+			const char *rtfmsg;
+		} rtfmsg;
+	} info;
+	void *destructor; /* used internally only */
+};
+
+/* Valid values for channel 4 args->type */
+#define AIM_ICQMSG_AUTHREQUEST 0x0006
+#define AIM_ICQMSG_AUTHDENIED 0x0007
+#define AIM_ICQMSG_AUTHGRANTED 0x0008
+
+struct aim_incomingim_ch4_args {
+	guint32 uin; /* Of the sender of the ICBM */
+	guint16 type;
+	char *msg; /* Reason for auth request, deny, or accept */
+};
+
+int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args);
+int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args);
+int aim_send_im(aim_session_t *, const char *destsn, unsigned short flags, const char *msg);
+int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum);
+guint16 aim_iconsum(const guint8 *buf, int buflen);
+int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing);
+int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len);
+const char *aim_directim_getsn(aim_conn_t *conn);
+aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn);
+aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const guint8 *cookie);
+
+int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type);
+int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, const char *message, const guint8 state, const guint16 dc);
+
+#endif /* __OSCAR_IM_H__ */
Index: protocols/oscar/info.c
===================================================================
--- protocols/oscar/info.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/info.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,725 @@
+/*
+ * aim_info.c
+ *
+ * The functions here are responsible for requesting and parsing information-
+ * gathering SNACs.  Or something like that. 
+ *
+ */
+
+#include <aim.h>
+#include "info.h"
+
+struct aim_priv_inforeq {
+	char sn[MAXSNLEN+1];
+	guint16 infotype;
+};
+
+int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, guint16 infotype)
+{
+	struct aim_priv_inforeq privdata;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn))))
+		return -ENOMEM;
+
+	strncpy(privdata.sn, sn, sizeof(privdata.sn));
+	privdata.infotype = infotype;
+	snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq));
+	
+	aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid);
+	aimbs_put16(&fr->data, infotype);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+const char *aim_userinfo_sn(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return NULL;
+
+	return ui->sn;
+}
+
+guint16 aim_userinfo_flags(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0;
+
+	return ui->flags;
+}
+
+guint16 aim_userinfo_idle(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0;
+
+	return ui->idletime;
+}
+
+float aim_userinfo_warnlevel(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0.00;
+
+	return (ui->warnlevel / 10);
+}
+
+time_t aim_userinfo_membersince(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0;
+
+	return (time_t)ui->membersince;
+}
+
+time_t aim_userinfo_onlinesince(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0;
+
+	return (time_t)ui->onlinesince;
+}
+
+guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui)
+{
+
+	if (!ui)
+		return 0;
+
+	return ui->sessionlen;
+}
+
+int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap)
+{
+
+	if (!ui || !(ui->present & AIM_USERINFO_PRESENT_CAPABILITIES))
+		return -1;
+
+	return !!(ui->capabilities & cap);
+}
+
+
+/*
+ * Capability blocks. 
+ *
+ * These are CLSIDs. They should actually be of the form:
+ *
+ * {0x0946134b, 0x4c7f, 0x11d1,
+ *  {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}},
+ *
+ * But, eh.
+ */
+static const struct {
+	guint32 flag;
+	guint8 data[16];
+} aim_caps[] = {
+
+	/*
+	 * Chat is oddball.
+	 */
+	{AIM_CAPS_CHAT,
+	 {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	 * These are mostly in order.
+	 */
+	{AIM_CAPS_VOICE,
+	 {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SENDFILE,
+	 {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	 * Advertised by the EveryBuddy client.
+	 */
+	{AIM_CAPS_ICQ,
+	 {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_IMIMAGE,
+	 {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_BUDDYICON,
+	 {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SAVESTOCKS,
+	 {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_GETFILE,
+	 {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+    /*
+     * Client supports channel 2 extended, TLV(0x2711) based messages.
+     * Currently used only by ICQ clients. ICQ clients and clones use this GUID
+     * as message format sign. Trillian client use another GUID in channel 2
+     * messages to implement its own message format (trillian doesn't use
+     * TLV(x2711) in SecureIM channel 2 messages!).
+     */
+	{AIM_CAPS_ICQSERVERRELAY,
+	 {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	/*
+	 * Indeed, there are two of these.  The former appears to be correct, 
+	 * but in some versions of winaim, the second one is set.  Either they 
+	 * forgot to fix endianness, or they made a typo. It really doesn't 
+	 * matter which.
+	 */
+	{AIM_CAPS_GAMES,
+	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+	{AIM_CAPS_GAMES2,
+	 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_SENDBUDDYLIST,
+	 {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_UTF8,
+	 {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICQRTF,
+	 {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, 
+	  0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}},
+
+	{AIM_CAPS_ICQUNKNOWN,
+	 {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8,
+	  0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}},
+
+	{AIM_CAPS_EMPTY,
+	 {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+
+	{AIM_CAPS_TRILLIANCRYPT,
+	 {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb,
+	  0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}},
+
+	{AIM_CAPS_APINFO, 
+     {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6,
+      0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}},
+
+	{AIM_CAPS_INTEROP,
+	 {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1,
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_ICHAT,
+	 {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, 
+	  0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}},
+
+	{AIM_CAPS_LAST}
+};
+
+/*
+ * This still takes a length parameter even with a bstream because capabilities
+ * are not naturally bounded.
+ * 
+ */
+guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len)
+{
+	guint32 flags = 0;
+	int offset;
+
+	for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) {
+		guint8 *cap;
+		int i, identified;
+
+		cap = aimbs_getraw(bs, 0x10);
+
+		for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) {
+
+			if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) {
+				flags |= aim_caps[i].flag;
+				identified++;
+				break; /* should only match once... */
+
+			}
+		}
+
+		if (!identified) {
+			/*FIXME*/
+			g_strdup_printf("unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
+					cap[0], cap[1], cap[2], cap[3],
+					cap[4], cap[5],
+					cap[6], cap[7],
+					cap[8], cap[9],
+					cap[10], cap[11], cap[12], cap[13],
+					cap[14], cap[15]);
+			
+		}
+
+		g_free(cap);
+	}
+
+	return flags;
+}
+
+int aim_putcap(aim_bstream_t *bs, guint32 caps)
+{
+	int i;
+
+	if (!bs)
+		return -EINVAL;
+
+	for (i = 0; aim_bstream_empty(bs); i++) {
+
+		if (aim_caps[i].flag == AIM_CAPS_LAST)
+			break;
+
+		if (caps & aim_caps[i].flag)
+			aimbs_putraw(bs, aim_caps[i].data, 0x10);
+
+	}
+
+	return 0;
+}
+
+/*
+ * AIM is fairly regular about providing user info.  This is a generic 
+ * routine to extract it in its standard form.
+ */
+int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo)
+{
+	int curtlv, tlvcnt;
+	guint8 snlen;
+
+	if (!bs || !outinfo)
+		return -EINVAL;
+
+	/* Clear out old data first */
+	memset(outinfo, 0x00, sizeof(aim_userinfo_t));
+
+	/*
+	 * Screen name.  Stored as an unterminated string prepended with a 
+	 * byte containing its length.
+	 */
+	snlen = aimbs_get8(bs);
+	aimbs_getrawbuf(bs, (guint8 *)outinfo->sn, snlen);
+
+	/*
+	 * Warning Level.  Stored as an unsigned short.
+	 */
+	outinfo->warnlevel = aimbs_get16(bs);
+
+	/*
+	 * TLV Count. Unsigned short representing the number of 
+	 * Type-Length-Value triples that follow.
+	 */
+	tlvcnt = aimbs_get16(bs);
+
+	/* 
+	 * Parse out the Type-Length-Value triples as they're found.
+	 */
+	for (curtlv = 0; curtlv < tlvcnt; curtlv++) {
+		int endpos;
+		guint16 type, length;
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+		endpos = aim_bstream_curpos(bs) + length;
+
+		if (type == 0x0001) {
+			/*
+			 * Type = 0x0001: User flags
+			 * 
+			 * Specified as any of the following ORed together:
+			 *      0x0001  Trial (user less than 60days)
+			 *      0x0002  Unknown bit 2
+			 *      0x0004  AOL Main Service user
+			 *      0x0008  Unknown bit 4
+			 *      0x0010  Free (AIM) user 
+			 *      0x0020  Away
+			 *      0x0400  ActiveBuddy
+			 *
+			 */
+			outinfo->flags = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_FLAGS;
+
+		} else if (type == 0x0002) {
+			/*
+			 * Type = 0x0002: Member-Since date. 
+			 *
+			 * The time/date that the user originally registered for
+			 * the service, stored in time_t format.
+			 */
+			outinfo->membersince = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE;
+
+		} else if (type == 0x0003) {
+			/*
+			 * Type = 0x0003: On-Since date.
+			 *
+			 * The time/date that the user started their current 
+			 * session, stored in time_t format.
+			 */
+			outinfo->onlinesince = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE;
+
+		} else if (type == 0x0004) {
+			/*
+			 * Type = 0x0004: Idle time.
+			 *
+			 * Number of seconds since the user actively used the 
+			 * service.
+			 *
+			 * Note that the client tells the server when to start
+			 * counting idle times, so this may or may not be 
+			 * related to reality.
+			 */
+			outinfo->idletime = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_IDLE;
+
+		} else if (type == 0x0006) {
+			/*
+			 * Type = 0x0006: ICQ Online Status
+			 *
+			 * ICQ's Away/DND/etc "enriched" status. Some decoding 
+			 * of values done by Scott <darkagl@pcnet.com>
+			 */
+			aimbs_get16(bs);
+			outinfo->icqinfo.status = aimbs_get16(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS;
+
+		} else if (type == 0x000a) {
+			/*
+			 * Type = 0x000a
+			 *
+			 * ICQ User IP Address.
+			 * Ahh, the joy of ICQ security.
+			 */
+			outinfo->icqinfo.ipaddr = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR;
+
+		} else if (type == 0x000c) {
+			/* 
+			 * Type = 0x000c
+			 *
+			 * random crap containing the IP address,
+			 * apparently a port number, and some Other Stuff.
+			 *
+			 */
+			aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25);
+			outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA;
+
+		} else if (type == 0x000d) {
+			/*
+			 * Type = 0x000d
+			 *
+			 * Capability information.
+			 *
+			 */
+			outinfo->capabilities = aim_getcap(sess, bs, length);
+			outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES;
+
+		} else if (type == 0x000e) {
+			/*
+			 * Type = 0x000e
+			 *
+			 * Unknown.  Always of zero length, and always only
+			 * on AOL users.
+			 *
+			 * Ignore.
+			 *
+			 */
+
+		} else if ((type == 0x000f) || (type == 0x0010)) {
+			/*
+			 * Type = 0x000f: Session Length. (AIM)
+			 * Type = 0x0010: Session Length. (AOL)
+			 *
+			 * The duration, in seconds, of the user's current 
+			 * session.
+			 *
+			 * Which TLV type this comes in depends on the
+			 * service the user is using (AIM or AOL).
+			 *
+			 */
+			outinfo->sessionlen = aimbs_get32(bs);
+			outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN;
+
+		} else {
+
+			/*
+			 * Reaching here indicates that either AOL has
+			 * added yet another TLV for us to deal with, 
+			 * or the parsing has gone Terribly Wrong.
+			 *
+			 * Either way, inform the owner and attempt
+			 * recovery.
+			 *
+			 */
+#ifdef DEBUG
+			// do_error_dialog(sess->aux_data, G_STRLOC, "Unknown TLV encountered");
+#endif
+
+		}
+
+		/* Save ourselves. */
+		aim_bstream_setpos(bs, endpos);
+	}
+
+	return 0;
+}
+
+/*
+ * Inverse of aim_extractuserinfo()
+ */
+int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info)
+{
+	aim_tlvlist_t *tlvlist = NULL;
+
+	if (!bs || !info)
+		return -EINVAL;
+
+	aimbs_put8(bs, strlen(info->sn));
+	aimbs_putraw(bs, (guint8 *)info->sn, strlen(info->sn));
+
+	aimbs_put16(bs, info->warnlevel);
+
+
+	aim_addtlvtochain16(&tlvlist, 0x0001, info->flags);
+	aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince);
+	aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince);
+	aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime);
+
+#if ICQ_OSCAR_SUPPORT
+	if (atoi(info->sn) != 0) {
+		aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status);
+		aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr);
+	}
+#endif
+
+	aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities);
+
+	aim_addtlvtochain32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen);
+
+	aimbs_put16(bs, aim_counttlvchain(&tlvlist));
+	aim_writetlvchain(bs, &tlvlist);
+	aim_freetlvchain(&tlvlist);
+
+	return 0;
+}
+
+int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !info)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid);
+	aim_putuserinfo(&fr->data, info);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Huh? What is this?
+ */
+int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid);
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Normally contains:
+ *   t(0001)  - short containing max profile length (value = 1024)
+ *   t(0002)  - short - unknown (value = 16) [max MIME type length?]
+ *   t(0003)  - short - unknown (value = 10)
+ *   t(0004)  - short - unknown (value = 2048) [ICQ only?]
+ */
+static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_tlvlist_t *tlvlist;
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	guint16 maxsiglen = 0;
+
+	tlvlist = aim_readtlvchain(bs);
+
+	if (aim_gettlv(tlvlist, 0x0001, 1))
+		maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, maxsiglen);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_userinfo_t userinfo;
+	char *text_encoding = NULL, *text = NULL;
+	guint16 text_length = 0;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	aim_tlv_t *tlv;
+	aim_snac_t *origsnac = NULL;
+	struct aim_priv_inforeq *inforeq;
+	int ret = 0;
+
+	origsnac = aim_remsnac(sess, snac->id);
+
+	if (!origsnac || !origsnac->data) {
+		do_error_dialog(sess->aux_data, "major problem: no snac stored!", "Gaim");
+		return 0;
+	}
+
+	inforeq = (struct aim_priv_inforeq *)origsnac->data;
+
+	if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) &&
+			(inforeq->infotype != AIM_GETINFO_AWAYMESSAGE) &&
+			(inforeq->infotype != AIM_GETINFO_CAPABILITIES)) {
+		do_error_dialog(sess->aux_data, "unknown infotype in request!", "Gaim");
+		return 0;
+	}
+
+	aim_extractuserinfo(sess, bs, &userinfo);
+
+	tlvlist = aim_readtlvchain(bs);
+
+	/* 
+	 * Depending on what informational text was requested, different
+	 * TLVs will appear here.
+	 *
+	 * Profile will be 1 and 2, away message will be 3 and 4, caps
+	 * will be 5.
+	 */
+	if (inforeq->infotype == AIM_GETINFO_GENERALINFO) {
+		text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1);
+		if((tlv = aim_gettlv(tlvlist, 0x0002, 1))) {
+			text = g_new0(char, tlv->length);
+			memcpy(text, tlv->value, tlv->length);
+			text_length = tlv->length;
+		}
+	} else if (inforeq->infotype == AIM_GETINFO_AWAYMESSAGE) {
+		text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1);
+		if((tlv = aim_gettlv(tlvlist, 0x0004, 1))) {
+			text = g_new0(char, tlv->length);
+			memcpy(text, tlv->value, tlv->length);
+			text_length = tlv->length;
+		}
+	} else if (inforeq->infotype == AIM_GETINFO_CAPABILITIES) {
+		aim_tlv_t *ct;
+
+		if ((ct = aim_gettlv(tlvlist, 0x0005, 1))) {
+			aim_bstream_t cbs;
+
+			aim_bstream_init(&cbs, ct->value, ct->length);
+
+			userinfo.capabilities = aim_getcap(sess, &cbs, ct->length);
+			userinfo.present = AIM_USERINFO_PRESENT_CAPABILITIES;
+		}
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text, text_length);
+
+	g_free(text_encoding);
+	g_free(text);
+
+	aim_freetlvchain(&tlvlist);
+
+	if (origsnac)
+		g_free(origsnac->data);
+	g_free(origsnac);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return rights(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0006)
+		return userinfo(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int locate_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0002;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "locate", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
Index: protocols/oscar/info.h
===================================================================
--- protocols/oscar/info.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/info.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,44 @@
+#ifndef __OSCAR_INFO_H__
+#define __OSCAR_INFO_H__
+
+#define AIM_CB_FAM_LOC 0x0002
+
+/*
+ * SNAC Family: Location Services.
+ */ 
+#define AIM_CB_LOC_ERROR 0x0001
+#define AIM_CB_LOC_REQRIGHTS 0x0002
+#define AIM_CB_LOC_RIGHTSINFO 0x0003
+#define AIM_CB_LOC_SETUSERINFO 0x0004
+#define AIM_CB_LOC_REQUSERINFO 0x0005
+#define AIM_CB_LOC_USERINFO 0x0006
+#define AIM_CB_LOC_WATCHERSUBREQ 0x0007
+#define AIM_CB_LOC_WATCHERNOT 0x0008
+#define AIM_CB_LOC_DEFAULT 0xffff
+
+#define AIM_CAPS_BUDDYICON      0x00000001
+#define AIM_CAPS_VOICE          0x00000002
+#define AIM_CAPS_IMIMAGE        0x00000004
+#define AIM_CAPS_CHAT           0x00000008
+#define AIM_CAPS_GETFILE        0x00000010
+#define AIM_CAPS_SENDFILE       0x00000020
+#define AIM_CAPS_GAMES          0x00000040
+#define AIM_CAPS_SAVESTOCKS     0x00000080
+#define AIM_CAPS_SENDBUDDYLIST  0x00000100
+#define AIM_CAPS_GAMES2         0x00000200
+#define AIM_CAPS_ICQ            0x00000400
+#define AIM_CAPS_APINFO         0x00000800
+#define AIM_CAPS_ICQRTF	        0x00001000
+#define AIM_CAPS_EMPTY	        0x00002000
+#define AIM_CAPS_ICQSERVERRELAY 0x00004000
+#define AIM_CAPS_ICQUNKNOWN     0x00008000
+#define AIM_CAPS_TRILLIANCRYPT  0x00010000
+#define AIM_CAPS_UTF8           0x00020000
+#define AIM_CAPS_INTEROP        0x00040000
+#define AIM_CAPS_ICHAT          0x00080000
+#define AIM_CAPS_EXTCHAN2       0x00100000
+#define AIM_CAPS_LAST           0x00200000
+
+int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn);
+
+#endif /* __OSCAR_INFO_H__ */
Index: protocols/oscar/misc.c
===================================================================
--- protocols/oscar/misc.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/misc.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,396 @@
+
+/*
+ * aim_misc.c
+ *
+ * TODO: Seperate a lot of this into an aim_bos.c.
+ *
+ * Other things...
+ *
+ *   - Idle setting 
+ * 
+ *
+ */
+
+#include <aim.h> 
+
+/*
+ * aim_bos_setbuddylist(buddylist)
+ *
+ * This just builds the "set buddy list" command then queues it.
+ *
+ * buddy_list = "Screen Name One&ScreenNameTwo&";
+ *
+ * TODO: Clean this up.  
+ *
+ * XXX: I can't stress the TODO enough.
+ *
+ */
+int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int len = 0;
+	char *localcpy = NULL;
+	char *tmpptr = NULL;
+
+	if (!buddy_list || !(localcpy = g_strdup(buddy_list))) 
+		return -EINVAL;
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+		len += 1 + strlen(tmpptr);
+		tmpptr = strtok(NULL, "&");
+	}
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid);
+
+	strncpy(localcpy, buddy_list, strlen(buddy_list) + 1);
+
+	for (tmpptr = strtok(localcpy, "&"); tmpptr; ) {
+
+		aimbs_put8(&fr->data, strlen(tmpptr));
+		aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr));
+		tmpptr = strtok(NULL, "&");
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	g_free(localcpy);
+
+	return 0;
+}
+
+/* 
+ * aim_bos_setprofile(profile)
+ *
+ * Gives BOS your profile.
+ * 
+ */
+int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps)
+{
+	static const char defencoding[] = {"text/aolrtf; charset=\"utf-8\""};
+	aim_frame_t *fr;
+	aim_tlvlist_t *tl = NULL;
+	aim_snacid_t snacid;
+
+	/* Build to packet first to get real length */
+	if (profile) {
+		aim_addtlvtochain_raw(&tl, 0x0001, strlen(defencoding), (guint8 *)defencoding);
+		aim_addtlvtochain_raw(&tl, 0x0002, strlen(profile), (guint8 *)profile);
+	}
+
+	/*
+	 * So here's how this works:
+	 *   - You are away when you have a non-zero-length type 4 TLV stored.
+	 *   - You become unaway when you clear the TLV with a zero-length
+	 *       type 4 TLV.
+	 *   - If you do not send the type 4 TLV, your status does not change
+	 *       (that is, if you were away, you'll remain away).
+	 */
+	if (awaymsg) {
+		if (strlen(awaymsg)) {
+			aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), (guint8 *)defencoding);
+			aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), (guint8 *)awaymsg);
+		} else
+			aim_addtlvtochain_noval(&tl, 0x0004);
+	}
+
+	aim_addtlvtochain_caps(&tl, 0x0005, caps);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * aim_bos_reqbuddyrights()
+ *
+ * Request Buddy List rights.
+ *
+ */
+int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0003, 0x0002);
+}
+
+/*
+ * Send a warning to destsn.
+ * 
+ * Flags:
+ *  AIM_WARN_ANON  Send as an anonymous (doesn't count as much)
+ *
+ * returns -1 on error (couldn't alloc packet), 0 on success. 
+ *
+ */
+int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	guint16 outflags = 0x0000;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1);
+
+	aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid);
+
+	if (flags & AIM_WARN_ANON)
+		outflags |= 0x0001;
+
+	aimbs_put16(&fr->data, outflags); 
+	aimbs_put8(&fr->data, strlen(destsn));
+	aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn));
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Generic routine for sending commands.
+ *
+ *
+ * I know I can do this in a smarter way...but I'm not thinking straight
+ * right now...
+ *
+ * I had one big function that handled all three cases, but then it broke
+ * and I split it up into three.  But then I fixed it.  I just never went
+ * back to the single.  I don't see any advantage to doing it either way.
+ *
+ */
+int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid = 0x00000000;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+		return -ENOMEM;
+
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *longdata)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!longdata)
+		return aim_genericreq_n(sess, conn, family, subtype);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4)))
+		return -ENOMEM; 
+
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+	aimbs_put32(&fr->data, *longdata);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *shortdata)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!shortdata)
+		return aim_genericreq_n(sess, conn, family, subtype);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2)))
+		return -ENOMEM; 
+
+	snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, family, subtype, 0x0000, snacid);
+	aimbs_put16(&fr->data, *shortdata);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * aim_bos_reqlocaterights()
+ *
+ * Request Location services rights.
+ *
+ */
+int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0002, 0x0002);
+}
+
+/* 
+ * Set directory profile data (not the same as aim_bos_setprofile!)
+ *
+ * privacy: 1 to allow searching, 0 to disallow.
+ */
+int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy) 
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+
+	aim_addtlvtochain16(&tl, 0x000a, privacy);
+
+	if (first)
+		aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), (guint8 *)first);
+	if (last)
+		aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), (guint8 *)last);
+	if (middle)
+		aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), (guint8 *)middle);
+	if (maiden)
+		aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), (guint8 *)maiden);
+
+	if (state)
+		aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), (guint8 *)state);
+	if (city)
+		aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), (guint8 *)city);
+
+	if (nickname)
+		aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), (guint8 *)nickname);
+	if (zip)
+		aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), (guint8 *)zip);
+
+	if (street)
+		aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), (guint8 *)street);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0);
+	
+	aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* XXX pass these in better */
+int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+
+	/* ?? privacy ?? */
+	aim_addtlvtochain16(&tl, 0x000a, privacy);
+
+	if (interest1)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), (guint8 *)interest1);
+	if (interest2)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), (guint8 *)interest2);
+	if (interest3)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), (guint8 *)interest3);
+	if (interest4)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), (guint8 *)interest4);
+	if (interest5)
+		aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), (guint8 *)interest5);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0);
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Should be generic enough to handle the errors for all groups.
+ *
+ */
+static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	int error = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+
+	snac2 = aim_remsnac(sess, snac->id);
+
+	if (aim_bstream_empty(bs))
+		error = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL);
+
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0001)
+		return generror(sess, mod, rx, snac, bs);
+	else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) {
+		aim_rxcallback_t userfunc;
+
+		if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+			return userfunc(sess, rx);
+	}
+
+	return 0;
+}
+
+int misc_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0xffff;
+	mod->version = 0x0000;
+	mod->flags = AIM_MODFLAG_MULTIFAMILY;
+	strncpy(mod->name, "misc", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+
Index: protocols/oscar/msgcookie.c
===================================================================
--- protocols/oscar/msgcookie.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/msgcookie.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,196 @@
+/*
+ * Cookie Caching stuff. Adam wrote this, apparently just some
+ * derivatives of n's SNAC work. I cleaned it up, added comments.
+ * 
+ */
+
+/*
+ * I'm assuming that cookies are type-specific. that is, we can have
+ * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we
+ * lose some error checking. if we assume cookies are not type-specific and are
+ * wrong, we get quirky behavior when cookies step on each others' toes.
+ */
+
+#include <aim.h>
+#include "info.h"
+
+/**
+ * aim_cachecookie - appends a cookie to the cookie list
+ * @sess: session to add to
+ * @cookie: pointer to struct to append
+ *
+ * if cookie->cookie for type cookie->type is found, updates the
+ * ->addtime of the found structure; otherwise adds the given cookie
+ * to the cache
+ *
+ * returns -1 on error, 0 on append, 1 on update.  the cookie you pass
+ * in may be free'd, so don't count on its value after calling this!
+ * 
+ */
+int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie)
+{
+	aim_msgcookie_t *newcook;
+
+	if (!sess || !cookie)
+		return -EINVAL;
+
+	newcook = aim_checkcookie(sess, cookie->cookie, cookie->type);
+	
+	if (newcook == cookie) {
+		newcook->addtime = time(NULL);
+		return 1;
+	} else if (newcook)
+		aim_cookie_free(sess, newcook);
+
+	cookie->addtime = time(NULL);  
+
+	cookie->next = sess->msgcookies;
+	sess->msgcookies = cookie;
+
+	return 0;
+}
+
+/**
+ * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list)
+ * @sess: session to grab cookie from
+ * @cookie: cookie string to look for
+ * @type: cookie type to look for
+ *
+ * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process.
+ *
+ * if found, returns the struct; if none found (or on error), returns NULL:
+ */
+aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type)
+{
+	aim_msgcookie_t *cur, **prev;
+
+	if (!cookie || !sess->msgcookies)
+		return NULL;
+
+	for (prev = &sess->msgcookies; (cur = *prev); ) {
+		if ((cur->type == type) && 
+				(memcmp(cur->cookie, cookie, 8) == 0)) {
+			*prev = cur->next;
+			return cur;
+		}
+		prev = &cur->next;
+	}
+
+	return NULL;
+}
+
+/**
+ * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer.
+ * @c: pointer to the cookie string array
+ * @type: cookie type to use
+ * @data: data to be cached with the cookie
+ *
+ * returns NULL on error, a pointer to the newly-allocated cookie on
+ * success.
+ *
+ */
+aim_msgcookie_t *aim_mkcookie(guint8 *c, int type, void *data) 
+{
+	aim_msgcookie_t *cookie;
+
+	if (!c)
+		return NULL;
+
+	if (!(cookie = g_new0(aim_msgcookie_t,1)))
+		return NULL;
+
+	cookie->data = data;
+	cookie->type = type;
+	memcpy(cookie->cookie, c, 8);
+
+	return cookie;
+}
+
+/**
+ * aim_checkcookie - check to see if a cookietuple has been cached
+ * @sess: session to check for the cookie in
+ * @cookie: pointer to the cookie string array
+ * @type: type of the cookie to look for
+ *
+ * this returns a pointer to the cookie struct (still in the list) on
+ * success; returns NULL on error/not found
+ *
+ */
+
+aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const guint8 *cookie, int type)
+{
+	aim_msgcookie_t *cur;
+
+	for (cur = sess->msgcookies; cur; cur = cur->next) {
+		if ((cur->type == type) && 
+				(memcmp(cur->cookie, cookie, 8) == 0))
+			return cur;   
+	}
+
+	return NULL;
+}
+
+#if 0 /* debugging feature */
+int aim_dumpcookie(aim_msgcookie_t *cookie) 
+{
+
+	if (!cookie)
+		return -EINVAL;
+
+	printf("\tCookie at %p: %d/%s with %p, next %p\n", 
+			cookie, cookie->type, cookie->cookie, 
+			cookie->data, cookie->next);
+
+	return 0;
+}
+#endif
+
+/**
+ * aim_cookie_free - free an aim_msgcookie_t struct
+ * @sess: session to remove the cookie from
+ * @cookiep: the address of a pointer to the cookie struct to remove
+ *
+ * this function removes the cookie *cookie from teh list of cookies
+ * in sess, and then frees all memory associated with it. including
+ * its data! if you want to use the private data after calling this,
+ * make sure you copy it first.
+ *
+ * returns -1 on error, 0 on success.
+ *
+ */
+int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie) 
+{
+	aim_msgcookie_t *cur, **prev;
+
+	if (!sess || !cookie)
+		return -EINVAL;
+
+	for (prev = &sess->msgcookies; (cur = *prev); ) {
+		if (cur == cookie)
+			*prev = cur->next;
+		else
+			prev = &cur->next;
+	}
+
+	g_free(cookie->data);
+	g_free(cookie);
+
+	return 0;
+} 
+
+/* XXX I hate switch */
+int aim_msgcookie_gettype(int reqclass) 
+{
+	/* XXX: hokey-assed. needs fixed. */
+	switch(reqclass) {
+	case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON;
+	case AIM_CAPS_VOICE: return AIM_COOKIETYPE_OFTVOICE;
+	case AIM_CAPS_IMIMAGE: return AIM_COOKIETYPE_OFTIMAGE;
+	case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT;
+	case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET;
+	case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND;
+	default: return AIM_COOKIETYPE_UNKNOWN;
+	}           
+}
+
+
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/oscar.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,2491 @@
+/*
+ * gaim
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "sock.h"
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <glib.h>
+#include "nogaim.h"
+#include "bitlbee.h"
+#include "proxy.h"
+
+#include "aim.h"
+#include "icq.h"
+#include "bos.h"
+#include "ssi.h"
+#include "im.h"
+#include "info.h"
+#include "buddylist.h"
+#include "chat.h"
+#include "chatnav.h"
+
+/* constants to identify proto_opts */
+#define USEROPT_AUTH      0
+#define USEROPT_AUTHPORT  1
+
+#define UC_AOL		0x02
+#define UC_ADMIN	0x04
+#define UC_UNCONFIRMED	0x08
+#define UC_NORMAL	0x10
+#define UC_AB		0x20
+#define UC_WIRELESS	0x40
+
+#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
+
+#define OSCAR_GROUP "Friends"
+
+/* Don't know if support for UTF8 is really working. For now it's UTF16 here.
+   static int gaim_caps = AIM_CAPS_UTF8; */
+
+static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY;
+static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};
+
+struct oscar_data {
+	aim_session_t *sess;
+	aim_conn_t *conn;
+
+	guint cnpa;
+	guint paspa;
+
+	GSList *create_rooms;
+
+	gboolean conf;
+	gboolean reqemail;
+	gboolean setemail;
+	char *email;
+	gboolean setnick;
+	char *newsn;
+	gboolean chpass;
+	char *oldp;
+	char *newp;
+
+	GSList *oscar_chats;
+
+	gboolean killme;
+	gboolean icq;
+	GSList *evilhack;
+
+	struct {
+		guint maxbuddies; /* max users you can watch */
+		guint maxwatchers; /* max users who can watch you */
+		guint maxpermits; /* max users on permit list */
+		guint maxdenies; /* max users on deny list */
+		guint maxsiglen; /* max size (bytes) of profile */
+		guint maxawaymsglen; /* max size (bytes) of posted away message */
+	} rights;
+};
+
+struct create_room {
+	char *name;
+	int exchange;
+};
+
+struct chat_connection {
+	char *name;
+	char *show; /* AOL did something funny to us */
+	guint16 exchange;
+	guint16 instance;
+	int fd; /* this is redundant since we have the conn below */
+	aim_conn_t *conn;
+	int inpa;
+	int id;
+	struct gaim_connection *gc; /* i hate this. */
+	struct conversation *cnv; /* bah. */
+	int maxlen;
+	int maxvis;
+};
+
+struct ask_direct {
+	struct gaim_connection *gc;
+	char *sn;
+	char ip[64];
+	guint8 cookie[8];
+};
+
+struct icq_auth {
+	struct gaim_connection *gc;
+	guint32 uin;
+};
+
+static char *extract_name(const char *name) {
+	char *tmp;
+	int i, j;
+	char *x = strchr(name, '-');
+	if (!x) return NULL;
+	x = strchr(++x, '-');
+	if (!x) return NULL;
+	tmp = g_strdup(++x);
+
+	for (i = 0, j = 0; x[i]; i++) {
+		char hex[3];
+		if (x[i] != '%') {
+			tmp[j++] = x[i];
+			continue;
+		}
+		strncpy(hex, x + ++i, 2); hex[2] = 0;
+		i++;
+		tmp[j++] = (char)strtol(hex, NULL, 16);
+	}
+
+	tmp[j] = 0;
+	return tmp;
+}
+
+#if 0
+static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {
+	GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
+	struct chat_connection *c = NULL;
+
+	while (g) {
+		c = (struct chat_connection *)g->data;
+		if (c->id == id)
+			break;
+		g = g->next;
+		c = NULL;
+	}
+
+	return c;
+}
+#endif
+
+static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,
+							aim_conn_t *conn) {
+	GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;
+	struct chat_connection *c = NULL;
+
+	while (g) {
+		c = (struct chat_connection *)g->data;
+		if (c->conn == conn)
+			break;
+		g = g->next;
+		c = NULL;
+	}
+
+	return c;
+}
+
+static int gaim_parse_auth_resp  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_login      (aim_session_t *, aim_frame_t *, ...);
+static int gaim_handle_redirect  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_oncoming   (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_join        (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_leave       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...);
+static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...);
+static int gaim_bosrights        (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_bos      (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_admin    (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
+static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_icbm_param_info  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...);
+static int gaim_memrequest       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_selfinfo         (aim_session_t *, aim_frame_t *, ...);
+static int gaim_offlinemsg       (aim_session_t *, aim_frame_t *, ...);
+static int gaim_offlinemsgdone   (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parserights  (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parselist    (aim_session_t *, aim_frame_t *, ...);
+static int gaim_ssi_parseack     (aim_session_t *, aim_frame_t *, ...);
+
+static int gaim_icqinfo          (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parseaiminfo     (aim_session_t *, aim_frame_t *, ...);
+
+static char *msgerrreason[] = {
+	"Invalid error",
+	"Invalid SNAC",
+	"Rate to host",
+	"Rate to client",
+	"Not logged in",
+	"Service unavailable",
+	"Service not defined",
+	"Obsolete SNAC",
+	"Not supported by host",
+	"Not supported by client",
+	"Refused by client",
+	"Reply too big",
+	"Responses lost",
+	"Request denied",
+	"Busted SNAC payload",
+	"Insufficient rights",
+	"In local permit/deny",
+	"Too evil (sender)",
+	"Too evil (receiver)",
+	"User temporarily unavailable",
+	"No match",
+	"List overflow",
+	"Request ambiguous",
+	"Queue full",
+	"Not while on AOL"
+};
+static int msgerrreasonlen = 25;
+
+static void oscar_callback(gpointer data, gint source,
+				GaimInputCondition condition) {
+	aim_conn_t *conn = (aim_conn_t *)data;
+	aim_session_t *sess = aim_conn_getsess(conn);
+	struct gaim_connection *gc = sess ? sess->aux_data : NULL;
+	struct oscar_data *odata;
+
+	if (!gc) {
+		/* gc is null. we return, else we seg SIGSEG on next line. */
+		return;
+	}
+      
+	if (!g_slist_find(get_connections(), gc)) {
+		/* oh boy. this is probably bad. i guess the only thing we 
+		 * can really do is return? */
+		return;
+	}
+
+	odata = (struct oscar_data *)gc->proto_data;
+
+	if (condition & GAIM_INPUT_READ) {
+		if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+			if (aim_handlerendconnect(odata->sess, conn) < 0) {
+				aim_conn_kill(odata->sess, &conn);
+			}
+		} else {
+			if (aim_get_command(odata->sess, conn) >= 0) {
+				aim_rxdispatch(odata->sess);
+                                if (odata->killme)
+                                        signoff(gc);
+			} else {
+				if ((conn->type == AIM_CONN_TYPE_BOS) ||
+					   !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) {
+					hide_login_progress_error(gc, _("Disconnected."));
+					signoff(gc);
+				} else if (conn->type == AIM_CONN_TYPE_CHAT) {
+					struct chat_connection *c = find_oscar_chat_by_conn(gc, conn);
+					char buf[BUF_LONG];
+					c->conn = NULL;
+					if (c->inpa > 0)
+						gaim_input_remove(c->inpa);
+					c->inpa = 0;
+					c->fd = -1;
+					aim_conn_kill(odata->sess, &conn);
+					sprintf(buf, _("You have been disconnected from chat room %s."), c->name);
+					do_error_dialog(sess->aux_data, buf, _("Chat Error!"));
+				} else if (conn->type == AIM_CONN_TYPE_CHATNAV) {
+					if (odata->cnpa > 0)
+						gaim_input_remove(odata->cnpa);
+					odata->cnpa = 0;
+					while (odata->create_rooms) {
+						struct create_room *cr = odata->create_rooms->data;
+						g_free(cr->name);
+						odata->create_rooms =
+							g_slist_remove(odata->create_rooms, cr);
+						g_free(cr);
+						do_error_dialog(sess->aux_data, _("Chat is currently unavailable"),
+								_("Gaim - Chat"));
+					}
+					aim_conn_kill(odata->sess, &conn);
+				} else if (conn->type == AIM_CONN_TYPE_AUTH) {
+					if (odata->paspa > 0)
+						gaim_input_remove(odata->paspa);
+					odata->paspa = 0;
+					aim_conn_kill(odata->sess, &conn);
+				} else {
+					aim_conn_kill(odata->sess, &conn);
+				}
+			}
+		}
+	}
+}
+
+static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct gaim_connection *gc = data;
+	struct oscar_data *odata;
+	aim_session_t *sess;
+	aim_conn_t *conn;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		return;
+	}
+
+	odata = gc->proto_data;
+	sess = odata->sess;
+	conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+
+	if (source < 0) {
+		hide_login_progress(gc, _("Couldn't connect to host"));
+		signoff(gc);
+		return;
+	}
+
+	aim_conn_completeconnect(sess, conn);
+	gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ,
+			oscar_callback, conn);
+}
+
+static void oscar_login(struct aim_user *user) {
+	aim_session_t *sess;
+	aim_conn_t *conn;
+	char buf[256];
+	struct gaim_connection *gc = new_gaim_conn(user);
+	struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1);
+
+	if (isdigit(*user->username)) {
+		odata->icq = TRUE;
+		/* this is odd but it's necessary for a proper do_import and do_export */
+		gc->protocol = PROTO_ICQ;
+		gc->password[8] = 0;
+	} else {
+		gc->protocol = PROTO_TOC;
+		gc->flags |= OPT_CONN_HTML;
+	}
+
+	sess = g_new0(aim_session_t, 1);
+
+	aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0);
+
+	/* we need an immediate queue because we don't use a while-loop to
+	 * see if things need to be sent. */
+	aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL);
+	odata->sess = sess;
+	sess->aux_data = gc;
+
+	conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
+	if (conn == NULL) {
+		hide_login_progress(gc, _("Unable to login to AIM"));
+		signoff(gc);
+		return;
+	}
+	
+	if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 &&
+	    g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) {
+		serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails.");
+	}
+	
+	g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username);
+	set_login_progress(gc, 2, buf);
+
+	aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0);
+	aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0);
+
+	conn->status |= AIM_CONN_STATUS_INPROGRESS;
+	conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ?
+					user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER,
+				 user->proto_opt[USEROPT_AUTHPORT][0] ?
+					atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+				 oscar_login_connect, gc);
+	if (conn->fd < 0) {
+		hide_login_progress(gc, _("Couldn't connect to host"));
+		signoff(gc);
+		return;
+	}
+	aim_request_login(sess, conn, gc->username);
+}
+
+static void oscar_close(struct gaim_connection *gc) {
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	
+	while (odata->oscar_chats) {
+		struct chat_connection *n = odata->oscar_chats->data;
+		if (n->inpa > 0)
+			gaim_input_remove(n->inpa);
+		g_free(n->name);
+		g_free(n->show);
+		odata->oscar_chats = g_slist_remove(odata->oscar_chats, n);
+		g_free(n);
+	}
+	while (odata->create_rooms) {
+		struct create_room *cr = odata->create_rooms->data;
+		g_free(cr->name);
+		odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+		g_free(cr);
+	}
+	if (odata->email)
+		g_free(odata->email);
+	if (odata->newp)
+		g_free(odata->newp);
+	if (odata->oldp)
+		g_free(odata->oldp);
+	if (gc->inpa > 0)
+		gaim_input_remove(gc->inpa);
+	if (odata->cnpa > 0)
+		gaim_input_remove(odata->cnpa);
+	if (odata->paspa > 0)
+		gaim_input_remove(odata->paspa);
+	aim_session_kill(odata->sess);
+	g_free(odata->sess);
+	odata->sess = NULL;
+	g_free(gc->proto_data);
+	gc->proto_data = NULL;
+}
+
+static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) {
+	struct gaim_connection *gc = data;
+	struct oscar_data *odata;
+	aim_session_t *sess;
+	aim_conn_t *bosconn;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		return;
+	}
+
+	odata = gc->proto_data;
+	sess = odata->sess;
+	bosconn = odata->conn;
+
+	if (source < 0) {
+		hide_login_progress(gc, _("Could Not Connect"));
+		signoff(gc);
+		return;
+	}
+
+	aim_conn_completeconnect(sess, bosconn);
+	gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ,
+			oscar_callback, bosconn);
+	set_login_progress(gc, 4, _("Connection established, cookie sent"));
+}
+
+static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	struct aim_authresp_info *info;
+	int i; char *host; int port;
+	struct aim_user *user;
+	aim_conn_t *bosconn;
+
+	struct gaim_connection *gc = sess->aux_data;
+        struct oscar_data *od = gc->proto_data;
+	user = gc->user;
+	port = user->proto_opt[USEROPT_AUTHPORT][0] ?
+		atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+
+	va_start(ap, fr);
+	info = va_arg(ap, struct aim_authresp_info *);
+	va_end(ap);
+
+	if (info->errorcode || !info->bosip || !info->cookie) {
+		switch (info->errorcode) {
+		case 0x05:
+			/* Incorrect nick/password */
+			hide_login_progress(gc, _("Incorrect nickname or password."));
+//			plugin_event(event_error, (void *)980, 0, 0, 0);
+			break;
+		case 0x11:
+			/* Suspended account */
+			hide_login_progress(gc, _("Your account is currently suspended."));
+			break;
+		case 0x18:
+			/* connecting too frequently */
+			hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer."));
+			break;
+		case 0x1c:
+			/* client too old */
+			hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE));
+			break;
+		default:
+			hide_login_progress(gc, _("Authentication Failed"));
+			break;
+		}
+		od->killme = TRUE;
+		return 1;
+	}
+
+
+	aim_conn_kill(sess, &fr->conn);
+
+	bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL);
+	if (bosconn == NULL) {
+		hide_login_progress(gc, _("Internal Error"));
+		od->killme = TRUE;
+		return 0;
+	}
+
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0);
+
+	((struct oscar_data *)gc->proto_data)->conn = bosconn;
+	for (i = 0; i < (int)strlen(info->bosip); i++) {
+		if (info->bosip[i] == ':') {
+			port = atoi(&(info->bosip[i+1]));
+			break;
+		}
+	}
+	host = g_strndup(info->bosip, i);
+	bosconn->status |= AIM_CONN_STATUS_INPROGRESS;
+	bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc);
+	g_free(host);
+	if (bosconn->fd < 0) {
+		hide_login_progress(gc, _("Could Not Connect"));
+		od->killme = TRUE;
+		return 0;
+	}
+	aim_sendcookie(sess, bosconn, info->cookie);
+	gaim_input_remove(gc->inpa);
+
+	return 1;
+}
+
+struct pieceofcrap {
+	struct gaim_connection *gc;
+	unsigned long offset;
+	unsigned long len;
+	char *modname;
+	int fd;
+	aim_conn_t *conn;
+	unsigned int inpa;
+};
+
+static void damn_you(gpointer data, gint source, GaimInputCondition c)
+{
+	struct pieceofcrap *pos = data;
+	struct oscar_data *od = pos->gc->proto_data;
+	char in = '\0';
+	int x = 0;
+	unsigned char m[17];
+
+	while (read(pos->fd, &in, 1) == 1) {
+		if (in == '\n')
+			x++;
+		else if (in != '\r')
+			x = 0;
+		if (x == 2)
+			break;
+		in = '\0';
+	}
+	if (in != '\n') {
+		do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
+				" You may be disconnected shortly.", "Login Error");
+		gaim_input_remove(pos->inpa);
+		closesocket(pos->fd);
+		g_free(pos);
+		return;
+	}
+	read(pos->fd, m, 16);
+	m[16] = '\0';
+	gaim_input_remove(pos->inpa);
+	closesocket(pos->fd);
+	aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH);
+	g_free(pos);
+}
+
+static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) {
+	struct pieceofcrap *pos = data;
+	char buf[BUF_LONG];
+
+	if (source < 0) {
+		do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM."
+				" You may be disconnected shortly.", "Login Error");
+		if (pos->modname)
+			g_free(pos->modname);
+		g_free(pos);
+		return;
+	}
+
+	g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA
+			"?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n",
+			pos->offset, pos->len, pos->modname ? pos->modname : "");
+	write(pos->fd, buf, strlen(buf));
+	if (pos->modname)
+		g_free(pos->modname);
+	pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos);
+	return;
+}
+
+/* size of icbmui.ocm, the largest module in AIM 3.5 */
+#define AIM_MAX_FILE_SIZE 98304
+
+int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	struct pieceofcrap *pos;
+	guint32 offset, len;
+	char *modname;
+	int fd;
+
+	va_start(ap, fr);
+	offset = (guint32)va_arg(ap, unsigned long);
+	len = (guint32)va_arg(ap, unsigned long);
+	modname = va_arg(ap, char *);
+	va_end(ap);
+
+	if (len == 0) {
+		aim_sendmemblock(sess, fr->conn, offset, len, NULL,
+				AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
+		return 1;
+	}
+	/* uncomment this when you're convinced it's right. remember, it's been wrong before.
+	if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) {
+		char *buf;
+		int i = 8;
+		if (modname)
+			i += strlen(modname);
+		buf = g_malloc(i);
+		i = 0;
+		if (modname) {
+			memcpy(buf, modname, strlen(modname));
+			i += strlen(modname);
+		}
+		buf[i++] = offset & 0xff;
+		buf[i++] = (offset >> 8) & 0xff;
+		buf[i++] = (offset >> 16) & 0xff;
+		buf[i++] = (offset >> 24) & 0xff;
+		buf[i++] = len & 0xff;
+		buf[i++] = (len >> 8) & 0xff;
+		buf[i++] = (len >> 16) & 0xff;
+		buf[i++] = (len >> 24) & 0xff;
+		aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST);
+		g_free(buf);
+		return 1;
+	}
+	*/
+
+	pos = g_new0(struct pieceofcrap, 1);
+	pos->gc = sess->aux_data;
+	pos->conn = fr->conn;
+
+	pos->offset = offset;
+	pos->len = len;
+	pos->modname = modname ? g_strdup(modname) : NULL;
+
+	fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos);
+	if (fd < 0) {
+		if (pos->modname)
+			g_free(pos->modname);
+		g_free(pos);
+		do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM."
+				" You may be disconnected shortly.", "Login Error");
+	}
+	pos->fd = fd;
+
+	return 1;
+}
+
+static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) {
+#if 0
+	struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b};
+#else
+	struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD;
+#endif
+	char *key;
+	va_list ap;
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, fr);
+	key = va_arg(ap, char *);
+	va_end(ap);
+
+	aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key);
+
+	return 1;
+}
+
+static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct chat_connection *chatcon;
+	static int id = 1;
+
+	aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0);
+
+	aim_clientready(sess, fr->conn);
+
+	chatcon = find_oscar_chat_by_conn(gc, fr->conn);
+	chatcon->id = id;
+	chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show);
+
+	return 1;
+}
+
+static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) {
+
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0);
+	aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0);
+
+	aim_clientready(sess, fr->conn);
+
+	aim_chatnav_reqrights(sess, fr->conn);
+
+	return 1;
+}
+
+static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) {
+	struct gaim_connection *gc = data;
+	struct oscar_data *odata;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		return;
+	}
+
+	odata = gc->proto_data;
+	sess = odata->sess;
+	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV);
+
+	if (source < 0) {
+		aim_conn_kill(sess, &tstconn);
+		return;
+	}
+
+	aim_conn_completeconnect(sess, tstconn);
+	odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
+					oscar_callback, tstconn);
+}
+
+static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct gaim_connection *gc = data;
+	struct oscar_data *odata;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		return;
+	}
+
+	odata = gc->proto_data;
+	sess = odata->sess;
+	tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH);
+
+	if (source < 0) {
+		aim_conn_kill(sess, &tstconn);
+		return;
+	}
+
+	aim_conn_completeconnect(sess, tstconn);
+	odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ,
+				oscar_callback, tstconn);
+}
+
+static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct chat_connection *ccon = data;
+	struct gaim_connection *gc = ccon->gc;
+	struct oscar_data *odata;
+	aim_session_t *sess;
+	aim_conn_t *tstconn;
+
+	if (!g_slist_find(get_connections(), gc)) {
+		closesocket(source);
+		g_free(ccon->show);
+		g_free(ccon->name);
+		g_free(ccon);
+		return;
+	}
+
+	odata = gc->proto_data;
+	sess = odata->sess;
+	tstconn = ccon->conn;
+
+	if (source < 0) {
+		aim_conn_kill(sess, &tstconn);
+		g_free(ccon->show);
+		g_free(ccon->name);
+		g_free(ccon);
+		return;
+	}
+
+	aim_conn_completeconnect(sess, ccon->conn);
+	ccon->inpa = gaim_input_add(tstconn->fd,
+			GAIM_INPUT_READ,
+			oscar_callback, tstconn);
+	odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon);
+}
+
+/* Hrmph. I don't know how to make this look better. --mid */
+static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	struct aim_redirect_data *redir;
+	struct gaim_connection *gc = sess->aux_data;
+	struct aim_user *user = gc->user;
+	aim_conn_t *tstconn;
+	int i;
+	char *host;
+	int port;
+
+	port = user->proto_opt[USEROPT_AUTHPORT][0] ?
+		atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT,
+
+	va_start(ap, fr);
+	redir = va_arg(ap, struct aim_redirect_data *);
+	va_end(ap);
+
+	for (i = 0; i < (int)strlen(redir->ip); i++) {
+		if (redir->ip[i] == ':') {
+			port = atoi(&(redir->ip[i+1]));
+			break;
+		}
+	}
+	host = g_strndup(redir->ip, i);
+
+	switch(redir->group) {
+	case 0x7: /* Authorizer */
+		tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL);
+		if (tstconn == NULL) {
+			g_free(host);
+			return 1;
+		}
+		aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0);
+//		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0);
+//		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0);
+//		aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0);
+
+		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
+		tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc);
+		if (tstconn->fd < 0) {
+			aim_conn_kill(sess, &tstconn);
+			g_free(host);
+			return 1;
+		}
+		aim_sendcookie(sess, tstconn, redir->cookie);
+		break;
+	case 0xd: /* ChatNav */
+		tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL);
+		if (tstconn == NULL) {
+			g_free(host);
+			return 1;
+		}
+		aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0);
+
+		tstconn->status |= AIM_CONN_STATUS_INPROGRESS;
+		tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc);
+		if (tstconn->fd < 0) {
+			aim_conn_kill(sess, &tstconn);
+			g_free(host);
+			return 1;
+		}
+		aim_sendcookie(sess, tstconn, redir->cookie);
+		break;
+	case 0xe: /* Chat */
+		{
+		struct chat_connection *ccon;
+
+		tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL);
+		if (tstconn == NULL) {
+			g_free(host);
+			return 1;
+		}
+
+		aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0);
+
+		ccon = g_new0(struct chat_connection, 1);
+		ccon->conn = tstconn;
+		ccon->gc = gc;
+		ccon->fd = -1;
+		ccon->name = g_strdup(redir->chat.room);
+		ccon->exchange = redir->chat.exchange;
+		ccon->instance = redir->chat.instance;
+		ccon->show = extract_name(redir->chat.room);
+		
+		ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS;
+		ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon);
+		if (ccon->conn->fd < 0) {
+			aim_conn_kill(sess, &tstconn);
+			g_free(host);
+			g_free(ccon->show);
+			g_free(ccon->name);
+			g_free(ccon);
+			return 1;
+		}
+		aim_sendcookie(sess, tstconn, redir->cookie);
+		}
+		break;
+	default: /* huh? */
+		break;
+	}
+
+	g_free(host);
+	return 1;
+}
+
+static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
+	aim_userinfo_t *info;
+	time_t time_idle = 0, signon = 0;
+	int type = 0;
+	int caps = 0;
+	char *tmp;
+
+	va_list ap;
+	va_start(ap, fr);
+	info = va_arg(ap, aim_userinfo_t *);
+	va_end(ap);
+
+	if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES)
+		caps = info->capabilities;
+	if (info->flags & AIM_FLAG_ACTIVEBUDDY)
+		type |= UC_AB;
+
+	if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) {
+		if (info->flags & AIM_FLAG_UNCONFIRMED)
+			type |= UC_UNCONFIRMED;
+		if (info->flags & AIM_FLAG_ADMINISTRATOR)
+			type |= UC_ADMIN;
+		if (info->flags & AIM_FLAG_AOL)
+			type |= UC_AOL;
+		if (info->flags & AIM_FLAG_FREE)
+			type |= UC_NORMAL;
+		if (info->flags & AIM_FLAG_AWAY)
+			type |= UC_UNAVAILABLE;
+		if (info->flags & AIM_FLAG_WIRELESS)
+			type |= UC_WIRELESS;
+	}
+	if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
+		type = (info->icqinfo.status << 7);
+		if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
+		      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
+			type |= UC_UNAVAILABLE;
+		}
+	}
+
+	if (caps & AIM_CAPS_ICQ)
+		caps ^= AIM_CAPS_ICQ;
+
+	if (info->present & AIM_USERINFO_PRESENT_IDLE) {
+		time(&time_idle);
+		time_idle -= info->idletime*60;
+	}
+
+	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)
+		signon = time(NULL) - info->sessionlen;
+
+	tmp = g_strdup(normalize(gc->username));
+	if (!strcmp(tmp, normalize(info->sn)))
+		g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn);
+	g_free(tmp);
+
+	serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon,
+			time_idle, type, caps);
+
+	return 1;
+}
+
+static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) {
+	aim_userinfo_t *info;
+	va_list ap;
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, fr);
+	info = va_arg(ap, aim_userinfo_t *);
+	va_end(ap);
+
+	serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0);
+
+	return 1;
+}
+
+static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) {
+	char *tmp = g_malloc(BUF_LONG + 1);
+	struct gaim_connection *gc = sess->aux_data;
+	int flags = 0;
+	
+	if (args->icbmflags & AIM_IMFLAGS_AWAY)
+		flags |= IM_FLAG_AWAY;
+	
+	if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) {
+		char *src;
+		
+		if (args->icbmflags & AIM_IMFLAGS_UNICODE)
+			src = "UNICODEBIG";
+		else
+			src = "ISO8859-1";
+		
+		/* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */
+		if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) {
+			// Successfully converted!
+		} else if (args->icbmflags & AIM_IMFLAGS_UNICODE) {
+			int i;
+			
+			for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) {
+				unsigned short uni;
+				
+				uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff);
+	
+				if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */
+					g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni);
+				} else { /* something else, do UNICODE entity */
+					g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni);
+				}
+			}
+		} else {
+			g_snprintf(tmp, BUF_LONG, "%s", args->msg);
+		}
+	} else
+		g_snprintf(tmp, BUF_LONG, "%s", args->msg);
+	
+	strip_linefeed(tmp);
+	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
+	g_free(tmp);
+	
+	return 1;
+}
+
+static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
+#if 0
+	struct gaim_connection *gc = sess->aux_data;
+#endif
+
+	if (args->status != AIM_RENDEZVOUS_PROPOSE)
+		return 1;
+#if 0
+	if (args->reqclass & AIM_CAPS_CHAT) {
+		char *name = extract_name(args->info.chat.roominfo.name);
+		int *exch = g_new0(int, 1);
+		GList *m = NULL;
+		m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));
+		*exch = args->info.chat.roominfo.exchange;
+		m = g_list_append(m, exch);
+		serv_got_chat_invite(gc,
+				     name ? name : args->info.chat.roominfo.name,
+				     userinfo->sn,
+				     (char *)args->msg,
+				     m);
+		if (name)
+			g_free(name);
+	}
+#endif
+	return 1;
+}
+
+static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
+	char *uin, message;
+	struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
+	
+	uin = g_strdup_printf("%u", data->uin);
+	message = 0;
+	aim_ssi_auth_reply(od->sess, od->conn, uin, 1, "");
+	// aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message);
+	show_got_added(data->gc, NULL, uin, NULL, NULL);
+	
+	g_free(uin);
+	g_free(data);
+}
+
+static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
+	char *uin, *message;
+	struct oscar_data *od = (struct oscar_data *)data->gc->proto_data;
+	
+	uin = g_strdup_printf("%u", data->uin);
+	message = g_strdup_printf("No reason given.");
+	aim_ssi_auth_reply(od->sess, od->conn, uin, 0, "");
+	// aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message);
+	g_free(message);
+	
+	g_free(uin);
+	g_free(data);
+}
+
+/*
+ * For when other people ask you for authorization
+ */
+static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) {
+	struct icq_auth *data = g_new(struct icq_auth, 1);
+	char *reason = NULL;
+	char *dialog_msg;
+	
+	if (strlen(msg) > 6)
+		reason = msg + 6;
+	
+	dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason:\n\n%s", uin, reason ? reason : "No reason given.");
+	data->gc = gc;
+	data->uin = uin;
+	do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny);
+	g_free(dialog_msg);
+}
+
+static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) {
+	struct gaim_connection *gc = sess->aux_data;
+
+	switch (args->type) {
+		case 0x0001: { /* An almost-normal instant message.  Mac ICQ sends this.  It's peculiar. */
+			char *uin, *message;
+			uin = g_strdup_printf("%u", args->uin);
+			message = g_strdup(args->msg);
+			strip_linefeed(message);
+			serv_got_im(gc, uin, message, 0, time(NULL), -1);
+			g_free(uin);
+			g_free(message);
+		} break;
+
+		case 0x0004: { /* Someone sent you a URL */
+		  	char *uin, *message;
+			char **m;
+	
+			uin = g_strdup_printf("%u", args->uin);
+			m = g_strsplit(args->msg, "\376", 2);
+
+			if ((strlen(m[0]) != 0)) {
+			  message = g_strjoinv(" -- ", m);
+			} else {
+			  message = m[1];
+			}
+
+			strip_linefeed(message);
+			serv_got_im(gc, uin, message, 0, time(NULL), -1);
+			g_free(uin);
+			g_free(m);
+			g_free(message);
+		} break;
+		
+		case 0x0006: { /* Someone requested authorization */
+			gaim_icq_authask(gc, args->uin, args->msg);
+		} break;
+
+		case 0x0007: { /* Someone has denied you authorization */
+			serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
+		} break;
+
+		case 0x0008: { /* Someone has granted you authorization */
+			serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") );
+		} break;
+
+		case 0x0012: {
+			/* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
+		} break;
+
+		default: {;
+		} break;
+	}
+
+	return 1;
+}
+/*
+int handle_cmp_aim(const char * a, const char * b) {
+	return handle_cmp(a, b, PROTO_TOC);
+}
+*/
+static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) {
+	int channel, ret = 0;
+	aim_userinfo_t *userinfo;
+	va_list ap;
+
+	va_start(ap, fr);
+	channel = va_arg(ap, int);
+	userinfo = va_arg(ap, aim_userinfo_t *);
+
+    if (set_getint(sess->aux_data, "debug")) {
+        serv_got_crap(sess->aux_data, "channel %i called", channel);
+    }
+
+	switch (channel) {
+		case 1: { /* standard message */
+			struct aim_incomingim_ch1_args *args;
+			args = va_arg(ap, struct aim_incomingim_ch1_args *);
+			ret = incomingim_chan1(sess, fr->conn, userinfo, args);
+		} break;
+
+		case 2: { /* rendevous */
+			struct aim_incomingim_ch2_args *args;
+			args = va_arg(ap, struct aim_incomingim_ch2_args *);
+			ret = incomingim_chan2(sess, fr->conn, userinfo, args);
+		} break;
+
+		case 4: { /* ICQ */
+			struct aim_incomingim_ch4_args *args;
+			args = va_arg(ap, struct aim_incomingim_ch4_args *);
+			ret = incomingim_chan4(sess, fr->conn, userinfo, args);
+		} break;
+
+		default: {;
+		} break;
+	}
+
+	va_end(ap);
+
+	return ret;
+}
+
+static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	guint16 chan, nummissed, reason;
+	aim_userinfo_t *userinfo;
+	char buf[1024];
+
+	va_start(ap, fr);
+	chan = (guint16)va_arg(ap, unsigned int);
+	userinfo = va_arg(ap, aim_userinfo_t *);
+	nummissed = (guint16)va_arg(ap, unsigned int);
+	reason = (guint16)va_arg(ap, unsigned int);
+	va_end(ap);
+
+	switch(reason) {
+		case 0:
+			/* Invalid (0) */
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ? 
+				   _("You missed %d message from %s because it was invalid.") :
+				   _("You missed %d messages from %s because they were invalid."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+		case 1:
+			/* Message too large */
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ?
+				   _("You missed %d message from %s because it was too large.") :
+				   _("You missed %d messages from %s because they were too large."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+		case 2:
+			/* Rate exceeded */
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ? 
+				   _("You missed %d message from %s because the rate limit has been exceeded.") :
+				   _("You missed %d messages from %s because the rate limit has been exceeded."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+		case 3:
+			/* Evil Sender */
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ?
+				   _("You missed %d message from %s because it was too evil.") : 
+				   _("You missed %d messages from %s because they are too evil."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+		case 4:
+			/* Evil Receiver */
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ? 
+				   _("You missed %d message from %s because you are too evil.") :
+				   _("You missed %d messages from %s because you are too evil."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+		default:
+			g_snprintf(buf,
+				   sizeof(buf),
+				   nummissed == 1 ? 
+				   _("You missed %d message from %s for unknown reasons.") :
+				   _("You missed %d messages from %s for unknown reasons."),
+				   nummissed,
+				   userinfo->sn);
+			break;
+	}
+	do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+	return 1;
+}
+
+static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	guint16 reason;
+	char *m;
+
+	va_start(ap, fr);
+	reason = (guint16)va_arg(ap, unsigned int);
+	va_end(ap);
+
+	m = g_strdup_printf(_("SNAC threw error: %s\n"),
+			reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error");
+	do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error"));
+	g_free(m);
+
+	return 1;
+}
+
+static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	char *destn;
+	guint16 reason;
+	char buf[1024];
+
+	va_start(ap, fr);
+	reason = (guint16)va_arg(ap, unsigned int);
+	destn = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(buf, _("Your message to %s did not get sent: %s"), destn,
+			(reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+	do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+	return 1;
+}
+
+static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	char *destn;
+	guint16 reason;
+	char buf[1024];
+
+	va_start(ap, fr);
+	reason = (guint16)va_arg(ap, unsigned int);
+	destn = va_arg(ap, char *);
+	va_end(ap);
+
+	sprintf(buf, _("User information for %s unavailable: %s"), destn,
+			(reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown"));
+	do_error_dialog(sess->aux_data, buf, _("Gaim - Error"));
+
+
+	return 1;
+}
+
+static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) {
+	char *msg;
+	guint16 id;
+	va_list ap;
+
+	va_start(ap, fr);
+	id  = (guint16)va_arg(ap, unsigned int);
+	msg = va_arg(ap, char *);
+	va_end(ap);
+
+	if (id < 4)
+		do_error_dialog(sess->aux_data, _("Your connection may be lost."),
+				_("AOL error"));
+
+	return 1;
+}
+
+static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	guint16 type;
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+	va_start(ap, fr);
+	type = (guint16)va_arg(ap, unsigned int);
+
+	switch(type) {
+		case 0x0002: {
+			guint8 maxrooms;
+			struct aim_chat_exchangeinfo *exchanges;
+			int exchangecount; // i;
+
+			maxrooms = (guint8)va_arg(ap, unsigned int);
+			exchangecount = va_arg(ap, int);
+			exchanges = va_arg(ap, struct aim_chat_exchangeinfo *);
+			va_end(ap);
+
+			while (odata->create_rooms) {
+				struct create_room *cr = odata->create_rooms->data;
+				aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange);
+				g_free(cr->name);
+				odata->create_rooms = g_slist_remove(odata->create_rooms, cr);
+				g_free(cr);
+			}
+			}
+			break;
+		case 0x0008: {
+			char *fqcn, *name, *ck;
+			guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange;
+			guint8 createperms;
+			guint32 createtime;
+
+			fqcn = va_arg(ap, char *);
+			instance = (guint16)va_arg(ap, unsigned int);
+			exchange = (guint16)va_arg(ap, unsigned int);
+			flags = (guint16)va_arg(ap, unsigned int);
+			createtime = va_arg(ap, guint32);
+			maxmsglen = (guint16)va_arg(ap, unsigned int);
+			maxoccupancy = (guint16)va_arg(ap, unsigned int);
+			createperms = (guint8)va_arg(ap, int);
+			unknown = (guint16)va_arg(ap, unsigned int);
+			name = va_arg(ap, char *);
+			ck = va_arg(ap, char *);
+			va_end(ap);
+
+			aim_chat_join(odata->sess, odata->conn, exchange, ck, instance);
+			}
+			break;
+		default:
+			va_end(ap);
+			break;
+	}
+	return 1;
+}
+
+static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	int count, i;
+	aim_userinfo_t *info;
+	struct gaim_connection *g = sess->aux_data;
+
+	struct chat_connection *c = NULL;
+
+	va_start(ap, fr);
+	count = va_arg(ap, int);
+	info  = va_arg(ap, aim_userinfo_t *);
+	va_end(ap);
+
+	c = find_oscar_chat_by_conn(g, fr->conn);
+	if (!c)
+		return 1;
+
+	for (i = 0; i < count; i++)
+		add_chat_buddy(c->cnv, info[i].sn);
+
+	return 1;
+}
+
+static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	int count, i;
+	aim_userinfo_t *info;
+	struct gaim_connection *g = sess->aux_data;
+
+	struct chat_connection *c = NULL;
+
+	va_start(ap, fr);
+	count = va_arg(ap, int);
+	info  = va_arg(ap, aim_userinfo_t *);
+	va_end(ap);
+
+	c = find_oscar_chat_by_conn(g, fr->conn);
+	if (!c)
+		return 1;
+
+	for (i = 0; i < count; i++)
+		remove_chat_buddy(c->cnv, info[i].sn, NULL);
+
+	return 1;
+}
+
+static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	aim_userinfo_t *userinfo;
+	struct aim_chat_roominfo *roominfo;
+	char *roomname;
+	int usercount;
+	char *roomdesc;
+	guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen;
+	guint32 creationtime;
+	struct gaim_connection *gc = sess->aux_data;
+	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
+
+	va_start(ap, fr);
+	roominfo = va_arg(ap, struct aim_chat_roominfo *);
+	roomname = va_arg(ap, char *);
+	usercount= va_arg(ap, int);
+	userinfo = va_arg(ap, aim_userinfo_t *);
+	roomdesc = va_arg(ap, char *);
+	unknown_c9 = (guint16)va_arg(ap, int);
+	creationtime = (guint32)va_arg(ap, unsigned long);
+	maxmsglen = (guint16)va_arg(ap, int);
+	unknown_d2 = (guint16)va_arg(ap, int);
+	unknown_d5 = (guint16)va_arg(ap, int);
+	maxvisiblemsglen = (guint16)va_arg(ap, int);
+	va_end(ap);
+
+	ccon->maxlen = maxmsglen;
+	ccon->maxvis = maxvisiblemsglen;
+
+	return 1;
+}
+
+static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	aim_userinfo_t *info;
+	char *msg;
+	struct gaim_connection *gc = sess->aux_data;
+	struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn);
+	char *tmp;
+
+	va_start(ap, fr);
+	info = va_arg(ap, aim_userinfo_t *);
+	msg  = va_arg(ap, char *);
+
+	tmp = g_malloc(BUF_LONG);
+	g_snprintf(tmp, BUF_LONG, "%s", msg);
+	serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL));
+	g_free(tmp);
+
+	return 1;
+}
+
+static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) {
+#if 0
+	static const char *codes[5] = {
+		"invalid",
+		 "change",
+		 "warning",
+		 "limit",
+		 "limit cleared",
+	};
+#endif
+	va_list ap;
+	guint16 code, rateclass;
+	guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg;
+
+	va_start(ap, fr); 
+	code = (guint16)va_arg(ap, unsigned int);
+	rateclass= (guint16)va_arg(ap, unsigned int);
+	windowsize = (guint32)va_arg(ap, unsigned long);
+	clear = (guint32)va_arg(ap, unsigned long);
+	alert = (guint32)va_arg(ap, unsigned long);
+	limit = (guint32)va_arg(ap, unsigned long);
+	disconnect = (guint32)va_arg(ap, unsigned long);
+	currentavg = (guint32)va_arg(ap, unsigned long);
+	maxavg = (guint32)va_arg(ap, unsigned long);
+	va_end(ap);
+
+	/* XXX fix these values */
+	if (code == AIM_RATE_CODE_CHANGE) {
+		if (currentavg >= clear)
+			aim_conn_setlatency(fr->conn, 0);
+	} else if (code == AIM_RATE_CODE_WARNING) {
+		aim_conn_setlatency(fr->conn, windowsize/4);
+	} else if (code == AIM_RATE_CODE_LIMIT) {
+		do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. "
+				  "Please wait 10 seconds and try again."), _("Gaim - Error"));
+		aim_conn_setlatency(fr->conn, windowsize/2);
+	} else if (code == AIM_RATE_CODE_CLEARLIMIT) {
+		aim_conn_setlatency(fr->conn, 0);
+	}
+
+	return 1;
+}
+
+static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	aim_userinfo_t *info;
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, fr);
+	info = va_arg(ap, aim_userinfo_t *);
+	va_end(ap);
+
+	gc->evil = info->warnlevel/10;
+	/* gc->correction_time = (info->onlinesince - gc->login_time); */
+
+	return 1;
+}
+
+static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) {
+
+	aim_reqpersonalinfo(sess, fr->conn);
+	aim_bos_reqlocaterights(sess, fr->conn);
+	aim_bos_reqbuddyrights(sess, fr->conn);
+
+	aim_reqicbmparams(sess);
+
+	aim_bos_reqrights(sess, fr->conn);
+	aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS);
+	aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE |
+						     AIM_PRIVFLAGS_ALLOWMEMBERSINCE);
+
+	return 1;
+}
+
+static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *od = gc->proto_data;
+
+	aim_clientready(sess, fr->conn);
+
+	if (od->chpass) {
+		aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp);
+		g_free(od->oldp);
+		od->oldp = NULL;
+		g_free(od->newp);
+		od->newp = NULL;
+		od->chpass = FALSE;
+	}
+	if (od->setnick) {
+		aim_admin_setnick(sess, fr->conn, od->newsn);
+		g_free(od->newsn);
+		od->newsn = NULL;
+		od->setnick = FALSE;
+	}
+	if (od->conf) {
+		aim_admin_reqconfirm(sess, fr->conn);
+		od->conf = FALSE;
+	}
+	if (od->reqemail) {
+		aim_admin_getinfo(sess, fr->conn, 0x0011);
+		od->reqemail = FALSE;
+	}
+	if (od->setemail) {
+		aim_admin_setemail(sess, fr->conn, od->email);
+		g_free(od->email);
+		od->setemail = FALSE;
+	}
+
+	return 1;
+}
+
+static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct aim_icbmparameters *params;
+	va_list ap;
+
+	va_start(ap, fr);
+	params = va_arg(ap, struct aim_icbmparameters *);
+	va_end(ap);
+
+	/* Maybe senderwarn and recverwarn should be user preferences... */
+	params->maxmsglen = 8000;
+	params->minmsginterval = 0;
+
+	aim_seticbmparam(sess, params);
+
+	return 1;
+}
+
+static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+	va_list ap;
+	guint16 maxsiglen;
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+	va_start(ap, fr);
+	maxsiglen = va_arg(ap, int);
+	va_end(ap);
+
+	odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen;
+
+	aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps);
+
+	return 1;
+}
+
+static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	guint16 maxbuddies, maxwatchers;
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+	va_start(ap, fr);
+	maxbuddies = (guint16)va_arg(ap, unsigned int);
+	maxwatchers = (guint16)va_arg(ap, unsigned int);
+	va_end(ap);
+
+	odata->rights.maxbuddies = (guint)maxbuddies;
+	odata->rights.maxwatchers = (guint)maxwatchers;
+
+	return 1;
+}
+
+static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) {
+	guint16 maxpermits, maxdenies;
+	va_list ap;
+	struct gaim_connection *gc = sess->aux_data;
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+
+	va_start(ap, fr);
+	maxpermits = (guint16)va_arg(ap, unsigned int);
+	maxdenies = (guint16)va_arg(ap, unsigned int);
+	va_end(ap);
+
+	odata->rights.maxpermits = (guint)maxpermits;
+	odata->rights.maxdenies = (guint)maxdenies;
+
+//	serv_finish_login(gc);
+
+	if (bud_list_cache_exists(gc))
+		do_import(gc, NULL);
+
+	aim_clientready(sess, fr->conn);
+
+	aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV);
+
+	aim_ssi_reqrights(sess, fr->conn);
+	aim_ssi_reqalldata(sess, fr->conn);
+
+	return 1;
+}
+
+static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) {
+	va_list ap;
+	struct aim_icq_offlinemsg *msg;
+	struct gaim_connection *gc = sess->aux_data;
+
+	va_start(ap, fr);
+	msg = va_arg(ap, struct aim_icq_offlinemsg *);
+	va_end(ap);
+
+	switch (msg->type) {
+		case 0x0001: { /* Basic offline message */
+			char sender[32];
+			char *dialog_msg = g_strdup(msg->msg);
+			time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
+			g_snprintf(sender, sizeof(sender), "%u", msg->sender);
+			strip_linefeed(dialog_msg);
+			serv_got_im(gc, sender, dialog_msg, 0, t, -1);
+			g_free(dialog_msg);
+		} break;
+
+		case 0x0004: { /* Someone sent you a URL */
+		  	char sender[32];
+		  	char *dialog_msg;
+			char **m;
+
+			time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
+			g_snprintf(sender, sizeof(sender), "%u", msg->sender);
+
+			m = g_strsplit(msg->msg, "\376", 2);
+
+			if ((strlen(m[0]) != 0)) {
+			  dialog_msg = g_strjoinv(" -- ", m);
+			} else {
+			  dialog_msg = m[1];
+			}
+
+			strip_linefeed(dialog_msg);
+			serv_got_im(gc, sender, dialog_msg, 0, t, -1);
+			g_free(dialog_msg);
+			g_free(m);
+		} break;
+		
+		case 0x0006: { /* Authorization request */
+			gaim_icq_authask(gc, msg->sender, msg->msg);
+		} break;
+
+		case 0x0007: { /* Someone has denied you authorization */
+			serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
+		} break;
+
+		case 0x0008: { /* Someone has granted you authorization */
+			serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") );
+		} break;
+
+		case 0x0012: {
+			/* Ack for authorizing/denying someone.  Or possibly an ack for sending any system notice */
+		} break;
+
+		default: {;
+		}
+	}
+
+	return 1;
+}
+
+static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+	aim_icq_ackofflinemsgs(sess);
+	return 1;
+}
+
+static void oscar_keepalive(struct gaim_connection *gc) {
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	aim_flap_nop(odata->sess, odata->conn);
+}
+
+static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) {
+	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
+	int ret = 0;
+	if (imflags & IM_FLAG_AWAY) {
+		ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message);
+	} else {
+		struct aim_sendimext_args args;
+		char *s;
+		
+		args.flags = AIM_IMFLAGS_ACK;
+		if (odata->icq)
+			args.flags |= AIM_IMFLAGS_OFFLINE;
+		for (s = message; *s; s++)
+			if (*s & 128)
+				break;
+		
+		/* Message contains high ASCII chars, time for some translation! */
+		if (*s) {
+			s = g_malloc(BUF_LONG);
+			/* Try if we can put it in an ISO8859-1 string first.
+			   If we can't, fall back to UTF16. */
+			if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {
+				args.flags |= AIM_IMFLAGS_ISO_8859_1;
+				len = ret;
+			} else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) {
+				args.flags |= AIM_IMFLAGS_UNICODE;
+				len = ret;
+			} else {
+				/* OOF, translation failed... Oh well.. */
+				g_free( s );
+				s = message;
+			}
+		} else {
+			s = message;
+		}
+		
+		args.features = gaim_features;
+		args.featureslen = sizeof(gaim_features);
+		
+		args.destsn = name;
+		args.msg    = s;
+		args.msglen = len;
+		
+		ret = aim_send_im_ext(odata->sess, &args);
+		
+		if (s != message) {
+			g_free(s);
+		}
+	}
+	if (ret >= 0)
+		return 1;
+	return ret;
+}
+
+static void oscar_get_info(struct gaim_connection *g, char *name) {
+	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	if (odata->icq)
+		aim_icq_getallinfo(odata->sess, name);
+	else {
+		aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
+		aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
+	}
+}
+
+static void oscar_get_away(struct gaim_connection *g, char *who) {
+	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	if (odata->icq) {
+		struct buddy *budlight = find_buddy(g, who);
+		if (budlight)
+			if ((budlight->uc & 0xff80) >> 7)
+				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
+					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
+	} else
+		aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE);
+}
+
+static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
+{
+
+	if (!g_strcasecmp(state, _("Visible"))) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+		return;
+	} else if (!g_strcasecmp(state, _("Invisible"))) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
+		return;
+	} /* else... */
+
+	if (od->rights.maxawaymsglen == 0)
+		do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error");
+
+	aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+
+	if (gc->away)
+		g_free(gc->away);
+	gc->away = NULL;
+
+	if (!message) {
+		aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps);
+		return;
+	}
+
+	if (strlen(message) > od->rights.maxawaymsglen) {
+		gchar *errstr;
+
+		errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen);
+
+		do_error_dialog(gc, errstr, "Away Message Too Long");
+
+		g_free(errstr);
+	}
+
+	gc->away = g_strndup(message, od->rights.maxawaymsglen);
+	aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps);
+
+	return;
+}
+
+static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message)
+{
+    const char *msg = NULL;
+	gboolean no_message = FALSE;
+
+	/* clean old states */
+    if (gc->away) {
+		g_free(gc->away);
+		gc->away = NULL;
+    }
+	od->sess->aim_icq_state = 0;
+
+	/* if no message, then use an empty message */
+    if (message) {
+        msg = message;
+    } else {
+        msg = "";
+		no_message = TRUE;
+    }
+
+	if (!g_strcasecmp(state, "Online")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+	} else if (!g_strcasecmp(state, "Away")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
+        gc->away = g_strdup(msg);
+		od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
+	} else if (!g_strcasecmp(state, "Do Not Disturb")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
+        gc->away = g_strdup(msg);
+		od->sess->aim_icq_state = AIM_MTYPE_AUTODND;
+	} else if (!g_strcasecmp(state, "Not Available")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
+        gc->away = g_strdup(msg);
+		od->sess->aim_icq_state = AIM_MTYPE_AUTONA;
+	} else if (!g_strcasecmp(state, "Occupied")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
+        gc->away = g_strdup(msg);
+		od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY;
+	} else if (!g_strcasecmp(state, "Free For Chat")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
+        gc->away = g_strdup(msg);
+		od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC;
+	} else if (!g_strcasecmp(state, "Invisible")) {
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE);
+        gc->away = g_strdup(msg);
+	} else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) {
+	 	if (no_message) {
+			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL);
+		} else {
+			aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
+            gc->away = g_strdup(msg);
+			od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY;
+		}
+	}
+
+	return;
+}
+
+static void oscar_set_away(struct gaim_connection *gc, char *state, char *message)
+{
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+
+    oscar_set_away_aim(gc, od, state, message);
+	if (od->icq)
+		oscar_set_away_icq(gc, od, state, message);
+
+	return;
+}
+
+static void oscar_add_buddy(struct gaim_connection *g, char *name) {
+	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0);
+}
+
+static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) {
+	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
+	struct aim_ssi_item *ssigroup;
+	while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1));
+}
+
+static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) {
+	return 1;
+}
+
+static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	struct aim_ssi_item *curitem;
+	int tmp;
+
+	/* Add from server list to local list */
+	tmp = 0;
+	for (curitem=sess->ssi.items; curitem; curitem=curitem->next) {
+		switch (curitem->type) {
+			case 0x0000: /* Buddy */
+				if ((curitem->name) && (!find_buddy(gc, curitem->name))) {
+					char *realname = NULL;
+
+					if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1))
+						    realname = aim_gettlv_str(curitem->data, 0x0131, 1);
+						
+					add_buddy(gc, NULL, curitem->name, realname);
+					
+					if (realname)
+					    g_free(realname);
+				}
+				break;
+
+			case 0x0002: /* Permit buddy */
+				if (curitem->name) {
+					GSList *list;
+					for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+					if (!list) {
+						char *name;
+						name = g_strdup(normalize(curitem->name));
+						gc->permit = g_slist_append(gc->permit, name);
+						build_allow_list();
+						tmp++;
+					}
+				}
+				break;
+
+			case 0x0003: /* Deny buddy */
+				if (curitem->name) {
+					GSList *list;
+					for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next);
+					if (!list) {
+						char *name;
+						name = g_strdup(normalize(curitem->name));
+						gc->deny = g_slist_append(gc->deny, name);
+						build_block_list();
+						tmp++;
+					}
+				}
+				break;
+
+			case 0x0004: /* Permit/deny setting */
+				if (curitem->data) {
+					guint8 permdeny;
+					if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) {
+						gc->permdeny = permdeny;
+						tmp++;
+					}
+				}
+				break;
+
+			case 0x0005: /* Presence setting */
+				/* We don't want to change Gaim's setting because it applies to all accounts */
+				break;
+		} /* End of switch on curitem->type */
+	} /* End of for loop */
+
+	if (tmp)
+		do_export(gc);
+	aim_ssi_enable(sess, fr->conn);
+	
+	/* Request offline messages, now that the buddy list is complete. */
+	aim_icq_reqofflinemsgs(sess);
+	
+	/* Now that we have a buddy list, we can tell BitlBee that we're online. */
+	account_online(gc);
+	
+	return 1;
+}
+
+static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... )
+{
+	aim_snac_t *origsnac;
+	va_list ap;
+
+	va_start( ap, fr );
+	origsnac = va_arg( ap, aim_snac_t * );
+	va_end( ap );
+	
+	if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data )
+	{
+		int i, st, count = aim_bstream_empty( &fr->data );
+		char *list;
+		
+		if( count & 1 )
+		{
+			/* Hmm, the length should be even... */
+			do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" );
+			return( 0 );
+		}
+		count >>= 1;
+		
+		list = (char *) origsnac->data;
+		for( i = 0; i < count; i ++ )
+		{
+			st = aimbs_get16( &fr->data );
+			if( st == 0x0E )
+			{
+				serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list );
+				
+				aim_ssi_auth_request( sess, fr->conn, list, "" );
+				aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 );
+			}
+			list += strlen( list ) + 1;
+		}
+	}
+	
+	return( 1 );
+}
+
+static void oscar_set_permit_deny(struct gaim_connection *gc) {
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	if (od->icq) {
+		GSList *list;
+		char buf[MAXMSGLEN];
+		int at;
+
+		switch(gc->permdeny) {
+		case 1:
+			aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username);
+			break;
+		case 2:
+			aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username);
+			break;
+		case 3:
+			list = gc->permit;
+			at = 0;
+			while (list) {
+				at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
+				list = list->next;
+			}
+			aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf);
+			break;
+		case 4:
+			list = gc->deny;
+			at = 0;
+			while (list) {
+				at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data);
+				list = list->next;
+			}
+			aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf);
+			break;
+			default:
+			break;
+		}
+		signoff_blocked(gc);
+	} else {
+		if (od->sess->ssi.received_data)
+			aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff);
+	}
+}
+
+static void oscar_add_permit(struct gaim_connection *gc, char *who) {
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	if (od->icq) {
+		aim_ssi_auth_reply(od->sess, od->conn, who, 1, "");
+	} else {
+		if (od->sess->ssi.received_data)
+			aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
+	}
+}
+
+static void oscar_add_deny(struct gaim_connection *gc, char *who) {
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	if (od->icq) {
+		aim_ssi_auth_reply(od->sess, od->conn, who, 0, "");
+	} else {
+		if (od->sess->ssi.received_data)
+			aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
+	}
+}
+
+static void oscar_rem_permit(struct gaim_connection *gc, char *who) {
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	if (!od->icq) {
+		if (od->sess->ssi.received_data)
+			aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
+	}
+}
+
+static void oscar_rem_deny(struct gaim_connection *gc, char *who) {
+	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
+	if (!od->icq) {
+		if (od->sess->ssi.received_data)
+			aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
+	}
+}
+
+static GList *oscar_away_states(struct gaim_connection *gc)
+{
+	struct oscar_data *od = gc->proto_data;
+	GList *m = NULL;
+
+	if (!od->icq)
+		return g_list_append(m, GAIM_AWAY_CUSTOM);
+
+	m = g_list_append(m, "Online");
+	m = g_list_append(m, "Away");
+	m = g_list_append(m, "Do Not Disturb");
+	m = g_list_append(m, "Not Available");
+	m = g_list_append(m, "Occupied");
+	m = g_list_append(m, "Free For Chat");
+	m = g_list_append(m, "Invisible");
+
+	return m;
+}
+
+static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+        struct gaim_connection *gc = sess->aux_data;
+        gchar who[16];
+        GString *str;
+        va_list ap;
+        struct aim_icq_info *info;
+
+        va_start(ap, fr);
+        info = va_arg(ap, struct aim_icq_info *);
+        va_end(ap);
+
+        if (!info->uin)
+                return 0;
+
+        str = g_string_sized_new(100);
+        g_snprintf(who, sizeof(who), "%u", info->uin);
+
+        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), 
+				info->nick ? info->nick : "-");
+        info_string_append(str, "\n", _("First Name"), info->first);
+        info_string_append(str, "\n", _("Last Name"), info->last);
+		info_string_append(str, "\n", _("Email Address"), info->email);
+        if (info->numaddresses && info->email2) {
+                int i;
+                for (i = 0; i < info->numaddresses; i++) {
+					info_string_append(str, "\n", _("Email Address"), info->email2[i]);
+                }
+        }
+        info_string_append(str, "\n", _("Mobile Phone"), info->mobile);
+        info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male"));
+        if (info->birthyear || info->birthmonth || info->birthday) {
+                char date[30];
+                struct tm tm;
+                tm.tm_mday = (int)info->birthday;
+                tm.tm_mon = (int)info->birthmonth-1;
+                tm.tm_year = (int)info->birthyear-1900;
+                strftime(date, sizeof(date), "%Y-%m-%d", &tm);
+                info_string_append(str, "\n", _("Birthday"), date);
+        }
+        if (info->age) {
+                char age[5];
+                g_snprintf(age, sizeof(age), "%hhd", info->age);
+                info_string_append(str, "\n", _("Age"), age);
+        }
+		info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage);
+        if (info->info && info->info[0]) {
+                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), 
+						info->info, _("End of Additional Information"));
+        }
+        g_string_sprintfa(str, "\n");
+        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) {
+                g_string_sprintfa(str, "%s:", _("Home Address"));
+                info_string_append(str, "\n", _("Address"), info->homeaddr);
+                info_string_append(str, "\n", _("City"), info->homecity);
+                info_string_append(str, "\n", _("State"), info->homestate); 
+				info_string_append(str, "\n", _("Zip Code"), info->homezip);
+                g_string_sprintfa(str, "\n");
+        }
+        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) {
+                g_string_sprintfa(str, "%s:", _("Work Address"));
+                info_string_append(str, "\n", _("Address"), info->workaddr);
+                info_string_append(str, "\n", _("City"), info->workcity);
+                info_string_append(str, "\n", _("State"), info->workstate);
+				info_string_append(str, "\n", _("Zip Code"), info->workzip);
+                g_string_sprintfa(str, "\n");
+        }
+        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) {
+                g_string_sprintfa(str, "%s:", _("Work Information"));
+                info_string_append(str, "\n", _("Company"), info->workcompany);
+                info_string_append(str, "\n", _("Division"), info->workdivision);
+                info_string_append(str, "\n", _("Position"), info->workposition);
+                if (info->workwebpage && info->workwebpage[0]) {
+                        info_string_append(str, "\n", _("Web Page"), info->workwebpage);
+                }
+                g_string_sprintfa(str, "\n");
+        }
+
+		serv_got_crap(gc, "%s\n%s", _("User Info"), str->str);
+        g_string_free(str, TRUE);
+
+        return 1;
+
+}
+
+static char *oscar_encoding_extract(const char *encoding)
+{
+	char *ret = NULL;
+	char *begin, *end;
+
+	g_return_val_if_fail(encoding != NULL, NULL);
+
+	/* Make sure encoding begins with charset= */
+	if (strncmp(encoding, "text/plain; charset=", 20) &&
+		strncmp(encoding, "text/aolrtf; charset=", 21) &&
+		strncmp(encoding, "text/x-aolrtf; charset=", 23))
+	{
+		return NULL;
+	}
+
+	begin = strchr(encoding, '"');
+	end = strrchr(encoding, '"');
+
+	if ((begin == NULL) || (end == NULL) || (begin >= end))
+		return NULL;
+
+	ret = g_strndup(begin+1, (end-1) - begin);
+
+	return ret;
+}
+
+static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen)
+{
+	char *utf8 = g_new0(char, 8192);
+
+	if ((encoding == NULL) || encoding[0] == '\0') {
+		/*		gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/
+	} else if (!g_strcasecmp(encoding, "iso-8859-1")) {
+		do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192);
+	} else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) {
+		do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192);
+	} else if (!g_strcasecmp(encoding, "unicode-2-0")) {
+		do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192);
+	} else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) {
+		/*		gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", "
+		  "attempting to convert to UTF-8 anyway\n", encoding);*/
+		do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192);
+	}
+
+	/*
+	 * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or
+	 * we have been unable to convert the text to utf-8 from the encoding
+	 * that was specified.  So we assume it's UTF-8 and hope for the best.
+	 */
+	if (*utf8 == 0) {
+	    strncpy(utf8, text, textlen);
+	}
+
+	return utf8;
+}
+
+static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...)
+{
+	struct gaim_connection *gc = sess->aux_data;
+	va_list ap;
+	aim_userinfo_t *userinfo;
+	guint16 infotype;
+	char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL;
+	guint16 text_length;
+	char *utf8 = NULL;
+
+	va_start(ap, fr);
+	userinfo = va_arg(ap, aim_userinfo_t *);
+	infotype = va_arg(ap, int);
+	text_encoding = va_arg(ap, char*);
+	text = va_arg(ap, char*);
+	text_length = va_arg(ap, int);
+	va_end(ap);
+
+	if(text_encoding)
+		extracted_encoding = oscar_encoding_extract(text_encoding);
+	if(infotype == AIM_GETINFO_GENERALINFO) {
+		/*Display idle time*/
+		char buff[256];
+		struct tm idletime;
+		if(userinfo->idletime) {
+			memset(&idletime, 0, sizeof(struct tm));
+			idletime.tm_mday = (userinfo->idletime / 60) / 24;
+			idletime.tm_hour = (userinfo->idletime / 60) % 24;
+			idletime.tm_min = userinfo->idletime % 60;
+			idletime.tm_sec = 0;
+			strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime);
+			serv_got_crap(gc, "%s: %s", _("Idle Time"), buff);
+		}
+		
+		if(text) {
+			utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
+			serv_got_crap(gc, "%s\n%s", _("User Info"), utf8);
+		} else {
+			serv_got_crap(gc, _("No user info available."));
+		}
+	} else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) {
+		utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length);
+		serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8);
+	}
+
+	g_free(utf8);
+   
+	return 1;
+}
+
+static char *oscar_get_status_string( struct gaim_connection *gc, int number )
+{
+	struct oscar_data *od = gc->proto_data;
+	
+	if( ! number & UC_UNAVAILABLE )
+	{
+		return( NULL );
+	}
+	else if( od->icq )
+	{
+		number >>= 7;
+		if( number & AIM_ICQ_STATE_DND )
+			return( "Do Not Disturb" );
+		else if( number & AIM_ICQ_STATE_OUT )
+			return( "Not Available" );
+		else if( number & AIM_ICQ_STATE_BUSY )
+			return( "Occupied" );
+		else if( number & AIM_ICQ_STATE_INVISIBLE )
+			return( "Invisible" );
+		else
+			return( "Away" );
+	}
+	else
+	{
+		return( "Away" );
+	}
+}
+
+static struct prpl *my_protocol = NULL;
+
+void oscar_init(struct prpl *ret) {
+	ret->protocol = PROTO_OSCAR;
+	ret->away_states = oscar_away_states;
+	ret->login = oscar_login;
+	ret->close = oscar_close;
+	ret->send_im = oscar_send_im;
+	ret->get_info = oscar_get_info;
+	ret->set_away = oscar_set_away;
+	ret->get_away = oscar_get_away;
+	ret->add_buddy = oscar_add_buddy;
+	ret->remove_buddy = oscar_remove_buddy;
+	ret->add_permit = oscar_add_permit;
+	ret->add_deny = oscar_add_deny;
+	ret->rem_permit = oscar_rem_permit;
+	ret->rem_deny = oscar_rem_deny;
+	ret->set_permit_deny = oscar_set_permit_deny;
+	ret->keepalive = oscar_keepalive;
+	ret->get_status_string = oscar_get_status_string;
+
+	my_protocol = ret;
+}
Index: protocols/oscar/oscar_util.c
===================================================================
--- protocols/oscar/oscar_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/oscar_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,167 @@
+/*
+ *
+ *
+ *
+ */
+
+#include <aim.h>
+#include <ctype.h>
+
+int aimutil_putstr(u_char *dest, const char *src, int len)
+{
+	memcpy(dest, src, len);
+	return len;
+}
+
+/*
+ * Tokenizing functions.  Used to portably replace strtok/sep.
+ *   -- DMP.
+ *
+ */
+int aimutil_tokslen(char *toSearch, int index, char dl)
+{
+	int curCount = 1;
+	char *next;
+	char *last;
+	int toReturn;
+
+	last = toSearch;
+	next = strchr(toSearch, dl);
+
+	while(curCount < index && next != NULL) {
+		curCount++;
+		last = next + 1;
+		next = strchr(last, dl);
+	}
+
+	if ((curCount < index) || (next == NULL))
+		toReturn = strlen(toSearch) - (curCount - 1);
+	else
+		toReturn = next - toSearch - (curCount - 1);
+
+	return toReturn;
+}
+
+int aimutil_itemcnt(char *toSearch, char dl)
+{
+	int curCount;
+	char *next;
+
+	curCount = 1;
+
+	next = strchr(toSearch, dl);
+
+	while(next != NULL) {
+		curCount++;
+		next = strchr(next + 1, dl);
+	}
+
+	return curCount;
+}
+
+char *aimutil_itemidx(char *toSearch, int index, char dl)
+{
+	int curCount;
+	char *next;
+	char *last;
+	char *toReturn;
+
+	curCount = 0;
+
+	last = toSearch;
+	next = strchr(toSearch, dl);
+
+	while (curCount < index && next != NULL) {
+		curCount++;
+		last = next + 1;
+		next = strchr(last, dl);
+	}
+
+	if (curCount < index) {
+		toReturn = g_strdup("");
+	}
+	next = strchr(last, dl);
+
+	if (curCount < index) {
+		toReturn = g_strdup("");
+	} else {
+		if (next == NULL) {
+			toReturn = g_malloc((strlen(last) + 1) * sizeof(char));
+			strcpy(toReturn, last);
+		} else {
+			toReturn = g_malloc((next - last + 1) * sizeof(char));
+			memcpy(toReturn, last, (next - last));
+			toReturn[next - last] = '\0';
+		}
+	}
+	return toReturn;
+}
+
+/*
+* int snlen(const char *)
+* 
+* This takes a screen name and returns its length without
+* spaces.  If there are no spaces in the SN, then the 
+* return is equal to that of strlen().
+*
+*/
+static int aim_snlen(const char *sn)
+{
+	int i = 0;
+	const char *curPtr = NULL;
+
+	if (!sn)
+		return 0;
+
+	curPtr = sn;
+	while ( (*curPtr) != (char) NULL) {
+		if ((*curPtr) != ' ')
+		i++;
+		curPtr++;
+	}
+
+	return i;
+}
+
+/*
+* int sncmp(const char *, const char *)
+*
+* This takes two screen names and compares them using the rules
+* on screen names for AIM/AOL.  Mainly, this means case and space
+* insensitivity (all case differences and spacing differences are
+* ignored).
+*
+* Return: 0 if equal
+*     non-0 if different
+*
+*/
+
+int aim_sncmp(const char *sn1, const char *sn2)
+{
+	const char *curPtr1 = NULL, *curPtr2 = NULL;
+
+	if (aim_snlen(sn1) != aim_snlen(sn2))
+		return 1;
+
+	curPtr1 = sn1;
+	curPtr2 = sn2;
+	while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) {
+		if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) {
+			if (*curPtr1 == ' ')
+				curPtr1++;
+			if (*curPtr2 == ' ')
+				curPtr2++;
+		} else {
+			if ( toupper(*curPtr1) != toupper(*curPtr2))
+				return 1;
+			curPtr1++;
+			curPtr2++;
+		}
+	}
+
+	/* Should both be NULL */
+	if (*curPtr1 != *curPtr2)
+		return 1;
+
+	return 0;
+}
Index: protocols/oscar/rxhandlers.c
===================================================================
--- protocols/oscar/rxhandlers.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/rxhandlers.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,408 @@
+/*
+ * aim_rxhandlers.c
+ *
+ * This file contains most all of the incoming packet handlers, along
+ * with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
+ * actually done in aim_rxqueue.c.
+ *
+ */
+
+#include <aim.h>
+
+struct aim_rxcblist_s {
+	guint16 family;
+	guint16 type;
+	aim_rxcallback_t handler;
+	u_short flags;
+	struct aim_rxcblist_s *next;
+};
+
+aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group)
+{
+	aim_module_t *cur;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+		if (cur->family == group)
+			return cur;
+	}
+
+	return NULL;
+}
+
+static aim_module_t *aim__findmodule(aim_session_t *sess, const char *name)
+{
+	aim_module_t *cur;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+		if (strcmp(name, cur->name) == 0)
+			return cur;
+	}
+
+	return NULL;
+}
+
+int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *))
+{
+	aim_module_t *mod;
+
+	if (!sess || !modfirst)
+		return -1;
+
+	if (!(mod = g_new0(aim_module_t,1)))
+		return -1;
+
+	if (modfirst(sess, mod) == -1) {
+		g_free(mod);
+		return -1;
+	}
+
+	if (aim__findmodule(sess, mod->name)) {
+		if (mod->shutdown)
+			mod->shutdown(sess, mod);
+		g_free(mod);
+		return -1;
+	}
+
+	mod->next = (aim_module_t *)sess->modlistv;
+	sess->modlistv = mod;
+
+
+	return 0;
+}
+
+void aim__shutdownmodules(aim_session_t *sess)
+{
+	aim_module_t *cur;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; ) {
+		aim_module_t *tmp;
+
+		tmp = cur->next;
+
+		if (cur->shutdown)
+			cur->shutdown(sess, cur);
+
+		g_free(cur);
+
+		cur = tmp;
+	}
+
+	sess->modlistv = NULL;
+
+	return;
+}
+
+static int consumesnac(aim_session_t *sess, aim_frame_t *rx)
+{
+	aim_module_t *cur;
+	aim_modsnac_t snac;
+
+	if (aim_bstream_empty(&rx->data) < 10)
+		return 0;
+
+	snac.family = aimbs_get16(&rx->data);
+	snac.subtype = aimbs_get16(&rx->data);
+	snac.flags = aimbs_get16(&rx->data);
+	snac.id = aimbs_get32(&rx->data);
+
+	/* Contains TLV(s) in the FNAC header */
+	if(snac.flags & 0x8000) {
+		aim_bstream_advance(&rx->data, aimbs_get16(&rx->data));
+	} else if(snac.flags & 0x0001) {
+		/* Following SNAC will be related */
+	}
+
+    if (set_getint(sess->aux_data, "debug")) {
+        serv_got_crap(sess->aux_data, "snac %x/%x received", snac.family, snac.subtype);
+    }
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+				(cur->family != snac.family))
+			continue;
+
+		if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+			return 1;
+
+	}
+
+	return 0;
+}
+
+static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, guint16 family, guint16 subtype)
+{
+	aim_module_t *cur;
+	aim_modsnac_t snac;
+
+	snac.family = family;
+	snac.subtype = subtype;
+	snac.flags = snac.id = 0;
+
+	for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+				(cur->family != snac.family))
+			continue;
+
+		if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+			return 1;
+
+	}
+
+	return 0;
+}
+
+static int negchan_middle(aim_session_t *sess, aim_frame_t *fr)
+{
+	aim_tlvlist_t *tlvlist;
+	char *msg = NULL;
+	guint16 code = 0;
+	aim_rxcallback_t userfunc;
+	int ret = 1;
+
+	if (aim_bstream_empty(&fr->data) == 0) {
+		/* XXX should do something with this */
+		return 1;
+	}
+
+	/* Used only by the older login protocol */
+	/* XXX remove this special case? */
+	if (fr->conn->type == AIM_CONN_TYPE_AUTH)
+		return consumenonsnac(sess, fr, 0x0017, 0x0003);
+
+	tlvlist = aim_readtlvchain(&fr->data);
+
+	if (aim_gettlv(tlvlist, 0x0009, 1))
+		code = aim_gettlv16(tlvlist, 0x0009, 1);
+
+	if (aim_gettlv(tlvlist, 0x000b, 1))
+		msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+	if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
+		ret = userfunc(sess, fr, code, msg);
+
+	aim_freetlvchain(&tlvlist);
+
+	g_free(msg);
+
+	return ret;
+}
+
+/*
+ * Some SNACs we do not allow to be hooked, for good reason.
+ */
+static int checkdisallowed(guint16 group, guint16 type)
+{
+	static const struct {
+		guint16 group;
+		guint16 type;
+	} dontuse[] = {
+		{0x0001, 0x0002},
+		{0x0001, 0x0003},
+		{0x0001, 0x0006},
+		{0x0001, 0x0007},
+		{0x0001, 0x0008},
+		{0x0001, 0x0017},
+		{0x0001, 0x0018},
+		{0x0000, 0x0000}
+	};
+	int i;
+
+	for (i = 0; dontuse[i].group != 0x0000; i++) {
+		if ((dontuse[i].group == group) && (dontuse[i].type == type))
+			return 1;
+	}
+
+	return 0;
+}
+
+int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags)
+{
+	struct aim_rxcblist_s *newcb;
+
+	if (!conn)
+		return -1;
+
+	if (checkdisallowed(family, type)) {
+		g_assert(0);
+		return -1;
+	}
+
+	if (!(newcb = (struct aim_rxcblist_s *)g_new0(struct aim_rxcblist_s, 1)))
+		return -1;
+
+	newcb->family = family;
+	newcb->type = type;
+	newcb->flags = flags;
+	newcb->handler = newhandler;
+	newcb->next = NULL;
+
+	if (!conn->handlerlist)
+		conn->handlerlist = (void *)newcb;
+	else {
+		struct aim_rxcblist_s *cur;
+
+		for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next)
+			;
+		cur->next = newcb;
+	}
+
+	return 0;
+}
+
+int aim_clearhandlers(aim_conn_t *conn)
+{
+	struct aim_rxcblist_s *cur;
+
+	if (!conn)
+		return -1;
+
+	for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) {
+		struct aim_rxcblist_s *tmp;
+
+		tmp = cur->next;
+		g_free(cur);
+		cur = tmp;
+	}
+	conn->handlerlist = NULL;
+
+	return 0;
+}
+
+aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type)
+{
+	struct aim_rxcblist_s *cur;
+
+	if (!conn)
+		return NULL;
+
+	for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) {
+		if ((cur->family == family) && (cur->type == type))
+			return cur->handler;
+	}
+
+	if (type == AIM_CB_SPECIAL_DEFAULT) {
+		return NULL; /* prevent infinite recursion */
+	}
+
+	return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+}
+
+void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src)
+{
+	struct aim_rxcblist_s *cur;
+
+	for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) {
+		aim_conn_addhandler(sess, dest, cur->family, cur->type, 
+						cur->handler, cur->flags);
+	}
+
+	return;
+}
+
+static int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,guint16 family, guint16 type, aim_frame_t *ptr)
+{
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, conn, family, type)))
+		return userfunc(sess, ptr);
+
+	return 1; /* XXX */
+}
+
+/*
+ * aim_rxdispatch()
+ *
+ * Basically, heres what this should do:
+ *   1) Determine correct packet handler for this packet
+ *   2) Mark the packet handled (so it can be dequeued in purge_queue())
+ *   3) Send the packet to the packet handler
+ *   4) Go to next packet in the queue and start over
+ *   5) When done, run purge_queue() to purge handled commands
+ *
+ * TODO: Clean up.
+ * TODO: More support for mid-level handlers.
+ * TODO: Allow for NULL handlers.
+ *
+ */
+void aim_rxdispatch(aim_session_t *sess)
+{
+	int i;
+	aim_frame_t *cur;
+
+	for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) {
+
+		/*
+		 * XXX: This is still fairly ugly.
+		 */
+
+		if (cur->handled)
+			continue;
+
+		/*
+		 * This is a debugging/sanity check only and probably 
+		 * could/should be removed for stable code.
+		 */
+		if (((cur->hdrtype == AIM_FRAMETYPE_OFT) && 
+		   (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
+		  ((cur->hdrtype == AIM_FRAMETYPE_FLAP) && 
+		   (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
+			do_error_dialog(sess->aux_data, "incompatible frame type/connection type combination", "Gaim");
+			cur->handled = 1;
+			continue;
+		}
+
+		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
+			if (cur->hdrtype != AIM_FRAMETYPE_OFT) {
+				do_error_dialog(sess->aux_data, "non-OFT frames on OFT connection", "Gaim");
+				cur->handled = 1; /* get rid of it */
+			} else {
+				/* FIXME: implement this (OFT frame) */
+				cur->handled = 1; /* get rid of it */
+			}
+			continue;
+		}
+
+		if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+			/* not possible */
+			do_error_dialog(sess->aux_data, "RENDEZVOUS packet in rxqueue", "Gaim");
+			cur->handled = 1;
+			continue;
+		}
+
+		if (cur->hdr.flap.type == 0x01) {
+			
+			cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
+			
+			continue;
+			
+		} else if (cur->hdr.flap.type == 0x02) {
+
+			if ((cur->handled = consumesnac(sess, cur)))
+				continue;
+
+		} else if (cur->hdr.flap.type == 0x04) {
+
+			cur->handled = negchan_middle(sess, cur);
+			continue;
+
+		} else if (cur->hdr.flap.type == 0x05)
+			;
+		
+		if (!cur->handled) {
+			consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */
+			cur->handled = 1;
+		}
+	}
+
+	/* 
+	 * This doesn't have to be called here.  It could easily be done
+	 * by a seperate thread or something. It's an administrative operation,
+	 * and can take a while. Though the less you call it the less memory
+	 * you'll have :)
+	 */
+	aim_purge_rxqueue(sess);
+
+	return;
+}
Index: protocols/oscar/rxqueue.c
===================================================================
--- protocols/oscar/rxqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/rxqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,508 @@
+/*
+ *  aim_rxqueue.c
+ *
+ * This file contains the management routines for the receive
+ * (incoming packet) queue.  The actual packet handlers are in
+ * aim_rxhandlers.c.
+ */
+
+#include <aim.h> 
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ *
+ */
+int aim_recv(int fd, void *buf, size_t count)
+{
+	int left, cur; 
+
+	for (cur = 0, left = count; left; ) {
+		int ret;
+		
+		ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
+
+		/* Of course EOF is an error, only morons disagree with that. */
+		if (ret <= 0)
+			return -1;
+
+		cur += ret;
+		left -= ret;
+	}
+
+	return cur;
+}
+
+/*
+ * Read into a byte stream.  Will not read more than count, but may read
+ * less if there is not enough room in the stream buffer.
+ */
+static int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
+{
+	int red = 0;
+
+	if (!bs || (fd < 0) || (count < 0))
+		return -1;
+	
+	if (count > (bs->len - bs->offset))
+		count = bs->len - bs->offset; /* truncate to remaining space */
+
+	if (count) {
+
+		red = aim_recv(fd, bs->data + bs->offset, count);
+
+		if (red <= 0)
+			return -1;
+	}
+
+	bs->offset += red;
+
+	return red;
+}
+
+int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len)
+{
+	
+	if (!bs)
+		return -1;
+
+	bs->data = data;
+	bs->len = len;
+	bs->offset = 0;
+
+	return 0;
+}
+
+int aim_bstream_empty(aim_bstream_t *bs)
+{
+	return bs->len - bs->offset;
+}
+
+int aim_bstream_curpos(aim_bstream_t *bs)
+{
+	return bs->offset;
+}
+
+int aim_bstream_setpos(aim_bstream_t *bs, int off)
+{
+
+	if (off > bs->len)
+		return -1;
+
+	bs->offset = off;
+
+	return off;
+}
+
+void aim_bstream_rewind(aim_bstream_t *bs)
+{
+
+	aim_bstream_setpos(bs, 0);
+
+	return;
+}
+
+int aim_bstream_advance(aim_bstream_t *bs, int n)
+{
+
+	if (aim_bstream_empty(bs) < n)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += n;
+
+	return n;
+}
+
+guint8 aimbs_get8(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset++;
+	
+	return aimutil_get8(bs->data + bs->offset - 1);
+}
+
+guint16 aimbs_get16(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 2;
+	
+	return aimutil_get16(bs->data + bs->offset - 2);
+}
+
+guint32 aimbs_get32(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 4;
+	
+	return aimutil_get32(bs->data + bs->offset - 4);
+}
+
+guint8 aimbs_getle8(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset++;
+	
+	return aimutil_getle8(bs->data + bs->offset - 1);
+}
+
+guint16 aimbs_getle16(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 2;
+	
+	return aimutil_getle16(bs->data + bs->offset - 2);
+}
+
+guint32 aimbs_getle32(aim_bstream_t *bs)
+{
+	
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+	
+	bs->offset += 4;
+	
+	return aimutil_getle32(bs->data + bs->offset - 4);
+}
+
+int aimbs_put8(aim_bstream_t *bs, guint8 v)
+{
+
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put8(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+int aimbs_put16(aim_bstream_t *bs, guint16 v)
+{
+
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put16(bs->data + bs->offset, v);
+
+	return 2;
+}
+
+int aimbs_put32(aim_bstream_t *bs, guint32 v)
+{
+
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_put32(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+int aimbs_putle8(aim_bstream_t *bs, guint8 v)
+{
+
+	if (aim_bstream_empty(bs) < 1)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle8(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+int aimbs_putle16(aim_bstream_t *bs, guint16 v)
+{
+
+	if (aim_bstream_empty(bs) < 2)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle16(bs->data + bs->offset, v);
+
+	return 2;
+}
+
+int aimbs_putle32(aim_bstream_t *bs, guint32 v)
+{
+
+	if (aim_bstream_empty(bs) < 4)
+		return 0; /* XXX throw an exception */
+
+	bs->offset += aimutil_putle32(bs->data + bs->offset, v);
+
+	return 1;
+}
+
+int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len)
+{
+
+	if (aim_bstream_empty(bs) < len)
+		return 0;
+
+	memcpy(buf, bs->data + bs->offset, len);
+	bs->offset += len;
+
+	return len;
+}
+
+guint8 *aimbs_getraw(aim_bstream_t *bs, int len)
+{
+	guint8 *ob;
+
+	if (!(ob = g_malloc(len)))
+		return NULL;
+
+	if (aimbs_getrawbuf(bs, ob, len) < len) {
+		g_free(ob);
+		return NULL;
+	}
+
+	return ob;
+}
+
+char *aimbs_getstr(aim_bstream_t *bs, int len)
+{
+	guint8 *ob;
+
+	if (!(ob = g_malloc(len+1)))
+		return NULL;
+
+	if (aimbs_getrawbuf(bs, ob, len) < len) {
+		g_free(ob);
+		return NULL;
+	}
+	
+	ob[len] = '\0';
+
+	return (char *)ob;
+}
+
+int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len)
+{
+
+	if (aim_bstream_empty(bs) < len)
+		return 0; /* XXX throw an exception */
+
+	memcpy(bs->data + bs->offset, v, len);
+	bs->offset += len;
+
+	return len;
+}
+
+int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len)
+{
+
+	if (aim_bstream_empty(srcbs) < len)
+		return 0; /* XXX throw exception (underrun) */
+
+	if (aim_bstream_empty(bs) < len)
+		return 0; /* XXX throw exception (overflow) */
+
+	memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len);
+	bs->offset += len;
+	srcbs->offset += len;
+
+	return len;
+}
+
+/**
+ * aim_frame_destroy - free aim_frame_t 
+ * @frame: the frame to free  
+ *
+ * returns -1 on error; 0 on success.  
+ *
+ */
+void aim_frame_destroy(aim_frame_t *frame)
+{
+
+	g_free(frame->data.data); /* XXX aim_bstream_free */
+
+	if (frame->hdrtype == AIM_FRAMETYPE_OFT)
+		g_free(frame->hdr.oft.hdr2);
+	g_free(frame);
+	
+	return;
+} 
+
+
+/*
+ * Grab a single command sequence off the socket, and enqueue
+ * it in the incoming event queue in a seperate struct.
+ */
+int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
+{
+	guint8 flaphdr_raw[6];
+	aim_bstream_t flaphdr;
+	aim_frame_t *newrx;
+	guint16 payloadlen;
+	
+	if (!sess || !conn)
+		return 0;
+
+	if (conn->fd == -1)
+		return -1; /* its a aim_conn_close()'d connection */
+
+	if (conn->fd < 3)  /* can happen when people abuse the interface */
+		return 0;
+
+	if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+		return aim_conn_completeconnect(sess, conn);
+
+	/*
+	 * Rendezvous (client-client) connections do not speak
+	 * FLAP, so this function will break on them.
+	 */
+	if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) 
+		return aim_get_command_rendezvous(sess, conn);
+	else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+		do_error_dialog(sess->aux_data,"AIM_CONN_TYPE_RENDEZVOUS_OUT shouldn't use FLAP", "Gaim");
+		return 0; 
+	}
+
+	aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw));
+
+	/*
+	 * Read FLAP header.  Six bytes:
+	 *    
+	 *   0 char  -- Always 0x2a
+	 *   1 char  -- Channel ID.  Usually 2 -- 1 and 4 are used during login.
+	 *   2 short -- Sequence number 
+	 *   4 short -- Number of data bytes that follow.
+	 */
+	if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) {
+		aim_conn_close(conn);
+		return -1;
+	}
+
+	aim_bstream_rewind(&flaphdr);
+
+	/*
+	 * This shouldn't happen unless the socket breaks, the server breaks,
+	 * or we break.  We must handle it just in case.
+	 */
+	if (aimbs_get8(&flaphdr) != 0x2a) {
+		guint8 start;
+
+		aim_bstream_rewind(&flaphdr);
+		start = aimbs_get8(&flaphdr);
+		do_error_dialog(sess->aux_data, "FLAP framing disrupted", "Gaim");
+		aim_conn_close(conn);
+		return -1;
+	}	
+
+	/* allocate a new struct */
+	if (!(newrx = (aim_frame_t *)g_new0(aim_frame_t,1)))
+		return -1;
+
+	/* we're doing FLAP if we're here */
+	newrx->hdrtype = AIM_FRAMETYPE_FLAP;
+	
+	newrx->hdr.flap.type = aimbs_get8(&flaphdr);
+	newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr);
+	payloadlen = aimbs_get16(&flaphdr);
+
+	newrx->nofree = 0; /* free by default */
+
+	if (payloadlen) {
+		guint8 *payload = NULL;
+
+		if (!(payload = (guint8 *) g_malloc(payloadlen))) {
+			aim_frame_destroy(newrx);
+			return -1;
+		}
+
+		aim_bstream_init(&newrx->data, payload, payloadlen);
+
+		/* read the payload */
+		if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) {
+			aim_frame_destroy(newrx); /* free's payload */
+			aim_conn_close(conn);
+			return -1;
+		}
+	} else
+		aim_bstream_init(&newrx->data, NULL, 0);
+
+
+	aim_bstream_rewind(&newrx->data);
+
+	newrx->conn = conn;
+
+	newrx->next = NULL;  /* this will always be at the bottom */
+
+	if (!sess->queue_incoming)
+		sess->queue_incoming = newrx;
+	else {
+		aim_frame_t *cur;
+
+		for (cur = sess->queue_incoming; cur->next; cur = cur->next)
+			;
+		cur->next = newrx;
+	}
+
+	newrx->conn->lastactivity = time(NULL);
+
+	return 0;  
+}
+
+/*
+ * Purge recieve queue of all handled commands (->handled==1).  Also
+ * allows for selective freeing using ->nofree so that the client can
+ * keep the data for various purposes.  
+ *
+ * If ->nofree is nonzero, the frame will be delinked from the global list, 
+ * but will not be free'ed.  The client _must_ keep a pointer to the
+ * data -- libfaim will not!  If the client marks ->nofree but
+ * does not keep a pointer, it's lost forever.
+ *
+ */
+void aim_purge_rxqueue(aim_session_t *sess)
+{
+	aim_frame_t *cur, **prev;
+
+	for (prev = &sess->queue_incoming; (cur = *prev); ) {
+		if (cur->handled) {
+
+			*prev = cur->next;
+			
+			if (!cur->nofree)
+				aim_frame_destroy(cur);
+
+		} else
+			prev = &cur->next;
+	}
+
+	return;
+}
+
+/*
+ * Since aim_get_command will aim_conn_kill dead connections, we need
+ * to clean up the rxqueue of unprocessed connections on that socket.
+ *
+ * XXX: this is something that was handled better in the old connection
+ * handling method, but eh.
+ */
+void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *currx;
+
+	for (currx = sess->queue_incoming; currx; currx = currx->next) {
+		if ((!currx->handled) && (currx->conn == conn))
+			currx->handled = 1;
+	}	
+	return;
+}
+
Index: protocols/oscar/search.c
===================================================================
--- protocols/oscar/search.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/search.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,121 @@
+
+/*
+ * aim_search.c
+ *
+ * TODO: Add aim_usersearch_name()
+ *
+ */
+
+#include <aim.h>
+
+int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn || !address)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address))))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, g_strdup(address), strlen(address)+1);
+	aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid);
+	
+	aimbs_putraw(&fr->data, (guint8 *)address, strlen(address)); 
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* XXX can this be integrated with the rest of the error handling? */
+static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+
+	/* XXX the modules interface should have already retrieved this for us */
+	if (!(snac2 = aim_remsnac(sess, snac->id))) {
+		do_error_dialog(sess->aux_data, "couldn't get snac", "Gaim");
+		return 0;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, snac2->data /* address */);
+
+	/* XXX freesnac()? */
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	return ret;
+}
+
+static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int j = 0, m, ret = 0;
+	aim_tlvlist_t *tlvlist;
+	char *cur = NULL, *buf = NULL;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *snac2;
+	char *searchaddr = NULL;
+
+	if ((snac2 = aim_remsnac(sess, snac->id)))
+		searchaddr = (char *)snac2->data;
+
+	tlvlist = aim_readtlvchain(bs);
+	m = aim_counttlvchain(&tlvlist);
+
+	/* XXX uhm. */
+	while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) {
+		buf = g_realloc(buf, (j+1) * (MAXSNLEN+1));
+
+		strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN);
+		g_free(cur);
+
+		j++; 
+	}
+
+	aim_freetlvchain(&tlvlist);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, searchaddr, j, buf);
+
+	/* XXX freesnac()? */
+	if (snac2)
+		g_free(snac2->data);
+	g_free(snac2);
+
+	g_free(buf);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0001)
+		return error(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0003)
+		return reply(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int search_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000a;
+	mod->version = 0x0001;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "search", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
+
Index: protocols/oscar/search.h
===================================================================
--- protocols/oscar/search.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/search.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,6 @@
+#ifndef __OSCAR_SEARCH_H__
+#define __OSCAR_SEARCH_H__
+
+int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *);
+
+#endif /* __OSCAR_SEARCH_H__ */
Index: protocols/oscar/service.c
===================================================================
--- protocols/oscar/service.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/service.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,946 @@
+/*
+ * Group 1.  This is a very special group.  All connections support
+ * this group, as it does some particularly good things (like rate limiting).
+ */
+
+#include <aim.h>
+
+#include "md5.h"
+
+/* Client Online (group 1, subtype 2) */
+int aim_clientready(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!ins)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid);
+
+	/*
+	 * Send only the tool versions that the server cares about (that it
+	 * marked as supporting in the server ready SNAC).  
+	 */
+	for (sg = ins->groups; sg; sg = sg->next) {
+		aim_module_t *mod;
+
+		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+			aimbs_put16(&fr->data, mod->family);
+			aimbs_put16(&fr->data, mod->version);
+			aimbs_put16(&fr->data, mod->toolid);
+			aimbs_put16(&fr->data, mod->toolversion);
+		} 
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Host Online (group 1, type 3)
+ * 
+ * See comments in conn.c about how the group associations are supposed
+ * to work, and how they really work.
+ *
+ * This info probably doesn't even need to make it to the client.
+ *
+ * We don't actually call the client here.  This starts off the connection
+ * initialization routine required by all AIM connections.  The next time
+ * the client is called is the CONNINITDONE callback, which should be 
+ * shortly after the rate information is acknowledged.
+ * 
+ */
+static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	guint16 *families;
+	int famcount;
+
+
+	if (!(families = g_malloc(aim_bstream_empty(bs))))
+		return 0;
+
+	for (famcount = 0; aim_bstream_empty(bs); famcount++) {
+		families[famcount] = aimbs_get16(bs);
+		aim_conn_addgroup(rx->conn, families[famcount]);
+	}
+
+	g_free(families);
+
+
+	/*
+	 * Next step is in the Host Versions handler.
+	 *
+	 * Note that we must send this before we request rates, since
+	 * the format of the rate information depends on the versions we
+	 * give it.
+	 *
+	 */
+	aim_setversions(sess, rx->conn);
+
+	return 1; 
+}
+
+/* Service request (group 1, type 4) */
+int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid)
+{
+	return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid);
+}
+
+/* Redirect (group 1, type 5) */
+static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	struct aim_redirect_data redir;
+	aim_rxcallback_t userfunc;
+	aim_tlvlist_t *tlvlist;
+	aim_snac_t *origsnac = NULL;
+	int ret = 0;
+
+	memset(&redir, 0, sizeof(redir));
+
+	tlvlist = aim_readtlvchain(bs);
+
+	if (!aim_gettlv(tlvlist, 0x000d, 1) ||
+			!aim_gettlv(tlvlist, 0x0005, 1) ||
+			!aim_gettlv(tlvlist, 0x0006, 1)) {
+		aim_freetlvchain(&tlvlist);
+		return 0;
+	}
+
+	redir.group = aim_gettlv16(tlvlist, 0x000d, 1);
+	redir.ip = aim_gettlv_str(tlvlist, 0x0005, 1);
+	redir.cookie = (guint8 *)aim_gettlv_str(tlvlist, 0x0006, 1);
+
+	/* Fetch original SNAC so we can get csi if needed */
+	origsnac = aim_remsnac(sess, snac->id);
+
+	if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) {
+		struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data;
+
+		redir.chat.exchange = csi->exchange;
+		redir.chat.room = csi->name;
+		redir.chat.instance = csi->instance;
+	}
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, &redir);
+
+	g_free((void *)redir.ip);
+	g_free((void *)redir.cookie);
+
+	if (origsnac)
+		g_free(origsnac->data);
+	g_free(origsnac);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+/* Request Rate Information. (group 1, type 6) */
+int aim_reqrates(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0001, 0x0006);
+}
+
+/*
+ * OSCAR defines several 'rate classes'.  Each class has seperate
+ * rate limiting properties (limit level, alert level, disconnect
+ * level, etc), and a set of SNAC family/type pairs associated with
+ * it.  The rate classes, their limiting properties, and the definitions
+ * of which SNACs are belong to which class, are defined in the
+ * Rate Response packet at login to each host.  
+ *
+ * Logically, all rate offenses within one class count against further
+ * offenses for other SNACs in the same class (ie, sending messages
+ * too fast will limit the number of user info requests you can send,
+ * since those two SNACs are in the same rate class).
+ *
+ * Since the rate classes are defined dynamically at login, the values
+ * below may change. But they seem to be fairly constant.
+ *
+ * Currently, BOS defines five rate classes, with the commonly used
+ * members as follows...
+ *
+ *  Rate class 0x0001:
+ *  	- Everything thats not in any of the other classes
+ *
+ *  Rate class 0x0002:
+ * 	- Buddy list add/remove
+ *	- Permit list add/remove
+ *	- Deny list add/remove
+ *
+ *  Rate class 0x0003:
+ *	- User information requests
+ *	- Outgoing ICBMs
+ *
+ *  Rate class 0x0004:
+ *	- A few unknowns: 2/9, 2/b, and f/2
+ *
+ *  Rate class 0x0005:
+ *	- Chat room create
+ *	- Outgoing chat ICBMs
+ *
+ * The only other thing of note is that class 5 (chat) has slightly looser
+ * limiting properties than class 3 (normal messages).  But thats just a 
+ * small bit of trivia for you.
+ *
+ * The last thing that needs to be learned about the rate limiting
+ * system is how the actual numbers relate to the passing of time.  This
+ * seems to be a big mystery.
+ * 
+ */
+
+static void rc_addclass(struct rateclass **head, struct rateclass *inrc)
+{
+	struct rateclass *rc, *rc2;
+
+	if (!(rc = g_malloc(sizeof(struct rateclass))))
+		return;
+
+	memcpy(rc, inrc, sizeof(struct rateclass));
+	rc->next = NULL;
+
+	for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next)
+		;
+
+	if (!rc2)
+		*head = rc;
+	else
+		rc2->next = rc;
+
+	return;
+}
+
+static struct rateclass *rc_findclass(struct rateclass **head, guint16 id)
+{
+	struct rateclass *rc;
+
+	for (rc = *head; rc; rc = rc->next) {
+		if (rc->classid == id)
+			return rc;
+	}
+
+	return NULL;
+}
+
+static void rc_addpair(struct rateclass *rc, guint16 group, guint16 type)
+{
+	struct snacpair *sp, *sp2;
+
+	if (!(sp = g_new0(struct snacpair, 1)))
+		return;
+
+	sp->group = group;
+	sp->subtype = type;
+	sp->next = NULL;
+
+	for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next)
+		;
+
+	if (!sp2)
+		rc->members = sp;
+	else
+		sp2->next = sp;
+
+	return;
+}
+
+/* Rate Parameters (group 1, type 7) */
+static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside;
+	guint16 numclasses, i;
+	aim_rxcallback_t userfunc;
+
+
+	/*
+	 * First are the parameters for each rate class.
+	 */
+	numclasses = aimbs_get16(bs);
+	for (i = 0; i < numclasses; i++) {
+		struct rateclass rc;
+
+		memset(&rc, 0, sizeof(struct rateclass));
+
+		rc.classid = aimbs_get16(bs);
+		rc.windowsize = aimbs_get32(bs);
+		rc.clear = aimbs_get32(bs);
+		rc.alert = aimbs_get32(bs);
+		rc.limit = aimbs_get32(bs);
+		rc.disconnect = aimbs_get32(bs);
+		rc.current = aimbs_get32(bs);
+		rc.max = aimbs_get32(bs);
+
+		/*
+		 * The server will send an extra five bytes of parameters
+		 * depending on the version we advertised in 1/17.  If we
+		 * didn't send 1/17 (evil!), then this will crash and you
+		 * die, as it will default to the old version but we have 
+		 * the new version hardcoded here. 
+		 */
+		if (mod->version >= 3)
+			aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown));
+
+		rc_addclass(&ins->rates, &rc);
+	}
+
+	/*
+	 * Then the members of each class.
+	 */
+	for (i = 0; i < numclasses; i++) {
+		guint16 classid, count;
+		struct rateclass *rc;
+		int j;
+
+		classid = aimbs_get16(bs);
+		count = aimbs_get16(bs);
+
+		rc = rc_findclass(&ins->rates, classid);
+
+		for (j = 0; j < count; j++) {
+			guint16 group, subtype;
+
+			group = aimbs_get16(bs);
+			subtype = aimbs_get16(bs);
+
+			if (rc)
+				rc_addpair(rc, group, subtype);
+		}
+	}
+
+	/*
+	 * We don't pass the rate information up to the client, as it really
+	 * doesn't care.  The information is stored in the connection, however
+	 * so that we can do more fun stuff later (not really).
+	 */
+
+	/*
+	 * Last step in the conn init procedure is to acknowledge that we
+	 * agree to these draconian limitations.
+	 */
+	aim_rates_addparam(sess, rx->conn);
+
+	/*
+	 * Finally, tell the client it's ready to go...
+	 */
+	if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE)))
+		userfunc(sess, rx);
+
+
+	return 1;
+}
+
+/* Add Rate Parameter (group 1, type 8) */
+int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	aim_frame_t *fr;	
+	aim_snacid_t snacid;
+	struct rateclass *rc;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM; 
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid);
+
+	for (rc = ins->rates; rc; rc = rc->next)
+		aimbs_put16(&fr->data, rc->classid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Delete Rate Parameter (group 1, type 9) */
+int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	aim_frame_t *fr;	
+	aim_snacid_t snacid;
+	struct rateclass *rc;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
+		return -ENOMEM; 
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid);
+
+	for (rc = ins->rates; rc; rc = rc->next)
+		aimbs_put16(&fr->data, rc->classid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Rate Change (group 1, type 0x0a) */
+static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint16 code, rateclass;
+	guint32 currentavg, maxavg, windowsize, clear, alert, limit, disconnect;
+
+	code = aimbs_get16(bs);
+	rateclass = aimbs_get16(bs);
+	
+	windowsize = aimbs_get32(bs);
+	clear = aimbs_get32(bs);
+	alert = aimbs_get32(bs);
+	limit = aimbs_get32(bs);
+	disconnect = aimbs_get32(bs);
+	currentavg = aimbs_get32(bs);
+	maxavg = aimbs_get32(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
+
+	return 0;
+}
+
+/*
+ * How Migrations work.  
+ *
+ * The server sends a Server Pause message, which the client should respond to 
+ * with a Server Pause Ack, which contains the families it needs on this 
+ * connection. The server will send a Migration Notice with an IP address, and 
+ * then disconnect. Next the client should open the connection and send the 
+ * cookie.  Repeat the normal login process and pretend this never happened.
+ *
+ * The Server Pause contains no data.
+ *
+ */
+
+/* Service Pause (group 1, type 0x0b) */
+static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx);
+
+	return 0;
+}
+
+/*
+ * Service Pause Acknowledgement (group 1, type 0x0c)
+ *
+ * It is rather important that aim_sendpauseack() gets called for the exact
+ * same connection that the Server Pause callback was called for, since
+ * libfaim extracts the data for the SNAC from the connection structure.
+ *
+ * Of course, if you don't do that, more bad things happen than just what
+ * libfaim can cause.
+ *
+ */
+int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid);
+
+	/*
+	 * This list should have all the groups that the original 
+	 * Host Online / Server Ready said this host supports.  And 
+	 * we want them all back after the migration.
+	 */
+	for (sg = ins->groups; sg; sg = sg->next)
+		aimbs_put16(&fr->data, sg->group);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Service Resume (group 1, type 0x0d) */
+static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx);
+
+	return 0;
+}
+
+/* Request self-info (group 1, type 0x0e) */
+int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0001, 0x000e);
+}
+
+/* Self User Info (group 1, type 0x0f) */
+static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	aim_userinfo_t userinfo;
+
+	aim_extractuserinfo(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, &userinfo);
+
+	return 0;
+}
+
+/* Evil Notification (group 1, type 0x10) */
+static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint16 newevil;
+	aim_userinfo_t userinfo;
+
+	memset(&userinfo, 0, sizeof(aim_userinfo_t));
+	
+	newevil = aimbs_get16(bs);
+
+	if (aim_bstream_empty(bs))
+		aim_extractuserinfo(sess, bs, &userinfo);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, newevil, &userinfo);
+
+	return 0;
+}
+
+/*
+ * Idle Notification (group 1, type 0x11)
+ *
+ * Should set your current idle time in seconds.  Note that this should
+ * never be called consecutively with a non-zero idle time.  That makes
+ * OSCAR do funny things.  Instead, just set it once you go idle, and then
+ * call it again with zero when you're back.
+ *
+ */
+int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, guint32 idletime)
+{
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime);
+}
+
+/*
+ * Service Migrate (group 1, type 0x12)
+ *
+ * This is the final SNAC sent on the original connection during a migration.
+ * It contains the IP and cookie used to connect to the new server, and 
+ * optionally a list of the SNAC groups being migrated.
+ *
+ */
+static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	int ret = 0;
+	guint16 groupcount, i;
+	aim_tlvlist_t *tl;
+	char *ip = NULL;
+	aim_tlv_t *cktlv;
+
+	/*
+	 * Apparently there's some fun stuff that can happen right here. The
+	 * migration can actually be quite selective about what groups it
+	 * moves to the new server.  When not all the groups for a connection
+	 * are migrated, or they are all migrated but some groups are moved
+	 * to a different server than others, it is called a bifurcated 
+	 * migration.
+	 *
+	 * Let's play dumb and not support that.
+	 *
+	 */
+	groupcount = aimbs_get16(bs);
+	for (i = 0; i < groupcount; i++) {
+		guint16 group;
+
+		group = aimbs_get16(bs);
+
+		do_error_dialog(sess->aux_data, "bifurcated migration unsupported", "Gaim");
+	}
+
+	tl = aim_readtlvchain(bs);
+
+	if (aim_gettlv(tl, 0x0005, 1))
+		ip = aim_gettlv_str(tl, 0x0005, 1);
+
+	cktlv = aim_gettlv(tl, 0x0006, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL);
+
+	aim_freetlvchain(&tl);
+	g_free(ip);
+
+	return ret;
+}
+
+/* Message of the Day (group 1, type 0x13) */
+static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	char *msg = NULL;
+	int ret = 0;
+	aim_tlvlist_t *tlvlist;
+	guint16 id;
+
+	/*
+	 * Code.
+	 *
+	 * Valid values:
+	 *   1 Mandatory upgrade
+	 *   2 Advisory upgrade
+	 *   3 System bulletin
+	 *   4 Nothing's wrong ("top o the world" -- normal)
+	 *   5 Lets-break-something. 
+	 *
+	 */
+	id = aimbs_get16(bs);
+
+	/* 
+	 * TLVs follow 
+	 */
+	tlvlist = aim_readtlvchain(bs);
+
+	msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, id, msg);
+
+	g_free(msg);
+
+	aim_freetlvchain(&tlvlist);
+
+	return ret;
+}
+
+/* 
+ * Set privacy flags (group 1, type 0x14)
+ *
+ * Normally 0x03.
+ *
+ *  Bit 1:  Allows other AIM users to see how long you've been idle.
+ *  Bit 2:  Allows other AIM users to see how long you've been a member.
+ *
+ */
+int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, guint32 flags)
+{
+	return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags);
+}
+
+/*
+ * No-op (group 1, type 0x16)
+ *
+ * WinAIM sends these every 4min or so to keep the connection alive.  Its not 
+ * real necessary.
+ *
+ */
+int aim_nop(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, 0x0001, 0x0016);
+}
+
+/* 
+ * Set client versions (group 1, subtype 0x17) 
+ *
+ * If you've seen the clientonline/clientready SNAC you're probably 
+ * wondering what the point of this one is.  And that point seems to be
+ * that the versions in the client online SNAC are sent too late for the
+ * server to be able to use them to change the protocol for the earlier
+ * login packets (client versions are sent right after Host Online is 
+ * received, but client online versions aren't sent until quite a bit later).
+ * We can see them already making use of this by changing the format of
+ * the rate information based on what version of group 1 we advertise here.
+ *
+ */
+int aim_setversions(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
+	struct snacgroup *sg;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!ins)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid);
+
+	/*
+	 * Send only the versions that the server cares about (that it
+	 * marked as supporting in the server ready SNAC).  
+	 */
+	for (sg = ins->groups; sg; sg = sg->next) {
+		aim_module_t *mod;
+
+		if ((mod = aim__findmodulebygroup(sess, sg->group))) {
+			aimbs_put16(&fr->data, mod->family);
+			aimbs_put16(&fr->data, mod->version);
+		}
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/* Host versions (group 1, subtype 0x18) */
+static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int vercount;
+	guint8 *versions;
+
+	/* This is frivolous. (Thank you SmarterChild.) */
+	vercount = aim_bstream_empty(bs)/4;
+	versions = aimbs_getraw(bs, aim_bstream_empty(bs));
+	g_free(versions);
+
+	/*
+	 * Now request rates.
+	 */
+	aim_reqrates(sess, rx->conn);
+
+	return 1;
+}
+
+/* 
+ * Subtype 0x001e - Extended Status
+ *
+ * Sets your ICQ status (available, away, do not disturb, etc.)
+ *
+ * These are the same TLVs seen in user info.  You can 
+ * also set 0x0008 and 0x000c.
+ */
+int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	aim_tlvlist_t *tl = NULL;
+	guint32 data;
+	int tlvlen;
+
+	data = AIM_ICQ_STATE_WEBAWARE | AIM_ICQ_STATE_HIDEIP | status; /* yay for error checking ;^) */
+
+	tlvlen = aim_addtlvtochain32(&tl, 0x0006, data);
+
+	printf("%d\n", tlvlen);
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid);
+	
+	aim_writetlvchain(&fr->data, &tl);
+	aim_freetlvchain(&tl);
+	
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Starting this past week (26 Mar 2001, say), AOL has started sending
+ * this nice little extra SNAC.  AFAIK, it has never been used until now.
+ *
+ * The request contains eight bytes.  The first four are an offset, the
+ * second four are a length.
+ *
+ * The offset is an offset into aim.exe when it is mapped during execution
+ * on Win32.  So far, AOL has only been requesting bytes in static regions
+ * of memory.  (I won't put it past them to start requesting data in
+ * less static regions -- regions that are initialized at run time, but still
+ * before the client recieves this request.)
+ *
+ * When the client recieves the request, it adds it to the current ds
+ * (0x00400000) and dereferences it, copying the data into a buffer which
+ * it then runs directly through the MD5 hasher.  The 16 byte output of
+ * the hash is then sent back to the server.
+ *
+ * If the client does not send any data back, or the data does not match
+ * the data that the specific client should have, the client will get the
+ * following message from "AOL Instant Messenger":
+ *    "You have been disconnected from the AOL Instant Message Service (SM) 
+ *     for accessing the AOL network using unauthorized software.  You can
+ *     download a FREE, fully featured, and authorized client, here 
+ *     http://www.aol.com/aim/download2.html"
+ * The connection is then closed, recieving disconnect code 1, URL
+ * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html.  
+ *
+ * Note, however, that numerous inconsistencies can cause the above error, 
+ * not just sending back a bad hash.  Do not immediatly suspect this code
+ * if you get disconnected.  AOL and the open/free software community have
+ * played this game for a couple years now, generating the above message
+ * on numerous ocassions.
+ *
+ * Anyway, neener.  We win again.
+ *
+ */
+/* Client verification (group 1, subtype 0x1f) */
+static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	aim_rxcallback_t userfunc;
+	guint32 offset, len;
+	aim_tlvlist_t *list;
+	char *modname;
+
+	offset = aimbs_get32(bs);
+	len = aimbs_get32(bs);
+	list = aim_readtlvchain(bs);
+
+	modname = aim_gettlv_str(list, 0x0001, 1);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, offset, len, modname);
+
+	g_free(modname);
+	aim_freetlvchain(&list);
+
+	return 0;
+}
+
+/* Client verification reply (group 1, subtype 0x20) */
+int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid);
+	aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */
+
+	if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */
+
+		aimbs_putraw(&fr->data, buf, 0x10); 
+
+	} else if (buf && (len > 0)) { /* use input buffer */
+		md5_state_t state;
+		md5_byte_t digest[0x10];
+
+		md5_init(&state);	
+		md5_append(&state, (const md5_byte_t *)buf, len);
+		md5_finish(&state, digest);
+
+		aimbs_putraw(&fr->data, (guint8 *)digest, 0x10);
+
+	} else if (len == 0) { /* no length, just hash NULL (buf is optional) */
+		md5_state_t state;
+		guint8 nil = '\0';
+		md5_byte_t digest[0x10];
+
+		/*
+		 * These MD5 routines are stupid in that you have to have
+		 * at least one append.  So thats why this doesn't look 
+		 * real logical.
+		 */
+		md5_init(&state);
+		md5_append(&state, (const md5_byte_t *)&nil, 0);
+		md5_finish(&state, digest);
+
+		aimbs_putraw(&fr->data, (guint8 *)digest, 0x10);
+
+	} else {
+
+		/* 
+		 * This data is correct for AIM 3.5.1670.
+		 *
+		 * Using these blocks is as close to "legal" as you can get
+		 * without using an AIM binary.
+		 *
+		 */
+		if ((offset == 0x03ffffff) && (len == 0x03ffffff)) {
+
+#if 1 /* with "AnrbnrAqhfzcd" */
+			aimbs_put32(&fr->data, 0x44a95d26);
+			aimbs_put32(&fr->data, 0xd2490423);
+			aimbs_put32(&fr->data, 0x93b8821f);
+			aimbs_put32(&fr->data, 0x51c54b01);
+#else /* no filename */
+			aimbs_put32(&fr->data, 0x1df8cbae);
+			aimbs_put32(&fr->data, 0x5523b839);
+			aimbs_put32(&fr->data, 0xa0e10db3);
+			aimbs_put32(&fr->data, 0xa46d3b39);
+#endif
+
+		} else if ((offset == 0x00001000) && (len == 0x00000000)) {
+
+			aimbs_put32(&fr->data, 0xd41d8cd9);
+			aimbs_put32(&fr->data, 0x8f00b204);
+			aimbs_put32(&fr->data, 0xe9800998);
+			aimbs_put32(&fr->data, 0xecf8427e);
+
+		} else
+			do_error_dialog(sess->aux_data, "WARNING: unknown hash request", "Gaim");
+
+	}
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0003)
+		return hostonline(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0005)
+		return redirect(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0007)
+		return rateresp(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000a)
+		return ratechange(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000b)
+		return serverpause(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000d)
+		return serverresume(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x000f)
+		return selfinfo(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0010)
+		return evilnotify(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0012)
+		return migrate(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0013)
+		return motd(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0018)
+		return hostversions(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x001f)
+		return memrequest(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int general_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x0001;
+	mod->version = 0x0003;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "general", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
+
Index: protocols/oscar/snac.c
===================================================================
--- protocols/oscar/snac.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/snac.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,145 @@
+/*
+ *
+ * Various SNAC-related dodads... 
+ *
+ * outstanding_snacs is a list of aim_snac_t structs.  A SNAC should be added
+ * whenever a new SNAC is sent and it should remain in the list until the
+ * response for it has been receieved.  
+ *
+ * cleansnacs() should be called periodically by the client in order
+ * to facilitate the aging out of unreplied-to SNACs. This can and does
+ * happen, so it should be handled.
+ *
+ */
+
+#include <aim.h>
+
+/*
+ * Called from aim_session_init() to initialize the hash.
+ */
+void aim_initsnachash(aim_session_t *sess)
+{
+	int i;
+
+	for (i = 0; i < AIM_SNAC_HASH_SIZE; i++)
+		sess->snac_hash[i] = NULL;
+
+	return;
+}
+
+aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen)
+{
+	aim_snac_t snac;
+
+	snac.id = sess->snacid_next++;
+	snac.family = family;
+	snac.type = type;
+	snac.flags = flags;
+
+	if (datalen) {
+		if (!(snac.data = g_malloc(datalen)))
+			return 0; /* er... */
+		memcpy(snac.data, data, datalen);
+	} else
+		snac.data = NULL;
+
+	return aim_newsnac(sess, &snac);
+}
+
+/*
+ * Clones the passed snac structure and caches it in the
+ * list/hash.
+ */
+aim_snacid_t aim_newsnac(aim_session_t *sess, aim_snac_t *newsnac)
+{
+	aim_snac_t *snac;
+	int index;
+
+	if (!newsnac)
+		return 0;
+
+	if (!(snac = g_malloc(sizeof(aim_snac_t))))
+		return 0;
+	memcpy(snac, newsnac, sizeof(aim_snac_t));
+	snac->issuetime = time(NULL);
+
+	index = snac->id % AIM_SNAC_HASH_SIZE;
+
+	snac->next = (aim_snac_t *)sess->snac_hash[index];
+	sess->snac_hash[index] = (void *)snac;
+
+	return snac->id;
+}
+
+/*
+ * Finds a snac structure with the passed SNAC ID, 
+ * removes it from the list/hash, and returns a pointer to it.
+ *
+ * The returned structure must be freed by the caller.
+ *
+ */
+aim_snac_t *aim_remsnac(aim_session_t *sess, aim_snacid_t id) 
+{
+	aim_snac_t *cur, **prev;
+	int index;
+
+	index = id % AIM_SNAC_HASH_SIZE;
+
+	for (prev = (aim_snac_t **)&sess->snac_hash[index]; (cur = *prev); ) {
+		if (cur->id == id) {
+			*prev = cur->next;
+			return cur;
+		} else
+			prev = &cur->next;
+	}
+
+	return cur;
+}
+
+/*
+ * This is for cleaning up old SNACs that either don't get replies or
+ * a reply was never received for.  Garabage collection. Plain and simple.
+ *
+ * maxage is the _minimum_ age in seconds to keep SNACs.
+ *
+ */
+void aim_cleansnacs(aim_session_t *sess, int maxage)
+{
+	int i;
+
+	for (i = 0; i < AIM_SNAC_HASH_SIZE; i++) {
+		aim_snac_t *cur, **prev;
+		time_t curtime;
+
+		if (!sess->snac_hash[i])
+			continue;
+
+		curtime = time(NULL); /* done here in case we waited for the lock */
+
+		for (prev = (aim_snac_t **)&sess->snac_hash[i]; (cur = *prev); ) {
+			if ((curtime - cur->issuetime) > maxage) {
+
+				*prev = cur->next;
+
+				/* XXX should we have destructors here? */
+				g_free(cur->data);
+				g_free(cur);
+
+			} else
+				prev = &cur->next;
+		}
+	}
+
+	return;
+}
+
+int aim_putsnac(aim_bstream_t *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid)
+{
+
+	aimbs_put16(bs, family);
+	aimbs_put16(bs, subtype);
+	aimbs_put16(bs, flags);
+	aimbs_put32(bs, snacid);
+
+	return 10;
+}
Index: protocols/oscar/ssi.c
===================================================================
--- protocols/oscar/ssi.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ssi.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,1523 @@
+/*
+ * Server-Side/Stored Information.
+ *
+ * Relatively new facility that allows storing of certain types of information,
+ * such as a users buddy list, permit/deny list, and permit/deny preferences, 
+ * to be stored on the server, so that they can be accessed from any client.
+ *
+ * We keep a copy of the ssi data in sess->ssi, because the data needs to be 
+ * accessed for various reasons.  So all the "aim_ssi_itemlist_bleh" functions 
+ * near the top just manage the local data.
+ *
+ * The SNAC sending and receiving functions are lower down in the file, and 
+ * they're simpler.  They are in the order of the subtypes they deal with, 
+ * starting with the request rights function (subtype 0x0002), then parse 
+ * rights (subtype 0x0003), then--well, you get the idea.
+ *
+ * This is entirely too complicated.
+ * You don't know the half of it.
+ *
+ * XXX - Test for memory leaks
+ * XXX - Better parsing of rights, and use the rights info to limit adds
+ *
+ */
+
+#include <aim.h>
+#include "ssi.h"
+
+/**
+ * Locally add a new item to the given item list.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param parent A pointer to the parent group, or NULL if the item should have no 
+ *        parent group (ie. the group ID# should be 0).
+ * @param name A null terminated string of the name of the new item, or NULL if the 
+ *        item should have no name.
+ * @param type The type of the item, 0x0001 for a contact, 0x0002 for a group, etc.
+ * @return The newly created item.
+ */
+static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, char *name, guint16 type)
+{
+	int i;
+	struct aim_ssi_item *cur, *newitem;
+
+	if (!(newitem = g_new0(struct aim_ssi_item, 1)))
+		return NULL;
+
+	/* Set the name */
+	if (name) {
+		if (!(newitem->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)))) {
+			g_free(newitem);
+			return NULL;
+		}
+		strcpy(newitem->name, name);
+	} else
+		newitem->name = NULL;
+
+	/* Set the group ID# and the buddy ID# */
+	newitem->gid = 0x0000;
+	newitem->bid = 0x0000;
+	if (type == AIM_SSI_TYPE_GROUP) {
+		if (name)
+			do {
+				newitem->gid += 0x0001;
+				for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+					if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid))
+						i=1;
+			} while (i);
+	} else {
+		if (parent)
+			newitem->gid = parent->gid;
+		do {
+			newitem->bid += 0x0001;
+			for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next)
+				if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid))
+					i=1;
+		} while (i);
+	}
+
+	/* Set the rest */
+	newitem->type = type;
+	newitem->data = NULL;
+	newitem->next = *list;
+	*list = newitem;
+
+	return newitem;
+}
+
+/**
+ * Locally rebuild the 0x00c8 TLV in the additional data of the given group.
+ *
+ * @param list A pointer to a pointer to the current list of items.
+ * @param parentgroup A pointer to the group who's additional data you want to rebuild.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup)
+{
+	int newlen; //, i;
+	struct aim_ssi_item *cur;
+
+	/* Free the old additional data */
+	if (parentgroup->data) {
+		aim_freetlvchain((aim_tlvlist_t **)&parentgroup->data);
+		parentgroup->data = NULL;
+	}
+
+	/* Find the length for the new additional data */
+	newlen = 0;
+	if (parentgroup->gid == 0x0000) {
+		for (cur=*list; cur; cur=cur->next)
+			if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
+				newlen += 2;
+	} else {
+		for (cur=*list; cur; cur=cur->next)
+			if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+				newlen += 2;
+	}
+
+	/* Rebuild the additional data */
+	if (newlen>0) {
+		guint8 *newdata;
+
+		if (!(newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8))))
+			return -ENOMEM;
+		newlen = 0;
+		if (parentgroup->gid == 0x0000) {
+			for (cur=*list; cur; cur=cur->next)
+				if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP))
+						newlen += aimutil_put16(newdata+newlen, cur->gid);
+		} else {
+			for (cur=*list; cur; cur=cur->next)
+				if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY))
+						newlen += aimutil_put16(newdata+newlen, cur->bid);
+		}
+		aim_addtlvtochain_raw((aim_tlvlist_t **)&(parentgroup->data), 0x00c8, newlen, newdata);
+
+		g_free(newdata);
+	}
+
+	return 0;
+}
+
+/**
+ * Locally free all of the stored buddy list information.
+ *
+ * @param sess The oscar session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_freelist(aim_session_t *sess)
+{
+	struct aim_ssi_item *cur, *delitem;
+
+	cur = sess->ssi.items;
+	while (cur) {
+		if (cur->name)  g_free(cur->name);
+		if (cur->data)  aim_freetlvchain((aim_tlvlist_t **)&cur->data);
+		delitem = cur;
+		cur = cur->next;
+		g_free(delitem);
+	}
+
+	sess->ssi.items = NULL;
+	sess->ssi.revision = 0;
+	sess->ssi.timestamp = (time_t)0;
+
+	return 0;
+}
+
+/**
+ * Locally find an item given a group ID# and a buddy ID#.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gid The group ID# of the desired item.
+ * @param bid The buddy ID# of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid)
+{
+	struct aim_ssi_item *cur;
+	for (cur=list; cur; cur=cur->next)
+		if ((cur->gid == gid) && (cur->bid == bid))
+			return cur;
+	return NULL;
+}
+
+/**
+ * Locally find an item given a group name, screen name, and type.  If group name 
+ * and screen name are null, then just return the first item of the given type.
+ *
+ * @param list A pointer to the current list of items.
+ * @param gn The group name of the desired item.
+ * @param bn The buddy name of the desired item.
+ * @param type The type of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type)
+{
+	struct aim_ssi_item *cur;
+	if (!list)
+		return NULL;
+
+	if (gn && sn) { /* For finding buddies in groups */
+		for (cur=list; cur; cur=cur->next)
+			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) {
+				struct aim_ssi_item *curg;
+				for (curg=list; curg; curg=curg->next)
+					if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn)))
+						return cur;
+			}
+
+	} else if (sn) { /* For finding groups, permits, denies, and ignores */
+		for (cur=list; cur; cur=cur->next)
+			if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn)))
+				return cur;
+
+	/* For stuff without names--permit deny setting, visibility mask, etc. */
+	} else for (cur=list; cur; cur=cur->next) {
+		if (cur->type == type)
+			return cur;
+	}
+
+	return NULL;
+}
+
+/**
+ * Locally find the parent item of the given buddy name.
+ *
+ * @param list A pointer to the current list of items.
+ * @param bn The buddy name of the desired item.
+ * @return Return a pointer to the item if found, else return NULL;
+ */
+struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn)
+{
+	struct aim_ssi_item *cur, *curg;
+	if (!list || !sn)
+		return NULL;
+	if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY)))
+		return NULL;
+	for (curg=list; curg; curg=curg->next)
+		if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid))
+			return curg;
+	return NULL;
+}
+
+/**
+ * Locally find the permit/deny setting item, and return the setting.
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current SSI permit deny setting, or 0 if no setting was found.
+ */
+int aim_ssi_getpermdeny(struct aim_ssi_item *list)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO);
+	if (cur) {
+		aim_tlvlist_t *tlvlist = cur->data;
+		if (tlvlist) {
+			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1);
+			if (tlv && tlv->value)
+				return aimutil_get8(tlv->value);
+		}
+	}
+	return 0;
+}
+
+/**
+ * Locally find the presence flag item, and return the setting.  The returned setting is a 
+ * bitmask of the user flags that you are visible to.  See the AIM_FLAG_* #defines 
+ * in aim.h
+ *
+ * @param list A pointer to the current list of items.
+ * @return Return the current visibility mask.
+ */
+guint32 aim_ssi_getpresence(struct aim_ssi_item *list)
+{
+	struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
+	if (cur) {
+		aim_tlvlist_t *tlvlist = cur->data;
+		if (tlvlist) {
+			aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1);
+			if (tlv && tlv->length)
+				return aimutil_get32(tlv->value);
+		}
+	}
+	return 0xFFFFFFFF;
+}
+
+/**
+ * Add the given packet to the holding queue.  We totally need to send SSI SNACs one at 
+ * a time, so we have a local queue where packets get put before they are sent, and 
+ * then we send stuff one at a time, nice and orderly-like.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param fr The newly created SNAC that you want to send.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
+{
+	aim_frame_t *cur;
+
+	if (!sess || !conn || !fr)
+		return -EINVAL;
+
+	fr->next = NULL;
+	if (sess->ssi.holding_queue == NULL) {
+		sess->ssi.holding_queue = fr;
+		if (!sess->ssi.waiting_for_ack)
+			aim_ssi_modbegin(sess, conn);
+	} else {
+		for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) ;
+		cur->next = fr;
+	}
+
+	return 0;
+}
+
+/**
+ * Send the next SNAC from the holding queue.  This is called 
+ * automatically when an ack from an add, mod, or del is received.  
+ * If the queue is empty, it sends the modend SNAC.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *cur;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	if (!sess->ssi.waiting_for_ack) {
+		if (sess->ssi.holding_queue) {
+			sess->ssi.waiting_for_ack = 1;
+			cur = sess->ssi.holding_queue->next;
+			sess->ssi.holding_queue->next = NULL;
+			aim_tx_enqueue(sess, sess->ssi.holding_queue);
+			sess->ssi.holding_queue = cur;
+		} else
+			aim_ssi_modend(sess, conn);
+	}
+
+	return 0;
+}
+
+/**
+ * Send SNACs necessary to remove all SSI data from the server list, 
+ * and then free the local copy as well.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn)
+{
+	int num;
+	struct aim_ssi_item *cur, **items;
+
+	for (cur=sess->ssi.items, num=0; cur; cur=cur->next)
+		num++;
+
+	if (!(items = g_new0(struct aim_ssi_item *, num)))
+		return -ENOMEM;
+
+	for (cur=sess->ssi.items, num=0; cur; cur=cur->next) {
+		items[num] = cur;
+		num++;
+	}
+
+	aim_ssi_addmoddel(sess, conn, items, num, AIM_CB_SSI_DEL);
+	g_free(items);
+	aim_ssi_dispatch(sess, conn);
+	aim_ssi_freelist(sess);
+
+	return 0;
+}
+
+/**
+ * This "cleans" the ssi list.  It does a few things, with the intent of making 
+ * sure there ain't nothin' wrong with your SSI.
+ *   -Make sure all buddies are in a group, and all groups have the correct 
+ *     additional data.
+ *   -Make sure there are no empty groups in the list.  While there is nothing 
+ *     wrong empty groups in the SSI, it's wiser to not have them.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn)
+{
+	unsigned int i;
+	struct aim_ssi_item *cur, *parentgroup;
+
+	/* Make sure we actually need to clean out the list */
+	for (cur=sess->ssi.items, i=0; cur && !i; cur=cur->next)
+		/* Any buddies directly in the master group */
+		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000))
+			i++;
+	if (!i)
+		return 0;
+
+	/* Remove all the additional data from all groups */
+	for (cur=sess->ssi.items; cur; cur=cur->next)
+		if ((cur->data) && (cur->type == AIM_SSI_TYPE_GROUP)) {
+			aim_freetlvchain((aim_tlvlist_t **)&cur->data);
+			cur->data = NULL;
+		}
+
+	/* If there are buddies directly in the master group, make sure  */
+	/* there is a group to put them in.  Any group, any group at all. */
+	for (cur=sess->ssi.items; ((cur) && ((cur->type != AIM_SSI_TYPE_BUDDY) || (cur->gid != 0x0000))); cur=cur->next);
+	if (!cur) {
+		for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next);
+		if (!parentgroup) {
+			char *newgroup;
+			newgroup = (char*)g_malloc(strlen("Unknown")*sizeof(char));
+			strcpy(newgroup, "Unknown");
+			aim_ssi_addgroups(sess, conn, &newgroup, 1);
+		}
+	}
+
+	/* Set parentgroup equal to any arbitray group */
+	for (parentgroup=sess->ssi.items; parentgroup->gid==0x0000 || parentgroup->type!=AIM_SSI_TYPE_GROUP; parentgroup=parentgroup->next);
+
+	/* If there are any buddies directly in the master group, put them in a real group */
+	for (cur=sess->ssi.items; cur; cur=cur->next)
+		if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) {
+			aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_DEL);
+			cur->gid = parentgroup->gid;
+			aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+		}
+
+	/* Rebuild additional data for all groups */
+	for (parentgroup=sess->ssi.items; parentgroup; parentgroup=parentgroup->next)
+		if (parentgroup->type == AIM_SSI_TYPE_GROUP)
+			aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+	/* Send a mod snac for all groups */
+	i = 0;
+	for (cur=sess->ssi.items; cur; cur=cur->next)
+		if (cur->type == AIM_SSI_TYPE_GROUP)
+			i++;
+	if (i > 0) {
+		/* Allocate an array of pointers to each of the groups */
+		struct aim_ssi_item **groups;
+		if (!(groups = g_new0(struct aim_ssi_item *, i)))
+			return -ENOMEM;
+
+		for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
+			if (cur->type == AIM_SSI_TYPE_GROUP)
+				groups[i] = cur;
+
+		aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_MOD);
+		g_free(groups);
+	}
+
+	/* Send a del snac for any empty groups */
+	i = 0;
+	for (cur=sess->ssi.items; cur; cur=cur->next)
+		if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
+			i++;
+	if (i > 0) {
+		/* Allocate an array of pointers to each of the groups */
+		struct aim_ssi_item **groups;
+		if (!(groups = g_new0(struct aim_ssi_item *, i)))
+			return -ENOMEM;
+
+		for (cur=sess->ssi.items, i=0; cur; cur=cur->next)
+			if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data))
+				groups[i] = cur;
+
+		aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_DEL);
+		g_free(groups);
+	}
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Add an array of screen names to the given group.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn The name of the group to which you want to add these names.
+ * @param sn An array of null terminated strings of the names you want to add.
+ * @param num The number of screen names you are adding (size of the sn array).
+ * @param flags 1 - Add with TLV(0x66)
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags)
+{
+	struct aim_ssi_item *parentgroup, **newitems;
+	guint16 i;
+
+	if (!sess || !conn || !gn || !sn || !num)
+		return -EINVAL;
+
+	/* Look up the parent group */
+	if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) {
+		aim_ssi_addgroups(sess, conn, &gn, 1);
+		if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
+			return -ENOMEM;
+	}
+
+	/* Allocate an array of pointers to each of the new items */
+	if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+		return -ENOMEM;
+
+	/* Add items to the local list, and index them in the array */
+	for (i=0; i<num; i++)
+		if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) {
+			g_free(newitems);
+			return -ENOMEM;
+		} else if (flags & 1) {
+			aim_tlvlist_t *tl = NULL;
+			aim_addtlvtochain_noval(&tl, 0x66);
+			newitems[i]->data = tl;
+		}
+
+	/* Send the add item SNAC */
+	if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+		g_free(newitems);
+		return -i;
+	}
+
+	/* Free the array of pointers to each of the new items */
+	g_free(newitems);
+
+	/* Rebuild the additional data in the parent group */
+	if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
+		return i;
+
+	/* Send the mod item SNAC */
+	if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD )))
+		return i;
+
+	/* Begin sending SSI SNACs */
+	if (!(i = aim_ssi_dispatch(sess, conn)))
+		return i;
+
+	return 0;
+}
+
+/**
+ * Add the master group (the group containing all groups).  This is called by 
+ * aim_ssi_addgroups, if necessary.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct aim_ssi_item *newitem;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	/* Add the item to the local list, and keep a pointer to it */
+	if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP)))
+		return -ENOMEM;
+
+	/* If there are any existing groups (technically there shouldn't be, but */
+	/* just in case) then add their group ID#'s to the additional data */
+	aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem);
+
+	/* Send the add item SNAC */
+	aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Add an array of groups to the list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn An array of null terminated strings of the names you want to add.
+ * @param num The number of groups names you are adding (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
+{
+	struct aim_ssi_item *parentgroup, **newitems;
+	guint16 i;
+
+	if (!sess || !conn || !gn || !num)
+		return -EINVAL;
+
+	/* Look up the parent group */
+	if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) {
+		aim_ssi_addmastergroup(sess, conn);
+		if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+			return -ENOMEM;
+	}
+
+	/* Allocate an array of pointers to each of the new items */
+	if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+		return -ENOMEM;
+
+	/* Add items to the local list, and index them in the array */
+	for (i=0; i<num; i++)
+		if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) {
+			g_free(newitems);
+			return -ENOMEM;
+		}
+
+	/* Send the add item SNAC */
+	if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+		g_free(newitems);
+		return -i;
+	}
+
+	/* Free the array of pointers to each of the new items */
+	g_free(newitems);
+
+	/* Rebuild the additional data in the parent group */
+	if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup)))
+		return i;
+
+	/* Send the mod item SNAC */
+	if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD)))
+		return i;
+
+	/* Begin sending SSI SNACs */
+	if (!(i = aim_ssi_dispatch(sess, conn)))
+		return i;
+
+	return 0;
+}
+
+/**
+ * Add an array of a certain type of item to the list.  This can be used for 
+ * permit buddies, deny buddies, ICQ's ignore buddies, and probably other 
+ * types, also.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param sn An array of null terminated strings of the names you want to add.
+ * @param num The number of groups names you are adding (size of the sn array).
+ * @param type The type of item you want to add.  See the AIM_SSI_TYPE_BLEH 
+ *        #defines in aim.h.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type)
+{
+	struct aim_ssi_item **newitems;
+	guint16 i;
+
+	if (!sess || !conn || !sn || !num)
+		return -EINVAL;
+
+	/* Allocate an array of pointers to each of the new items */
+	if (!(newitems = g_new0(struct aim_ssi_item *, num)))
+		return -ENOMEM;
+
+	/* Add items to the local list, and index them in the array */
+	for (i=0; i<num; i++)
+		if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) {
+			g_free(newitems);
+			return -ENOMEM;
+		}
+
+	/* Send the add item SNAC */
+	if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) {
+		g_free(newitems);
+		return -i;
+	}
+
+	/* Free the array of pointers to each of the new items */
+	g_free(newitems);
+
+	/* Begin sending SSI SNACs */
+	if (!(i = aim_ssi_dispatch(sess, conn)))
+		return i;
+
+	return 0;
+}
+
+/**
+ * Move a buddy from one group to another group.  This basically just deletes the 
+ * buddy and re-adds it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param oldgn The group that the buddy is currently in.
+ * @param newgn The group that the buddy should be moved in to.
+ * @param sn The name of the buddy to be moved.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn)
+{
+	struct aim_ssi_item **groups, *buddy, *cur;
+	guint16 i;
+
+	if (!sess || !conn || !oldgn || !newgn || !sn)
+		return -EINVAL;
+
+	/* Look up the buddy */
+	if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY)))
+		return -ENOMEM;
+
+	/* Allocate an array of pointers to the two groups */
+	if (!(groups = g_new0(struct aim_ssi_item *, 2)))
+		return -ENOMEM;
+
+	/* Look up the old parent group */
+	if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) {
+		g_free(groups);
+		return -ENOMEM;
+	}
+
+	/* Look up the new parent group */
+	if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
+		g_free(groups);
+		return -ENOMEM;
+	}
+
+	/* Send the delete item SNAC */
+	aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL);
+
+	/* Put the buddy in the new group */
+	buddy->gid = groups[1]->gid;
+
+	/* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */
+	buddy->bid = 0;
+	do {
+		buddy->bid += 0x0001;
+		for (cur=sess->ssi.items, i=0; ((cur) && (!i)); cur=cur->next)
+			if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && aim_sncmp(cur->name, buddy->name))
+				i=1;
+	} while (i);
+
+	/* Rebuild the additional data in the two parent groups */
+	aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]);
+	aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]);
+
+	/* Send the add item SNAC */
+	aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD);
+
+	/* Send the mod item SNAC */
+	aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD);
+
+	/* Free the temporary array */
+	g_free(groups);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Delete an array of screen names from the given group.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn The name of the group from which you want to delete these names.
+ * @param sn An array of null terminated strings of the names you want to delete.
+ * @param num The number of screen names you are deleting (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num)
+{
+	struct aim_ssi_item *cur, *parentgroup, **delitems;
+	int i;
+
+	if (!sess || !conn || !gn || !sn || !num)
+		return -EINVAL;
+
+	/* Look up the parent group */
+	if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP)))
+		return -EINVAL;
+
+	/* Allocate an array of pointers to each of the items to be deleted */
+	delitems = g_new0(struct aim_ssi_item *, num);
+
+	/* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+	for (i=0; i<num; i++) {
+		if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) {
+			g_free(delitems);
+			return -EINVAL;
+		}
+
+		/* Remove the delitems from the item list */
+		if (sess->ssi.items == delitems[i]) {
+			sess->ssi.items = sess->ssi.items->next;
+		} else {
+			for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+			if (cur->next)
+				cur->next = cur->next->next;
+		}
+	}
+
+	/* Send the del item SNAC */
+	aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+	/* Free the items */
+	for (i=0; i<num; i++) {
+		if (delitems[i]->name)
+			g_free(delitems[i]->name);
+		if (delitems[i]->data)
+			aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+		g_free(delitems[i]);
+	}
+	g_free(delitems);
+
+	/* Rebuild the additional data in the parent group */
+	aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+	/* Send the mod item SNAC */
+	aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
+
+	/* Delete the group, but only if it's empty */
+	if (!parentgroup->data)
+		aim_ssi_delgroups(sess, conn, &parentgroup->name, 1);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Delete the master group from the item list.  There can be only one.
+ * Er, so just find the one master group and delete it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn)
+{
+	struct aim_ssi_item *cur, *delitem;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	/* Make delitem a pointer to the aim_ssi_item to be deleted */
+	if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+		return -EINVAL;
+
+	/* Remove delitem from the item list */
+	if (sess->ssi.items == delitem) {
+		sess->ssi.items = sess->ssi.items->next;
+	} else {
+		for (cur=sess->ssi.items; (cur->next && (cur->next!=delitem)); cur=cur->next);
+		if (cur->next)
+			cur->next = cur->next->next;
+	}
+
+	/* Send the del item SNAC */
+	aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL);
+
+	/* Free the item */
+	if (delitem->name)
+		g_free(delitem->name);
+	if (delitem->data)
+		aim_freetlvchain((aim_tlvlist_t **)&delitem->data);
+	g_free(delitem);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Delete an array of groups.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param gn An array of null terminated strings of the groups you want to delete.
+ * @param num The number of groups you are deleting (size of the gn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) {
+	struct aim_ssi_item *cur, *parentgroup, **delitems;
+	int i;
+
+	if (!sess || !conn || !gn || !num)
+		return -EINVAL;
+
+	/* Look up the parent group */
+	if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0)))
+		return -EINVAL;
+
+	/* Allocate an array of pointers to each of the items to be deleted */
+	delitems = g_new0(struct aim_ssi_item *, num);
+
+	/* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+	for (i=0; i<num; i++) {
+		if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) {
+			g_free(delitems);
+			return -EINVAL;
+		}
+
+		/* Remove the delitems from the item list */
+		if (sess->ssi.items == delitems[i]) {
+			sess->ssi.items = sess->ssi.items->next;
+		} else {
+			for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+			if (cur->next)
+				cur->next = cur->next->next;
+		}
+	}
+
+	/* Send the del item SNAC */
+	aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+	/* Free the items */
+	for (i=0; i<num; i++) {
+		if (delitems[i]->name)
+			g_free(delitems[i]->name);
+		if (delitems[i]->data)
+			aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+		g_free(delitems[i]);
+	}
+	g_free(delitems);
+
+	/* Rebuild the additional data in the parent group */
+	aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup);
+
+	/* Send the mod item SNAC */
+	aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD);
+
+	/* Delete the group, but only if it's empty */
+	if (!parentgroup->data)
+		aim_ssi_delmastergroup(sess, conn);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Delete an array of a certain type of item from the list.  This can be 
+ * used for permit buddies, deny buddies, ICQ's ignore buddies, and 
+ * probably other types, also.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param sn An array of null terminated strings of the items you want to delete.
+ * @param num The number of items you are deleting (size of the sn array).
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type) {
+	struct aim_ssi_item *cur, **delitems;
+	int i;
+
+	if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY))
+		return -EINVAL;
+
+	/* Allocate an array of pointers to each of the items to be deleted */
+	delitems = g_new0(struct aim_ssi_item *, num);
+
+	/* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */
+	for (i=0; i<num; i++) {
+		if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) {
+			g_free(delitems);
+			return -EINVAL;
+		}
+
+		/* Remove the delitems from the item list */
+		if (sess->ssi.items == delitems[i]) {
+			sess->ssi.items = sess->ssi.items->next;
+		} else {
+			for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next);
+			if (cur->next)
+				cur->next = cur->next->next;
+		}
+	}
+
+	/* Send the del item SNAC */
+	aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL);
+
+	/* Free the items */
+	for (i=0; i<num; i++) {
+		if (delitems[i]->name)
+			g_free(delitems[i]->name);
+		if (delitems[i]->data)
+			aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data);
+		g_free(delitems[i]);
+	}
+	g_free(delitems);
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Stores your permit/deny setting on the server, and starts using it.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param permdeny Your permit/deny setting.  Can be one of the following:
+ *        1 - Allow all users
+ *        2 - Block all users
+ *        3 - Allow only the users below
+ *        4 - Block only the users below
+ *        5 - Allow only users on my buddy list
+ * @param vismask A bitmask of the class of users to whom you want to be 
+ *        visible.  See the AIM_FLAG_BLEH #defines in aim.h
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask) {
+	struct aim_ssi_item *cur; //, *tmp;
+//	guint16 j;
+	aim_tlv_t *tlv;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	/* Look up the permit/deny settings item */
+	cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO);
+
+	if (cur) {
+		/* The permit/deny item exists */
+		if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) {
+			/* Just change the value of the x00ca TLV */
+			if (tlv->length != 1) {
+				tlv->length = 1;
+				g_free(tlv->value);
+				tlv->value = (guint8 *)g_malloc(sizeof(guint8));
+			}
+			tlv->value[0] = permdeny;
+		} else {
+			/* Need to add the x00ca TLV to the TLV chain */
+			aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
+		}
+
+		if (cur->data && (tlv = aim_gettlv(cur->data, 0x00cb, 1))) {
+			/* Just change the value of the x00cb TLV */
+			if (tlv->length != 4) {
+				tlv->length = 4;
+				g_free(tlv->value);
+				tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
+			}
+			aimutil_put32(tlv->value, vismask);
+		} else {
+			/* Need to add the x00cb TLV to the TLV chain */
+			aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
+		}
+
+		/* Send the mod item SNAC */
+		aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
+	} else {
+		/* Need to add the permit/deny item */
+		if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO)))
+			return -ENOMEM;
+		aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny);
+		aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask);
+		aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+	}
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/**
+ * Stores your setting for whether you should show up as idle or not.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param presence I think it's a bitmask, but I only know what one of the bits is:
+ *        0x00000400 - Allow others to see your idle time
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence) {
+	struct aim_ssi_item *cur; //, *tmp;
+//	guint16 j;
+	aim_tlv_t *tlv;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	/* Look up the item */
+	cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS);
+
+	if (cur) {
+		/* The item exists */
+		if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) {
+			/* Just change the value of the x00c9 TLV */
+			if (tlv->length != 4) {
+				tlv->length = 4;
+				g_free(tlv->value);
+				tlv->value = (guint8 *)g_malloc(4*sizeof(guint8));
+			}
+			aimutil_put32(tlv->value, presence);
+		} else {
+			/* Need to add the x00c9 TLV to the TLV chain */
+			aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
+		}
+
+		/* Send the mod item SNAC */
+		aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD);
+	} else {
+		/* Need to add the item */
+		if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS)))
+			return -ENOMEM;
+		aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence);
+		aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD);
+	}
+
+	/* Begin sending SSI SNACs */
+	aim_ssi_dispatch(sess, conn);
+
+	return 0;
+}
+
+/*
+ * Request SSI Rights.
+ */
+int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS);
+}
+
+/*
+ * SSI Rights Information.
+ */
+static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+/*
+ * Request SSI Data.
+ *
+ * The data will only be sent if it is newer than the posted local
+ * timestamp and revision.
+ * 
+ * Note that the client should never increment the revision, only the server.
+ * 
+ */
+int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid);
+	aimbs_put32(&fr->data, localstamp);
+	aimbs_put16(&fr->data, localrev);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !conn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0);
+
+	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * SSI Data.
+ */
+static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	struct aim_ssi_item *cur = NULL;
+	guint8 fmtver; /* guess */
+	guint16 revision;
+	guint32 timestamp;
+
+	/* When you set the version for the SSI family to 2-4, the beginning of this changes.
+	 * Instead of the version and then the revision, there is "0x0006" and then a type 
+	 * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier.  Also, 
+	 * the SNAC flags go from 0x0000 to 0x8000.  I guess the 0x0006 is the length of the 
+	 * TLV(s) that follow.  The rights SNAC does the same thing, with the differing flag 
+	 * and everything.
+	 */
+
+	fmtver = aimbs_get8(bs); /* Version of ssi data.  Should be 0x00 */
+	revision = aimbs_get16(bs); /* # of times ssi data has been modified */
+	if (revision != 0)
+		sess->ssi.revision = revision;
+
+	for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ;
+
+	while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */
+		guint16 namelen, tbslen;
+
+		if (!sess->ssi.items) {
+			if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1)))
+				return -ENOMEM;
+			cur = sess->ssi.items;
+		} else {
+			if (!(cur->next = g_new0(struct aim_ssi_item, 1)))
+				return -ENOMEM;
+			cur = cur->next;
+		}
+
+		if ((namelen = aimbs_get16(bs)))
+			cur->name = aimbs_getstr(bs, namelen);
+		cur->gid = aimbs_get16(bs);
+		cur->bid = aimbs_get16(bs);
+		cur->type = aimbs_get16(bs);
+
+		if ((tbslen = aimbs_get16(bs))) {
+			aim_bstream_t tbs;
+
+			aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen);
+			cur->data = (void *)aim_readtlvchain(&tbs);
+			aim_bstream_advance(bs, tbslen);
+		}
+	}
+
+	timestamp = aimbs_get32(bs);
+	if (timestamp != 0)
+		sess->ssi.timestamp = timestamp;
+	sess->ssi.received_data = 1;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items);
+
+	return ret;
+}
+
+/*
+ * SSI Data Enable Presence.
+ *
+ * Should be sent after receiving 13/6 or 13/f to tell the server you
+ * are ready to begin using the list.  It will promptly give you the
+ * presence information for everyone in your list and put your permit/deny
+ * settings into effect.
+ * 
+ */
+int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007);
+}
+
+/*
+ * Stuff for SSI authorizations. The code used to work with the old im_ch4
+ * messages, but those are supposed to be obsolete. This is probably
+ * ICQ-specific.
+ */
+
+/**
+ * Request authorization to add someone to the server-side buddy list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param uin The contact's ICQ UIN.
+ * @param reason The reason string to send with the request.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_auth_request( aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason )
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int snaclen;
+	
+	snaclen = 10 + 1 + strlen( uin ) + 2 + strlen( reason ) + 2;
+	
+	if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
+		return -ENOMEM;
+
+	snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0 );
+	aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid );
+	
+	aimbs_put8( &fr->data, strlen( uin ) );
+	aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
+	aimbs_put16( &fr->data, strlen( reason ) );
+	aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
+	aimbs_put16( &fr->data, 0 );
+	
+	aim_tx_enqueue( sess, fr );
+	
+	return( 0 );
+}
+
+/**
+ * Reply to an authorization request to add someone to the server-side buddy list.
+ *
+ * @param sess The oscar session.
+ * @param conn The bos connection for this session.
+ * @param uin The contact's ICQ UIN.
+ * @param yesno 1 == Permit, 0 == Deny
+ * @param reason The reason string to send with the request.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+int aim_ssi_auth_reply( aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason )
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int snaclen;
+	
+	snaclen = 10 + 1 + strlen( uin ) + 3 + strlen( reason );
+	
+	if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) )
+		return -ENOMEM;
+	
+	snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0 );
+	aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid );
+	
+	aimbs_put8( &fr->data, strlen( uin ) );
+	aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) );
+	aimbs_put8( &fr->data, yesno );
+	aimbs_put16( &fr->data, strlen( reason ) );
+	aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) );
+	
+	aim_tx_enqueue( sess, fr );
+	
+	return( 0 );
+}
+
+
+/*
+ * SSI Add/Mod/Del Item(s).
+ *
+ * Sends the SNAC to add, modify, or delete an item from the server-stored
+ * information.  These 3 SNACs all have an identical structure.  The only
+ * difference is the subtype that is set for the SNAC.
+ * 
+ */
+int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype)
+{
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+	int i, snaclen, listlen;
+	char *list = NULL;
+
+	if (!sess || !conn || !items || !num)
+		return -EINVAL;
+
+	snaclen = 10; /* For family, subtype, flags, and SNAC ID */
+	listlen = 0;
+	for (i=0; i<num; i++) {
+		snaclen += 10; /* For length, GID, BID, type, and length */
+		if (items[i]->name) {
+			snaclen += strlen(items[i]->name);
+			
+			if (subtype == AIM_CB_SSI_ADD) {
+				list = g_realloc(list, listlen + strlen(items[i]->name) + 1);
+				strcpy(list + listlen, items[i]->name);
+				listlen += strlen(items[i]->name) + 1;
+			}
+		} else {
+			if (subtype == AIM_CB_SSI_ADD) {
+				list = g_realloc(list, listlen + 1);
+				list[listlen] = '\0';
+				listlen ++;
+			}
+		}
+		if (items[i]->data)
+			snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data);
+	}
+	
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0);
+	aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid);
+	
+	g_free(list);
+
+	for (i=0; i<num; i++) {
+		aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0);
+		if (items[i]->name)
+			aimbs_putraw(&fr->data, (guint8 *)items[i]->name, strlen(items[i]->name));
+		aimbs_put16(&fr->data, items[i]->gid);
+		aimbs_put16(&fr->data, items[i]->bid);
+		aimbs_put16(&fr->data, items[i]->type);
+		aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0);
+		if (items[i]->data)
+			aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data);
+	}
+
+	aim_ssi_enqueue(sess, conn, fr);
+
+	return 0;
+}
+
+/*
+ * SSI Add/Mod/Del Ack.
+ *
+ * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel).
+ *
+ */
+static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+	aim_snac_t *origsnac;
+
+	sess->ssi.waiting_for_ack = 0;
+	aim_ssi_dispatch(sess, rx->conn);
+	
+	origsnac = aim_remsnac(sess, snac->id);
+	
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, origsnac);
+	
+	if (origsnac) {
+		g_free(origsnac->data);
+		g_free(origsnac);
+	}
+	
+	return ret;
+}
+
+/*
+ * SSI Begin Data Modification.
+ *
+ * Tells the server you're going to start modifying data.
+ * 
+ */
+int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART);
+}
+
+/*
+ * SSI End Data Modification.
+ *
+ * Tells the server you're done modifying data.
+ *
+ */
+int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn)
+{
+	return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP);
+}
+
+/*
+ * SSI Data Unchanged.
+ *
+ * Response to aim_ssi_reqdata() if the server-side data is not newer than
+ * posted local stamp/revision.
+ *
+ */
+static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	int ret = 0;
+	aim_rxcallback_t userfunc;
+
+	sess->ssi.received_data = 1;
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx);
+
+	return ret;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == AIM_CB_SSI_RIGHTSINFO)
+		return parserights(sess, mod, rx, snac, bs);
+	else if (snac->subtype == AIM_CB_SSI_LIST)
+		return parsedata(sess, mod, rx, snac, bs);
+	else if (snac->subtype == AIM_CB_SSI_SRVACK)
+		return parseack(sess, mod, rx, snac, bs);
+	else if (snac->subtype == AIM_CB_SSI_NOLIST)
+		return parsedataunchanged(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod)
+{
+	aim_ssi_freelist(sess);
+
+	return;
+}
+
+int ssi_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = AIM_CB_FAM_SSI;
+	mod->version = 0x0003;
+	mod->toolid = 0x0110;
+	mod->toolversion = 0x0629;
+	mod->flags = 0;
+	strncpy(mod->name, "ssi", sizeof(mod->name));
+	mod->snachandler = snachandler;
+	mod->shutdown = ssi_shutdown;
+
+	return 0;
+}
Index: protocols/oscar/ssi.h
===================================================================
--- protocols/oscar/ssi.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ssi.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,78 @@
+#ifndef __OSCAR_SSI_H__
+#define __OSCAR_SSI_H__
+
+#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */
+
+/*
+ * SNAC Family: Server-Stored Buddy Lists
+ */
+#define AIM_CB_SSI_ERROR 0x0001
+#define AIM_CB_SSI_REQRIGHTS 0x0002
+#define AIM_CB_SSI_RIGHTSINFO 0x0003
+#define AIM_CB_SSI_REQFULLLIST 0x0004
+#define AIM_CB_SSI_REQLIST 0x0005
+#define AIM_CB_SSI_LIST 0x0006
+#define AIM_CB_SSI_ACTIVATE 0x0007
+#define AIM_CB_SSI_ADD 0x0008
+#define AIM_CB_SSI_MOD 0x0009
+#define AIM_CB_SSI_DEL 0x000A
+#define AIM_CB_SSI_SRVACK 0x000E
+#define AIM_CB_SSI_NOLIST 0x000F
+#define AIM_CB_SSI_EDITSTART 0x0011
+#define AIM_CB_SSI_EDITSTOP 0x0012
+#define AIM_CB_SSI_SENDAUTHREQ 0x0018
+#define AIM_CB_SSI_SERVAUTHREQ 0x0019
+#define AIM_CB_SSI_SENDAUTHREP 0x001A
+#define AIM_CB_SSI_SERVAUTHREP 0x001B
+
+
+#define AIM_SSI_TYPE_BUDDY         0x0000
+#define AIM_SSI_TYPE_GROUP         0x0001
+#define AIM_SSI_TYPE_PERMIT        0x0002
+#define AIM_SSI_TYPE_DENY          0x0003
+#define AIM_SSI_TYPE_PDINFO        0x0004
+#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005
+
+struct aim_ssi_item {
+	char *name;
+	guint16 gid;
+	guint16 bid;
+	guint16 type;
+	void *data;
+	struct aim_ssi_item *next;
+};
+
+/* These build the actual SNACs and queue them to be sent */
+int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev);
+int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype);
+int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn);
+
+/* These handle the local variables */
+struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid);
+struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type);
+struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn);
+int aim_ssi_getpermdeny(struct aim_ssi_item *list);
+guint32 aim_ssi_getpresence(struct aim_ssi_item *list);
+
+/* Send packets */
+int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags);
+int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num);
+int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type);
+int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn);
+int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num);
+int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num);
+int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn);
+int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type);
+int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask);
+int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence);
+int aim_ssi_auth_request(aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason);
+int aim_ssi_auth_reply(aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason);
+
+#endif /* __OSCAR_SSI_H__ */
Index: protocols/oscar/stats.c
===================================================================
--- protocols/oscar/stats.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/stats.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,38 @@
+
+#include <aim.h>
+
+static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+	guint16 interval;
+	aim_rxcallback_t userfunc;
+
+	interval = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		return userfunc(sess, rx, interval);
+
+	return 0;
+}
+
+static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+{
+
+	if (snac->subtype == 0x0002)
+		return reportinterval(sess, mod, rx, snac, bs);
+
+	return 0;
+}
+
+int stats_modfirst(aim_session_t *sess, aim_module_t *mod)
+{
+
+	mod->family = 0x000b;
+	mod->version = 0x0001;
+	mod->toolid = 0x0104;
+	mod->toolversion = 0x0001;
+	mod->flags = 0;
+	strncpy(mod->name, "stats", sizeof(mod->name));
+	mod->snachandler = snachandler;
+
+	return 0;
+}
Index: protocols/oscar/tlv.c
===================================================================
--- protocols/oscar/tlv.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/tlv.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,600 @@
+#include <aim.h>
+
+static aim_tlv_t *createtlv(void)
+{
+	return g_new0(aim_tlv_t, 1);
+}
+
+static void freetlv(aim_tlv_t **oldtlv)
+{
+
+	if (!oldtlv || !*oldtlv)
+		return;
+	
+	g_free((*oldtlv)->value);
+	g_free(*oldtlv);
+	*oldtlv = NULL;
+
+	return;
+}
+
+/**
+ * aim_readtlvchain - Read a TLV chain from a buffer.
+ * @buf: Input buffer
+ * @maxlen: Length of input buffer
+ *
+ * Reads and parses a series of TLV patterns from a data buffer; the
+ * returned structure is manipulatable with the rest of the TLV
+ * routines.  When done with a TLV chain, aim_freetlvchain() should
+ * be called to free the dynamic substructures.
+ *
+ * XXX There should be a flag setable here to have the tlvlist contain
+ * bstream references, so that at least the ->value portion of each 
+ * element doesn't need to be malloc/memcpy'd.  This could prove to be
+ * just as effecient as the in-place TLV parsing used in a couple places
+ * in libfaim.
+ *
+ */
+aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs)
+{
+	aim_tlvlist_t *list = NULL, *cur;
+	guint16 type, length;
+
+	while (aim_bstream_empty(bs)) {
+
+		type = aimbs_get16(bs);
+		length = aimbs_get16(bs);
+
+#if 0 /* temporarily disabled until I know if they're still doing it or not */
+		/*
+		 * Okay, so now AOL has decided that any TLV of
+		 * type 0x0013 can only be two bytes, despite
+		 * what the actual given length is.  So here 
+		 * we dump any invalid TLVs of that sort.  Hopefully
+		 * theres no special cases to this special case.
+		 *   - mid (30jun2000)
+		 */
+		if ((type == 0x0013) && (length != 0x0002))
+			length = 0x0002;
+#else
+		if (0)
+			;
+#endif
+		else {
+
+			cur = g_new0(aim_tlvlist_t, 1);
+
+			cur->tlv = createtlv();	
+			cur->tlv->type = type;
+			if ((cur->tlv->length = length))
+			       cur->tlv->value = aimbs_getraw(bs, length);	
+
+			cur->next = list;
+			list = cur;
+		}
+	}
+
+	return list;
+}
+
+/**
+ * aim_freetlvchain - Free a TLV chain structure
+ * @list: Chain to be freed
+ *
+ * Walks the list of TLVs in the passed TLV chain and
+ * frees each one. Note that any references to this data
+ * should be removed before calling this.
+ *
+ */
+void aim_freetlvchain(aim_tlvlist_t **list)
+{
+	aim_tlvlist_t *cur;
+
+	if (!list || !*list)
+		return;
+
+	for (cur = *list; cur; ) {
+		aim_tlvlist_t *tmp;
+		
+		freetlv(&cur->tlv);
+
+		tmp = cur->next;
+		g_free(cur);
+		cur = tmp;
+	}
+
+	list = NULL;
+
+	return;
+}
+
+/**
+ * aim_counttlvchain - Count the number of TLVs in a chain
+ * @list: Chain to be counted
+ *
+ * Returns the number of TLVs stored in the passed chain.
+ *
+ */
+int aim_counttlvchain(aim_tlvlist_t **list)
+{
+	aim_tlvlist_t *cur;
+	int count;
+
+	if (!list || !*list)
+		return 0;
+
+	for (cur = *list, count = 0; cur; cur = cur->next)
+		count++;
+
+	return count;
+}
+
+/**
+ * aim_sizetlvchain - Count the number of bytes in a TLV chain
+ * @list: Chain to be sized
+ *
+ * Returns the number of bytes that would be needed to 
+ * write the passed TLV chain to a data buffer.
+ *
+ */
+int aim_sizetlvchain(aim_tlvlist_t **list)
+{
+	aim_tlvlist_t *cur;
+	int size;
+
+	if (!list || !*list)
+		return 0;
+
+	for (cur = *list, size = 0; cur; cur = cur->next)
+		size += (4 + cur->tlv->length);
+
+	return size;
+}
+
+/**
+ * aim_addtlvtochain_str - Add a string to a TLV chain
+ * @list: Desination chain (%NULL pointer if empty)
+ * @type: TLV type
+ * @str: String to add
+ * @len: Length of string to add (not including %NULL)
+ *
+ * Adds the passed string as a TLV element of the passed type
+ * to the TLV chain.
+ *
+ */
+int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v)
+{
+	aim_tlvlist_t *newtlv, *cur;
+
+	if (!list)
+		return 0;
+
+	if (!(newtlv = g_new0(aim_tlvlist_t, 1)))
+		return 0;
+
+	if (!(newtlv->tlv = createtlv())) {
+		g_free(newtlv);
+		return 0;
+	}
+	newtlv->tlv->type = t;
+	if ((newtlv->tlv->length = l)) {
+		newtlv->tlv->value = (guint8 *)g_malloc(newtlv->tlv->length);
+		memcpy(newtlv->tlv->value, v, newtlv->tlv->length);
+	}
+
+	if (!*list)
+		*list = newtlv;
+	else {
+		for(cur = *list; cur->next; cur = cur->next)
+			;
+		cur->next = newtlv;
+	}
+
+	return newtlv->tlv->length;
+}
+
+/**
+ * aim_addtlvtochain8 - Add a 8bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a one-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v)
+{
+	guint8 v8[1];
+
+	aimutil_put8(v8, v);
+
+	return aim_addtlvtochain_raw(list, t, 1, v8);
+}
+
+/**
+ * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a two-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v)
+{
+	guint8 v16[2];
+
+	aimutil_put16(v16, v);
+
+	return aim_addtlvtochain_raw(list, t, 2, v16);
+}
+
+/**
+ * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a four-byte unsigned integer to a TLV chain.
+ *
+ */
+int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 t, const guint32 v)
+{
+	guint8 v32[4];
+
+	aimutil_put32(v32, v);
+
+	return aim_addtlvtochain_raw(list, t, 4, v32);
+}
+
+/**
+ * aim_addtlvtochain_availmsg - Add a ICQ availability message to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @val: Value to add
+ *
+ * Adds a available message to a TLV chain
+ *
+ */
+int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 t, const char *msg)
+{
+	int ret;
+	guint16 unknown_data = 0x00;
+	guint8 add_data_len = 4;
+	guint16 msg_len = strlen(msg);
+	guint8 total_len = strlen(msg) + add_data_len;
+	guint8 *data, *cur;
+	guint8 alloc_len = msg_len + (3*sizeof(guint16)) + (2*sizeof(guint8));
+	data = cur = g_malloc(alloc_len);
+	
+	cur += aimutil_put16(cur, 2);
+	cur += aimutil_put8(cur, add_data_len);
+	cur += aimutil_put8(cur, total_len);
+	cur += aimutil_put16(cur, msg_len);
+	cur += aimutil_putstr(cur, msg, msg_len);
+	cur += aimutil_put16(cur, unknown_data);
+
+	ret = aim_addtlvtochain_raw(list, t, alloc_len, data);
+	g_free(data);
+
+	return ret;
+}
+
+/**
+ * aim_addtlvtochain_caps - Add a capability block to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ * @caps: Bitfield of capability flags to send
+ *
+ * Adds a block of capability blocks to a TLV chain. The bitfield
+ * passed in should be a bitwise %OR of any of the %AIM_CAPS constants:
+ *
+ */
+int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps)
+{
+	guint8 buf[16*16]; /* XXX icky fixed length buffer */
+	aim_bstream_t bs;
+
+	if (!caps)
+		return 0; /* nothing there anyway */
+
+	aim_bstream_init(&bs, buf, sizeof(buf));
+
+	aim_putcap(&bs, caps);
+
+	return aim_addtlvtochain_raw(list, t, aim_bstream_curpos(&bs), buf);
+}
+
+int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui)
+{
+	guint8 buf[1024]; /* bleh */
+	aim_bstream_t bs;
+
+	aim_bstream_init(&bs, buf, sizeof(buf));
+
+	aim_putuserinfo(&bs, ui);
+
+	return aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+}
+
+/**
+ * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain
+ * @list: Destination chain
+ * @type: TLV type to add
+ *
+ * Adds a TLV with a zero length to a TLV chain.
+ *
+ */
+int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 t)
+{
+	return aim_addtlvtochain_raw(list, t, 0, NULL);
+}
+
+/*
+ * Note that the inner TLV chain will not be modifiable as a tlvchain once
+ * it is written using this.  Or rather, it can be, but updates won't be
+ * made to this.
+ *
+ * XXX should probably support sublists for real.
+ * 
+ * This is so neat.
+ *
+ */
+int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl)
+{
+	guint8 *buf;
+	int buflen;
+	aim_bstream_t bs;
+
+	buflen = aim_sizetlvchain(tl);
+
+	if (buflen <= 0)
+		return 0;
+
+	if (!(buf = g_malloc(buflen)))
+		return 0;
+
+	aim_bstream_init(&bs, buf, buflen);
+
+	aim_writetlvchain(&bs, tl);
+
+	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf);
+
+	g_free(buf);
+
+	return buflen;
+}
+
+/**
+ * aim_writetlvchain - Write a TLV chain into a data buffer.
+ * @buf: Destination buffer
+ * @buflen: Maximum number of bytes that will be written to buffer
+ * @list: Source TLV chain
+ *
+ * Copies a TLV chain into a raw data buffer, writing only the number
+ * of bytes specified. This operation does not free the chain; 
+ * aim_freetlvchain() must still be called to free up the memory used
+ * by the chain structures.
+ *
+ * XXX clean this up, make better use of bstreams 
+ */
+int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list)
+{
+	int goodbuflen;
+	aim_tlvlist_t *cur;
+
+	/* do an initial run to test total length */
+	for (cur = *list, goodbuflen = 0; cur; cur = cur->next) {
+		goodbuflen += 2 + 2; /* type + len */
+		goodbuflen += cur->tlv->length;
+	}
+
+	if (goodbuflen > aim_bstream_empty(bs))
+		return 0; /* not enough buffer */
+
+	/* do the real write-out */
+	for (cur = *list; cur; cur = cur->next) {
+		aimbs_put16(bs, cur->tlv->type);
+		aimbs_put16(bs, cur->tlv->length);
+		if (cur->tlv->length)
+			aimbs_putraw(bs, cur->tlv->value, cur->tlv->length);
+	}
+
+	return 1; /* XXX this is a nonsensical return */
+}
+
+
+/**
+ * aim_gettlv - Grab the Nth TLV of type type in the TLV list list.
+ * @list: Source chain
+ * @type: Requested TLV type
+ * @nth: Index of TLV of type to get
+ *
+ * Returns a pointer to an aim_tlv_t of the specified type; 
+ * %NULL on error.  The @nth parameter is specified starting at %1.
+ * In most cases, there will be no more than one TLV of any type
+ * in a chain.
+ *
+ */
+aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+	aim_tlvlist_t *cur;
+	int i;
+
+	for (cur = list, i = 0; cur; cur = cur->next) {
+		if (cur && cur->tlv) {
+			if (cur->tlv->type == t)
+				i++;
+			if (i >= n)
+				return cur->tlv;
+		}
+	}
+
+	return NULL;
+}
+
+/**
+ * aim_gettlv_str - Retrieve the Nth TLV in chain as a string.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a %NULL-
+ * terminated string instead of an aim_tlv_t.  This is a 
+ * dynamic buffer and must be freed by the caller.
+ *
+ */
+char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+	aim_tlv_t *tlv;
+	char *newstr;
+
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return NULL;
+
+	newstr = (char *) g_malloc(tlv->length + 1);
+	memcpy(newstr, tlv->value, tlv->length);
+	*(newstr + tlv->length) = '\0';
+
+	return newstr;
+}
+
+/**
+ * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 8bit integer instead of an aim_tlv_t. 
+ *
+ */
+guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+	aim_tlv_t *tlv;
+
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get8(tlv->value);
+}
+
+/**
+ * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 16bit integer instead of an aim_tlv_t. 
+ *
+ */
+guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+	aim_tlv_t *tlv;
+
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get16(tlv->value);
+}
+
+/**
+ * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer.
+ * @list: Source TLV chain
+ * @type: TLV type to search for
+ * @nth: Index of TLV to return
+ *
+ * Same as aim_gettlv(), except that the return value is a 
+ * 32bit integer instead of an aim_tlv_t. 
+ *
+ */
+guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n)
+{
+	aim_tlv_t *tlv;
+
+	if (!(tlv = aim_gettlv(list, t, n)))
+		return 0; /* erm */
+	return aimutil_get32(tlv->value);
+}
+
+#if 0
+/**
+ * aim_puttlv_8 - Write a one-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a one-byte integer value portion.
+ *
+ */
+int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v)
+{
+	guint8 v8[1];
+
+	aimutil_put8(v8, v);
+
+	return aim_puttlv_raw(buf, t, 1, v8);
+}
+
+/**
+ * aim_puttlv_16 - Write a two-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a two-byte integer value portion.
+ *
+ */
+int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v)
+{
+	guint8 v16[2];
+
+	aimutil_put16(v16, v);
+
+	return aim_puttlv_raw(buf, t, 2, v16);
+}
+
+
+/**
+ * aim_puttlv_32 - Write a four-byte TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @v: Value
+ *
+ * Writes a TLV with a four-byte integer value portion.
+ *
+ */
+int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v)
+{
+	guint8 v32[4];
+
+	aimutil_put32(v32, v);
+
+	return aim_puttlv_raw(buf, t, 4, v32);
+}
+
+/**
+ * aim_puttlv_raw - Write a raw TLV.
+ * @buf: Destination buffer
+ * @t: TLV type
+ * @l: Length of string
+ * @v: String to write
+ *
+ * Writes a TLV with a raw value portion.  (Only the first @l
+ * bytes of the passed buffer will be written, which should not
+ * include a terminating NULL.)
+ *
+ */
+int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v)
+{
+	int i;
+
+	i = aimutil_put16(buf, t);
+	i += aimutil_put16(buf+i, l);
+	if (l)
+		memcpy(buf+i, v, l);
+	i += l;
+
+	return i;
+}
+#endif
+
Index: protocols/oscar/txqueue.c
===================================================================
--- protocols/oscar/txqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/txqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,440 @@
+/*
+ *  aim_txqueue.c
+ *
+ * Herein lies all the mangement routines for the transmit (Tx) queue.
+ *
+ */
+
+#include <aim.h>
+#include "im.h"
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ * Allocate a new tx frame.
+ *
+ * This is more for looks than anything else.
+ *
+ * Right now, that is.  If/when we implement a pool of transmit
+ * frames, this will become the request-an-unused-frame part.
+ *
+ * framing = AIM_FRAMETYPE_OFT/FLAP
+ * chan = channel for FLAP, hdrtype for OFT
+ *
+ */
+aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen)
+{
+	aim_frame_t *fr;
+
+	if (!conn) {
+		do_error_dialog(sess->aux_data, "no connection specified", "Gaim");
+		return NULL;
+	}
+
+	/* For sanity... */
+	if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || 
+			(conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) {
+		if (framing != AIM_FRAMETYPE_OFT) {
+			do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for rendezvous connection", "Gaim");
+			return NULL;
+		}
+	} else {
+		if (framing != AIM_FRAMETYPE_FLAP) {
+			do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for FLAP connection", "Gaim");
+			return NULL;
+		}
+	}
+
+	if (!(fr = (aim_frame_t *)g_new0(aim_frame_t,1)))
+		return NULL;
+
+	fr->conn = conn; 
+
+	fr->hdrtype = framing;
+
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+
+		fr->hdr.flap.type = chan;
+
+	} else if (fr->hdrtype == AIM_FRAMETYPE_OFT) {
+
+		fr->hdr.oft.type = chan;
+		fr->hdr.oft.hdr2len = 0; /* this will get setup by caller */
+
+	} else 
+		do_error_dialog(sess->aux_data, "unknown framing", "Gaim");
+
+	if (datalen > 0) {
+		guint8 *data;
+
+		if (!(data = (unsigned char *)g_malloc(datalen))) {
+			aim_frame_destroy(fr);
+			return NULL;
+		}
+
+		aim_bstream_init(&fr->data, data, datalen);
+	}
+
+	return fr;
+}
+
+/*
+ * aim_tx_enqeue__queuebased()
+ *
+ * The overall purpose here is to enqueue the passed in command struct
+ * into the outgoing (tx) queue.  Basically...
+ *   1) Make a scope-irrelevent copy of the struct
+ *   3) Mark as not-sent-yet
+ *   4) Enqueue the struct into the list
+ *   6) Return
+ *
+ * Note that this is only used when doing queue-based transmitting;
+ * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
+ *
+ */
+static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr)
+{
+
+	if (!fr->conn) {
+		do_error_dialog(sess->aux_data, "WARNING: enqueueing packet with no connection", "Gaim");
+		fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+	}
+
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+		/* assign seqnum -- XXX should really not assign until hardxmit */
+		fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+	}
+
+	fr->handled = 0; /* not sent yet */
+
+	/* see overhead note in aim_rxqueue counterpart */
+	if (!sess->queue_outgoing)
+		sess->queue_outgoing = fr;
+	else {
+		aim_frame_t *cur;
+
+		for (cur = sess->queue_outgoing; cur->next; cur = cur->next)
+			;
+		cur->next = fr;
+	}
+
+	return 0;
+}
+
+/*
+ * aim_tx_enqueue__immediate()
+ *
+ * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
+ * the whole queue mess when you want immediate writes to happen.
+ *
+ * Basically the same as its __queuebased couterpart, however
+ * instead of doing a list append, it just calls aim_tx_sendframe()
+ * right here. 
+ * 
+ */
+static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr)
+{
+
+	if (!fr->conn) {
+		do_error_dialog(sess->aux_data, "packet has no connection", "Gaim");
+		aim_frame_destroy(fr);
+		return 0;
+	}
+
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+		fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+
+	fr->handled = 0; /* not sent yet */
+
+	aim_tx_sendframe(sess, fr);
+
+	aim_frame_destroy(fr);
+
+	return 0;
+}
+
+int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
+{
+	
+	if (what == AIM_TX_QUEUED)
+		sess->tx_enqueue = &aim_tx_enqueue__queuebased;
+	else if (what == AIM_TX_IMMEDIATE) 
+		sess->tx_enqueue = &aim_tx_enqueue__immediate;
+	else if (what == AIM_TX_USER) {
+		if (!func)
+			return -EINVAL;
+		sess->tx_enqueue = func;
+	} else
+		return -EINVAL; /* unknown action */
+
+	return 0;
+}
+
+int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr)
+{
+	
+	/*
+	 * If we want to send a connection thats inprogress, we have to force
+	 * them to use the queue based version. Otherwise, use whatever they
+	 * want.
+	 */
+	if (fr && fr->conn && 
+			(fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
+		return aim_tx_enqueue__queuebased(sess, fr);
+	}
+
+	return (*sess->tx_enqueue)(sess, fr);
+}
+
+/* 
+ *  aim_get_next_txseqnum()
+ *
+ *   This increments the tx command count, and returns the seqnum
+ *   that should be stamped on the next FLAP packet sent.  This is
+ *   normally called during the final step of packet preparation
+ *   before enqueuement (in aim_tx_enqueue()).
+ *
+ */
+flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn)
+{
+	flap_seqnum_t ret;
+	
+	ret = ++conn->seqnum;
+
+	return ret;
+}
+
+static int aim_send(int fd, const void *buf, size_t count)
+{
+	int left, cur;
+
+	for (cur = 0, left = count; left; ) {
+		int ret;
+
+		ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
+		if (ret == -1)
+			return -1;
+		else if (ret == 0)
+			return cur;
+
+		cur += ret;
+		left -= ret;
+	}
+
+	return cur;
+}
+
+static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
+{
+	int wrote = 0;
+	if (!bs || !conn || (count < 0))
+		return -EINVAL;
+
+	if (count > aim_bstream_empty(bs))
+		count = aim_bstream_empty(bs); /* truncate to remaining space */
+
+	if (count) {
+		if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && 
+		    (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+			/* I strongly suspect that this is a horrible thing to do
+			 * and I feel really guilty doing it. */
+			const char *sn = aim_directim_getsn(conn);
+			aim_rxcallback_t userfunc;
+			while (count - wrote > 1024) {
+				wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
+				if ((userfunc=aim_callhandler(conn->sessv, conn, 
+							      AIM_CB_FAM_SPECIAL, 
+							      AIM_CB_SPECIAL_IMAGETRANSFER)))
+				  userfunc(conn->sessv, NULL, sn, 
+					   count-wrote>1024 ? ((double)wrote / count) : 1);
+			}
+		}
+		if (count - wrote) {
+			wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
+		}
+		
+	}
+	
+	bs->offset += wrote;
+
+	return wrote;	
+}
+
+static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
+{
+	aim_bstream_t obs;
+	guint8 *obs_raw;
+	int payloadlen, err = 0, obslen;
+
+	payloadlen = aim_bstream_curpos(&fr->data);
+
+	if (!(obs_raw = g_malloc(6 + payloadlen)))
+		return -ENOMEM;
+
+	aim_bstream_init(&obs, obs_raw, 6 + payloadlen);
+
+	/* FLAP header */
+	aimbs_put8(&obs, 0x2a);
+	aimbs_put8(&obs, fr->hdr.flap.type);
+	aimbs_put16(&obs, fr->hdr.flap.seqnum);
+	aimbs_put16(&obs, payloadlen);
+
+	/* payload */
+	aim_bstream_rewind(&fr->data);
+	aimbs_putbs(&obs, &fr->data, payloadlen);
+
+	obslen = aim_bstream_curpos(&obs);
+	aim_bstream_rewind(&obs);
+	if (aim_bstream_send(&obs, fr->conn, obslen) != obslen)
+		err = -errno;
+	
+	g_free(obs_raw); /* XXX aim_bstream_free */
+
+	fr->handled = 1;
+	fr->conn->lastactivity = time(NULL);
+
+	return err;
+}
+
+static int sendframe_oft(aim_session_t *sess, aim_frame_t *fr)
+{
+	aim_bstream_t hbs;
+	guint8 *hbs_raw;
+	int hbslen;
+	int err = 0;
+	
+	hbslen = 8 + fr->hdr.oft.hdr2len;
+	if (!(hbs_raw = g_malloc(hbslen)))
+		return -1;
+
+	aim_bstream_init(&hbs, hbs_raw, hbslen);
+
+	aimbs_putraw(&hbs, fr->hdr.oft.magic, 4);
+	aimbs_put16(&hbs, fr->hdr.oft.hdr2len + 8);
+	aimbs_put16(&hbs, fr->hdr.oft.type);
+	aimbs_putraw(&hbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len);
+
+	aim_bstream_rewind(&hbs);
+	
+	
+	if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) {
+
+		err = -errno;
+		
+	} else if (aim_bstream_curpos(&fr->data)) {
+		int len;
+
+		len = aim_bstream_curpos(&fr->data);
+		aim_bstream_rewind(&fr->data);
+		
+		if (aim_bstream_send(&fr->data, fr->conn, len) != len)
+			err = -errno;
+	}
+
+	g_free(hbs_raw); /* XXX aim_bstream_free */
+
+	fr->handled = 1;
+	fr->conn->lastactivity = time(NULL);
+
+
+	return err;
+
+
+}
+
+int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
+{
+	if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+		return sendframe_flap(sess, fr);
+	else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
+		return sendframe_oft(sess, fr);
+	return -1;
+}
+
+int aim_tx_flushqueue(aim_session_t *sess)
+{
+	aim_frame_t *cur;
+
+	for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+
+		if (cur->handled)
+	       		continue; /* already been sent */
+
+		if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
+			continue;
+
+		/*
+		 * And now for the meager attempt to force transmit
+		 * latency and avoid missed messages.
+		 */
+		if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
+			/* 
+			 * XXX should be a break! we dont want to block the 
+			 * upper layers
+			 *
+			 * XXX or better, just do this right.
+			 *
+			 */
+			sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
+		}
+
+		/* XXX this should call the custom "queuing" function!! */
+		aim_tx_sendframe(sess, cur);
+	}
+
+	/* purge sent commands from queue */
+	aim_tx_purgequeue(sess);
+
+	return 0;
+}
+
+/*
+ *  aim_tx_purgequeue()
+ *  
+ *  This is responsable for removing sent commands from the transmit 
+ *  queue. This is not a required operation, but it of course helps
+ *  reduce memory footprint at run time!  
+ *
+ */
+void aim_tx_purgequeue(aim_session_t *sess)
+{
+	aim_frame_t *cur, **prev;
+
+	for (prev = &sess->queue_outgoing; (cur = *prev); ) {
+
+		if (cur->handled) {
+			*prev = cur->next;
+
+			aim_frame_destroy(cur);
+
+		} else
+			prev = &cur->next;
+	}
+
+	return;
+}
+
+/**
+ * aim_tx_cleanqueue - get rid of packets waiting for tx on a dying conn
+ * @sess: session
+ * @conn: connection that's dying
+ *
+ * for now this simply marks all packets as sent and lets them
+ * disappear without warning.
+ *
+ */
+void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
+{
+	aim_frame_t *cur;
+
+	for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+		if (cur->conn == conn)
+			cur->handled = 1;
+	}
+
+	return;
+}
+
+
Index: protocols/proxy.c
===================================================================
--- protocols/proxy.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/proxy.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,672 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/* this is a little piece of code to handle proxy connection */
+/* it is intended to : 1st handle http proxy, using the CONNECT command
+ , 2nd provide an easy way to add socks support */
+
+#define BITLBEE_CORE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#else
+#include "sock.h"
+#define ETIMEDOUT WSAETIMEDOUT
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include "nogaim.h"
+#include "proxy.h"
+
+#define GAIM_READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR)
+#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+#define GAIM_ERR_COND   (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+
+/*FIXME*		
+	#ifndef _WIN32
+		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+			closesocket(fd);
+			g_free(phb);
+			return -1;
+		}
+		fcntl(fd, F_SETFL, 0);
+#endif*/
+
+char proxyhost[128] = "";
+int proxyport = 0;
+int proxytype = PROXY_NONE;
+char proxyuser[128] = "";
+char proxypass[128] = "";
+
+struct PHB {
+	GaimInputFunction func, proxy_func;
+	gpointer data, proxy_data;
+	char *host;
+	int port;
+	int fd;
+	gint inpa;
+};
+
+typedef struct _GaimIOClosure {
+	GaimInputFunction function;
+	guint result;
+	gpointer data;
+} GaimIOClosure;
+
+
+
+static struct sockaddr_in *gaim_gethostbyname(char *host, int port)
+{
+	static struct sockaddr_in sin;
+
+	if (!inet_aton(host, &sin.sin_addr)) {
+		struct hostent *hp;
+		if (!(hp = gethostbyname(host))) {
+			return NULL;
+		}
+		memset(&sin, 0, sizeof(struct sockaddr_in));
+		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+		sin.sin_family = hp->h_addrtype;
+	} else
+		sin.sin_family = AF_INET;
+	sin.sin_port = htons(port);
+
+	return &sin;
+}
+
+static void gaim_io_destroy(gpointer data)
+{
+	g_free(data);
+}
+
+#ifdef PROXYPROFILER
+struct proxyprofiler
+{
+	GaimInputFunction function;
+	gpointer data;
+	
+	int count;
+	
+	struct proxyprofiler *next;
+} *pp = NULL;
+
+void proxyprofiler_dump()
+{
+	struct proxyprofiler *l;
+	char s[128];
+	FILE *fp;
+	
+	sprintf( s, "proxyprofiler.%d", (int) getpid() );
+	fp = fopen( s, "w" );
+	
+	fprintf( fp, "%-18s  %-18s  %10s\n", "Function", "Data", "Count" );
+	for( l = pp; l; l = l->next )
+		fprintf( fp, "0x%-16x  0x%-16x  %10d\n", (int) l->function, (int) l->data, l->count );
+	
+	fclose( fp );
+}
+#endif
+
+static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+	GaimIOClosure *closure = data;
+	GaimInputCondition gaim_cond = 0;
+
+#ifdef PROXYPROFILER
+	struct proxyprofiler *l;
+	
+	for( l = pp; l; l = l->next )
+	{
+		if( closure->function == l->function && closure->data == l->data )
+			break;
+	}
+	if( l )
+	{
+		l->count ++;
+	}
+	else
+	{
+		l = g_new0( struct proxyprofiler, 1 );
+		l->function = closure->function;
+		l->data = closure->data;
+		l->count = 1;
+		
+		l->next = pp;
+		pp = l;
+	}
+#endif
+	
+	count_io_event(source, "proxy");
+	
+	if (condition & GAIM_READ_COND)
+		gaim_cond |= GAIM_INPUT_READ;
+	if (condition & GAIM_WRITE_COND)
+		gaim_cond |= GAIM_INPUT_WRITE;
+//	if (condition & GAIM_ERR_COND)
+//		fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) );
+
+	closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
+
+	return TRUE;
+}
+
+static void no_one_calls(gpointer data, gint source, GaimInputCondition cond)
+{
+	struct PHB *phb = data;
+	unsigned int len;
+	int error = ETIMEDOUT;
+	len = sizeof(error);
+	
+#ifndef _WIN32
+	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+		closesocket(source);
+		gaim_input_remove(phb->inpa);
+		if( phb->proxy_func )
+			phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
+		else {
+			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			g_free(phb);
+		}
+		return;
+	}
+	fcntl(source, F_SETFL, 0);
+#endif
+	gaim_input_remove(phb->inpa);
+	if( phb->proxy_func )
+		phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
+	else {
+		phb->func(phb->data, source, GAIM_INPUT_READ);
+		g_free(phb);
+	}
+}
+
+static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb)
+{
+	struct sockaddr_in *sin;
+	int fd = -1;
+
+	if (!(sin = gaim_gethostbyname(host, port))) {
+		g_free(phb);
+		return -1;
+	}
+
+	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
+		g_free(phb);
+		return -1;
+	}
+
+	sock_make_nonblocking(fd);
+
+	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {
+		if (sockerr_again()) {
+			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb);
+			phb->fd = fd;
+		} else {
+			closesocket(fd);
+			g_free(phb);
+			return -1;
+		}
+	}
+
+	return fd;
+}
+
+
+/* Connecting to HTTP proxies */
+
+#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
+#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
+
+static void http_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+	int nlc = 0;
+	int pos = 0;
+	struct PHB *phb = data;
+	char inputline[8192];
+
+	gaim_input_remove(phb->inpa);
+
+	while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
+		if (inputline[pos - 1] == '\n')
+			nlc++;
+		else if (inputline[pos - 1] != '\r')
+			nlc = 0;
+	}
+	inputline[pos] = '\0';
+
+	if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
+	    (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
+		phb->func(phb->data, source, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	close(source);
+	phb->func(phb->data, -1, GAIM_INPUT_READ);
+	g_free(phb->host);
+	g_free(phb);
+	return;
+}
+
+static void http_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+	char cmd[384];
+	struct PHB *phb = data;
+	unsigned int len;
+	int error = ETIMEDOUT;
+	if (phb->inpa > 0)
+		gaim_input_remove(phb->inpa);
+	len = sizeof(error);
+	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+#ifdef F_SETFL
+	fcntl(source, F_SETFL, 0);
+#endif
+
+	g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
+		   phb->host, phb->port);
+	if (send(source, cmd, strlen(cmd), 0) < 0) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	if (proxyuser && *proxyuser) {
+		char *t1, *t2;
+		t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
+		t2 = tobase64(t1);
+		g_free(t1);
+		g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
+		g_free(t2);
+		if (send(source, cmd, strlen(cmd), 0) < 0) {
+			close(source);
+			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			g_free(phb->host);
+			g_free(phb);
+			return;
+		}
+	}
+
+	g_snprintf(cmd, sizeof(cmd), "\r\n");
+	if (send(source, cmd, strlen(cmd), 0) < 0) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb);
+}
+
+static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb)
+{
+	phb->host = g_strdup(host);
+	phb->port = port;
+	phb->proxy_func = http_canwrite;
+	phb->proxy_data = phb;
+	
+	return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS4 proxies */
+
+static void s4_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char packet[12];
+	struct PHB *phb = data;
+
+	gaim_input_remove(phb->inpa);
+
+	memset(packet, 0, sizeof(packet));
+	if (read(source, packet, 9) >= 4 && packet[1] == 90) {
+		phb->func(phb->data, source, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	close(source);
+	phb->func(phb->data, -1, GAIM_INPUT_READ);
+	g_free(phb->host);
+	g_free(phb);
+}
+
+static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char packet[12];
+	struct hostent *hp;
+	struct PHB *phb = data;
+	unsigned int len;
+	int error = ETIMEDOUT;
+	if (phb->inpa > 0)
+		gaim_input_remove(phb->inpa);
+	len = sizeof(error);
+	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+#ifdef F_SETFL
+	fcntl(source, F_SETFL, 0);
+#endif
+
+	/* XXX does socks4 not support host name lookups by the proxy? */
+	if (!(hp = gethostbyname(phb->host))) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	packet[0] = 4;
+	packet[1] = 1;
+	packet[2] = phb->port >> 8;
+	packet[3] = phb->port & 0xff;
+	packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
+	packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
+	packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
+	packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
+	packet[8] = 0;
+	if (write(source, packet, 9) != 9) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb);
+}
+
+static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb)
+{
+	phb->host = g_strdup(host);
+	phb->port = port;
+	phb->proxy_func = s4_canwrite;
+	phb->proxy_data = phb;
+	
+	return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS5 proxies */
+
+static void s5_canread_again(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char buf[512];
+	struct PHB *phb = data;
+
+	gaim_input_remove(phb->inpa);
+
+	if (read(source, buf, 10) < 10) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+	if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	phb->func(phb->data, source, GAIM_INPUT_READ);
+	g_free(phb->host);
+	g_free(phb);
+	return;
+}
+
+static void s5_sendconnect(gpointer data, gint source)
+{
+	unsigned char buf[512];
+	struct PHB *phb = data;
+	int hlen = strlen(phb->host);
+
+	buf[0] = 0x05;
+	buf[1] = 0x01;		/* CONNECT */
+	buf[2] = 0x00;		/* reserved */
+	buf[3] = 0x03;		/* address type -- host name */
+	buf[4] = hlen;
+	memcpy(buf + 5, phb->host, hlen);
+	buf[5 + strlen(phb->host)] = phb->port >> 8;
+	buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
+
+	if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb);
+}
+
+static void s5_readauth(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char buf[512];
+	struct PHB *phb = data;
+
+	gaim_input_remove(phb->inpa);
+
+	if (read(source, buf, 2) < 2) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	s5_sendconnect(phb, source);
+}
+
+static void s5_canread(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char buf[512];
+	struct PHB *phb = data;
+
+	gaim_input_remove(phb->inpa);
+
+	if (read(source, buf, 2) < 2) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	if (buf[1] == 0x02) {
+		unsigned int i = strlen(proxyuser), j = strlen(proxypass);
+		buf[0] = 0x01;	/* version 1 */
+		buf[1] = i;
+		memcpy(buf + 2, proxyuser, i);
+		buf[2 + i] = j;
+		memcpy(buf + 2 + i + 1, proxypass, j);
+		if (write(source, buf, 3 + i + j) < 3 + i + j) {
+			close(source);
+			phb->func(phb->data, -1, GAIM_INPUT_READ);
+			g_free(phb->host);
+			g_free(phb);
+			return;
+		}
+
+		phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb);
+	} else {
+		s5_sendconnect(phb, source);
+	}
+}
+
+static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond)
+{
+	unsigned char buf[512];
+	int i;
+	struct PHB *phb = data;
+	unsigned int len;
+	int error = ETIMEDOUT;
+	if (phb->inpa > 0)
+		gaim_input_remove(phb->inpa);
+	len = sizeof(error);
+	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+#ifdef F_SETFL
+	fcntl(source, F_SETFL, 0);
+#endif
+
+	i = 0;
+	buf[0] = 0x05;		/* SOCKS version 5 */
+	if (proxyuser[0]) {
+		buf[1] = 0x02;	/* two methods */
+		buf[2] = 0x00;	/* no authentication */
+		buf[3] = 0x02;	/* username/password authentication */
+		i = 4;
+	} else {
+		buf[1] = 0x01;
+		buf[2] = 0x00;
+		i = 3;
+	}
+
+	if (write(source, buf, i) < i) {
+		close(source);
+		phb->func(phb->data, -1, GAIM_INPUT_READ);
+		g_free(phb->host);
+		g_free(phb);
+		return;
+	}
+
+	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);
+}
+
+static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb)
+{
+	phb->host = g_strdup(host);
+	phb->port = port;
+	phb->proxy_func = s5_canwrite;
+	phb->proxy_data = phb;
+	
+	return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Export functions */
+
+gint gaim_input_add(gint source, GaimInputCondition condition, GaimInputFunction function, gpointer data)
+{
+	GaimIOClosure *closure = g_new0(GaimIOClosure, 1);
+	GIOChannel *channel;
+	GIOCondition cond = 0;
+	
+	closure->function = function;
+	closure->data = data;
+	
+	if (condition & GAIM_INPUT_READ)
+		cond |= GAIM_READ_COND;
+	if (condition & GAIM_INPUT_WRITE)
+		cond |= GAIM_WRITE_COND;
+	
+	channel = g_io_channel_unix_new(source);
+	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
+					      gaim_io_invoke, closure, gaim_io_destroy);
+	
+	g_io_channel_unref(channel);
+	return closure->result;
+}
+
+void gaim_input_remove(gint tag)
+{
+	if (tag > 0)
+		g_source_remove(tag);
+}
+
+int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data)
+{
+	struct PHB *phb;
+	
+	if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+		return -1;
+	}
+	
+	phb = g_new0(struct PHB, 1);
+	phb->func = func;
+	phb->data = data;
+	
+#ifndef _WIN32
+	sethostent(1);
+#endif
+	
+	if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
+		return proxy_connect_none(host, port, phb);
+	else if (proxytype == PROXY_HTTP)
+		return proxy_connect_http(host, port, phb);
+	else if (proxytype == PROXY_SOCKS4)
+		return proxy_connect_socks4(host, port, phb);
+	else if (proxytype == PROXY_SOCKS5)
+		return proxy_connect_socks5(host, port, phb);
+	
+	if (phb->host) g_free(phb);
+	g_free(phb);
+	return -1;
+}
Index: protocols/proxy.h
===================================================================
--- protocols/proxy.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/proxy.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,60 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/* this is the export part of the proxy.c file. it does a little
+   prototype-ing stuff and redefine some net function to mask them
+   with some kind of transparent layer */ 
+
+#ifndef _PROXY_H_
+#define _PROXY_H_
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+#include <glib.h>
+#include <gmodule.h>
+
+#define PROXY_NONE 0
+#define PROXY_HTTP 1
+#define PROXY_SOCKS4 2
+#define PROXY_SOCKS5 3
+
+extern char proxyhost[128];
+extern int  proxyport;
+extern int  proxytype;
+extern char proxyuser[128];
+extern char proxypass[128];
+
+typedef enum {
+	GAIM_INPUT_READ = 1 << 0,
+	GAIM_INPUT_WRITE = 1 << 1
+} GaimInputCondition;
+typedef void (*GaimInputFunction)(gpointer, gint, GaimInputCondition);
+
+G_MODULE_EXPORT gint gaim_input_add(int, GaimInputCondition, GaimInputFunction, gpointer);
+G_MODULE_EXPORT void gaim_input_remove(gint);
+
+G_MODULE_EXPORT int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data);
+
+#endif /* _PROXY_H_ */
Index: protocols/purple/Makefile
===================================================================
--- protocols/purple/Makefile	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,47 +1,0 @@
-###########################
-## Makefile for BitlBee  ##
-##                       ##
-## Copyright 2002 Lintux ##
-###########################
-
-### DEFINITIONS
-
--include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/purple/
-endif
-
-# [SH] Program variables
-objects = ft.o purple.o
-
-CFLAGS += -Wall $(PURPLE_CFLAGS)
-LFLAGS += -r
-
-# [SH] Phony targets
-all: purple_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
-
-.PHONY: all clean distclean
-
-clean:
-	rm -f *.o core
-
-distclean: clean
-	rm -rf .depend
-
-### MAIN PROGRAM
-
-$(objects): ../../Makefile.settings Makefile
-
-$(objects): %.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
-purple_mod.o: $(objects)
-	@echo '*' Linking purple_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o purple_mod.o
-
--include .depend/*.d
Index: protocols/purple/bpurple.h
===================================================================
--- protocols/purple/bpurple.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,23 +1,0 @@
-#ifndef BPURPLE_H
-# define BPURPLE_H
-
-#include <purple.h>
-#include <glib.h>
-
-#define PURPLE_REQUEST_HANDLE "purple_request"
-
-#define PURPLE_OPT_SHOULD_SET_NICK 1
-
-struct purple_data
-{
-    PurpleAccount *account;
-
-    GHashTable *input_requests;
-    guint next_request_id;
-    char *chat_list_server;
-    GSList *filetransfers;
-
-    int flags;
-};
-
-#endif /* !BPURPLE_H */
Index: protocols/purple/ft-direct.c
===================================================================
--- protocols/purple/ft-direct.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,239 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - File transfer stuff                                   *
-*                                                                           *
-*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* This code tries to do direct file transfers, i.e. without caching the file
-   locally on disk first. Since libpurple can only do this since version 2.6.0
-   and even then very unreliably (and not with all IM modules), I'm canning
-   this code for now. */
-
-#include "bitlbee.h"
-#include "bpurple.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-struct prpl_xfer_data {
-	PurpleXfer *xfer;
-	file_transfer_t *ft;
-	gint ready_timer;
-	char *buf;
-	int buf_len;
-};
-
-static file_transfer_t *next_ft;
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa);
-
-/* Glorious hack: We seem to have to remind at least some libpurple plugins
-   that we're ready because this info may get lost if we give it too early.
-   So just do it ten times a second. :-/ */
-static gboolean prplcb_xfer_write_request_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	struct prpl_xfer_data *px = data;
-
-	purple_xfer_ui_ready(px->xfer);
-
-	return purple_xfer_get_type(px->xfer) == PURPLE_XFER_RECEIVE;
-}
-
-static gboolean prpl_xfer_write_request(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->ready_timer = b_timeout_add(100, prplcb_xfer_write_request_cb, px);
-	return TRUE;
-}
-
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->buf = g_memdup2(buffer, len);
-	px->buf_len = len;
-
-	//purple_xfer_ui_ready( px->xfer );
-	px->ready_timer = b_timeout_add(0, prplcb_xfer_write_request_cb, px);
-
-	return TRUE;
-}
-
-static void prpl_xfer_accept(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_accepted(px->xfer, NULL);
-	prpl_xfer_write_request(ft);
-}
-
-static void prpl_xfer_canceled(struct file_transfer *ft, char *reason)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_denied(px->xfer);
-}
-
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	PurpleXfer *xfer = data;
-	struct im_connection *ic = purple_ic_by_pa(xfer->account);
-	struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-	PurpleBuddy *buddy;
-	const char *who;
-
-	buddy = purple_find_buddy(xfer->account, xfer->who);
-	who = buddy ? purple_buddy_get_name(buddy) : xfer->who;
-
-	/* TODO(wilmer): After spreading some more const goodness in BitlBee,
-	   remove the evil cast below. */
-	px->ft = imcb_file_send_start(ic, (char *) who, xfer->filename, xfer->size);
-	px->ft->data = px;
-	px->xfer = data;
-	px->xfer->ui_data = px;
-
-	px->ft->accept = prpl_xfer_accept;
-	px->ft->canceled = prpl_xfer_canceled;
-	px->ft->write_request = prpl_xfer_write_request;
-
-	return FALSE;
-}
-
-static void prplcb_xfer_new(PurpleXfer *xfer)
-{
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
-		/* This should suppress the stupid file dialog. */
-		purple_xfer_set_local_filename(xfer, "/tmp/wtf123");
-
-		/* Sadly the xfer struct is still empty ATM so come back after
-		   the caller is done. */
-		b_timeout_add(0, prplcb_xfer_new_send_cb, xfer);
-	} else {
-		struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-
-		px->ft = next_ft;
-		px->ft->data = px;
-		px->xfer = xfer;
-		px->xfer->ui_data = px;
-
-		purple_xfer_set_filename(xfer, px->ft->file_name);
-		purple_xfer_set_size(xfer, px->ft->file_size);
-
-		next_ft = NULL;
-	}
-}
-
-static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
-{
-	fprintf(stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent);
-}
-
-static void prplcb_xfer_dbg(PurpleXfer *xfer)
-{
-	fprintf(stderr, "prplcb_xfer_dbg 0x%p\n", xfer);
-}
-
-static gssize prplcb_xfer_write(PurpleXfer *xfer, const guchar *buffer, gssize size)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-	gboolean st;
-
-	fprintf(stderr, "xfer_write %d %d\n", size, px->buf_len);
-
-	b_event_remove(px->ready_timer);
-	px->ready_timer = 0;
-
-	st = px->ft->write(px->ft, (char *) buffer, size);
-
-	if (st && xfer->bytes_remaining == size) {
-		imcb_file_finished(px->ft);
-	}
-
-	return st ? size : 0;
-}
-
-gssize prplcb_xfer_read(PurpleXfer *xfer, guchar **buffer, gssize size)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	fprintf(stderr, "xfer_read %d %d\n", size, px->buf_len);
-
-	if (px->buf) {
-		*buffer = px->buf;
-		px->buf = NULL;
-
-		px->ft->write_request(px->ft);
-
-		return px->buf_len;
-	}
-
-	return 0;
-}
-
-PurpleXferUiOps bee_xfer_uiops =
-{
-	prplcb_xfer_new,           /* new_xfer */
-	prplcb_xfer_dbg,           /* destroy */
-	prplcb_xfer_dbg,           /* add_xfer */
-	prplcb_xfer_progress,      /* update_progress */
-	prplcb_xfer_dbg,           /* cancel_local */
-	prplcb_xfer_dbg,           /* cancel_remote */
-	prplcb_xfer_write,         /* ui_write */
-	prplcb_xfer_read,          /* ui_read */
-	prplcb_xfer_dbg,           /* data_not_sent */
-};
-
-static gboolean prplcb_xfer_send_cb(gpointer data, gint fd, b_input_condition cond);
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
-{
-	struct purple_data *pd = ic->proto_data;
-	struct prpl_xfer_data *px;
-
-	/* xfer_new() will pick up this variable. It's a hack but we're not
-	   multi-threaded anyway. */
-	next_ft = ft;
-	serv_send_file(purple_account_get_connection(pd->account), handle,
-	               ft->file_name);
-
-	ft->write = prpl_xfer_write;
-
-	px = ft->data;
-	imcb_file_recv_start(ft);
-
-	px->ready_timer = b_timeout_add(100, prplcb_xfer_send_cb, px);
-}
-
-static gboolean prplcb_xfer_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	struct prpl_xfer_data *px = data;
-
-	if (px->ft->status & FT_STATUS_TRANSFERRING) {
-		fprintf(stderr, "The ft, it is ready...\n");
-		px->ft->write_request(px->ft);
-
-		return FALSE;
-	}
-
-	return TRUE;
-}
Index: protocols/purple/ft.c
===================================================================
--- protocols/purple/ft.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,404 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - File transfer stuff                                   *
-*                                                                           *
-*  Copyright 2009-2010 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* Do file transfers via disk for now, since libpurple was really designed
-   for straight-to/from disk fts and is only just learning how to pass the
-   file contents the the UI instead (2.6.0 and higher it seems, and with
-   varying levels of success). */
-
-#include "bitlbee.h"
-#include "bpurple.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-struct prpl_xfer_data {
-	PurpleXfer *xfer;
-	file_transfer_t *ft;
-	struct im_connection *ic;
-	int fd;
-	char *fn, *handle;
-	gboolean ui_wants_data;
-	int timeout;
-};
-
-static file_transfer_t *next_ft;
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa);
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond);
-static gboolean prpl_xfer_write_request(struct file_transfer *ft);
-
-
-/* Receiving files (IM->UI): */
-static void prpl_xfer_accept(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_accepted(px->xfer, NULL);
-	prpl_xfer_write_request(ft);
-}
-
-static void prpl_xfer_canceled(struct file_transfer *ft, char *reason)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	if (px->xfer) {
-		if (!purple_xfer_is_completed(px->xfer) && !purple_xfer_is_canceled(px->xfer)) {
-			purple_xfer_cancel_local(px->xfer);
-		}
-		px->xfer->ui_data = NULL;
-		purple_xfer_unref(px->xfer);
-		px->xfer = NULL;
-	}
-}
-
-static void prpl_xfer_free(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-	struct purple_data *pd = px->ic->proto_data;
-
-	pd->filetransfers = g_slist_remove(pd->filetransfers, px);
-
-	if (px->xfer) {
-		px->xfer->ui_data = NULL;
-		purple_xfer_unref(px->xfer);
-	}
-
-	if (px->timeout) {
-		b_event_remove(px->timeout);
-	}
-
-	g_free(px->fn);
-	g_free(px->handle);
-	if (px->fd >= 0) {
-		close(px->fd);
-	}
-	g_free(px);
-}
-
-static void prplcb_xfer_new(PurpleXfer *xfer)
-{
-	purple_xfer_ref(xfer);
-
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
-		struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-		struct purple_data *pd;
-
-		xfer->ui_data = px;
-		px->xfer = xfer;
-		px->fn = mktemp(g_strdup("/tmp/bitlbee-purple-ft.XXXXXX"));
-		px->fd = -1;
-		px->ic = purple_ic_by_pa(xfer->account);
-
-		pd = px->ic->proto_data;
-		pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
-
-		purple_xfer_set_local_filename(xfer, px->fn);
-
-		/* Sadly the xfer struct is still empty ATM so come back after
-		   the caller is done. */
-		b_timeout_add(0, prplcb_xfer_new_send_cb, xfer);
-	} else {
-		struct file_transfer *ft = next_ft;
-		struct prpl_xfer_data *px = ft->data;
-
-		xfer->ui_data = px;
-		px->xfer = xfer;
-
-		next_ft = NULL;
-	}
-}
-
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	PurpleXfer *xfer = data;
-	struct im_connection *ic = purple_ic_by_pa(xfer->account);
-	struct prpl_xfer_data *px = xfer->ui_data;
-	PurpleBuddy *buddy;
-	const char *who;
-
-	buddy = purple_find_buddy(xfer->account, xfer->who);
-	who = buddy ? purple_buddy_get_name(buddy) : xfer->who;
-
-	/* TODO(wilmer): After spreading some more const goodness in BitlBee,
-	   remove the evil cast below. */
-	px->ft = imcb_file_send_start(ic, (char *) who, xfer->filename, xfer->size);
-
-	if (!px->ft) {
-		return FALSE;
-	}
-	px->ft->data = px;
-
-	px->ft->accept = prpl_xfer_accept;
-	px->ft->canceled = prpl_xfer_canceled;
-	px->ft->free = prpl_xfer_free;
-	px->ft->write_request = prpl_xfer_write_request;
-
-	return FALSE;
-}
-
-gboolean try_write_to_ui(gpointer data, gint fd, b_input_condition cond)
-{
-	struct file_transfer *ft = data;
-	struct prpl_xfer_data *px = ft->data;
-	struct stat fs;
-	off_t tx_bytes;
-
-	/* If we don't have the file opened yet, there's no data so wait. */
-	if (px->fd < 0 || !px->ui_wants_data) {
-		return FALSE;
-	}
-
-	tx_bytes = lseek(px->fd, 0, SEEK_CUR);
-	fstat(px->fd, &fs);
-
-	if (fs.st_size > tx_bytes) {
-		char buf[1024];
-		size_t n = MIN(fs.st_size - tx_bytes, sizeof(buf));
-
-		if (read(px->fd, buf, n) == n && ft->write(ft, buf, n)) {
-			px->ui_wants_data = FALSE;
-		} else {
-			purple_xfer_cancel_local(px->xfer);
-			imcb_file_canceled(px->ic, ft, "Read error");
-		}
-	}
-
-	if (lseek(px->fd, 0, SEEK_CUR) == px->xfer->size) {
-		/*purple_xfer_end( px->xfer );*/
-		imcb_file_finished(px->ic, ft);
-	}
-
-	return FALSE;
-}
-
-/* UI calls this when its buffer is empty and wants more data to send to the user. */
-static gboolean prpl_xfer_write_request(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->ui_wants_data = TRUE;
-	try_write_to_ui(ft, 0, 0);
-
-	return FALSE;
-}
-
-
-static void prplcb_xfer_destroy(PurpleXfer *xfer)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px) {
-		px->xfer = NULL;
-	}
-}
-
-static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px == NULL) {
-		return;
-	}
-
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
-		if (*px->fn) {
-			char *slash;
-
-			unlink(px->fn);
-			if ((slash = strrchr(px->fn, '/'))) {
-				*slash = '\0';
-				rmdir(px->fn);
-			}
-			*px->fn = '\0';
-		}
-
-		return;
-	}
-
-	if (px->fd == -1 && percent > 0) {
-		/* Weeeeeeeee, we're getting data! That means the file exists
-		   by now so open it and start sending to the UI. */
-		px->fd = open(px->fn, O_RDONLY);
-
-		/* Unlink it now, because we don't need it after this. */
-		unlink(px->fn);
-	}
-
-	if (percent < 1) {
-		try_write_to_ui(px->ft, 0, 0);
-	} else {
-		/* Another nice problem: If we have the whole file, it only
-		   gets closed when we return. Problem: There may still be
-		   stuff buffered and not written, we'll only see it after
-		   the caller close()s the file. So poll the file after that. */
-		b_timeout_add(0, try_write_to_ui, px->ft);
-	}
-}
-
-static void prplcb_xfer_cancel_remote(PurpleXfer *xfer)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px && px->ft) {
-		imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
-	} else if (px) {
-		/* px->ft == NULL for sends, because of the two stages. :-/ */
-		imcb_error(px->ic, "File transfer cancelled by remote end");
-	}
-}
-
-
-/* Sending files (UI->IM): */
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len);
-static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond);
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
-{
-	struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-	struct purple_data *pd;
-	char *dir, *basename;
-
-	ft->data = px;
-	px->ft = ft;
-	px->ft->free = prpl_xfer_free;
-
-	dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
-	if (!mkdtemp(dir)) {
-		imcb_error(ic, "Could not create temporary file for file transfer");
-		g_free(px);
-		g_free(dir);
-		return;
-	}
-
-	if ((basename = strrchr(ft->file_name, '/'))) {
-		basename++;
-	} else {
-		basename = ft->file_name;
-	}
-	px->fn = g_strdup_printf("%s/%s", dir, basename);
-	px->fd = open(px->fn, O_WRONLY | O_CREAT, 0600);
-	g_free(dir);
-
-	if (px->fd < 0) {
-		imcb_error(ic, "Could not create temporary file for file transfer");
-		g_free(px);
-		g_free(px->fn);
-		return;
-	}
-
-	px->ic = ic;
-	px->handle = g_strdup(handle);
-
-	pd = px->ic->proto_data;
-	pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
-
-	imcb_log(ic,
-	         "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
-
-	px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
-}
-
-static void purple_transfer_forward(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-	struct purple_data *pd = px->ic->proto_data;
-
-	/* xfer_new() will pick up this variable. It's a hack but we're not
-	   multi-threaded anyway. */
-	next_ft = ft;
-	serv_send_file(purple_account_get_connection(pd->account),
-                   px->handle, px->fn);
-}
-
-static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	file_transfer_t *ft = data;
-	struct prpl_xfer_data *px = ft->data;
-
-	px->timeout = 0;
-
-	if (ft->write == NULL) {
-		ft->write = prpl_xfer_write;
-		imcb_file_recv_start(px->ic, ft);
-	}
-
-	ft->write_request(ft);
-
-	return FALSE;
-}
-
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	if (write(px->fd, buffer, len) != len) {
-		imcb_file_canceled(px->ic, ft, "Error while writing temporary file");
-		return FALSE;
-	}
-
-	if (lseek(px->fd, 0, SEEK_CUR) >= ft->file_size) {
-		close(px->fd);
-		px->fd = -1;
-
-		purple_transfer_forward(ft);
-		imcb_file_finished(px->ic, ft);
-		px->ft = NULL;
-	} else {
-		px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
-	}
-
-	return TRUE;
-}
-
-void purple_transfer_cancel_all(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	while (pd->filetransfers) {
-		struct prpl_xfer_data *px = pd->filetransfers->data;
-
-		if (px->ft) {
-			imcb_file_canceled(ic, px->ft, "Logging out");
-		}
-
-		pd->filetransfers = g_slist_remove(pd->filetransfers, px);
-	}
-}
-
-
-
-PurpleXferUiOps bee_xfer_uiops =
-{
-	prplcb_xfer_new,           /* new_xfer */
-	prplcb_xfer_destroy,       /* destroy */
-	NULL,                      /* add_xfer */
-	prplcb_xfer_progress,      /* update_progress */
-	NULL,                      /* cancel_local */
-	prplcb_xfer_cancel_remote, /* cancel_remote */
-	NULL,                      /* ui_write */
-	NULL,                      /* ui_read */
-	NULL,                      /* data_not_sent */
-};
Index: protocols/purple/purple.c
===================================================================
--- protocols/purple/purple.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,1976 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - Main file                                             *
-*                                                                           *
-*  Copyright 2009-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "bitlbee.h"
-#include "bpurple.h"
-#include "help.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-#if !PURPLE_VERSION_CHECK(2, 12, 0)
-#define PURPLE_MESSAGE_REMOTE_SEND 0x10000
-#endif
-
-GSList *purple_connections;
-
-/* This makes me VERY sad... :-( But some libpurple callbacks come in without
-   any context so this is the only way to get that. Don't want to support
-   libpurple in daemon mode anyway. */
-static bee_t *local_bee;
-
-static char *set_eval_display_name(set_t *set, char *value);
-
-void purple_request_input_callback(guint id, struct im_connection *ic,
-                                   const char *message, const char *who);
-
-void purple_transfer_cancel_all(struct im_connection *ic);
-
-/* purple_request_input specific stuff */
-typedef void (*ri_callback_t)(gpointer, const gchar *);
-
-struct request_input_data {
-	ri_callback_t data_callback;
-	void *user_data;
-	struct im_connection *ic;
-	char *buddy;
-	guint id;
-};
-
-struct purple_roomlist_data {
-	GSList *chats;
-	gint topic;
-	gboolean initialized;
-};
-
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa)
-{
-	GSList *i;
-	struct purple_data *pd;
-
-	for (i = purple_connections; i; i = i->next) {
-		pd = ((struct im_connection *) i->data)->proto_data;
-		if (pd->account == pa) {
-			return i->data;
-		}
-	}
-
-	return NULL;
-}
-
-static struct im_connection *purple_ic_by_gc(PurpleConnection *gc)
-{
-	return purple_ic_by_pa(purple_connection_get_account(gc));
-}
-
-static gboolean purple_menu_cmp(const char *a, const char *b)
-{
-	while (*a && *b) {
-		while (*a == '_') {
-			a++;
-		}
-		while (*b == '_') {
-			b++;
-		}
-		if (g_ascii_tolower(*a) != g_ascii_tolower(*b)) {
-			return FALSE;
-		}
-
-		a++;
-		b++;
-	}
-
-	return (*a == '\0' && *b == '\0');
-}
-
-static gboolean purple_account_should_set_nick(account_t *acc)
-{
-	/* whitelist of protocols that tend to have numeric or meaningless usernames, and should
-	 * always offer the 'alias' as a nick.  this is just so that users don't have to do
-	 * 'account whatever set nick_format %full_name'
-	 */
-	char *whitelist[] = {
-		"prpl-hangouts",
-		"prpl-eionrobb-funyahoo-plusplus",
-		"prpl-line",
-		NULL,
-	};
-	char **p;
-
-	for (p = whitelist; *p; p++) {
-		if (g_strcmp0(acc->prpl->data, *p) == 0) {
-			return TRUE;
-		}
-	}
-
-	return FALSE;
-}
-
-static void purple_init(account_t *acc)
-{
-	char *prpl_id = acc->prpl->data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(prpl_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	PurpleAccount *pa;
-	GList *i, *st;
-	set_t *s;
-	char help_title[64];
-	GString *help;
-	static gboolean dir_fixed = FALSE;
-
-	/* Layer violation coming up: Making an exception for libpurple here.
-	   Dig in the IRC state a bit to get a username. Ideally we should
-	   check if s/he identified but this info doesn't seem *that* important.
-	   It's just that fecking libpurple can't *not* store this shit.
-
-	   Remember that libpurple is not really meant to be used on public
-	   servers anyway! */
-	if (!dir_fixed) {
-		PurpleCertificatePool *pool;
-		irc_t *irc = acc->bee->ui_data;
-		char *dir;
-
-		dir = g_strdup_printf("%s/purple/%s", global.conf->configdir, irc->user->nick);
-		purple_util_set_user_dir(dir);
-		g_free(dir);
-
-		purple_blist_load();
-		purple_prefs_load();
-
-		if (proxytype == PROXY_SOCKS4A) {
-			/* do this here after loading prefs. yes, i know, it sucks */
-			purple_prefs_set_bool("/purple/proxy/socks4_remotedns", TRUE);
-		}
-
-		/* re-create the certificate cache directory */
-		pool = purple_certificate_find_pool("x509", "tls_peers");
-		dir = purple_certificate_pool_mkpath(pool, NULL);
-		purple_build_dir(dir, 0700);
-		g_free(dir);
-
-		dir_fixed = TRUE;
-	}
-
-	help = g_string_new("");
-	g_string_printf(help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
-	                (char *) acc->prpl->name, prpl->info->name);
-
-	if (pi->user_splits) {
-		GList *l;
-		g_string_append_printf(help, "\n* username: Username");
-		for (l = pi->user_splits; l; l = l->next) {
-			g_string_append_printf(help, "%c%s",
-			                       purple_account_user_split_get_separator(l->data),
-			                       purple_account_user_split_get_text(l->data));
-		}
-	}
-
-	/* Convert all protocol_options into per-account setting variables. */
-	for (i = pi->protocol_options; i; i = i->next) {
-		PurpleAccountOption *o = i->data;
-		const char *name;
-		char *def = NULL;
-		set_eval eval = NULL;
-		void *eval_data = NULL;
-		GList *io = NULL;
-		GSList *opts = NULL;
-
-		name = purple_account_option_get_setting(o);
-
-		switch (purple_account_option_get_type(o)) {
-		case PURPLE_PREF_STRING:
-			def = g_strdup(purple_account_option_get_default_string(o));
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "string", def);
-
-			break;
-
-		case PURPLE_PREF_INT:
-			def = g_strdup_printf("%d", purple_account_option_get_default_int(o));
-			eval = set_eval_int;
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "integer", def);
-
-			break;
-
-		case PURPLE_PREF_BOOLEAN:
-			if (purple_account_option_get_default_bool(o)) {
-				def = g_strdup("true");
-			} else {
-				def = g_strdup("false");
-			}
-			eval = set_eval_bool;
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "boolean", def);
-
-			break;
-
-		case PURPLE_PREF_STRING_LIST:
-			def = g_strdup(purple_account_option_get_default_list_value(o));
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "list", def);
-			g_string_append(help, "\n  Possible values: ");
-
-			for (io = purple_account_option_get_list(o); io; io = io->next) {
-				PurpleKeyValuePair *kv = io->data;
-				opts = g_slist_append(opts, kv->value);
-				/* TODO: kv->value is not a char*, WTF? */
-				if (strcmp(kv->value, kv->key) != 0) {
-					g_string_append_printf(help, "%s (%s), ", (char *) kv->value, kv->key);
-				} else {
-					g_string_append_printf(help, "%s, ", (char *) kv->value);
-				}
-			}
-			g_string_truncate(help, help->len - 2);
-			eval = set_eval_list;
-			eval_data = opts;
-
-			break;
-
-		default:
-			/** No way to talk to the user right now, invent one when
-			this becomes important.
-			irc_rootmsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
-			             name, purple_account_option_get_type( o ) );
-			*/
-			g_string_append_printf(help, "\n* [%s] UNSUPPORTED (type %d)",
-			                       name, purple_account_option_get_type(o));
-			name = NULL;
-		}
-
-		if (name != NULL) {
-			s = set_add(&acc->set, name, def, eval, acc);
-			s->flags |= ACC_SET_OFFLINE_ONLY;
-			s->eval_data = eval_data;
-			g_free(def);
-		}
-	}
-
-	g_snprintf(help_title, sizeof(help_title), "purple %s", (char *) acc->prpl->name);
-	help_add_mem(&global.help, help_title, help->str);
-	g_string_free(help, TRUE);
-
-	s = set_add(&acc->set, "display_name", NULL, set_eval_display_name, acc);
-	s->flags |= ACC_SET_ONLINE_ONLY;
-
-        s = set_add(&acc->set, "no_proxy", "false", set_eval_bool, acc);
-
-	if (pi->options & OPT_PROTO_MAIL_CHECK) {
-		s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY;
-
-		s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-	}
-
-	if (strcmp(prpl->info->name, "Gadu-Gadu") == 0) {
-		s = set_add(&acc->set, "gg_sync_contacts", "true", set_eval_bool, acc);
-	}
-
-	if (g_strcmp0(prpl->info->id, "prpl-line") == 0) {
-		s = set_add(&acc->set, "line-auth-token", NULL, NULL, acc);
-		s->flags |= SET_HIDDEN;
-	}
-
-	/* Go through all away states to figure out if away/status messages
-	   are possible. */
-	pa = purple_account_new(acc->user, prpl_id);
-	for (st = purple_account_get_status_types(pa); st; st = st->next) {
-		PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
-
-		if (prim == PURPLE_STATUS_AVAILABLE) {
-			if (purple_status_type_get_attr(st->data, "message")) {
-				acc->flags |= ACC_FLAG_STATUS_MESSAGE;
-			}
-		} else if (prim != PURPLE_STATUS_OFFLINE) {
-			if (purple_status_type_get_attr(st->data, "message")) {
-				acc->flags |= ACC_FLAG_AWAY_MESSAGE;
-			}
-		}
-	}
-	purple_accounts_remove(pa);
-}
-
-static void purple_sync_settings(account_t *acc, PurpleAccount *pa)
-{
-	PurplePlugin *prpl = purple_plugins_find_with_id(pa->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GList *i;
-
-	for (i = pi->protocol_options; i; i = i->next) {
-		PurpleAccountOption *o = i->data;
-		const char *name;
-		set_t *s;
-
-		name = purple_account_option_get_setting(o);
-		s = set_find(&acc->set, name);
-		if (s->value == NULL) {
-			continue;
-		}
-
-		switch (purple_account_option_get_type(o)) {
-		case PURPLE_PREF_STRING:
-		case PURPLE_PREF_STRING_LIST:
-			purple_account_set_string(pa, name, set_getstr(&acc->set, name));
-			break;
-
-		case PURPLE_PREF_INT:
-			purple_account_set_int(pa, name, set_getint(&acc->set, name));
-			break;
-
-		case PURPLE_PREF_BOOLEAN:
-			purple_account_set_bool(pa, name, set_getbool(&acc->set, name));
-			break;
-
-		default:
-			break;
-		}
-	}
-
-	if (pi->options & OPT_PROTO_MAIL_CHECK) {
-		purple_account_set_check_mail(pa, set_getbool(&acc->set, "mail_notifications"));
-	}
-
-	if (g_strcmp0(prpl->info->id, "prpl-line") == 0) {
-		const char *name = "line-auth-token";
-		purple_account_set_string(pa, name, set_getstr(&acc->set, name));
-	}
-
-        PurpleProxyInfo *proxyinfo = purple_account_get_proxy_info(pa);
-        if (!proxyinfo) {
-          proxyinfo = purple_proxy_info_new();
-        }
-        g_return_if_fail (proxyinfo);
-        purple_proxy_info_set_type(proxyinfo, set_getbool(&acc->set, "no_proxy") ? PURPLE_PROXY_NONE : PURPLE_PROXY_USE_GLOBAL);
-        purple_account_set_proxy_info(pa, proxyinfo);
-}
-
-static void purple_login(account_t *acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct purple_data *pd;
-
-	if ((local_bee != NULL && local_bee != acc->bee) ||
-	    (global.conf->runmode == RUNMODE_DAEMON && !getenv("BITLBEE_DEBUG"))) {
-		imcb_error(ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
-		           "Please use inetd or ForkDaemon mode instead.");
-		imc_logout(ic, FALSE);
-		return;
-	}
-	local_bee = acc->bee;
-
-	/* For now this is needed in the _connected() handlers if using
-	   GLib event handling, to make sure we're not handling events
-	   on dead connections. */
-	purple_connections = g_slist_prepend(purple_connections, ic);
-
-	ic->proto_data = pd = g_new0(struct purple_data, 1);
-	pd->account = purple_account_new(acc->user, acc->prpl->data);
-	pd->input_requests = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-	                                           NULL, g_free);
-	pd->next_request_id = 0;
-	purple_account_set_password(pd->account, acc->pass);
-	purple_sync_settings(acc, pd->account);
-
-	if (purple_account_should_set_nick(acc)) {
-		pd->flags = PURPLE_OPT_SHOULD_SET_NICK;
-	}
-
-	purple_account_set_enabled(pd->account, "BitlBee", TRUE);
-
-	if (set_getbool(&acc->set, "mail_notifications") && set_getstr(&acc->set, "mail_notifications_handle")) {
-		imcb_add_buddy(ic, set_getstr(&acc->set, "mail_notifications_handle"), NULL);
-	}
-}
-
-static void purple_logout(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	if (!pd) {
-		return;
-	}
-
-	while (ic->groupchats) {
-		imcb_chat_free(ic->groupchats->data);
-	}
-
-	if (pd->filetransfers) {
-		purple_transfer_cancel_all(ic);
-	}
-
-	purple_account_set_enabled(pd->account, "BitlBee", FALSE);
-	purple_connections = g_slist_remove(purple_connections, ic);
-	purple_accounts_remove(pd->account);
-	imcb_chat_list_free(ic);
-	g_free(pd->chat_list_server);
-	g_hash_table_destroy(pd->input_requests);
-	g_free(pd);
-}
-
-static int purple_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
-{
-	PurpleConversation *conv;
-	struct purple_data *pd = ic->proto_data;
-
-	if (!strncmp(who, PURPLE_REQUEST_HANDLE, sizeof(PURPLE_REQUEST_HANDLE) - 1)) {
-		guint request_id = atoi(who + sizeof(PURPLE_REQUEST_HANDLE));
-		purple_request_input_callback(request_id, ic, message, who);
-		return 1;
-	}
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-	                                                  who, pd->account)) == NULL) {
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
-		                               pd->account, who);
-	}
-
-	purple_conv_im_send(purple_conversation_get_im_data(conv), message);
-
-	return 1;
-}
-
-static GList *purple_away_states(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-	GList *st, *ret = NULL;
-
-	for (st = purple_account_get_status_types(pd->account); st; st = st->next) {
-		PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
-		if (prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE) {
-			ret = g_list_append(ret, (void *) purple_status_type_get_name(st->data));
-		}
-	}
-
-	return ret;
-}
-
-static void purple_set_away(struct im_connection *ic, char *state_txt, char *message)
-{
-	struct purple_data *pd = ic->proto_data;
-	GList *status_types = purple_account_get_status_types(pd->account), *st;
-	PurpleStatusType *pst = NULL;
-	GList *args = NULL;
-
-	for (st = status_types; st; st = st->next) {
-		pst = st->data;
-
-		if (state_txt == NULL &&
-		    purple_status_type_get_primitive(pst) == PURPLE_STATUS_AVAILABLE) {
-			break;
-		}
-
-		if (state_txt != NULL &&
-		    g_strcasecmp(state_txt, purple_status_type_get_name(pst)) == 0) {
-			break;
-		}
-	}
-
-	if (message && purple_status_type_get_attr(pst, "message")) {
-		args = g_list_append(args, "message");
-		args = g_list_append(args, message);
-	}
-
-	purple_account_set_status_list(pd->account,
-	                               st ? purple_status_type_get_id(pst) : "away",
-	                               TRUE, args);
-
-	g_list_free(args);
-}
-
-static char *set_eval_display_name(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-	struct im_connection *ic = acc->ic;
-
-	if (ic) {
-		imcb_log(ic, "Changing display_name not currently supported with libpurple!");
-	}
-
-	return NULL;
-}
-
-/* Bad bad gadu-gadu, not saving buddy list by itself */
-static void purple_gg_buddylist_export(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
-		GList *actions = gc->prpl->info->actions(gc->prpl, gc);
-		GList *p;
-		for (p = g_list_first(actions); p; p = p->next) {
-			if (((PurplePluginAction *) p->data) &&
-			    purple_menu_cmp(((PurplePluginAction *) p->data)->label,
-			                    "Upload buddylist to Server") == 0) {
-				PurplePluginAction action;
-				action.plugin = gc->prpl;
-				action.context = gc;
-				action.user_data = NULL;
-				((PurplePluginAction *) p->data)->callback(&action);
-				break;
-			}
-		}
-		g_list_free(actions);
-	}
-}
-
-static void purple_gg_buddylist_import(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
-		GList *actions = gc->prpl->info->actions(gc->prpl, gc);
-		GList *p;
-		for (p = g_list_first(actions); p; p = p->next) {
-			if (((PurplePluginAction *) p->data) &&
-			    purple_menu_cmp(((PurplePluginAction *) p->data)->label,
-			                    "Download buddylist from Server") == 0) {
-				PurplePluginAction action;
-				action.plugin = gc->prpl;
-				action.context = gc;
-				action.user_data = NULL;
-				((PurplePluginAction *) p->data)->callback(&action);
-				break;
-			}
-		}
-		g_list_free(actions);
-	}
-}
-
-static void purple_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	PurpleBuddy *pb;
-	PurpleGroup *pg = NULL;
-	struct purple_data *pd = ic->proto_data;
-
-	if (group && !(pg = purple_find_group(group))) {
-		pg = purple_group_new(group);
-		purple_blist_add_group(pg, NULL);
-	}
-
-	pb = purple_buddy_new(pd->account, who, NULL);
-	purple_blist_add_buddy(pb, NULL, pg, NULL);
-	purple_account_add_buddy(pd->account, pb);
-
-	purple_gg_buddylist_export(pd->account->gc);
-}
-
-static void purple_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	PurpleBuddy *pb;
-	struct purple_data *pd = ic->proto_data;
-
-	pb = purple_find_buddy(pd->account, who);
-	if (pb != NULL) {
-		PurpleGroup *group;
-
-		group = purple_buddy_get_group(pb);
-		purple_account_remove_buddy(pd->account, pb, group);
-
-		purple_blist_remove_buddy(pb);
-	}
-
-	purple_gg_buddylist_export(pd->account->gc);
-}
-
-static void purple_add_permit(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_permit_add(pd->account, who, FALSE);
-}
-
-static void purple_add_deny(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_deny_add(pd->account, who, FALSE);
-}
-
-static void purple_rem_permit(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_permit_remove(pd->account, who, FALSE);
-}
-
-static void purple_rem_deny(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_deny_remove(pd->account, who, FALSE);
-}
-
-static void purple_get_info(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	serv_get_info(purple_account_get_connection(pd->account), who);
-}
-
-static void purple_keepalive(struct im_connection *ic)
-{
-}
-
-static int purple_send_typing(struct im_connection *ic, char *who, int flags)
-{
-	PurpleTypingState state = PURPLE_NOT_TYPING;
-	struct purple_data *pd = ic->proto_data;
-
-	if (flags & OPT_TYPING) {
-		state = PURPLE_TYPING;
-	} else if (flags & OPT_THINKING) {
-		state = PURPLE_TYPED;
-	}
-
-	serv_send_typing(purple_account_get_connection(pd->account), who, state);
-
-	return 1;
-}
-
-static void purple_chat_msg(struct groupchat *gc, char *message, int flags)
-{
-	PurpleConversation *pc = gc->data;
-
-	purple_conv_chat_send(purple_conversation_get_chat_data(pc), message);
-}
-
-struct groupchat *purple_chat_with(struct im_connection *ic, char *who)
-{
-	/* No, "of course" this won't work this way. Or in fact, it almost
-	   does, but it only lets you send msgs to it, you won't receive
-	   any. Instead, we have to click the virtual menu item.
-	PurpleAccount *pa = ic->proto_data;
-	PurpleConversation *pc;
-	PurpleConvChat *pcc;
-	struct groupchat *gc;
-
-	gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
-	gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
-	pc->ui_data = gc;
-
-	pcc = PURPLE_CONV_CHAT( pc );
-	purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
-	purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
-	//purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
-	*/
-
-	/* There went my nice afternoon. :-( */
-
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	PurpleBuddy *pb = purple_find_buddy(pd->account, who);
-	PurpleMenuAction *mi;
-	GList *menu;
-
-	void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
-
-	if (!pb || !pi || !pi->blist_node_menu) {
-		return NULL;
-	}
-
-	menu = pi->blist_node_menu(&pb->node);
-	while (menu) {
-		mi = menu->data;
-		if (purple_menu_cmp(mi->label, "initiate chat") ||
-		    purple_menu_cmp(mi->label, "initiate conference")) {
-			break;
-		}
-		menu = menu->next;
-	}
-
-	if (menu == NULL) {
-		return NULL;
-	}
-
-	/* Call the fucker. */
-	callback = (void *) mi->callback;
-	callback(&pb->node, mi->data);
-
-	return NULL;
-}
-
-void purple_chat_invite(struct groupchat *gc, char *who, char *message)
-{
-	PurpleConversation *pc = gc->data;
-	PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
-	struct purple_data *pd = gc->ic->proto_data;
-
-	serv_chat_invite(purple_account_get_connection(pd->account),
-	                 purple_conv_chat_get_id(pcc),
-	                 message && *message ? message : "Please join my chat",
-	                 who);
-}
-
-void purple_chat_set_topic(struct groupchat *gc, char *topic)
-{
-	PurpleConversation *pc = gc->data;
-	PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
-	struct purple_data *pd = gc->ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (pi->set_chat_topic) {
-		pi->set_chat_topic(purple_account_get_connection(pd->account),
-		                   purple_conv_chat_get_id(pcc),
-		                   topic);
-	}
-}
-
-void purple_chat_kick(struct groupchat *gc, char *who, const char *message)
-{
-	PurpleConversation *pc = gc->data;
-	char *str = g_strdup_printf("kick %s %s", who, message);
-
-	purple_conversation_do_command(pc, str, NULL, NULL);
-	g_free(str);
-}
-
-void purple_chat_leave(struct groupchat *gc)
-{
-	PurpleConversation *pc = gc->data;
-
-	purple_conversation_destroy(pc);
-}
-
-struct groupchat *purple_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   set_t **sets)
-{
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GHashTable *chat_hash;
-	PurpleConversation *conv;
-	struct groupchat *gc;
-	GList *info, *l;
-	GString *missing_settings = NULL;
-
-	if (!pi->chat_info || !pi->chat_info_defaults ||
-	    !(info = pi->chat_info(purple_account_get_connection(pd->account)))) {
-		imcb_error(ic, "Joining chatrooms not supported by this protocol");
-		return NULL;
-	}
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-	                                                  room, pd->account))) {
-		purple_conversation_destroy(conv);
-	}
-
-	chat_hash = pi->chat_info_defaults(
-	        purple_account_get_connection(pd->account), room
-	);
-
-	for (l = info; l; l = l->next) {
-		struct proto_chat_entry *pce = l->data;
-
-		if (strcmp(pce->identifier, "handle") == 0) {
-			g_hash_table_replace(chat_hash, "handle", g_strdup(nick));
-		} else if (strcmp(pce->identifier, "password") == 0) {
-			g_hash_table_replace(chat_hash, "password", g_strdup(password));
-		} else if (strcmp(pce->identifier, "passwd") == 0) {
-			g_hash_table_replace(chat_hash, "passwd", g_strdup(password));
-		} else {
-			char *key, *value;
-
-			key = g_strdup_printf("purple_%s", pce->identifier);
-			str_reject_chars(key, " -", '_');
-
-			if ((value = set_getstr(sets, key))) {
-				/* sync from bitlbee to the prpl */
-				g_hash_table_replace(chat_hash, (char *) pce->identifier, g_strdup(value));
-			} else if ((value = g_hash_table_lookup(chat_hash, pce->identifier))) {
-				/* if the bitlbee one was empty, sync from prpl to bitlbee */
-				set_setstr(sets, key, value);
-			}
-
-			g_free(key);
-		}
-
-		if (pce->required && !g_hash_table_lookup(chat_hash, pce->identifier)) {
-			if (!missing_settings) {
-				missing_settings = g_string_sized_new(32);
-			}
-			g_string_append_printf(missing_settings, "%s, ", pce->identifier);
-		}
-
-		g_free(pce);
-	}
-
-	g_list_free(info);
-
-	if (missing_settings) {
-		/* remove the ", " from the end */
-		g_string_truncate(missing_settings, missing_settings->len - 2);
-
-		imcb_error(ic, "Can't join %s. The following settings are required: %s", room, missing_settings->str);
-
-		g_string_free(missing_settings, TRUE);
-		g_hash_table_destroy(chat_hash);
-		return NULL;
-	}
-
-	/* do this before serv_join_chat to handle cases where prplcb_conv_new is called immediately (not async) */
-	gc = imcb_chat_new(ic, room);
-
-	serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
-
-	g_hash_table_destroy(chat_hash);
-
-	return gc;
-}
-
-void purple_chat_list(struct im_connection *ic, const char *server)
-{
-	PurpleRoomlist *list;
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (!pi || !pi->roomlist_get_list) {
-		imcb_log(ic, "Room listing unsupported by this purple plugin");
-		return;
-	}
-
-	g_free(pd->chat_list_server);
-	pd->chat_list_server = (server && *server) ? g_strdup(server) : NULL;
-
-	list = purple_roomlist_get_list(pd->account->gc);
-
-	if (list) {
-		struct purple_roomlist_data *rld = list->ui_data;
-		rld->initialized = TRUE;
-
-		purple_roomlist_ref(list);
-	}
-}
-
-/* handles either prpl->chat_(add|free)_settings depending on the value of 'add' */
-static void purple_chat_update_settings(account_t *acc, set_t **head, gboolean add)
-{
-	PurplePlugin *prpl = purple_plugins_find_with_id((char *) acc->prpl->data);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GList *info, *l;
-
-	if (!pi->chat_info || !pi->chat_info_defaults) {
-		return;
-	}
-
-	/* hack / leap of faith: pass a NULL here because we don't have a connection yet.
-	 * i reviewed all the built-in prpls and a bunch of third-party ones and none
-	 * of them seem to need this parameter at all, so... i hope it never crashes */
-	info = pi->chat_info(NULL);
-
-	for (l = info; l; l = l->next) {
-		struct proto_chat_entry *pce = l->data;
-		char *key;
-
-		if (strcmp(pce->identifier, "handle") == 0 ||
-		    strcmp(pce->identifier, "password") == 0 ||
-		    strcmp(pce->identifier, "passwd") == 0) {
-			/* skip these, they are handled above */
-			g_free(pce);
-			continue;
-		}
-
-		key = g_strdup_printf("purple_%s", pce->identifier);
-		str_reject_chars(key, " -", '_');
-
-		if (add) {
-			set_add(head, key, NULL, NULL, NULL);
-		} else {
-			set_del(head, key);
-		}
-
-		g_free(key);
-		g_free(pce);
-	}
-
-	g_list_free(NULL);
-	g_list_free(info);
-}
-
-static void purple_chat_add_settings(account_t *acc, set_t **head)
-{
-	purple_chat_update_settings(acc, head, TRUE);
-}
-
-static void purple_chat_free_settings(account_t *acc, set_t **head)
-{
-	purple_chat_update_settings(acc, head, FALSE);
-}
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle);
-
-static void purple_ui_init();
-
-GHashTable *prplcb_ui_info()
-{
-	static GHashTable *ret;
-
-	if (ret == NULL) {
-		ret = g_hash_table_new(g_str_hash, g_str_equal);
-		g_hash_table_insert(ret, "name", "BitlBee");
-		g_hash_table_insert(ret, "version", BITLBEE_VERSION);
-	}
-
-	return ret;
-}
-
-static PurpleCoreUiOps bee_core_uiops =
-{
-	NULL,                      /* ui_prefs_init */
-	NULL,                      /* debug_ui_init */
-	purple_ui_init,            /* ui_init */
-	NULL,                      /* quit */
-	prplcb_ui_info,            /* get_ui_info */
-};
-
-static void prplcb_conn_progress(PurpleConnection *gc, const char *text, size_t step, size_t step_count)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	imcb_log(ic, "%s", text);
-}
-
-static void prplcb_conn_connected(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-	struct purple_data *pd = ic->proto_data;
-	const char *dn, *token;
-	set_t *s;
-
-	imcb_connected(ic);
-
-	if ((dn = purple_connection_get_display_name(gc)) &&
-	    (s = set_find(&ic->acc->set, "display_name"))) {
-		g_free(s->value);
-		s->value = g_strdup(dn);
-	}
-
-	// user list needs to be requested for Gadu-Gadu
-	purple_gg_buddylist_import(gc);
-
-	/* more awful hacks, because clearly we didn't have enough of those */
-	if ((s = set_find(&ic->acc->set, "line-auth-token")) &&
-	    (token = purple_account_get_string(pd->account, "line-auth-token", NULL))) {
-		g_free(s->value);
-		s->value = g_strdup(token);
-	}
-
-	ic->flags |= OPT_DOES_HTML;
-}
-
-static void prplcb_conn_disconnected(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (ic != NULL) {
-		imc_logout(ic, !gc->wants_to_die);
-	}
-}
-
-static void prplcb_conn_notice(PurpleConnection *gc, const char *text)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (ic != NULL) {
-		imcb_log(ic, "%s", text);
-	}
-}
-
-static void prplcb_conn_report_disconnect_reason(PurpleConnection *gc, PurpleConnectionError reason, const char *text)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	/* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
-	   should probably handle that. */
-	if (ic != NULL) {
-		imcb_error(ic, "%s", text);
-	}
-}
-
-static PurpleConnectionUiOps bee_conn_uiops =
-{
-	prplcb_conn_progress,                    /* connect_progress */
-	prplcb_conn_connected,                   /* connected */
-	prplcb_conn_disconnected,                /* disconnected */
-	prplcb_conn_notice,                      /* notice */
-	NULL,                                    /* report_disconnect */
-	NULL,                                    /* network_connected */
-	NULL,                                    /* network_disconnected */
-	prplcb_conn_report_disconnect_reason,    /* report_disconnect_reason */
-};
-
-static void prplcb_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-	if (node->type == PURPLE_BLIST_BUDDY_NODE) {
-		PurpleBuddy *bud = (PurpleBuddy *) node;
-		PurpleGroup *group = purple_buddy_get_group(bud);
-		struct im_connection *ic = purple_ic_by_pa(bud->account);
-		struct purple_data *pd = ic->proto_data;
-		PurpleStatus *as;
-		int flags = 0;
-		char *alias = NULL;
-
-		if (ic == NULL) {
-			return;
-		}
-
-		alias = bud->server_alias ? : bud->alias;
-
-		if (alias) {
-			imcb_rename_buddy(ic, bud->name, alias);
-			if (pd->flags & PURPLE_OPT_SHOULD_SET_NICK) {
-				imcb_buddy_nick_change(ic, bud->name, alias);
-			}
-		}
-
-		if (group) {
-			imcb_add_buddy(ic, bud->name, purple_group_get_name(group));
-		}
-
-		flags |= purple_presence_is_online(bud->presence) ? OPT_LOGGED_IN : 0;
-		flags |= purple_presence_is_available(bud->presence) ? 0 : OPT_AWAY;
-
-		as = purple_presence_get_active_status(bud->presence);
-
-		imcb_buddy_status(ic, bud->name, flags, purple_status_get_name(as),
-		                  purple_status_get_attr_string(as, "message"));
-
-		imcb_buddy_times(ic, bud->name,
-		                 purple_presence_get_login_time(bud->presence),
-		                 purple_presence_get_idle_time(bud->presence));
-	}
-}
-
-static void prplcb_blist_new(PurpleBlistNode *node)
-{
-	if (node->type == PURPLE_BLIST_BUDDY_NODE) {
-		PurpleBuddy *bud = (PurpleBuddy *) node;
-		struct im_connection *ic = purple_ic_by_pa(bud->account);
-
-		if (ic == NULL) {
-			return;
-		}
-
-		imcb_add_buddy(ic, bud->name, NULL);
-
-		prplcb_blist_update(NULL, node);
-	}
-}
-
-static void prplcb_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-/*
-        PurpleBuddy *bud = (PurpleBuddy*) node;
-
-        if( node->type == PURPLE_BLIST_BUDDY_NODE )
-        {
-                struct im_connection *ic = purple_ic_by_pa( bud->account );
-
-                if( ic == NULL )
-                        return;
-
-                imcb_remove_buddy( ic, bud->name, NULL );
-        }
-*/
-}
-
-static PurpleBlistUiOps bee_blist_uiops =
-{
-	NULL,                      /* new_list */
-	prplcb_blist_new,          /* new_node */
-	NULL,                      /* show */
-	prplcb_blist_update,       /* update */
-	prplcb_blist_remove,       /* remove */
-};
-
-void prplcb_conv_new(PurpleConversation *conv)
-{
-	if (conv->type == PURPLE_CONV_TYPE_CHAT) {
-		struct im_connection *ic = purple_ic_by_pa(conv->account);
-		struct groupchat *gc;
-
-		gc = bee_chat_by_title(ic->bee, ic, conv->name);
-
-		if (!gc) {
-			gc = imcb_chat_new(ic, conv->name);
-			if (conv->title != NULL) {
-				imcb_chat_name_hint(gc, conv->title);
-			}
-		}
-
-		/* don't set the topic if it's just the name */
-		if (conv->title != NULL && strcmp(conv->name, conv->title) != 0) {
-			imcb_chat_topic(gc, NULL, conv->title, 0);
-		}
-
-		conv->ui_data = gc;
-		gc->data = conv;
-
-		/* libpurple brokenness: Whatever. Show that we join right away,
-		   there's no clear "This is you!" signaling in _add_users so
-		   don't even try. */
-		imcb_chat_add_buddy(gc, gc->ic->acc->user);
-	}
-}
-
-void prplcb_conv_free(PurpleConversation *conv)
-{
-	struct groupchat *gc = conv->ui_data;
-
-	imcb_chat_free(gc);
-}
-
-void prplcb_conv_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
-{
-	struct groupchat *gc = conv->ui_data;
-	GList *b;
-
-	for (b = cbuddies; b; b = b->next) {
-		PurpleConvChatBuddy *pcb = b->data;
-
-		imcb_chat_add_buddy(gc, pcb->name);
-	}
-}
-
-void prplcb_conv_del_users(PurpleConversation *conv, GList *cbuddies)
-{
-	struct groupchat *gc = conv->ui_data;
-	GList *b;
-
-	for (b = cbuddies; b; b = b->next) {
-		imcb_chat_remove_buddy(gc, b->data, "");
-	}
-}
-
-/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */
-static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message_, guint32 bee_flags, time_t mtime)
-{
-	struct im_connection *ic = purple_ic_by_pa(conv->account);
-	struct groupchat *gc = conv->ui_data;
-	char *message = g_strdup(message_);
-	PurpleBuddy *buddy;
-
-	buddy = purple_find_buddy(conv->account, who);
-	if (buddy != NULL) {
-		who = purple_buddy_get_name(buddy);
-	}
-
-	if (conv->type == PURPLE_CONV_TYPE_IM) {
-		imcb_buddy_msg(ic, who, message, bee_flags, mtime);
-	} else if (gc) {
-		imcb_chat_msg(gc, who, message, bee_flags, mtime);
-	}
-
-	g_free(message);
-}
-
-/* Handles write_im and write_chat. Removes echoes of locally sent messages.
- *
- * PURPLE_MESSAGE_DELAYED is used for chat backlogs - if a message has both
- * that flag and _SEND, it's a self-message from before joining the channel.
- * Those are safe to display. The rest (with just _SEND) may be echoes. */
-static void prplcb_conv_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime)
-{
-	if ((!(flags & PURPLE_MESSAGE_SEND)) ||
-	    (flags & PURPLE_MESSAGE_DELAYED) ||
-	    (flags & PURPLE_MESSAGE_REMOTE_SEND)
-	) {
-		handle_conv_msg(conv, who, message, (flags & PURPLE_MESSAGE_SEND) ? OPT_SELFMESSAGE : 0, mtime);
-	}
-}
-
-/* Handles write_conv. Only passes self messages from other locations through.
- * That is, only writes of PURPLE_MESSAGE_SEND.
- * There are more events which might be handled in the future, but some are tricky.
- * (images look like <img id="123">, what do i do with that?) */
-static void prplcb_conv_write(PurpleConversation *conv, const char *who, const char *alias, const char *message,
-                              PurpleMessageFlags flags, time_t mtime)
-{
-	if (flags & PURPLE_MESSAGE_SEND) {
-		handle_conv_msg(conv, who, message, OPT_SELFMESSAGE, mtime);
-	}
-}
-
-/* No, this is not a ui_op but a signal. */
-static void prplcb_buddy_typing(PurpleAccount *account, const char *who, gpointer null)
-{
-	PurpleConversation *conv;
-	PurpleConvIm *im;
-	int state;
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account)) == NULL) {
-		return;
-	}
-
-	im = PURPLE_CONV_IM(conv);
-	switch (purple_conv_im_get_typing_state(im)) {
-	case PURPLE_TYPING:
-		state = OPT_TYPING;
-		break;
-	case PURPLE_TYPED:
-		state = OPT_THINKING;
-		break;
-	default:
-		state = 0;
-	}
-
-	imcb_buddy_typing(purple_ic_by_pa(account), who, state);
-}
-
-static PurpleConversationUiOps bee_conv_uiops =
-{
-	prplcb_conv_new,           /* create_conversation  */
-	prplcb_conv_free,          /* destroy_conversation */
-	prplcb_conv_msg,           /* write_chat           */
-	prplcb_conv_msg,           /* write_im             */
-	prplcb_conv_write,         /* write_conv           */
-	prplcb_conv_add_users,     /* chat_add_users       */
-	NULL,                      /* chat_rename_user     */
-	prplcb_conv_del_users,     /* chat_remove_users    */
-	NULL,                      /* chat_update_user     */
-	NULL,                      /* present              */
-	NULL,                      /* has_focus            */
-	NULL,                      /* custom_smiley_add    */
-	NULL,                      /* custom_smiley_write  */
-	NULL,                      /* custom_smiley_close  */
-	NULL,                      /* send_confirm         */
-};
-
-struct prplcb_request_action_data {
-	void *user_data, *bee_data;
-	PurpleRequestActionCb yes, no;
-	int yes_i, no_i;
-};
-
-static void prplcb_request_action_yes(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	if (pqad->yes) {
-		pqad->yes(pqad->user_data, pqad->yes_i);
-	}
-}
-
-static void prplcb_request_action_no(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	if (pqad->no) {
-		pqad->no(pqad->user_data, pqad->no_i);
-	}
-}
-
-/* q->free() callback from query_del()*/
-static void prplcb_request_action_free(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	pqad->bee_data = NULL;
-	purple_request_close(PURPLE_REQUEST_ACTION, pqad);
-}
-
-static void *prplcb_request_action(const char *title, const char *primary, const char *secondary,
-                                   int default_action, PurpleAccount *account, const char *who,
-                                   PurpleConversation *conv, void *user_data, size_t action_count,
-                                   va_list actions)
-{
-	struct prplcb_request_action_data *pqad;
-	int i;
-	char *q;
-
-	pqad = g_new0(struct prplcb_request_action_data, 1);
-
-	for (i = 0; i < action_count; i++) {
-		char *caption;
-		void *fn;
-
-		caption = va_arg(actions, char*);
-		fn = va_arg(actions, void*);
-
-		if (strstr(caption, "Accept") || strstr(caption, "OK")) {
-			pqad->yes = fn;
-			pqad->yes_i = i;
-		} else if (strstr(caption, "Reject") || strstr(caption, "Cancel")) {
-			pqad->no = fn;
-			pqad->no_i = i;
-		}
-	}
-
-	pqad->user_data = user_data;
-
-	/* TODO: IRC stuff here :-( */
-	q = g_strdup_printf("Request: %s\n\n%s\n\n%s", title, primary, secondary);
-	pqad->bee_data = query_add(local_bee->ui_data, purple_ic_by_pa(account), q,
-	                           prplcb_request_action_yes, prplcb_request_action_no,
-	                           prplcb_request_action_free, pqad);
-
-	g_free(q);
-
-	return pqad;
-}
-
-/* So it turns out some requests have no account context at all, because
- * libpurple hates us. This means that query_del_by_conn() won't remove those
- * on logout, and will segfault if the user replies. That's why this exists.
- */
-static void prplcb_close_request(PurpleRequestType type, void *data)
-{
-	struct prplcb_request_action_data *pqad;
-	struct request_input_data *ri;
-	struct purple_data *pd;
-
-	if (!data) {
-		return;
-	}
-
-	switch (type) {
-	case PURPLE_REQUEST_ACTION:
-		pqad = data;
-		/* if this is null, it's because query_del was run already */
-		if (pqad->bee_data) {
-			query_del(local_bee->ui_data, pqad->bee_data);
-		}
-		g_free(pqad);
-		break;
-	case PURPLE_REQUEST_INPUT:
-		ri = data;
-		pd = ri->ic->proto_data;
-		imcb_remove_buddy(ri->ic, ri->buddy, NULL);
-		g_free(ri->buddy);
-		g_hash_table_remove(pd->input_requests, GUINT_TO_POINTER(ri->id));
-		break;
-	default:
-		g_free(data);
-		break;
-	}
-
-}
-
-void* prplcb_request_input(const char *title, const char *primary,
-        const char *secondary, const char *default_value, gboolean multiline,
-        gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb,
-        const char *cancel_text, GCallback cancel_cb, PurpleAccount *account,
-        const char *who, PurpleConversation *conv, void *user_data)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	struct purple_data *pd = ic->proto_data;
-	struct request_input_data *ri;
-	guint id;
-
-	/* hack so that jabber's chat list doesn't ask for conference server twice */
-	if (pd->chat_list_server && title && g_strcmp0(title, "Enter a Conference Server") == 0) {
-		((ri_callback_t) ok_cb)(user_data, pd->chat_list_server);
-		g_free(pd->chat_list_server);
-		pd->chat_list_server = NULL;
-		return NULL;
-	}
-
-	id = pd->next_request_id++;
-	ri = g_new0(struct request_input_data, 1);
-
-	ri->id = id;
-	ri->ic = ic;
-	ri->buddy = g_strdup_printf("%s_%u", PURPLE_REQUEST_HANDLE, id);
-	ri->data_callback = (ri_callback_t) ok_cb;
-	ri->user_data = user_data;
-	g_hash_table_insert(pd->input_requests, GUINT_TO_POINTER(id), ri);
-
-	imcb_add_buddy(ic, ri->buddy, NULL);
-
-	if (title && *title) {
-		imcb_buddy_msg(ic, ri->buddy, title, 0, 0);
-	}
-
-	if (primary && *primary) {
-		imcb_buddy_msg(ic, ri->buddy, primary, 0, 0);
-	}
-
-	if (secondary && *secondary) {
-		imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
-	}
-
-	return ri;
-}
-
-void purple_request_input_callback(guint id, struct im_connection *ic,
-                                   const char *message, const char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-	struct request_input_data *ri;
-
-	if (!(ri = g_hash_table_lookup(pd->input_requests, GUINT_TO_POINTER(id)))) {
-		return;
-	}
-
-	ri->data_callback(ri->user_data, message);
-
-	purple_request_close(PURPLE_REQUEST_INPUT, ri);
-}
-
-
-static PurpleRequestUiOps bee_request_uiops =
-{
-	prplcb_request_input,      /* request_input */
-	NULL,                      /* request_choice */
-	prplcb_request_action,     /* request_action */
-	NULL,                      /* request_fields */
-	NULL,                      /* request_file */
-	prplcb_close_request,      /* close_request */
-	NULL,                      /* request_folder */
-};
-
-static void prplcb_privacy_permit_added(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	if (!g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
-		ic->permit = g_slist_prepend(ic->permit, g_strdup(name));
-	}
-}
-
-static void prplcb_privacy_permit_removed(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	void *n;
-
-	n = g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
-	ic->permit = g_slist_remove(ic->permit, n);
-}
-
-static void prplcb_privacy_deny_added(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	if (!g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
-		ic->deny = g_slist_prepend(ic->deny, g_strdup(name));
-	}
-}
-
-static void prplcb_privacy_deny_removed(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	void *n;
-
-	n = g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
-	ic->deny = g_slist_remove(ic->deny, n);
-}
-
-static PurplePrivacyUiOps bee_privacy_uiops =
-{
-	prplcb_privacy_permit_added,       /* permit_added */
-	prplcb_privacy_permit_removed,     /* permit_removed */
-	prplcb_privacy_deny_added,         /* deny_added */
-	prplcb_privacy_deny_removed,       /* deny_removed */
-};
-
-static void prplcb_roomlist_create(PurpleRoomlist *list)
-{
-	struct purple_roomlist_data *rld;
-
-	list->ui_data = rld = g_new0(struct purple_roomlist_data, 1);
-	rld->topic = -1;
-}
-
-static void prplcb_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
-{
-	gint topic = -1;
-	GList *l;
-	guint i;
-	PurpleRoomlistField *field;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	for (i = 0, l = fields; l; i++, l = l->next) {
-		field = l->data;
-
-		/* Use the first visible string field as a fallback topic */
-		if (i != 0 && topic < 0 && !field->hidden &&
-		    field->type == PURPLE_ROOMLIST_FIELD_STRING) {
-			topic = i;
-		}
-
-		if ((g_strcasecmp(field->name, "description") == 0) ||
-		    (g_strcasecmp(field->name, "topic") == 0)) {
-			if (field->type == PURPLE_ROOMLIST_FIELD_STRING) {
-				rld->topic = i;
-			}
-		}
-	}
-
-	if (rld->topic < 0) {
-		rld->topic = topic;
-	}
-}
-
-static char *prplcb_roomlist_get_room_name(PurpleRoomlist *list, PurpleRoomlistRoom *room)
-{
-	struct im_connection *ic = purple_ic_by_pa(list->account);
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (pi && pi->roomlist_room_serialize) {
-		return pi->roomlist_room_serialize(room);
-	} else {
-		return g_strdup(purple_roomlist_room_get_name(room));
-	}
-}
-
-static void prplcb_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room)
-{
-	bee_chat_info_t *ci;
-	char *title;
-	const char *topic;
-	GList *fields;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	fields = purple_roomlist_room_get_fields(room);
-	title = prplcb_roomlist_get_room_name(list, room);
-
-	if (rld->topic >= 0) {
-		topic = g_list_nth_data(fields, rld->topic);
-	} else {
-		topic = NULL;
-	}
-
-	ci = g_new(bee_chat_info_t, 1);
-	ci->title = title;
-	ci->topic = g_strdup(topic);
-	rld->chats = g_slist_prepend(rld->chats, ci);
-}
-
-static void prplcb_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
-{
-	struct im_connection *ic;
-	struct purple_data *pd;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	if (in_progress || !rld) {
-		return;
-	}
-
-	ic = purple_ic_by_pa(list->account);
-	imcb_chat_list_free(ic);
-
-	pd = ic->proto_data;
-	g_free(pd->chat_list_server);
-	pd->chat_list_server = NULL;
-
-	ic->chatlist = g_slist_reverse(rld->chats);
-	rld->chats = NULL;
-
-	imcb_chat_list_finish(ic);
-
-	if (rld->initialized) {
-		purple_roomlist_unref(list);
-	}
-}
-
-static void prplcb_roomlist_destroy(PurpleRoomlist *list)
-{
-	g_free(list->ui_data);
-	list->ui_data = NULL;
-}
-
-static PurpleRoomlistUiOps bee_roomlist_uiops =
-{
-	NULL,                         /* show_with_account */
-	prplcb_roomlist_create,       /* create */
-	prplcb_roomlist_set_fields,   /* set_fields */
-	prplcb_roomlist_add_room,     /* add_room */
-	prplcb_roomlist_in_progress,  /* in_progress */
-	prplcb_roomlist_destroy,      /* destroy */
-};
-
-static void prplcb_debug_print(PurpleDebugLevel level, const char *category, const char *arg_s)
-{
-	fprintf(stderr, "DEBUG %s: %s", category, arg_s);
-}
-
-static PurpleDebugUiOps bee_debug_uiops =
-{
-	prplcb_debug_print,        /* print */
-};
-
-static guint prplcb_ev_timeout_add(guint interval, GSourceFunc func, gpointer udata)
-{
-	return b_timeout_add(interval, (b_event_handler) func, udata);
-}
-
-static guint prplcb_ev_input_add(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata)
-{
-	return b_input_add(fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata);
-}
-
-static gboolean prplcb_ev_remove(guint id)
-{
-	b_event_remove((gint) id);
-	return TRUE;
-}
-
-static PurpleEventLoopUiOps glib_eventloops =
-{
-	prplcb_ev_timeout_add,     /* timeout_add */
-	prplcb_ev_remove,          /* timeout_remove */
-	prplcb_ev_input_add,       /* input_add */
-	prplcb_ev_remove,          /* input_remove */
-};
-
-/* Absolutely no connection context at all. Thanks purple! brb crying */
-static void *prplcb_notify_message(PurpleNotifyMsgType type, const char *title,
-                                   const char *primary, const char *secondary)
-{
-	char *text = g_strdup_printf("%s%s - %s%s%s",
-		(type == PURPLE_NOTIFY_MSG_ERROR) ? "Error: " : "",
-		title,
-		primary ?: "",
-		(primary && secondary) ? " - " : "",
-		secondary ?: ""
-	);
-
-	if (local_bee->ui->log) {
-		local_bee->ui->log(local_bee, "purple", text);
-	}
-
-	g_free(text);
-
-	return NULL;
-}
-
-static void *prplcb_notify_email(PurpleConnection *gc, const char *subject, const char *from,
-                                 const char *to, const char *url)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	imcb_notify_email(ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url);
-
-	return NULL;
-}
-
-static void *prplcb_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-	GString *info = g_string_new("");
-	GList *l = purple_notify_user_info_get_entries(user_info);
-	char *key;
-	const char *value;
-	int n;
-
-	while (l) {
-		PurpleNotifyUserInfoEntry *e = l->data;
-
-		switch (purple_notify_user_info_entry_get_type(e)) {
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
-			key = g_strdup(purple_notify_user_info_entry_get_label(e));
-			value = purple_notify_user_info_entry_get_value(e);
-
-			if (key) {
-				strip_html(key);
-				g_string_append_printf(info, "%s: ", key);
-
-				if (value) {
-					n = strlen(value) - 1;
-					while (g_ascii_isspace(value[n])) {
-						n--;
-					}
-					g_string_append_len(info, value, n + 1);
-				}
-				g_string_append_c(info, '\n');
-				g_free(key);
-			}
-
-			break;
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
-			g_string_append(info, "------------------------\n");
-			break;
-		}
-
-		l = l->next;
-	}
-
-	imcb_log(ic, "User %s info:\n%s", who, info->str);
-	g_string_free(info, TRUE);
-
-	return NULL;
-}
-
-static PurpleNotifyUiOps bee_notify_uiops =
-{
-	prplcb_notify_message,     /* notify_message */
-	prplcb_notify_email,       /* notify_email */
-	NULL,                      /* notify_emails */
-	NULL,                      /* notify_formatted */
-	NULL,                      /* notify_searchresults */
-	NULL,                      /* notify_searchresults_new_rows */
-	prplcb_notify_userinfo,    /* notify_userinfo */
-};
-
-static void *prplcb_account_request_authorize(PurpleAccount *account, const char *remote_user,
-                                              const char *id, const char *alias, const char *message, gboolean on_list,
-                                              PurpleAccountRequestAuthorizationCb authorize_cb,
-                                              PurpleAccountRequestAuthorizationCb deny_cb, void *user_data)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	char *q;
-
-	if (alias) {
-		q = g_strdup_printf("%s (%s) wants to add you to his/her contact "
-		                    "list. (%s)", alias, remote_user, message);
-	} else {
-		q = g_strdup_printf("%s wants to add you to his/her contact "
-		                    "list. (%s)", remote_user, message);
-	}
-
-	imcb_ask_with_free(ic, q, user_data, authorize_cb, deny_cb, NULL);
-	g_free(q);
-
-	return NULL;
-}
-
-static PurpleAccountUiOps bee_account_uiops =
-{
-	NULL,                              /* notify_added */
-	NULL,                              /* status_changed */
-	NULL,                              /* request_add */
-	prplcb_account_request_authorize,  /* request_authorize */
-	NULL,                              /* close_account_request */
-};
-
-static void *prplcb_bitlbee_set_account_password(PurpleAccount *account, char *password)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	set_setstr(&ic->acc->set, "password", password ? password : "");
-
-	return GINT_TO_POINTER(TRUE);
-}
-
-extern PurpleXferUiOps bee_xfer_uiops;
-
-static void purple_ui_init()
-{
-	purple_connections_set_ui_ops(&bee_conn_uiops);
-	purple_blist_set_ui_ops(&bee_blist_uiops);
-	purple_conversations_set_ui_ops(&bee_conv_uiops);
-	purple_request_set_ui_ops(&bee_request_uiops);
-	purple_privacy_set_ui_ops(&bee_privacy_uiops);
-	purple_roomlist_set_ui_ops(&bee_roomlist_uiops);
-	purple_notify_set_ui_ops(&bee_notify_uiops);
-	purple_accounts_set_ui_ops(&bee_account_uiops);
-	purple_xfers_set_ui_ops(&bee_xfer_uiops);
-
-	if (getenv("BITLBEE_DEBUG")) {
-		purple_debug_set_ui_ops(&bee_debug_uiops);
-	}
-}
-
-/* borrowing this semi-private function
- * TODO: figure out a better interface later (famous last words) */
-gboolean plugin_info_add(struct plugin_info *info);
-
-void purple_initmodule()
-{
-	struct prpl funcs;
-	GList *prots;
-	GString *help;
-	char *dir;
-	gboolean debug_enabled = !!getenv("BITLBEE_DEBUG");
-
-	if (purple_get_core() != NULL) {
-		log_message(LOGLVL_ERROR, "libpurple already initialized. "
-		            "Please use inetd or ForkDaemon mode instead.");
-		return;
-	}
-
-	g_return_if_fail((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ);
-	g_return_if_fail((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE);
-
-	dir = g_strdup_printf("%s/purple", global.conf->configdir);
-	purple_util_set_user_dir(dir);
-	g_free(dir);
-
-	dir = g_strdup_printf("%s/purple", global.conf->plugindir);
-	purple_plugins_add_search_path(dir);
-	g_free(dir);
-
-	purple_debug_set_enabled(debug_enabled);
-	purple_core_set_ui_ops(&bee_core_uiops);
-	purple_eventloop_set_ui_ops(&glib_eventloops);
-	if (!purple_core_init("BitlBee")) {
-		/* Initializing the core failed. Terminate. */
-		fprintf(stderr, "libpurple initialization failed.\n");
-		abort();
-	}
-	purple_debug_set_enabled(FALSE);
-
-	if (proxytype != PROXY_NONE) {
-		PurpleProxyInfo *pi = purple_global_proxy_get_info();
-		switch (proxytype) {
-		case PROXY_SOCKS4A:
-		case PROXY_SOCKS4:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS4);
-			break;
-		case PROXY_SOCKS5:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS5);
-			break;
-		case PROXY_HTTP:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_HTTP);
-			break;
-		}
-		purple_proxy_info_set_host(pi, proxyhost);
-		purple_proxy_info_set_port(pi, proxyport);
-		purple_proxy_info_set_username(pi, proxyuser);
-		purple_proxy_info_set_password(pi, proxypass);
-	}
-
-	purple_set_blist(purple_blist_new());
-
-	/* No, really. So far there were ui_ops for everything, but now suddenly
-	   one needs to use signals for typing notification stuff. :-( */
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typed",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-
-	/* "bitlbee-set-account-password" signal:
-	 * Replacement API for purple_account_set_password() to be called by
-	 * prpls that wish to store updated passwords or oauth tokens, since
-	 * our password storage doesn't get notified of calls to
-	 * purple_account_set_password().
-	 *
-	 * When used with purple_signal_emit_return_1() returns:
-	 *  - GINT_TO_POINTER(TRUE) if implemented by this version of bitlbee
-	 *  - NULL otherwise.
-	 *
-	 * Originally made for the hangouts plugin.
-	 */
-
-	purple_signal_register(purple_accounts_get_handle(), "bitlbee-set-account-password",
-	                       purple_marshal_POINTER__POINTER_POINTER,
-	                       purple_value_new(PURPLE_TYPE_BOOLEAN), 2,
-	                       purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT),
-	                       purple_value_new(PURPLE_TYPE_STRING));
-
-	purple_signal_connect(purple_accounts_get_handle(), "bitlbee-set-account-password",
-	                      &funcs, PURPLE_CALLBACK(prplcb_bitlbee_set_account_password), NULL);
-
-	memset(&funcs, 0, sizeof(funcs));
-	funcs.login = purple_login;
-	funcs.init = purple_init;
-	funcs.logout = purple_logout;
-	funcs.buddy_msg = purple_buddy_msg;
-	funcs.away_states = purple_away_states;
-	funcs.set_away = purple_set_away;
-	funcs.add_buddy = purple_add_buddy;
-	funcs.remove_buddy = purple_remove_buddy;
-	funcs.add_permit = purple_add_permit;
-	funcs.add_deny = purple_add_deny;
-	funcs.rem_permit = purple_rem_permit;
-	funcs.rem_deny = purple_rem_deny;
-	funcs.get_info = purple_get_info;
-	funcs.keepalive = purple_keepalive;
-	funcs.send_typing = purple_send_typing;
-	funcs.handle_cmp = g_strcasecmp;
-	/* TODO(wilmer): Set these only for protocols that support them? */
-	funcs.chat_msg = purple_chat_msg;
-	funcs.chat_with = purple_chat_with;
-	funcs.chat_invite = purple_chat_invite;
-	funcs.chat_topic = purple_chat_set_topic;
-	funcs.chat_kick = purple_chat_kick;
-	funcs.chat_leave = purple_chat_leave;
-	funcs.chat_join = purple_chat_join;
-	funcs.chat_list = purple_chat_list;
-	funcs.chat_add_settings = purple_chat_add_settings;
-	funcs.chat_free_settings = purple_chat_free_settings;
-	funcs.transfer_request = purple_transfer_request;
-
-	help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
-
-	/* Add a protocol entry to BitlBee's structures for every protocol
-	   supported by this libpurple instance. */
-	for (prots = purple_plugins_get_protocols(); prots; prots = prots->next) {
-		PurplePlugin *prot = prots->data;
-		PurplePluginProtocolInfo *pi = prot->info->extra_info;
-		struct prpl *ret;
-		struct plugin_info *info;
-
-		/* If we already have this one (as a native module), don't
-		   add a libpurple duplicate. */
-		if (find_protocol(prot->info->id)) {
-			continue;
-		}
-
-		ret = g_memdup2(&funcs, sizeof(funcs));
-		ret->name = ret->data = prot->info->id;
-		if (strncmp(ret->name, "prpl-", 5) == 0) {
-			ret->name += 5;
-		}
-
-		if (pi->options & OPT_PROTO_NO_PASSWORD) {
-			ret->options |= PRPL_OPT_NO_PASSWORD;
-		}
-
-		if (pi->options & OPT_PROTO_PASSWORD_OPTIONAL) {
-			ret->options |= PRPL_OPT_PASSWORD_OPTIONAL;
-		}
-
-		register_protocol(ret);
-
-		g_string_append_printf(help, "\n* %s (%s)", ret->name, prot->info->name);
-
-		info = g_new0(struct plugin_info, 1);
-		info->abiver = BITLBEE_ABI_VERSION_CODE;
-		info->name = ret->name;
-		info->version = prot->info->version;
-		info->description = prot->info->description;
-		info->author = prot->info->author;
-		info->url = prot->info->homepage;
-
-		plugin_info_add(info);
-	}
-
-	g_string_append(help, "\n\nFor used protocols, more information about available "
-	                "settings can be found using \x02help purple <protocol name>\x02 "
-	                "(create an account using that protocol first!)");
-
-	/* Add a simple dynamically-generated help item listing all
-	   the supported protocols. */
-	help_add_mem(&global.help, "purple", help->str);
-	g_string_free(help, TRUE);
-}
Index: protocols/sha.c
===================================================================
--- protocols/sha.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/sha.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,173 @@
+/* 
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ * 
+ * The Original Code is SHA 180-1 Reference Implementation (Compact version)
+ * 
+ * The Initial Developer of the Original Code is Paul Kocher of
+ * Cryptography Research.  Portions created by Paul Kocher are 
+ * Copyright (C) 1995-9 by Cryptography Research, Inc.  All
+ * Rights Reserved.
+ * 
+ * Contributor(s):
+ *
+ */
+
+#define BITLBEE_CORE
+#include "nogaim.h"
+
+static void shaHashBlock(SHA_CTX *ctx);
+
+void shaInit(SHA_CTX *ctx) {
+  int i;
+
+  ctx->lenW = 0;
+  ctx->sizeHi = ctx->sizeLo = 0;
+
+  /* Initialize H with the magic constants (see FIPS180 for constants)
+   */
+  ctx->H[0] = 0x67452301L;
+  ctx->H[1] = 0xefcdab89L;
+  ctx->H[2] = 0x98badcfeL;
+  ctx->H[3] = 0x10325476L;
+  ctx->H[4] = 0xc3d2e1f0L;
+
+  for (i = 0; i < 80; i++)
+    ctx->W[i] = 0;
+}
+
+
+void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len) {
+  int i;
+
+  /* Read the data into W and process blocks as they get full
+   */
+  for (i = 0; i < len; i++) {
+    ctx->W[ctx->lenW / 4] <<= 8;
+    ctx->W[ctx->lenW / 4] |= (guint32)dataIn[i];
+    if ((++ctx->lenW) % 64 == 0) {
+      shaHashBlock(ctx);
+      ctx->lenW = 0;
+    }
+    ctx->sizeLo += 8;
+    ctx->sizeHi += (ctx->sizeLo < 8);
+  }
+}
+
+
+void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]) {
+  unsigned char pad0x80 = 0x80;
+  unsigned char pad0x00 = 0x00;
+  unsigned char padlen[8];
+  int i;
+
+  /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
+   */
+  padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
+  padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
+  padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
+  padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
+  padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
+  padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
+  padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
+  padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
+  shaUpdate(ctx, &pad0x80, 1);
+  while (ctx->lenW != 56)
+    shaUpdate(ctx, &pad0x00, 1);
+  shaUpdate(ctx, padlen, 8);
+
+  /* Output hash
+   */
+  for (i = 0; i < 20; i++) {
+    hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
+    ctx->H[i / 4] <<= 8;
+  }
+
+  /*
+   *  Re-initialize the context (also zeroizes contents)
+   */
+  shaInit(ctx); 
+}
+
+
+void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) {
+  SHA_CTX ctx;
+
+  shaInit(&ctx);
+  shaUpdate(&ctx, dataIn, len);
+  shaFinal(&ctx, hashout);
+}
+
+
+#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
+
+static void shaHashBlock(SHA_CTX *ctx) {
+  int t;
+  guint32 A,B,C,D,E,TEMP;
+
+  for (t = 16; t <= 79; t++)
+    ctx->W[t] =
+      SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
+
+  A = ctx->H[0];
+  B = ctx->H[1];
+  C = ctx->H[2];
+  D = ctx->H[3];
+  E = ctx->H[4];
+
+  for (t = 0; t <= 19; t++) {
+    TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D)     + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL;
+    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+  }
+  for (t = 20; t <= 39; t++) {
+    TEMP = (SHA_ROTL(A,5) + (B^C^D)           + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL;
+    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+  }
+  for (t = 40; t <= 59; t++) {
+    TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL;
+    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+  }
+  for (t = 60; t <= 79; t++) {
+    TEMP = (SHA_ROTL(A,5) + (B^C^D)           + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL;
+    E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+  }
+
+  ctx->H[0] += A;
+  ctx->H[1] += B;
+  ctx->H[2] += C;
+  ctx->H[3] += D;
+  ctx->H[4] += E;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * This code added by Thomas "temas" Muldowney for Jabber compatability
+ *
+ *---------------------------------------------------------------------------*/
+char *shahash(char *str)
+{
+    static char final[41];
+    char *pos;
+    unsigned char hashval[20];
+    int x;
+
+    if(!str || strlen(str) == 0)
+        return NULL;
+
+    shaBlock((unsigned char *)str, strlen(str), hashval);
+
+    pos = final;
+    for(x=0;x<20;x++)
+    {
+        g_snprintf(pos, 3, "%02x", hashval[x]);
+        pos += 2;
+    }
+    return (char *)final;
+}
Index: protocols/sha.h
===================================================================
--- protocols/sha.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/sha.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,21 @@
+#ifndef __SHA_H__
+#define __SHA_H__
+
+#include <gmodule.h>
+
+G_MODULE_EXPORT int strprintsha(char *dest, int *hashval);
+ 
+typedef struct {
+  guint32 H[5];
+  guint32 W[80];
+  int lenW;
+  guint32 sizeHi,sizeLo;
+} SHA_CTX;
+ 
+G_MODULE_EXPORT void shaInit(SHA_CTX *ctx);
+G_MODULE_EXPORT void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len);
+G_MODULE_EXPORT void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]);
+G_MODULE_EXPORT void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]);
+G_MODULE_EXPORT char *shahash(char *str);
+
+#endif
Index: protocols/ssl_bogus.c
===================================================================
--- protocols/ssl_bogus.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_bogus.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,50 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - dummy version                                           */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "ssl_client.h"
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+	return( NULL );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+	return( -1 );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+	return( -1 );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+}
+
+int ssl_getfd( void *conn )
+{
+	return( -1 );
+}
Index: protocols/ssl_client.h
===================================================================
--- protocols/ssl_client.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_client.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,35 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module                                                           */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <glib.h>
+#include "proxy.h"
+
+typedef void (*SslInputFunction)(gpointer, void*, GaimInputCondition);
+
+G_MODULE_EXPORT void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data );
+G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
+G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
+G_MODULE_EXPORT void ssl_disconnect( void *conn_ );
+G_MODULE_EXPORT int ssl_getfd( void *conn );
Index: protocols/ssl_gnutls.c
===================================================================
--- protocols/ssl_gnutls.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_gnutls.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,136 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - GnuTLS version                                          */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <gnutls/gnutls.h>
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include "stdlib.h"
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+	SslInputFunction func;
+	gpointer data;
+	int fd;
+	gboolean established;
+	
+	gnutls_session session;
+	gnutls_certificate_credentials xcred;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+	struct scd *conn = g_new0( struct scd, 1 );
+	
+	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	conn->func = func;
+	conn->data = data;
+	
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return( NULL );
+	}
+	
+	if( !initialized )
+	{
+		gnutls_global_init();
+		initialized = TRUE;
+		atexit( gnutls_global_deinit );
+	}
+	
+	gnutls_certificate_allocate_credentials( &conn->xcred );
+	gnutls_init( &conn->session, GNUTLS_CLIENT );
+	gnutls_set_default_priority( conn->session );
+	gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
+	
+	return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct scd *conn = data;
+	
+	if( source == -1 )
+		goto ssl_connected_failure;
+	
+	gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd );
+	
+	if( gnutls_handshake( conn->session ) < 0 )
+		goto ssl_connected_failure;
+	
+	conn->established = TRUE;
+	conn->func( conn->data, conn, cond );
+	return;
+	
+ssl_connected_failure:
+	conn->func( conn->data, NULL, cond );
+	
+	gnutls_deinit( conn->session );
+	gnutls_certificate_free_credentials( conn->xcred );
+	if( source >= 0 ) closesocket( source );
+	g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( gnutls_record_recv( ((struct scd*)conn)->session, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( gnutls_record_send( ((struct scd*)conn)->session, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+	struct scd *conn = conn_;
+	
+	if( conn->established )
+		gnutls_bye( conn->session, GNUTLS_SHUT_WR );
+	
+	closesocket( conn->fd );
+	
+	gnutls_deinit( conn->session );
+	gnutls_certificate_free_credentials( conn->xcred );
+	g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+	return( ((struct scd*)conn)->fd );
+}
Index: protocols/ssl_nss.c
===================================================================
--- protocols/ssl_nss.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_nss.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,180 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - NSS version                                             */
+
+/* Copyright 2004 Jelmer Vernooij                                       */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include "bitlbee.h"
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include <nspr.h>
+#include <prio.h>
+#include <sslproto.h>
+#include <nss.h>
+#include <private/pprio.h>
+#include <ssl.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+	SslInputFunction func;
+	gpointer data;
+	int fd;
+	PRFileDesc *prfd;
+	gboolean established;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver)
+{
+	return SECSuccess;
+}
+
+static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) 
+{
+	PRErrorCode err;
+
+	if(!arg) return SECFailure;
+
+	*(PRErrorCode *)arg = err = PORT_GetError();
+
+	switch(err) {
+	case SEC_ERROR_INVALID_AVA:
+	case SEC_ERROR_INVALID_TIME:
+	case SEC_ERROR_BAD_SIGNATURE:
+	case SEC_ERROR_EXPIRED_CERTIFICATE:
+	case SEC_ERROR_UNKNOWN_ISSUER:
+	case SEC_ERROR_UNTRUSTED_CERT:
+	case SEC_ERROR_CERT_VALID:
+	case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+	case SEC_ERROR_CRL_EXPIRED:
+	case SEC_ERROR_CRL_BAD_SIGNATURE:
+	case SEC_ERROR_EXTENSION_VALUE_INVALID:
+	case SEC_ERROR_CA_CERT_INVALID:
+	case SEC_ERROR_CERT_USAGES_INVALID:
+	case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+		return SECSuccess;
+
+	default:
+		return SECFailure;
+	}
+}
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+	struct scd *conn = g_new0( struct scd, 1 );
+	
+	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	conn->func = func;
+	conn->data = data;
+	
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return( NULL );
+	}
+	
+	if( !initialized )
+	{
+		PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+		NSS_NoDB_Init(NULL);
+		NSS_SetDomesticPolicy();
+	}
+
+	
+	return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct scd *conn = data;
+	
+	if( source == -1 )
+		goto ssl_connected_failure;
+
+	
+
+
+	conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
+	SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
+	SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+	SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL);
+	SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB());
+	SSL_ResetHandshake(conn->prfd, PR_FALSE);
+
+	if (SSL_ForceHandshake(conn->prfd)) {
+		goto ssl_connected_failure;
+	}
+	
+	
+	conn->established = TRUE;
+	conn->func( conn->data, conn, cond );
+	return;
+	
+	ssl_connected_failure:
+	
+	conn->func( conn->data, NULL, cond );
+	
+	PR_Close( conn -> prfd );
+	if( source >= 0 ) closesocket( source );
+	g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+	struct scd *conn = conn_;
+	
+	PR_Close( conn->prfd );
+	closesocket( conn->fd );
+	
+	g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+	return( ((struct scd*)conn)->fd );
+}
Index: protocols/ssl_openssl.c
===================================================================
--- protocols/ssl_openssl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_openssl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,158 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* SSL module - GnuTLS version                                          */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+	SslInputFunction func;
+	gpointer data;
+	int fd;
+	gboolean established;
+	
+	SSL *ssl;
+	SSL_CTX *ssl_ctx;
+};
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
+
+
+
+void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+{
+	struct scd *conn = g_new0( struct scd, 1 );
+	SSL_METHOD *meth;
+	
+	conn->fd = proxy_connect( host, port, ssl_connected, conn );
+	conn->func = func;
+	conn->data = data;
+	
+	if( conn->fd < 0 )
+	{
+		g_free( conn );
+		return( NULL );
+	}
+	
+	if( !initialized )
+	{
+		initialized = TRUE;
+		SSLeay_add_ssl_algorithms();
+	}
+	
+	meth = TLSv1_client_method();
+	conn->ssl_ctx = SSL_CTX_new( meth );
+	if( conn->ssl_ctx == NULL )
+	{
+		conn->fd = -1;
+		return( NULL );
+	}
+	
+	conn->ssl = SSL_new( conn->ssl_ctx );
+	if( conn->ssl == NULL )
+	{
+		conn->fd = -1;
+		return( NULL );
+	}
+	
+	return( conn );
+}
+
+static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct scd *conn = data;
+	
+	if( source == -1 )
+		goto ssl_connected_failure;
+	
+	SSL_set_fd( conn->ssl, conn->fd );
+	
+	if( SSL_connect( conn->ssl ) < 0 )
+		goto ssl_connected_failure;
+	
+	conn->established = TRUE;
+	conn->func( conn->data, conn, cond );
+	return;
+	
+ssl_connected_failure:
+	conn->func( conn->data, NULL, cond );
+	
+	if( conn->ssl )
+	{
+		SSL_shutdown( conn->ssl );
+		SSL_free( conn->ssl );
+	}
+	if( conn->ssl_ctx )
+	{
+		SSL_CTX_free( conn->ssl_ctx );
+	}
+	if( source >= 0 ) closesocket( source );
+	g_free( conn );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( SSL_read( ((struct scd*)conn)->ssl, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+	if( !((struct scd*)conn)->established )
+		return( 0 );
+	
+	return( SSL_write( ((struct scd*)conn)->ssl, buf, len ) );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+	struct scd *conn = conn_;
+	
+	if( conn->established )
+		SSL_shutdown( conn->ssl );
+	
+	closesocket( conn->fd );
+	
+	SSL_free( conn->ssl );
+	SSL_CTX_free( conn->ssl_ctx );
+	g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+	return( ((struct scd*)conn)->fd );
+}
Index: protocols/twitter/Makefile
===================================================================
--- protocols/twitter/Makefile	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,46 +1,0 @@
-###########################
-## Makefile for BitlBee  ##
-##                       ##
-## Copyright 2002 Lintux ##
-###########################
-
-### DEFINITIONS
-
--include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/twitter/
-endif
-
-# [SH] Program variables
-objects = twitter.o twitter_http.o twitter_lib.o
-
-LFLAGS += -r
-
-# [SH] Phony targets
-all: twitter_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
-	
-.PHONY: all clean distclean
-
-clean:
-	rm -f *.o core
-
-distclean: clean
-	rm -rf .depend
-
-### MAIN PROGRAM
-
-$(objects): ../../Makefile.settings Makefile
-
-$(objects): %.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
-twitter_mod.o: $(objects)
-	@echo '*' Linking twitter_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o twitter_mod.o
-	
--include .depend/*.d
Index: protocols/twitter/twitter.c
===================================================================
--- protocols/twitter/twitter.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,1123 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include "nogaim.h"
-#include "oauth.h"
-#include "twitter.h"
-#include "twitter_http.h"
-#include "twitter_lib.h"
-#include "url.h"
-
-GSList *twitter_connections = NULL;
-
-static int twitter_filter_cmp(struct twitter_filter *tf1,
-                              struct twitter_filter *tf2)
-{
-	int i1 = 0;
-	int i2 = 0;
-	int i;
-
-	static const twitter_filter_type_t types[] = {
-		/* Order of the types */
-		TWITTER_FILTER_TYPE_FOLLOW,
-		TWITTER_FILTER_TYPE_TRACK
-	};
-
-	for (i = 0; i < G_N_ELEMENTS(types); i++) {
-		if (types[i] == tf1->type) {
-			i1 = i + 1;
-			break;
-		}
-	}
-
-	for (i = 0; i < G_N_ELEMENTS(types); i++) {
-		if (types[i] == tf2->type) {
-			i2 = i + 1;
-			break;
-		}
-	}
-
-	if (i1 != i2) {
-		/* With different types, return their difference */
-		return i1 - i2;
-	}
-
-	/* With the same type, return the text comparison */
-	return g_strcasecmp(tf1->text, tf2->text);
-}
-
-static gboolean twitter_filter_update(gpointer data, gint fd,
-                                      b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct twitter_data *td = ic->proto_data;
-
-	if (td->filters) {
-		twitter_open_filter_stream(ic);
-	} else if (td->filter_stream) {
-		http_close(td->filter_stream);
-		td->filter_stream = NULL;
-	}
-
-	td->filter_update_id = 0;
-	return FALSE;
-}
-
-static struct twitter_filter *twitter_filter_get(struct groupchat *c,
-                                                 twitter_filter_type_t type,
-                                                 const char *text)
-{
-	struct twitter_data *td = c->ic->proto_data;
-	struct twitter_filter *tf = NULL;
-	struct twitter_filter tfc = { type, (char *) text };
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (twitter_filter_cmp(tf, &tfc) == 0) {
-			break;
-		}
-
-		tf = NULL;
-	}
-
-	if (!tf) {
-		tf = g_new0(struct twitter_filter, 1);
-		tf->type = type;
-		tf->text = g_strdup(text);
-		td->filters = g_slist_prepend(td->filters, tf);
-	}
-
-	if (!g_slist_find(tf->groupchats, c)) {
-		tf->groupchats = g_slist_prepend(tf->groupchats, c);
-	}
-
-	if (td->filter_update_id > 0) {
-		b_event_remove(td->filter_update_id);
-	}
-
-	/* Wait for other possible filter changes to avoid request spam */
-	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT,
-	                                     twitter_filter_update, c->ic);
-	return tf;
-}
-
-static void twitter_filter_free(struct twitter_filter *tf)
-{
-	g_slist_free(tf->groupchats);
-	g_free(tf->text);
-	g_free(tf);
-}
-
-static void twitter_filter_remove(struct groupchat *c)
-{
-	struct twitter_data *td = c->ic->proto_data;
-	struct twitter_filter *tf;
-	GSList *l = td->filters;
-	GSList *p;
-
-	while (l != NULL) {
-		tf = l->data;
-		tf->groupchats = g_slist_remove(tf->groupchats, c);
-
-		p = l;
-		l = g_slist_next(l);
-
-		if (!tf->groupchats) {
-			twitter_filter_free(tf);
-			td->filters = g_slist_delete_link(td->filters, p);
-		}
-	}
-
-	if (td->filter_update_id > 0) {
-		b_event_remove(td->filter_update_id);
-	}
-
-	/* Wait for other possible filter changes to avoid request spam */
-	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT,
-	                                     twitter_filter_update, c->ic);
-}
-
-static void twitter_filter_remove_all(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	GSList *chats = NULL;
-	struct twitter_filter *tf;
-	GSList *l = td->filters;
-	GSList *p;
-
-	while (l != NULL) {
-		tf = l->data;
-
-		/* Build up a list of groupchats to be freed */
-		for (p = tf->groupchats; p; p = g_slist_next(p)) {
-			if (!g_slist_find(chats, p->data)) {
-				chats = g_slist_prepend(chats, p->data);
-			}
-		}
-
-		p = l;
-		l = g_slist_next(l);
-		twitter_filter_free(p->data);
-		td->filters = g_slist_delete_link(td->filters, p);
-	}
-
-	l = chats;
-
-	while (l != NULL) {
-		p = l;
-		l = g_slist_next(l);
-
-		/* Freed each remaining groupchat */
-		imcb_chat_free(p->data);
-		chats = g_slist_delete_link(chats, p);
-	}
-
-	if (td->filter_stream) {
-		http_close(td->filter_stream);
-		td->filter_stream = NULL;
-	}
-}
-
-static GSList *twitter_filter_parse(struct groupchat *c, const char *text)
-{
-	char **fs = g_strsplit(text, ";", 0);
-	GSList *ret = NULL;
-	struct twitter_filter *tf;
-	char **f;
-	char *v;
-	int i;
-	int t;
-
-	static const twitter_filter_type_t types[] = {
-		TWITTER_FILTER_TYPE_FOLLOW,
-		TWITTER_FILTER_TYPE_TRACK
-	};
-
-	static const char *typestrs[] = {
-		"follow",
-		"track"
-	};
-
-	for (f = fs; *f; f++) {
-		if ((v = strchr(*f, ':')) == NULL) {
-			continue;
-		}
-
-		*(v++) = 0;
-
-		for (t = -1, i = 0; i < G_N_ELEMENTS(types); i++) {
-			if (g_strcasecmp(typestrs[i], *f) == 0) {
-				t = i;
-				break;
-			}
-		}
-
-		if (t < 0 || strlen(v) == 0) {
-			continue;
-		}
-
-		tf = twitter_filter_get(c, types[t], v);
-		ret = g_slist_prepend(ret, tf);
-	}
-
-	g_strfreev(fs);
-	return ret;
-}
-
-/**
- * Main loop function
- */
-gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	// Check if we are still logged in...
-	if (!g_slist_find(twitter_connections, ic)) {
-		return FALSE;
-	}
-
-	// Do stuff..
-	return twitter_get_timeline(ic, -1) &&
-	       ((ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN);
-}
-
-static void twitter_main_loop_start(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	char *last_tweet = set_getstr(&ic->acc->set, "_last_tweet");
-
-	if (last_tweet) {
-		td->timeline_id = g_ascii_strtoull(last_tweet, NULL, 0);
-	}
-
-	/* Create the room now that we "logged in". */
-	if (td->flags & TWITTER_MODE_CHAT) {
-		twitter_groupchat_init(ic);
-	}
-
-	imcb_log(ic, "Getting initial statuses");
-
-	// Run this once. After this queue the main loop function (or open the
-	// stream if available).
-	twitter_main_loop(ic, -1, 0);
-
-	if (set_getbool(&ic->acc->set, "stream")) {
-		/* That fetch was just to get backlog, the stream will give
-		   us the rest. \o/ */
-		twitter_open_stream(ic);
-
-		/* Stream sends keepalives (empty lines) or actual data at
-		   least twice a minute. Disconnect if this stops. */
-		ic->flags |= OPT_PONGS;
-	} else {
-		/* Not using the streaming API, so keep polling the old-
-		   fashioned way. :-( */
-		td->main_loop_id =
-		        b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000,
-		                      twitter_main_loop, ic);
-	}
-}
-
-struct groupchat *twitter_groupchat_init(struct im_connection *ic)
-{
-	char *name_hint;
-	struct groupchat *gc;
-	struct twitter_data *td = ic->proto_data;
-	GSList *l;
-
-	if (td->timeline_gc) {
-		return td->timeline_gc;
-	}
-
-	td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline");
-
-	name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
-	imcb_chat_name_hint(gc, name_hint);
-	g_free(name_hint);
-
-	for (l = ic->bee->users; l; l = l->next) {
-		bee_user_t *bu = l->data;
-		if (bu->ic == ic) {
-			imcb_chat_add_buddy(gc, bu->handle);
-		}
-	}
-	imcb_chat_add_buddy(gc, ic->acc->user);
-
-	return gc;
-}
-
-static void twitter_oauth_start(struct im_connection *ic);
-
-void twitter_login_finish(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	td->flags &= ~TWITTER_DOING_TIMELINE;
-
-	if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) {
-		twitter_oauth_start(ic);
-	} else if (!(td->flags & TWITTER_MODE_ONE) &&
-	           !(td->flags & TWITTER_HAVE_FRIENDS)) {
-		imcb_log(ic, "Getting contact list");
-		twitter_get_friends_ids(ic, -1);
-		twitter_get_mutes_ids(ic, -1);
-		twitter_get_noretweets_ids(ic, -1);
-	} else {
-		twitter_main_loop_start(ic);
-	}
-}
-
-static const struct oauth_service twitter_oauth = {
-	"https://api.twitter.com/oauth/request_token",
-	"https://api.twitter.com/oauth/access_token",
-	"https://api.twitter.com/oauth/authorize",
-	.consumer_key = "ItChnf5KelJE5DOWVhz9ZuwYb",
-	.consumer_secret = "yoWJ16DRmJlILoB9Tf5T5lIUUPnIarqoO0YUWkYUOD7ttR0J6T",
-};
-
-static gboolean twitter_oauth_callback(struct oauth_info *info);
-
-static const struct oauth_service *get_oauth_service(struct im_connection *ic)
-{
-	return &twitter_oauth;
-
-	/* Could add more services, or allow configuring your own base URL +
-	   API keys. */
-}
-
-static void twitter_oauth_start(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	const char *url = set_getstr(&ic->acc->set, "base_url");
-
-	imcb_log(ic, "Requesting OAuth request token");
-
-	if (!strstr(url, "twitter.com")) {
-		imcb_log(ic, "Warning: OAuth only works with Twitter.");
-	}
-
-	td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic);
-
-	/* We need help from the user to complete OAuth login, so don't time
-	   out on this login. */
-	ic->flags |= OPT_SLOW_LOGIN;
-}
-
-static gboolean twitter_oauth_callback(struct oauth_info *info)
-{
-	struct im_connection *ic = info->data;
-	struct twitter_data *td;
-
-	if (!g_slist_find(twitter_connections, ic)) {
-		return FALSE;
-	}
-
-	td = ic->proto_data;
-	if (info->stage == OAUTH_REQUEST_TOKEN) {
-		char *name, *msg;
-
-		if (info->request_token == NULL) {
-			imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-
-		name = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
-		msg = g_strdup_printf("To finish OAuth authentication, please visit "
-		                      "%s and respond with the resulting PIN code.",
-		                      info->auth_url);
-		imcb_buddy_msg(ic, name, msg, 0, 0);
-		g_free(name);
-		g_free(msg);
-	} else if (info->stage == OAUTH_ACCESS_TOKEN) {
-		const char *sn;
-
-		if (info->token == NULL || info->token_secret == NULL) {
-			imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-
-		if ((sn = oauth_params_get(&info->params, "screen_name"))) {
-			if (ic->acc->prpl->handle_cmp(sn, ic->acc->user) != 0) {
-				imcb_log(ic, "Warning: You logged in via OAuth as %s "
-				         "instead of %s.", sn, ic->acc->user);
-			}
-			g_free(td->user);
-			td->user = g_strdup(sn);
-		}
-
-		/* IM mods didn't do this so far and it's ugly but I should
-		   be able to get away with it... */
-		g_free(ic->acc->pass);
-		ic->acc->pass = oauth_to_string(info);
-
-		twitter_login_finish(ic);
-	}
-
-	return TRUE;
-}
-
-int twitter_url_len_diff(gchar *msg, unsigned int target_len)
-{
-	int url_len_diff = 0;
-
-	static GRegex *regex = NULL;
-	GMatchInfo *match_info;
-
-	if (regex == NULL) {
-		regex = g_regex_new("(^|\\s)(http(s)?://[^\\s$]+)", 0, 0, NULL);
-	}
-
-	g_regex_match(regex, msg, 0, &match_info);
-	while (g_match_info_matches(match_info)) {
-		gchar *url;
-
-		url = g_match_info_fetch(match_info, 2);
-		url_len_diff += target_len - g_utf8_strlen(url, -1);
-
-		g_free(url);
-		g_match_info_next(match_info, NULL);
-	}
-	g_match_info_free(match_info);
-
-	return url_len_diff;
-}
-
-int twitter_message_len(gchar *msg, int target_len)
-{
-	int url_len_diff = 0;
-
-	if (target_len > 0) {
-		url_len_diff = twitter_url_len_diff(msg, target_len);
-	}
-
-	return g_utf8_strlen(msg, -1) + url_len_diff;
-}
-
-static gboolean twitter_length_check(struct im_connection *ic, gchar * msg)
-{
-	int max = set_getint(&ic->acc->set, "message_length");
-	int target_len = set_getint(&ic->acc->set, "target_url_length");
-	int len = twitter_message_len(msg, target_len);
-
-	if (max == 0 || len <= max) {
-		return TRUE;
-	}
-
-	twitter_log(ic, "Maximum message length exceeded: %d > %d", len, max);
-
-	return FALSE;
-}
-
-static char *set_eval_commands(set_t * set, char *value)
-{
-	if (g_strcasecmp(value, "strict") == 0) {
-		return value;
-	} else {
-		return set_eval_bool(set, value);
-	}
-}
-
-static char *set_eval_mode(set_t * set, char *value)
-{
-	if (g_strcasecmp(value, "one") == 0 ||
-	    g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0) {
-		return value;
-	} else {
-		return NULL;
-	}
-}
-
-static void twitter_init(account_t * acc)
-{
-	set_t *s;
-	char *def_url;
-	char *def_tul;
-	char *def_mentions;
-
-	if (strcmp(acc->prpl->name, "twitter") == 0) {
-		def_url = TWITTER_API_URL;
-		def_tul = "23";
-		def_mentions = "true";
-	} else {
-		def_url = "";
-		def_tul = "0";
-		def_mentions = "false";
-	}
-
-	s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc);
-
-	s = set_add(&acc->set, "base_url", def_url, NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "commands", "true", set_eval_commands, acc);
-
-	s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "fetch_mentions", def_mentions, set_eval_bool, acc);
-
-	s = set_add(&acc->set, "message_length", "280", set_eval_int, acc);
-
-	s = set_add(&acc->set, "target_url_length", def_tul, set_eval_int, acc);
-
-	s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "oauth", "true", set_eval_oauth, acc);
-
-	s = set_add(&acc->set, "show_ids", "true", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "show_old_mentions", "0", set_eval_int, acc);
-
-	s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "_last_tweet", "0", NULL, acc);
-	s->flags |= SET_HIDDEN | SET_NOSAVE;
-
-	s = set_add(&acc->set, "in_korea", "false", set_eval_bool, acc);
-	s->flags |= SET_HIDDEN;
-
-	if (strcmp(acc->prpl->name, "twitter") == 0) {
-		s = set_add(&acc->set, "stream", "false", set_eval_bool, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY;
-	}
-}
-
-/**
- * Login method. Since the twitter API works with separate HTTP request we
- * only save the user and pass to the twitter_data object.
- */
-static void twitter_login(account_t * acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct twitter_data *td;
-	char name[strlen(acc->user) + 9];
-	url_t url;
-	char *s;
-
-	if (!url_set(&url, set_getstr(&ic->acc->set, "base_url")) ||
-	    (url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS)) {
-		imcb_error(ic, "Incorrect API base URL: %s", set_getstr(&ic->acc->set, "base_url"));
-		imc_logout(ic, FALSE);
-		return;
-	}
-
-	if (!strstr(url.host, "twitter.com") &&
-	    set_getbool(&ic->acc->set, "stream")) {
-		imcb_error(ic, "Warning: The streaming API is only supported by Twitter, "
-		           "and you seem to be connecting to a different service.");
-	}
-
-	imcb_log(ic, "Connecting");
-
-	twitter_connections = g_slist_append(twitter_connections, ic);
-	td = g_new0(struct twitter_data, 1);
-	ic->proto_data = td;
-	td->user = g_strdup(acc->user);
-
-	td->url_ssl = url.proto == PROTO_HTTPS;
-	td->url_port = url.port;
-	td->url_host = g_strdup(url.host);
-	if (strcmp(url.file, "/") != 0) {
-		td->url_path = g_strdup(url.file);
-	} else {
-		td->url_path = g_strdup("");
-		if (g_str_has_suffix(url.host, "twitter.com")) {
-			/* May fire for people who turned on HTTPS. */
-			imcb_error(ic, "Warning: Twitter requires a version number in API calls "
-			           "now. Try resetting the base_url account setting.");
-		}
-	}
-
-	/* Hacky string mangling: Turn identi.ca into identi.ca and api.twitter.com
-	   into twitter, and try to be sensible if we get anything else. */
-	td->prefix = g_strdup(url.host);
-	if (g_str_has_suffix(td->prefix, ".com")) {
-		td->prefix[strlen(url.host) - 4] = '\0';
-	}
-	if ((s = strrchr(td->prefix, '.')) && strlen(s) > 4) {
-		/* If we have at least 3 chars after the last dot, cut off the rest.
-		   (mostly a www/api prefix or sth) */
-		s = g_strdup(s + 1);
-		g_free(td->prefix);
-		td->prefix = s;
-	}
-
-	if (strstr(acc->pass, "oauth_token=")) {
-		td->oauth_info = oauth_from_string(acc->pass, get_oauth_service(ic));
-	}
-
-	sprintf(name, "%s_%s", td->prefix, acc->user);
-	imcb_add_buddy(ic, name, NULL);
-	imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
-
-	td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
-	td->log_id = -1;
-
-	s = set_getstr(&ic->acc->set, "mode");
-	if (g_strcasecmp(s, "one") == 0) {
-		td->flags |= TWITTER_MODE_ONE;
-	} else if (g_strcasecmp(s, "many") == 0) {
-		td->flags |= TWITTER_MODE_MANY;
-	} else {
-		td->flags |= TWITTER_MODE_CHAT;
-	}
-
-	twitter_login_finish(ic);
-}
-
-/**
- * Logout method. Just free the twitter_data.
- */
-static void twitter_logout(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	// Set the status to logged out.
-	ic->flags &= ~OPT_LOGGED_IN;
-
-	if (td) {
-		// Remove the main_loop function from the function queue.
-		b_event_remove(td->main_loop_id);
-
-		if (td->timeline_gc) {
-			imcb_chat_free(td->timeline_gc);
-		}
-
-		if (td->filter_update_id > 0) {
-			b_event_remove(td->filter_update_id);
-		}
-
-		g_slist_foreach(td->mutes_ids, (GFunc) g_free, NULL);
-		g_slist_free(td->mutes_ids);
-
-		g_slist_foreach(td->noretweets_ids, (GFunc) g_free, NULL);
-		g_slist_free(td->noretweets_ids);
-
-		http_close(td->stream);
-		twitter_filter_remove_all(ic);
-		oauth_info_free(td->oauth_info);
-		g_free(td->user);
-		g_free(td->prefix);
-		g_free(td->url_host);
-		g_free(td->url_path);
-		g_free(td->log);
-		g_free(td);
-	}
-
-	twitter_connections = g_slist_remove(twitter_connections, ic);
-}
-
-static void twitter_handle_command(struct im_connection *ic, char *message);
-
-/**
- *
- */
-static int twitter_buddy_msg(struct im_connection *ic, char *who, char *message, int away)
-{
-	struct twitter_data *td = ic->proto_data;
-	int plen = strlen(td->prefix);
-
-	if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' &&
-	    g_strcasecmp(who + plen + 1, ic->acc->user) == 0) {
-		if (set_getbool(&ic->acc->set, "oauth") &&
-		    td->oauth_info && td->oauth_info->token == NULL) {
-			char pin[strlen(message) + 1], *s;
-
-			strcpy(pin, message);
-			for (s = pin + sizeof(pin) - 2; s > pin && g_ascii_isspace(*s); s--) {
-				*s = '\0';
-			}
-			for (s = pin; *s && g_ascii_isspace(*s); s++) {
-			}
-
-			if (!oauth_access_token(s, td->oauth_info)) {
-				imcb_error(ic, "OAuth error: %s",
-				           "Failed to send access token request");
-				imc_logout(ic, TRUE);
-				return FALSE;
-			}
-		} else {
-			twitter_handle_command(ic, message);
-		}
-	} else {
-		twitter_direct_messages_new(ic, who, message);
-	}
-	return (0);
-}
-
-static void twitter_get_info(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	twitter_friendships_create_destroy(ic, who, 1);
-}
-
-static void twitter_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	twitter_friendships_create_destroy(ic, who, 0);
-}
-
-static void twitter_chat_msg(struct groupchat *c, char *message, int flags)
-{
-	if (c && message) {
-		twitter_handle_command(c->ic, message);
-	}
-}
-
-static void twitter_chat_invite(struct groupchat *c, char *who, char *message)
-{
-}
-
-static struct groupchat *twitter_chat_join(struct im_connection *ic,
-                                           const char *room, const char *nick,
-                                           const char *password, set_t **sets)
-{
-	struct groupchat *c = imcb_chat_new(ic, room);
-	GSList *fs = twitter_filter_parse(c, room);
-	GString *topic = g_string_new("");
-	struct twitter_filter *tf;
-	GSList *l;
-
-	fs = g_slist_sort(fs, (GCompareFunc) twitter_filter_cmp);
-
-	for (l = fs; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (topic->len > 0) {
-			g_string_append(topic, ", ");
-		}
-
-		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {
-			g_string_append_c(topic, '@');
-		}
-
-		g_string_append(topic, tf->text);
-	}
-
-	if (topic->len > 0) {
-		g_string_prepend(topic, "Twitter Filter: ");
-	}
-
-	imcb_chat_topic(c, NULL, topic->str, 0);
-	imcb_chat_add_buddy(c, ic->acc->user);
-
-	if (topic->len == 0) {
-		imcb_error(ic, "Failed to handle any filters");
-		imcb_chat_free(c);
-		c = NULL;
-	}
-
-	g_string_free(topic, TRUE);
-	g_slist_free(fs);
-
-	return c;
-}
-
-static void twitter_chat_leave(struct groupchat *c)
-{
-	struct twitter_data *td = c->ic->proto_data;
-
-	if (c != td->timeline_gc) {
-		twitter_filter_remove(c);
-		imcb_chat_free(c);
-		return;
-	}
-
-	/* If the user leaves the channel: Fine. Rejoin him/her once new
-	   tweets come in. */
-	imcb_chat_free(td->timeline_gc);
-	td->timeline_gc = NULL;
-}
-
-static void twitter_keepalive(struct im_connection *ic)
-{
-}
-
-static void twitter_add_permit(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_rem_permit(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_add_deny(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_rem_deny(struct im_connection *ic, char *who)
-{
-}
-
-//static char *twitter_set_display_name( set_t *set, char *value )
-//{
-//      return value;
-//}
-
-static void twitter_buddy_data_add(struct bee_user *bu)
-{
-	bu->data = g_new0(struct twitter_user_data, 1);
-}
-
-static void twitter_buddy_data_free(struct bee_user *bu)
-{
-	g_free(bu->data);
-}
-
-bee_user_t twitter_log_local_user;
-
-/** Convert the given bitlbee tweet ID, bitlbee username, or twitter tweet ID
- *  into a twitter tweet ID.
- *
- *  Returns 0 if the user provides garbage.
- */
-static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct twitter_user_data *tud;
-	bee_user_t *bu = NULL;
-	guint64 id = 0;
-
-	if (bu_) {
-		*bu_ = NULL;
-	}
-	if (!arg || !arg[0]) {
-		return 0;
-	}
-
-	if (arg[0] != '#' && (bu = bee_user_by_handle(ic->bee, ic, arg))) {
-		if ((tud = bu->data)) {
-			id = tud->last_id;
-		}
-	} else {
-		if (arg[0] == '#') {
-			arg++;
-		}
-		if (parse_int64(arg, 16, &id) && id < TWITTER_LOG_LENGTH) {
-			bu = td->log[id].bu;
-			id = td->log[id].id;
-		} else if (parse_int64(arg, 10, &id)) {
-			/* Allow normal tweet IDs as well; not a very useful
-			   feature but it's always been there. Just ignore
-			   very low IDs to avoid accidents. */
-			if (id < 1000000) {
-				id = 0;
-			}
-		}
-	}
-	if (bu_) {
-		if (bu == &twitter_log_local_user) {
-			/* HACK alert. There's no bee_user object for the local
-			 * user so just fake one for the few cmds that need it. */
-			twitter_log_local_user.handle = td->user;
-		} else {
-			/* Beware of dangling pointers! */
-			if (!g_slist_find(ic->bee->users, bu)) {
-				bu = NULL;
-			}
-		}
-		*bu_ = bu;
-	}
-	return id;
-}
-
-static void twitter_handle_command(struct im_connection *ic, char *message)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *cmds, **cmd, *new = NULL;
-	guint64 in_reply_to = 0, id;
-	gboolean allow_post =
-	        g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0;
-	gboolean auto_populate_reply_metadata = FALSE;
-	bee_user_t *bu = NULL;
-
-	cmds = g_strdup(message);
-	cmd = split_command_parts(cmds, 2);
-
-	if (cmd[0] == NULL) {
-		goto eof;
-	} else if (!set_getbool(&ic->acc->set, "commands") && allow_post) {
-		/* Not supporting commands if "commands" is set to true/strict. */
-	} else if (g_strcasecmp(cmd[0], "undo") == 0) {
-		if (cmd[1] == NULL) {
-			twitter_status_destroy(ic, td->last_status_id);
-		} else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
-			twitter_status_destroy(ic, id);
-		} else {
-			twitter_log(ic, "Could not undo last action");
-		}
-
-		goto eof;
-	} else if ((g_strcasecmp(cmd[0], "favourite") == 0 ||
-	            g_strcasecmp(cmd[0], "favorite") == 0 ||
-	            g_strcasecmp(cmd[0], "fav") == 0 ||
-	            g_strcasecmp(cmd[0], "like") == 0) && cmd[1]) {
-		if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
-			twitter_favourite_tweet(ic, id);
-		} else {
-			twitter_log(ic, "Please provide a message ID or username.");
-		}
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) {
-		twitter_add_buddy(ic, cmd[1], NULL);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
-		twitter_remove_buddy(ic, cmd[1], NULL);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "mute") == 0 && cmd[1]) {
-		twitter_mute_create_destroy(ic, cmd[1], 1);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "unmute") == 0 && cmd[1]) {
-		twitter_mute_create_destroy(ic, cmd[1], 0);
-		goto eof;
-	} else if ((g_strcasecmp(cmd[0], "report") == 0 ||
-	            g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) {
-		char *screen_name;
-
-		/* Report nominally works on users but look up the user who
-		   posted the given ID if the user wants to do it that way */
-		twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (bu) {
-			screen_name = bu->handle;
-		} else {
-			screen_name = cmd[1];
-		}
-
-		twitter_report_spam(ic, screen_name);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);
-
-		td->last_status_id = 0;
-		if (id) {
-			twitter_status_retweet(ic, id);
-		} else {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-		}
-
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id || !bu) {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-			goto eof;
-		}
-		message = new = g_strdup_printf("@%s %s", bu->handle, cmd[2]);
-		in_reply_to = id;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "greply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id || !bu) {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-			goto eof;
-		}
-		message = new = g_strdup_printf("%s", cmd[2]);
-		in_reply_to = id;
-		auto_populate_reply_metadata = TRUE;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "rawreply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);
-		if (!id) {
-			twitter_log(ic, "Tweet `%s' does not exist", cmd[1]);
-			goto eof;
-		}
-		message = cmd[2];
-		in_reply_to = id;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "url") == 0) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id) {
-			twitter_log(ic, "Tweet `%s' does not exist", cmd[1]);
-		} else {
-			twitter_status_show_url(ic, id);
-		}
-		goto eof;
-
-	} else if (g_strcasecmp(cmd[0], "post") == 0) {
-		message += 5;
-		allow_post = TRUE;
-	}
-
-	if (allow_post) {
-		char *s;
-
-		if (!twitter_length_check(ic, message)) {
-			goto eof;
-		}
-
-		s = cmd[0] + strlen(cmd[0]) - 1;
-		if (!new && s > cmd[0] && (*s == ':' || *s == ',')) {
-			*s = '\0';
-
-			if ((bu = bee_user_by_handle(ic->bee, ic, cmd[0]))) {
-				struct twitter_user_data *tud = bu->data;
-
-				new = g_strdup_printf("@%s %s", bu->handle,
-				                      message + (s - cmd[0]) + 2);
-				message = new;
-
-				if (time(NULL) < tud->last_time +
-				    set_getint(&ic->acc->set, "auto_reply_timeout")) {
-					in_reply_to = tud->last_id;
-				}
-			}
-		}
-
-		/* If the user runs undo between this request and its response
-		   this would delete the second-last Tweet. Prevent that. */
-		td->last_status_id = 0;
-		twitter_post_status(ic, message, in_reply_to, auto_populate_reply_metadata);
-	} else {
-		twitter_log(ic, "Unknown command: %s", cmd[0]);
-	}
-eof:
-	g_free(new);
-	g_free(cmds);
-}
-
-void twitter_log(struct im_connection *ic, char *format, ...)
-{
-	struct twitter_data *td = ic->proto_data;
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (td->timeline_gc) {
-		imcb_chat_log(td->timeline_gc, "%s", text);
-	} else {
-		imcb_log(ic, "%s", text);
-	}
-
-	g_free(text);
-}
-
-
-void twitter_initmodule()
-{
-	struct prpl *ret = g_new0(struct prpl, 1);
-
-	ret->options = PRPL_OPT_NOOTR | PRPL_OPT_NO_PASSWORD;
-	ret->name = "twitter";
-	ret->login = twitter_login;
-	ret->init = twitter_init;
-	ret->logout = twitter_logout;
-	ret->buddy_msg = twitter_buddy_msg;
-	ret->get_info = twitter_get_info;
-	ret->add_buddy = twitter_add_buddy;
-	ret->remove_buddy = twitter_remove_buddy;
-	ret->chat_msg = twitter_chat_msg;
-	ret->chat_invite = twitter_chat_invite;
-	ret->chat_join = twitter_chat_join;
-	ret->chat_leave = twitter_chat_leave;
-	ret->keepalive = twitter_keepalive;
-	ret->add_permit = twitter_add_permit;
-	ret->rem_permit = twitter_rem_permit;
-	ret->add_deny = twitter_add_deny;
-	ret->rem_deny = twitter_rem_deny;
-	ret->buddy_data_add = twitter_buddy_data_add;
-	ret->buddy_data_free = twitter_buddy_data_free;
-	ret->handle_cmp = g_strcasecmp;
-
-	register_protocol(ret);
-
-	/* And an identi.ca variant: */
-	ret = g_memdup2(ret, sizeof(struct prpl));
-	ret->name = "identica";
-	ret->options =  PRPL_OPT_NOOTR;
-	register_protocol(ret);
-}
Index: protocols/twitter/twitter.h
===================================================================
--- protocols/twitter/twitter.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,133 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include "nogaim.h"
-
-#ifndef _TWITTER_H
-#define _TWITTER_H
-
-#ifdef DEBUG_TWITTER
-#define debug(text ...) imcb_log(ic, text);
-#else
-#define debug(text ...)
-#endif
-
-typedef enum {
-	TWITTER_HAVE_FRIENDS   = 0x00001,
-	TWITTER_MODE_ONE       = 0x00002,
-	TWITTER_MODE_MANY      = 0x00004,
-	TWITTER_MODE_CHAT      = 0x00008,
-	TWITTER_DOING_TIMELINE = 0x10000,
-	TWITTER_GOT_TIMELINE   = 0x20000,
-	TWITTER_GOT_MENTIONS   = 0x40000,
-} twitter_flags_t;
-
-typedef enum {
-	TWITTER_FILTER_TYPE_FOLLOW = 0,
-	TWITTER_FILTER_TYPE_TRACK
-} twitter_filter_type_t;
-
-struct twitter_log_data;
-
-struct twitter_data {
-	char* user;
-	struct oauth_info *oauth_info;
-
-	gpointer home_timeline_obj;
-	gpointer mentions_obj;
-
-	guint64 timeline_id;
-
-	GSList *follow_ids;
-	GSList *mutes_ids;
-	GSList *noretweets_ids;
-	GSList *filters;
-
-	guint64 last_status_id; /* For undo */
-	gint main_loop_id;
-	gint filter_update_id;
-	struct http_request *stream;
-	struct http_request *filter_stream;
-	struct groupchat *timeline_gc;
-	gint http_fails;
-	twitter_flags_t flags;
-
-	/* set base_url */
-	gboolean url_ssl;
-	int url_port;
-	char *url_host;
-	char *url_path;
-
-	char *prefix; /* Used to generate contact + channel name. */
-
-	/* set show_ids */
-	struct twitter_log_data *log;
-	int log_id;
-};
-
-#define TWITTER_FILTER_UPDATE_WAIT 3000
-struct twitter_filter {
-	twitter_filter_type_t type;
-	char *text;
-	guint64 uid;
-	GSList *groupchats;
-};
-
-struct twitter_user_data {
-	guint64 last_id;
-	time_t last_time;
-};
-
-#define TWITTER_LOG_LENGTH 256
-struct twitter_log_data {
-	guint64 id;
-	/* DANGER: bu can be a dead pointer. Check it first.
-	 * twitter_message_id_from_command_arg() will do this. */
-	struct bee_user *bu;
-};
-
-/**
- * This has the same function as the msn_connections GSList. We use this to
- * make sure the connection is still alive in callbacks before we do anything
- * else.
- */
-extern GSList *twitter_connections;
-
-/**
- * Evil hack: Fake bee_user which will always point at the local user.
- * Sometimes used as a return value by twitter_message_id_from_command_arg.
- * NOT thread safe but don't you dare to even think of ever making BitlBee
- * threaded. :-)
- */
-extern bee_user_t twitter_log_local_user;
-
-void twitter_login_finish(struct im_connection *ic);
-
-struct http_request;
-char *twitter_parse_error(struct http_request *req);
-
-void twitter_log(struct im_connection *ic, char *format, ...);
-struct groupchat *twitter_groupchat_init(struct im_connection *ic);
-
-#endif //_TWITTER_H
Index: protocols/twitter/twitter_http.c
===================================================================
--- protocols/twitter/twitter_http.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,171 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-/***************************************************************************\
-*                                                                           *
-*  Some functions within this file have been copied from other files within  *
-*  BitlBee.                                                                 *
-*                                                                           *
-****************************************************************************/
-
-#include "twitter.h"
-#include "bitlbee.h"
-#include "url.h"
-#include "misc.h"
-#include "base64.h"
-#include "oauth.h"
-#include <ctype.h>
-#include <errno.h>
-
-#include "twitter_http.h"
-
-
-static char *twitter_url_append(char *url, char *key, char *value);
-
-/**
- * Do a request.
- * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
- */
-struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
-                                  gpointer data, int is_post, char **arguments, int arguments_len)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *tmp;
-	GString *request = g_string_new("");
-	void *ret = NULL;
-	char *url_arguments;
-	url_t *base_url = NULL;
-
-	url_arguments = g_strdup("");
-
-	// Construct the url arguments.
-	if (arguments_len != 0) {
-		int i;
-		for (i = 0; i < arguments_len; i += 2) {
-			tmp = twitter_url_append(url_arguments, arguments[i], arguments[i + 1]);
-			g_free(url_arguments);
-			url_arguments = tmp;
-		}
-	}
-
-	if (strstr(url_string, "://")) {
-		base_url = g_new0(url_t, 1);
-		if (!url_set(base_url, url_string)) {
-			goto error;
-		}
-	}
-
-	// Make the request.
-	g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
-	                "Host: %s\r\n"
-	                "User-Agent: BitlBee " BITLBEE_VERSION "\r\n",
-	                is_post ? "POST" : "GET",
-	                base_url ? base_url->file : td->url_path,
-	                base_url ? "" : url_string,
-	                is_post ? "" : "?", is_post ? "" : url_arguments,
-	                base_url ? base_url->host : td->url_host);
-
-	// If a pass and user are given we append them to the request.
-	if (td->oauth_info) {
-		char *full_header;
-		char *full_url;
-
-		if (base_url) {
-			full_url = g_strdup(url_string);
-		} else {
-			full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL);
-		}
-		full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET",
-		                                full_url, url_arguments);
-
-		g_string_append_printf(request, "Authorization: %s\r\n", full_header);
-		g_free(full_header);
-		g_free(full_url);
-	} else {
-		char userpass[strlen(ic->acc->user) + 2 + strlen(ic->acc->pass)];
-		char *userpass_base64;
-
-		g_snprintf(userpass, sizeof(userpass), "%s:%s", ic->acc->user, ic->acc->pass);
-		userpass_base64 = base64_encode((unsigned char *) userpass, strlen(userpass));
-		g_string_append_printf(request, "Authorization: Basic %s\r\n", userpass_base64);
-		g_free(userpass_base64);
-	}
-
-	// Do POST stuff..
-	if (is_post) {
-		// Append the Content-Type and url-encoded arguments.
-		g_string_append_printf(request,
-		                       "Content-Type: application/x-www-form-urlencoded\r\n"
-		                       "Content-Length: %zd\r\n\r\n%s",
-		                       strlen(url_arguments), url_arguments);
-	} else {
-		// Append an extra \r\n to end the request...
-		g_string_append(request, "\r\n");
-	}
-
-	if (base_url) {
-		ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func,
-		                     data);
-	} else {
-		ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data);
-	}
-
-error:
-	g_free(url_arguments);
-	g_string_free(request, TRUE);
-	g_free(base_url);
-	return ret;
-}
-
-struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func,
-                                    gpointer data, int is_post, char **arguments, int arguments_len,
-                                    twitter_http_flags_t flags)
-{
-	struct http_request *ret = twitter_http(ic, url_string, func, data, is_post, arguments, arguments_len);
-
-	if (ret) {
-		ret->flags |= flags;
-	}
-	return ret;
-}
-
-static char *twitter_url_append(char *url, char *key, char *value)
-{
-	char *key_encoded = g_strndup(key, 3 * strlen(key));
-
-	http_encode(key_encoded);
-	char *value_encoded = g_strndup(value, 3 * strlen(value));
-	http_encode(value_encoded);
-
-	char *retval;
-	if (strlen(url) != 0) {
-		retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded);
-	} else {
-		retval = g_strdup_printf("%s=%s", key_encoded, value_encoded);
-	}
-
-	g_free(key_encoded);
-	g_free(value_encoded);
-
-	return retval;
-}
Index: protocols/twitter/twitter_http.h
===================================================================
--- protocols/twitter/twitter_http.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#ifndef _TWITTER_HTTP_H
-#define _TWITTER_HTTP_H
-
-#include "nogaim.h"
-#include "http_client.h"
-
-typedef enum {
-	/* With this set, twitter_http_post() will post a generic confirmation
-	   message to the user. */
-	TWITTER_HTTP_USER_ACK = 0x1000000,
-} twitter_http_flags_t;
-
-struct oauth_info;
-
-struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
-                                  gpointer data, int is_post, char** arguments, int arguments_len);
-struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func,
-                                    gpointer data, int is_post, char** arguments, int arguments_len,
-                                    twitter_http_flags_t flags);
-
-#endif //_TWITTER_HTTP_H
-
Index: protocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,1876 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-/* For strptime(): */
-#if (__sun)
-#else
-#define _XOPEN_SOURCE
-#endif
-
-#include "twitter_http.h"
-#include "twitter.h"
-#include "bitlbee.h"
-#include "url.h"
-#include "misc.h"
-#include "base64.h"
-#include "twitter_lib.h"
-#include "json_util.h"
-#include <ctype.h>
-#include <errno.h>
-
-#define TXL_STATUS 1
-#define TXL_USER 2
-#define TXL_ID 3
-
-struct twitter_xml_list {
-	int type;
-	gint64 next_cursor;
-	GSList *list;
-};
-
-struct twitter_xml_user {
-	guint64 uid;
-	char *name;
-	char *screen_name;
-};
-
-struct twitter_xml_status {
-	time_t created_at;
-	char *text;
-	struct twitter_xml_user *user;
-	guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */
-	guint64 reply_to;
-	gboolean from_filter;
-};
-
-/**
- * Frees a twitter_xml_user struct.
- */
-static void txu_free(struct twitter_xml_user *txu)
-{
-	if (txu == NULL) {
-		return;
-	}
-
-	g_free(txu->name);
-	g_free(txu->screen_name);
-	g_free(txu);
-}
-
-/**
- * Frees a twitter_xml_status struct.
- */
-static void txs_free(struct twitter_xml_status *txs)
-{
-	if (txs == NULL) {
-		return;
-	}
-
-	g_free(txs->text);
-	txu_free(txs->user);
-	g_free(txs);
-}
-
-/**
- * Free a twitter_xml_list struct.
- * type is the type of list the struct holds.
- */
-static void txl_free(struct twitter_xml_list *txl)
-{
-	GSList *l;
-
-	if (txl == NULL) {
-		return;
-	}
-
-	for (l = txl->list; l; l = g_slist_next(l)) {
-		if (txl->type == TXL_STATUS) {
-			txs_free((struct twitter_xml_status *) l->data);
-		} else if (txl->type == TXL_ID) {
-			g_free(l->data);
-		} else if (txl->type == TXL_USER) {
-			txu_free(l->data);
-		}
-	}
-
-	g_slist_free(txl->list);
-	g_free(txl);
-}
-
-/**
- * Compare status elements
- */
-static gint twitter_compare_elements(gconstpointer a, gconstpointer b)
-{
-	struct twitter_xml_status *a_status = (struct twitter_xml_status *) a;
-	struct twitter_xml_status *b_status = (struct twitter_xml_status *) b;
-
-	if (a_status->created_at < b_status->created_at) {
-		return -1;
-	} else if (a_status->created_at > b_status->created_at) {
-		return 1;
-	} else {
-		return a_status->id < b_status->id ? -1 : 1;
-	}
-}
-
-/**
- * Add a buddy if it is not already added, set the status to logged in.
- */
-static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	// Check if the buddy is already in the buddy list.
-	if (!bee_user_by_handle(ic->bee, ic, name)) {
-		// The buddy is not in the list, add the buddy and set the status to logged in.
-		imcb_add_buddy(ic, name, NULL);
-		imcb_rename_buddy(ic, name, fullname);
-		if (td->flags & TWITTER_MODE_CHAT) {
-			/* Necessary so that nicks always get translated to the
-			   exact Twitter username. */
-			imcb_buddy_nick_hint(ic, name, name);
-			if (td->timeline_gc) {
-				imcb_chat_add_buddy(td->timeline_gc, name);
-			}
-		} else if (td->flags & TWITTER_MODE_MANY) {
-			imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
-		}
-	}
-}
-
-/* Warning: May return a malloc()ed value, which will be free()d on the next
-   call. Only for short-term use. NOT THREADSAFE!  */
-char *twitter_parse_error(struct http_request *req)
-{
-	static char *ret = NULL;
-	json_value *root, *err;
-
-	g_free(ret);
-	ret = NULL;
-
-	if (req->body_size > 0) {
-		root = json_parse(req->reply_body, req->body_size);
-		err = json_o_get(root, "errors");
-		if (err && err->type == json_array && (err = err->u.array.values[0]) &&
-		    err->type == json_object) {
-			const char *msg = json_o_str(err, "message");
-			if (msg) {
-				ret = g_strdup_printf("%s (%s)", req->status_string, msg);
-			}
-		}
-		json_value_free(root);
-	}
-
-	return ret ? ret : req->status_string;
-}
-
-/* WATCH OUT: This function might or might not destroy your connection.
-   Sub-optimal indeed, but just be careful when this returns NULL! */
-static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req)
-{
-	gboolean logging_in = !(ic->flags & OPT_LOGGED_IN);
-	gboolean periodic;
-	struct twitter_data *td = ic->proto_data;
-	json_value *ret;
-	char path[64] = "", *s;
-
-	if ((s = strchr(req->request, ' '))) {
-		path[sizeof(path) - 1] = '\0';
-		strncpy(path, s + 1, sizeof(path) - 1);
-		if ((s = strchr(path, '?')) || (s = strchr(path, ' '))) {
-			*s = '\0';
-		}
-	}
-
-	/* Kinda nasty. :-( Trying to suppress error messages, but only
-	   for periodic (i.e. mentions/timeline) queries. */
-	periodic = strstr(path, "timeline") || strstr(path, "mentions");
-
-	if (req->status_code == 401 && logging_in) {
-		/* IIRC Twitter once had an outage where they were randomly
-		   throwing 401s so I'll keep treating this one as fatal
-		   only during login. */
-		imcb_error(ic, "Authentication failure (%s)",
-		           twitter_parse_error(req));
-		imc_logout(ic, FALSE);
-		return NULL;
-	} else if (req->status_code != 200) {
-		// It didn't go well, output the error and return.
-		if (!periodic || logging_in || ++td->http_fails >= 5) {
-			twitter_log(ic, "Error: Could not retrieve %s: %s",
-			            path, twitter_parse_error(req));
-		}
-
-		if (logging_in) {
-			imc_logout(ic, TRUE);
-		}
-		return NULL;
-	} else {
-		td->http_fails = 0;
-	}
-
-	if ((ret = json_parse(req->reply_body, req->body_size)) == NULL) {
-		imcb_error(ic, "Could not retrieve %s: %s",
-		           path, "JSON parse error");
-	}
-	return ret;
-}
-
-static void twitter_http_get_friends_ids(struct http_request *req);
-static void twitter_http_get_mutes_ids(struct http_request *req);
-static void twitter_http_get_noretweets_ids(struct http_request *req);
-
-/**
- * Get the friends ids.
- */
-void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	// Primitive, but hey! It works...
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Get the muted users ids.
- */
-void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_MUTES_IDS_URL, twitter_http_get_mutes_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Get the ids for users from whom we should ignore retweets.
- */
-void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_NORETWEETS_IDS_URL, twitter_http_get_noretweets_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Fill a list of ids.
- */
-static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl)
-{
-	json_value *c;
-	int i;
-
-	// Set the list type.
-	txl->type = TXL_ID;
-
-	c = json_o_get(node, "ids");
-	if (!c || c->type != json_array) {
-		return FALSE;
-	}
-
-	for (i = 0; i < c->u.array.length; i++) {
-		if (c->u.array.values[i]->type != json_integer) {
-			continue;
-		}
-
-		txl->list = g_slist_prepend(txl->list,
-		                            g_strdup_printf("%" PRId64, c->u.array.values[i]->u.integer));
-	}
-
-	c = json_o_get(node, "next_cursor");
-	if (c && c->type == json_integer) {
-		txl->next_cursor = c->u.integer;
-	} else {
-		txl->next_cursor = -1;
-	}
-
-	return TRUE;
-}
-
-static void twitter_get_users_lookup(struct im_connection *ic);
-
-/**
- * Callback for getting the friends ids.
- */
-static void twitter_http_get_friends_ids(struct http_request *req)
-{
-	struct im_connection *ic;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	ic = req->data;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->follow_ids;
-
-	twitter_xt_get_friends_id_list(parsed, txl);
-	json_value_free(parsed);
-
-	td->follow_ids = txl->list;
-	if (txl->next_cursor) {
-		/* These were just numbers. Up to 4000 in a response AFAIK so if we get here
-		   we may be using a spammer account. \o/ */
-		twitter_get_friends_ids(ic, txl->next_cursor);
-	} else {
-		/* Now to convert all those numbers into names.. */
-		twitter_get_users_lookup(ic);
-	}
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-/**
- * Callback for getting the mutes ids.
- */
-static void twitter_http_get_mutes_ids(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	// Check if the connection is stil active
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if (req->status_code != 200) {
-		/* Fail silently */
-		return;
-	}
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->mutes_ids;
-
-	/* mute ids API response is similar enough to friends response
-	   to reuse this method */
-	twitter_xt_get_friends_id_list(parsed, txl);
-	json_value_free(parsed);
-
-	td->mutes_ids = txl->list;
-	if (txl->next_cursor) {
-		/* Recurse while there are still more pages */
-		twitter_get_mutes_ids(ic, txl->next_cursor);
-	}
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-/**
- * Callback for getting the no-retweets ids.
- */
-static void twitter_http_get_noretweets_ids(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	// Check if the connection is stil active
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	if (req->status_code != 200) {
-		/* Fail silently */
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->noretweets_ids;
-
-	// Process the retweet ids
-	txl->type = TXL_ID;
-	if (parsed->type == json_array) {
-		unsigned int i;
-		for (i = 0; i < parsed->u.array.length; i++) {
-			json_value *c = parsed->u.array.values[i];
-			if (c->type != json_integer) {
-				continue;
-			}
-			txl->list = g_slist_prepend(txl->list,
-			                            g_strdup_printf("%" PRId64, c->u.integer));
-		}
-	}
-
-	json_value_free(parsed);
-	td->noretweets_ids = txl->list;
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl);
-static void twitter_http_get_users_lookup(struct http_request *req);
-
-static void twitter_get_users_lookup(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = {
-		"user_id",
-		NULL,
-	};
-	GString *ids = g_string_new("");
-	int i;
-
-	/* We can request up to 100 users at a time. */
-	for (i = 0; i < 100 && td->follow_ids; i++) {
-		g_string_append_printf(ids, ",%s", (char *) td->follow_ids->data);
-		g_free(td->follow_ids->data);
-		td->follow_ids = g_slist_remove(td->follow_ids, td->follow_ids->data);
-	}
-	if (ids->len > 0) {
-		args[1] = ids->str + 1;
-		/* POST, because I think ids can be up to 1KB long. */
-		twitter_http(ic, TWITTER_USERS_LOOKUP_URL, twitter_http_get_users_lookup, ic, 1, args, 2);
-	} else {
-		/* We have all users. Continue with login. (Get statuses.) */
-		td->flags |= TWITTER_HAVE_FRIENDS;
-		twitter_login_finish(ic);
-	}
-	g_string_free(ids, TRUE);
-}
-
-/**
- * Callback for getting (twitter)friends...
- *
- * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has
- * hundreds of friends?" you wonder? You probably not, since you are reading the source of
- * BitlBee... Get a life and meet new people!
- */
-static void twitter_http_get_users_lookup(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	GSList *l = NULL;
-	struct twitter_xml_user *user;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	// Get the user list from the parsed xml feed.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_users(parsed, txl);
-	json_value_free(parsed);
-
-	// Add the users as buddies.
-	for (l = txl->list; l; l = g_slist_next(l)) {
-		user = l->data;
-		twitter_add_buddy(ic, user->screen_name, user->name);
-	}
-
-	// Free the structure.
-	txl_free(txl);
-
-	twitter_get_users_lookup(ic);
-}
-
-struct twitter_xml_user *twitter_xt_get_user(const json_value *node)
-{
-	struct twitter_xml_user *txu;
-	json_value *jv;
-
-	txu = g_new0(struct twitter_xml_user, 1);
-	txu->name = g_strdup(json_o_str(node, "name"));
-	txu->screen_name = g_strdup(json_o_str(node, "screen_name"));
-
-	jv = json_o_get(node, "id");
-	txu->uid = jv->u.integer;
-
-	return txu;
-}
-
-/**
- * Function to fill a twitter_xml_list struct.
- * It sets:
- *  - all <user>s from the <users> element.
- */
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl)
-{
-	struct twitter_xml_user *txu;
-	int i;
-
-	// Set the type of the list.
-	txl->type = TXL_USER;
-
-	if (!node || node->type != json_array) {
-		return FALSE;
-	}
-
-	// The root <users> node should hold the list of users <user>
-	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txu = twitter_xt_get_user(node->u.array.values[i]);
-		if (txu) {
-			txl->list = g_slist_prepend(txl->list, txu);
-		}
-	}
-
-	return TRUE;
-}
-
-#ifdef __GLIBC__
-#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S %z %Y"
-#else
-#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y"
-#endif
-
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node);
-
-/**
- * Function to fill a twitter_xml_status struct.
- * It sets:
- *  - the status text and
- *  - the created_at timestamp and
- *  - the status id and
- *  - the user in a twitter_xml_user struct.
- */
-static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)
-{
-	struct twitter_xml_status *txs = {0};
-	const json_value *rt = NULL;
-	const json_value *text_value = NULL;
-	const json_value *extended_node = NULL;
-
-	if (node->type != json_object) {
-		return FALSE;
-	}
-	txs = g_new0(struct twitter_xml_status, 1);
-
-	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string && text_value == NULL) {
-			text_value = v;
-		} else if (strcmp("full_text", k) == 0 && v->type == json_string) {
-			text_value = v;
-		} else if (strcmp("extended_tweet", k) == 0 && v->type == json_object) {
-			text_value = json_o_get(v, "full_text");
-			extended_node = v;
-		} else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) {
-			rt = v;
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
-			struct tm parsed;
-
-			/* Very sensitive to changes to the formatting of
-			   this field. :-( Also assumes the timezone used
-			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
-				txs->created_at = mktime_utc(&parsed);
-			}
-		} else if (strcmp("user", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->rt_id = txs->id = v->u.integer;
-		} else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) {
-			txs->reply_to = v->u.integer;
-		}
-	}
-
-	/* If it's a (truncated) retweet, get the original. Even if the API claims it
-	   wasn't truncated because it may be lying. */
-	if (rt) {
-		struct twitter_xml_status *rtxs = twitter_xt_get_status(rt);
-		if (rtxs) {
-			txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
-			txs->id = rtxs->id;
-			txs_free(rtxs);
-		}
-	} else if (text_value && text_value->type == json_string) {
-		txs->text = g_memdup2(text_value->u.string.ptr, text_value->u.string.length + 1);
-		strip_html(txs->text);
-		expand_entities(&txs->text, node, extended_node);
-	}
-
-	if (txs->text && txs->user && txs->id) {
-		return txs;
-	}
-
-	txs_free(txs);
-	return NULL;
-}
-
-/**
- * Function to fill a twitter_xml_status struct (DM variant).
- */
-static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)
-{
-	struct twitter_xml_status *txs;
-
-	if (node->type != json_object) {
-		return FALSE;
-	}
-	txs = g_new0(struct twitter_xml_status, 1);
-
-	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string) {
-			txs->text = g_memdup2(v->u.string.ptr, v->u.string.length + 1);
-			strip_html(txs->text);
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
-			struct tm parsed;
-
-			/* Very sensitive to changes to the formatting of
-			   this field. :-( Also assumes the timezone used
-			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
-				txs->created_at = mktime_utc(&parsed);
-			}
-		} else if (strcmp("sender", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->id = v->u.integer;
-		}
-	}
-
-	expand_entities(&txs->text, node, NULL);
-
-	if (txs->text && txs->user && txs->id) {
-		return txs;
-	}
-
-	txs_free(txs);
-	return NULL;
-}
-
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node)
-{
-	json_value *entities, *extended_entities, *quoted, *quoted_permalink;
-	char *quote_url = NULL, *quote_text = NULL, *quote_kort = NULL;
-	gboolean quote_used = FALSE;
-
-	if (!((entities = json_o_get(node, "entities")) && entities->type == json_object))
-		return;
-	if ((quoted = json_o_get(node, "quoted_status")) && quoted->type == json_object) {
-		/* New "retweets with comments" feature. Grab the
-		 * full message and try to insert it when we run into the
-		 * Tweet entity. */
-		struct twitter_xml_status *txs = twitter_xt_get_status(quoted);
-		quote_text = g_strdup_printf("@%s: %s", txs->user->screen_name, txs->text);
-		quote_url = g_strdup_printf("%s/status/%" G_GUINT64_FORMAT, txs->user->screen_name, txs->id);
-		if ((quoted_permalink = json_o_get(node, "quoted_status_permalink")) && quoted->type == json_object) {
-			quote_kort = json_o_strdup(quoted_permalink, "url");
-		}
-		txs_free(txs);
-	} else {
-		quoted = NULL;
-	}
-
-	if (extended_node) {
-		extended_entities = json_o_get(extended_node, "entities");
-		if (extended_entities && extended_entities->type == json_object) {
-			entities = extended_entities;
-		}
-	}
-
-	JSON_O_FOREACH(entities, k, v) {
-		int i;
-
-		if (v->type != json_array) {
-			continue;
-		}
-		if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) {
-			continue;
-		}
-
-		for (i = 0; i < v->u.array.length; i++) {
-			const char *format = "%s%s <%s>%s";
-
-			if (v->u.array.values[i]->type != json_object) {
-				continue;
-			}
-
-			const char *kort = json_o_str(v->u.array.values[i], "url");
-			const char *disp = json_o_str(v->u.array.values[i], "display_url");
-			const char *full = json_o_str(v->u.array.values[i], "expanded_url");
-			char *pos, *new;
-
-			/* Skip if a required field is missing, if the t.co URL is not in fact
-			   in the Tweet at all, or if the full-ish one *is* in it already
-			   (dupes appear, especially in streaming API). */
-			if (!kort || !disp || !(pos = strstr(*text, kort)) || strstr(*text, disp)) {
-				continue;
-			}
-			if (quote_url && strstr(full, quote_url)) {
-				format = "%s<%s> [%s]%s";
-				disp = quote_text;
-				quote_used = TRUE;
-			}
-
-			*pos = '\0';
-			new = g_strdup_printf(format, *text, kort,
-			                      disp, pos + strlen(kort));
-
-			g_free(*text);
-			*text = new;
-		}
-	}
-
-	if (quote_text && !quote_used) {
-		const char *url = quote_kort ?: quote_url;
-		char *new = g_strdup_printf("%s <%s>[%s]", *text, url, quote_text);
-		g_free(*text);
-		*text = new;
-	}
-
-	g_free(quote_text);
-	g_free(quote_url);
-	g_free(quote_kort);
-}
-
-/**
- * Function to fill a twitter_xml_list struct.
- * It sets:
- *  - all <status>es within the <status> element and
- *  - the next_cursor.
- */
-static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node,
-                                           struct twitter_xml_list *txl)
-{
-	struct twitter_xml_status *txs;
-	int i;
-
-	// Set the type of the list.
-	txl->type = TXL_STATUS;
-
-	if (node->type != json_array) {
-		return FALSE;
-	}
-
-	// The root <statuses> node should hold the list of statuses <status>
-	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txs = twitter_xt_get_status(node->u.array.values[i]);
-		if (!txs) {
-			continue;
-		}
-
-		txl->list = g_slist_prepend(txl->list, txs);
-	}
-
-	return TRUE;
-}
-
-/* Will log messages either way. Need to keep track of IDs for stream deduping.
-   Plus, show_ids is on by default and I don't see why anyone would disable it. */
-static char *twitter_msg_add_id(struct im_connection *ic,
-                                struct twitter_xml_status *txs, const char *prefix)
-{
-	struct twitter_data *td = ic->proto_data;
-	int reply_to = -1;
-	bee_user_t *bu;
-
-	if (txs->reply_to) {
-		int i;
-		for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
-			if (td->log[i].id == txs->reply_to) {
-				reply_to = i;
-				break;
-			}
-		}
-	}
-
-	if (txs->user && txs->user->screen_name &&
-	    (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
-		struct twitter_user_data *tud = bu->data;
-
-		if (txs->id > tud->last_id) {
-			tud->last_id = txs->id;
-			tud->last_time = txs->created_at;
-		}
-	}
-
-	td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
-	td->log[td->log_id].id = txs->id;
-	td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
-
-	/* This is all getting hairy. :-( If we RT'ed something ourselves,
-	   remember OUR id instead so undo will work. In other cases, the
-	   original tweet's id should be remembered for deduplicating. */
-	if (g_strcasecmp(txs->user->screen_name, td->user) == 0) {
-		td->log[td->log_id].id = txs->rt_id;
-		/* More useful than NULL. */
-		td->log[td->log_id].bu = &twitter_log_local_user;
-	}
-
-	if (set_getbool(&ic->acc->set, "show_ids")) {
-		if (reply_to != -1) {
-			return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s",
-			                       td->log_id, reply_to, prefix, txs->text);
-		} else {
-			return g_strdup_printf("\002[\002%02x\002]\002 %s%s",
-			                       td->log_id, prefix, txs->text);
-		}
-	} else {
-		if (*prefix) {
-			return g_strconcat(prefix, txs->text, NULL);
-		} else {
-			return NULL;
-		}
-	}
-}
-
-/**
- * Function that is called to see the filter statuses in groupchat windows.
- */
-static void twitter_status_show_filter(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *msg = twitter_msg_add_id(ic, status, "");
-	struct twitter_filter *tf;
-	GSList *f;
-	GSList *l;
-
-	for (f = td->filters; f; f = g_slist_next(f)) {
-		tf = f->data;
-
-		switch (tf->type) {
-		case TWITTER_FILTER_TYPE_FOLLOW:
-			if (status->user->uid != tf->uid) {
-				continue;
-			}
-			break;
-
-		case TWITTER_FILTER_TYPE_TRACK:
-			if (strcasestr(status->text, tf->text) == NULL) {
-				continue;
-			}
-			break;
-
-		default:
-			continue;
-		}
-
-		for (l = tf->groupchats; l; l = g_slist_next(l)) {
-			imcb_chat_msg(l->data, status->user->screen_name,
-			              msg ? msg : status->text, 0, 0);
-		}
-	}
-
-	g_free(msg);
-}
-
-/**
- * Function that is called to see the statuses in a groupchat window.
- */
-static void twitter_status_show_chat(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct groupchat *gc;
-	gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0;
-	char *msg;
-
-	// Create a new groupchat if it does not exist.
-	gc = twitter_groupchat_init(ic);
-
-	if (!me) {
-		/* MUST be done before twitter_msg_add_id() to avoid #872. */
-		twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-	}
-	msg = twitter_msg_add_id(ic, status, "");
-
-	// Say it!
-	if (me) {
-		imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
-	} else {
-		imcb_chat_msg(gc, status->user->screen_name,
-		              msg ? msg : status->text, 0, status->created_at);
-	}
-
-	g_free(msg);
-}
-
-/**
- * Function that is called to see statuses as private messages.
- */
-static void twitter_status_show_msg(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char from[MAX_STRING] = "";
-	char *prefix = NULL, *text = NULL;
-	gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0;
-
-	if (td->flags & TWITTER_MODE_ONE) {
-		g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user);
-		from[MAX_STRING - 1] = '\0';
-	}
-
-	if (td->flags & TWITTER_MODE_ONE) {
-		prefix = g_strdup_printf("\002<\002%s\002>\002 ",
-		                         status->user->screen_name);
-	} else if (!me) {
-		twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-	} else {
-		prefix = g_strdup("You: ");
-	}
-
-	text = twitter_msg_add_id(ic, status, prefix ? prefix : "");
-
-	imcb_buddy_msg(ic,
-	               *from ? from : status->user->screen_name,
-	               text ? text : status->text, 0, status->created_at);
-
-	g_free(text);
-	g_free(prefix);
-}
-
-static void twitter_status_show(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *last_id_str;
-	char *uid_str;
-
-	if (status->user == NULL || status->text == NULL) {
-		return;
-	}
-
-	/* Check this is not a tweet that should be muted */
-	uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
-
-	if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
-		g_free(uid_str);
-		return;
-	}
-	if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) {
-		g_free(uid_str);
-		return;
-	}
-
-	/* Grrrr. Would like to do this during parsing, but can't access
-	   settings from there. */
-	if (set_getbool(&ic->acc->set, "strip_newlines")) {
-		strip_newlines(status->text);
-	}
-
-	if (status->from_filter) {
-		twitter_status_show_filter(ic, status);
-	} else if (td->flags & TWITTER_MODE_CHAT) {
-		twitter_status_show_chat(ic, status);
-	} else {
-		twitter_status_show_msg(ic, status);
-	}
-
-	// Update the timeline_id to hold the highest id, so that by the next request
-	// we won't pick up the updates already in the list.
-	td->timeline_id = MAX(td->timeline_id, status->rt_id);
-
-	last_id_str = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	set_setstr(&ic->acc->set, "_last_tweet", last_id_str);
-	g_free(last_id_str);
-	g_free(uid_str);
-}
-
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter);
-
-static void twitter_http_stream(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	int len = 0;
-	char c, *nl;
-	gboolean from_filter;
-
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if ((req->flags & HTTPC_EOF) || !req->reply_body) {
-		if (req == td->stream) {
-			td->stream = NULL;
-		} else if (req == td->filter_stream) {
-			td->filter_stream = NULL;
-		}
-
-		imcb_error(ic, "Stream closed (%s)", req->status_string);
-		if (req->status_code == 401) {
-			imcb_error(ic, "Check your system clock.");
-		}
-		imc_logout(ic, TRUE);
-		return;
-	}
-
-	if (req == td->stream) {
-		ic->flags |= OPT_PONGED;
-	}
-
-	/* MUST search for CRLF, not just LF:
-	   https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */
-	if (!(nl = strstr(req->reply_body, "\r\n"))) {
-		return;
-	}
-
-	len = nl - req->reply_body;
-	if (len > 0) {
-		c = req->reply_body[len];
-		req->reply_body[len] = '\0';
-
-		if ((parsed = json_parse(req->reply_body, req->body_size))) {
-			from_filter = (req == td->filter_stream);
-			twitter_stream_handle_object(ic, parsed, from_filter);
-		}
-		json_value_free(parsed);
-		req->reply_body[len] = c;
-	}
-
-	http_flush_bytes(req, len + 2);
-
-	/* One notification might bring multiple events! */
-	if (req->body_size > 0) {
-		twitter_http_stream(req);
-	}
-}
-
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o);
-static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs);
-
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct twitter_xml_status *txs;
-	json_value *c;
-
-	if ((txs = twitter_xt_get_status(o))) {
-		txs->from_filter = from_filter;
-		gboolean ret = twitter_stream_handle_status(ic, txs);
-		txs_free(txs);
-		return ret;
-	} else if ((c = json_o_get(o, "direct_message")) &&
-	           (txs = twitter_xt_get_dm(c))) {
-		if (g_strcasecmp(txs->user->screen_name, td->user) != 0) {
-			imcb_buddy_msg(ic, txs->user->screen_name,
-			               txs->text, 0, txs->created_at);
-		}
-		txs_free(txs);
-		return TRUE;
-	} else if ((c = json_o_get(o, "event")) && c->type == json_string) {
-		twitter_stream_handle_event(ic, o);
-		return TRUE;
-	} else if ((c = json_o_get(o, "disconnect")) && c->type == json_object) {
-		/* HACK: Because we're inside an event handler, we can't just
-		   disconnect here. Instead, just change the HTTP status string
-		   into a Twitter status string. */
-		char *reason = json_o_strdup(c, "reason");
-		if (reason) {
-			g_free(td->stream->status_string);
-			td->stream->status_string = reason;
-		}
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs)
-{
-	struct twitter_data *td = ic->proto_data;
-	int i;
-
-	for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
-		if (td->log[i].id == txs->id) {
-			/* Got a duplicate (RT, probably). Drop it. */
-			return TRUE;
-		}
-	}
-
-	if (!(g_strcasecmp(txs->user->screen_name, td->user) == 0 ||
-	      set_getbool(&ic->acc->set, "fetch_mentions") ||
-	      bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
-		/* Tweet is from an unknown person and the user does not want
-		   to see @mentions, so drop it. twitter_stream_handle_event()
-		   picks up new follows so this simple filter should be safe. */
-		/* TODO: The streaming API seems to do poor @mention matching.
-		   I.e. I'm getting mentions for @WilmerSomething, not just for
-		   @Wilmer. But meh. You want spam, you get spam. */
-		return TRUE;
-	}
-
-	twitter_status_show(ic, txs);
-
-	return TRUE;
-}
-
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o)
-{
-	struct twitter_data *td = ic->proto_data;
-	json_value *source = json_o_get(o, "source");
-	json_value *target = json_o_get(o, "target");
-	const char *type = json_o_str(o, "event");
-	struct twitter_xml_user *us = NULL;
-	struct twitter_xml_user *ut = NULL;
-
-	if (!type || !source || source->type != json_object
-	    || !target || target->type != json_object) {
-		return FALSE;
-	}
-
-	if (strcmp(type, "follow") == 0) {
-		us = twitter_xt_get_user(source);
-		ut = twitter_xt_get_user(target);
-		if (g_strcasecmp(us->screen_name, td->user) == 0) {
-			twitter_add_buddy(ic, ut->screen_name, ut->name);
-		}
-	} else if (strcmp(type, "mute") == 0) {
-		GSList *found;
-		char *uid_str;
-		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
-		if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
-		                                  (GCompareFunc)strcmp))) {
-			td->mutes_ids = g_slist_prepend(td->mutes_ids, uid_str);
-		}
-		twitter_log(ic, "Muted user %s", ut->screen_name);
-		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
-			        ut->screen_name, ut->uid);
-		}
-	} else if (strcmp(type, "unmute") == 0) {
-		GSList *found;
-		char *uid_str;
-		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
-		if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
-		                                (GCompareFunc)strcmp))) {
-			char *found_str = found->data;
-			td->mutes_ids = g_slist_delete_link(td->mutes_ids, found);
-			g_free(found_str);
-		}
-		g_free(uid_str);
-		twitter_log(ic, "Unmuted user %s", ut->screen_name);
-		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
-			        ut->screen_name, ut->uid);
-		}
-	}
-
-	txu_free(us);
-	txu_free(ut);
-
-	return TRUE;
-}
-
-gboolean twitter_open_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = { "with", "followings" };
-
-	if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL,
-	                               twitter_http_stream, ic, 0, args, 2))) {
-		/* This flag must be enabled or we'll get no data until EOF
-		   (which err, kind of, defeats the purpose of a streaming API). */
-		td->stream->flags |= HTTPC_STREAMING;
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean twitter_filter_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[4] = { "follow", NULL, "track", NULL };
-	GString *followstr = g_string_new("");
-	GString *trackstr = g_string_new("");
-	gboolean ret = FALSE;
-	struct twitter_filter *tf;
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		switch (tf->type) {
-		case TWITTER_FILTER_TYPE_FOLLOW:
-			if (followstr->len > 0) {
-				g_string_append_c(followstr, ',');
-			}
-
-			g_string_append_printf(followstr, "%" G_GUINT64_FORMAT,
-			                       tf->uid);
-			break;
-
-		case TWITTER_FILTER_TYPE_TRACK:
-			if (trackstr->len > 0) {
-				g_string_append_c(trackstr, ',');
-			}
-
-			g_string_append(trackstr, tf->text);
-			break;
-
-		default:
-			continue;
-		}
-	}
-
-	args[1] = followstr->str;
-	args[3] = trackstr->str;
-
-	if (td->filter_stream) {
-		http_close(td->filter_stream);
-	}
-
-	if ((td->filter_stream = twitter_http(ic, TWITTER_FILTER_STREAM_URL,
-	                                      twitter_http_stream, ic, 0,
-	                                      args, 4))) {
-		/* This flag must be enabled or we'll get no data until EOF
-		   (which err, kind of, defeats the purpose of a streaming API). */
-		td->filter_stream->flags |= HTTPC_STREAMING;
-		ret = TRUE;
-	}
-
-	g_string_free(followstr, TRUE);
-	g_string_free(trackstr, TRUE);
-
-	return ret;
-}
-
-static void twitter_filter_users_post(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	struct twitter_filter *tf;
-	GList *users = NULL;
-	json_value *parsed;
-	json_value *id;
-	const char *name;
-	GString *fstr;
-	GSList *l;
-	GList *u;
-	int i;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {
-			users = g_list_prepend(users, tf);
-		}
-	}
-
-	if (parsed->type != json_array) {
-		goto finish;
-	}
-
-	for (i = 0; i < parsed->u.array.length; i++) {
-		id = json_o_get(parsed->u.array.values[i], "id");
-		name = json_o_str(parsed->u.array.values[i], "screen_name");
-
-		if (!name || !id || id->type != json_integer) {
-			continue;
-		}
-
-		for (u = users; u; u = g_list_next(u)) {
-			tf = u->data;
-
-			if (g_strcasecmp(tf->text, name) == 0) {
-				tf->uid = id->u.integer;
-				users = g_list_delete_link(users, u);
-				break;
-			}
-		}
-	}
-
-finish:
-	json_value_free(parsed);
-	twitter_filter_stream(ic);
-
-	if (!users) {
-		return;
-	}
-
-	fstr = g_string_new("");
-
-	for (u = users; u; u = g_list_next(u)) {
-		if (fstr->len > 0) {
-			g_string_append(fstr, ", ");
-		}
-
-		g_string_append(fstr, tf->text);
-	}
-
-	imcb_error(ic, "Failed UID acquisitions: %s", fstr->str);
-
-	g_string_free(fstr, TRUE);
-	g_list_free(users);
-}
-
-gboolean twitter_open_filter_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = { "screen_name", NULL };
-	GString *ustr = g_string_new("");
-	struct twitter_filter *tf;
-	struct http_request *req;
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) {
-			continue;
-		}
-
-		if (ustr->len > 0) {
-			g_string_append_c(ustr, ',');
-		}
-
-		g_string_append(ustr, tf->text);
-	}
-
-	if (ustr->len == 0) {
-		g_string_free(ustr, TRUE);
-		return twitter_filter_stream(ic);
-	}
-
-	args[1] = ustr->str;
-	req = twitter_http(ic, TWITTER_USERS_LOOKUP_URL,
-	                   twitter_filter_users_post,
-	                   ic, 0, args, 2);
-
-	g_string_free(ustr, TRUE);
-	return req != NULL;
-}
-
-static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);
-static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);
-
-/**
- * Get the timeline with optionally mentions
- */
-gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
-
-	if (td->flags & TWITTER_DOING_TIMELINE) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Fetch timeout (%d)", td->flags);
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-	}
-
-	td->flags |= TWITTER_DOING_TIMELINE;
-
-	twitter_get_home_timeline(ic, next_cursor);
-
-	if (include_mentions) {
-		twitter_get_mentions(ic, next_cursor);
-	}
-
-	return TRUE;
-}
-
-/**
- * Call this one after receiving timeline/mentions. Show to user once we have
- * both.
- */
-void twitter_flush_timeline(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
-	int show_old_mentions = set_getint(&ic->acc->set, "show_old_mentions");
-	struct twitter_xml_list *home_timeline = td->home_timeline_obj;
-	struct twitter_xml_list *mentions = td->mentions_obj;
-	guint64 last_id = 0;
-	GSList *output = NULL;
-	GSList *l;
-
-	imcb_connected(ic);
-
-	if (!(td->flags & TWITTER_GOT_TIMELINE)) {
-		return;
-	}
-
-	if (include_mentions && !(td->flags & TWITTER_GOT_MENTIONS)) {
-		return;
-	}
-
-	if (home_timeline && home_timeline->list) {
-		for (l = home_timeline->list; l; l = g_slist_next(l)) {
-			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
-		}
-	}
-
-	if (include_mentions && mentions && mentions->list) {
-		for (l = mentions->list; l; l = g_slist_next(l)) {
-			if (show_old_mentions < 1 && output && twitter_compare_elements(l->data, output->data) < 0) {
-				continue;
-			}
-
-			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
-		}
-	}
-
-	// See if the user wants to see the messages in a groupchat window or as private messages.
-	while (output) {
-		struct twitter_xml_status *txs = output->data;
-		if (txs->id != last_id) {
-			twitter_status_show(ic, txs);
-		}
-		last_id = txs->id;
-		output = g_slist_remove(output, txs);
-	}
-
-	txl_free(home_timeline);
-	txl_free(mentions);
-
-	td->flags &= ~(TWITTER_DOING_TIMELINE | TWITTER_GOT_TIMELINE | TWITTER_GOT_MENTIONS);
-	td->home_timeline_obj = td->mentions_obj = NULL;
-}
-
-static void twitter_http_get_home_timeline(struct http_request *req);
-static void twitter_http_get_mentions(struct http_request *req);
-
-/**
- * Get the timeline.
- */
-static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	txl_free(td->home_timeline_obj);
-	td->home_timeline_obj = NULL;
-	td->flags &= ~TWITTER_GOT_TIMELINE;
-
-	char *args[8];
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	args[2] = "include_entities";
-	args[3] = "true";
-	args[4] = "tweet_mode";
-	args[5] = "extended";
-	if (td->timeline_id) {
-		args[6] = "since_id";
-		args[7] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	}
-
-	if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
-	                 td->timeline_id ? 8 : 6) == NULL) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Could not retrieve %s: %s",
-			           TWITTER_HOME_TIMELINE_URL, "connection failed");
-		}
-		td->flags |= TWITTER_GOT_TIMELINE;
-		twitter_flush_timeline(ic);
-	}
-
-	g_free(args[1]);
-	if (td->timeline_id) {
-		g_free(args[7]);
-	}
-}
-
-/**
- * Get mentions.
- */
-static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	txl_free(td->mentions_obj);
-	td->mentions_obj = NULL;
-	td->flags &= ~TWITTER_GOT_MENTIONS;
-
-	char *args[8];
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	args[2] = "include_entities";
-	args[3] = "true";
-	if (td->timeline_id) {
-		args[4] = "since_id";
-		args[5] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	} else {
-		args[4] = "count";
-		args[5] = g_strdup_printf("%d", set_getint(&ic->acc->set, "show_old_mentions"));
-	}
-	args[6] = "tweet_mode";
-	args[7] = "extended";
-
-	if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions,
-	                 ic, 0, args, 8) == NULL) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Could not retrieve %s: %s",
-			           TWITTER_MENTIONS_URL, "connection failed");
-		}
-		td->flags |= TWITTER_GOT_MENTIONS;
-		twitter_flush_timeline(ic);
-	}
-
-	g_free(args[1]);
-	g_free(args[5]);
-}
-
-/**
- * Callback for getting the home timeline.
- */
-static void twitter_http_get_home_timeline(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// The root <statuses> node should hold the list of statuses <status>
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		goto end;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_status_list(ic, parsed, txl);
-	json_value_free(parsed);
-
-	td->home_timeline_obj = txl;
-
-end:
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td->flags |= TWITTER_GOT_TIMELINE;
-
-	twitter_flush_timeline(ic);
-}
-
-/**
- * Callback for getting mentions.
- */
-static void twitter_http_get_mentions(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// The root <statuses> node should hold the list of statuses <status>
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		goto end;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_status_list(ic, parsed, txl);
-	json_value_free(parsed);
-
-	td->mentions_obj = txl;
-
-end:
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td->flags |= TWITTER_GOT_MENTIONS;
-
-	twitter_flush_timeline(ic);
-}
-
-/**
- * Callback to use after sending a POST request to twitter.
- * (Generic, used for a few kinds of queries.)
- */
-static void twitter_http_post(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed, *id;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-	td->last_status_id = 0;
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	if ((id = json_o_get(parsed, "id")) && id->type == json_integer) {
-		td->last_status_id = id->u.integer;
-	}
-
-	json_value_free(parsed);
-
-	if (req->flags & TWITTER_HTTP_USER_ACK) {
-		twitter_log(ic, "Command processed successfully");
-	}
-}
-
-/**
- * Function to POST a new status to twitter.
- */
-void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to, gboolean auto_populate_reply_metadata)
-{
-	size_t args_len = 0;
-	char * in_reply_to_str = NULL;
-	char * place_id_str = NULL;
-	gboolean in_korea = (set_getbool(&ic->acc->set, "in_korea") && !in_reply_to);
-
-	args_len = 2; /* "status", msg */
-	if (in_reply_to) {
-		args_len += 2;
-	}
-	if (auto_populate_reply_metadata) {
-		args_len += 2;
-	}
-	if (in_korea) {
-		args_len += 2;
-	}
-
-	char **args = g_new0(char *, args_len);
-	args_len = 0;
-	args[args_len++] = "status";
-	args[args_len++] = msg;
-	if (in_reply_to) {
-		in_reply_to_str = g_strdup_printf("%" G_GUINT64_FORMAT, in_reply_to);
-		args[args_len++] = "in_reply_to_status_id";
-		args[args_len++] = in_reply_to_str;
-	}
-	if (auto_populate_reply_metadata) {
-		args[args_len++] = "auto_populate_reply_metadata";
-		args[args_len++] = "true";
-	}
-	if (in_korea) {
-		place_id_str = g_strdup("c999e6a453e9ef72");
-		args[args_len++] = "place_id";
-		args[args_len++] = place_id_str;
-		in_reply_to = 1;
-	}
-
-	twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
-	             args, args_len);
-	if (in_reply_to_str) {
-		g_free(in_reply_to_str);
-	}
-	if (place_id_str) {
-		g_free(place_id_str);
-	}
-	g_free(args);
-}
-
-
-/**
- * Function to POST a new message to twitter.
- */
-void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
-{
-	char *args[4];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	args[2] = "text";
-	args[3] = msg;
-	// Use the same callback as for twitter_post_status, since it does basically the same.
-	twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
-}
-
-void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
-{
-	char *args[2];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL,
-	             twitter_http_post, ic, 1, args, 2);
-}
-
-/**
- * Mute or unmute a user
- */
-void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create)
-{
-	char *args[2];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	twitter_http(ic, create ? TWITTER_MUTES_CREATE_URL : TWITTER_MUTES_DESTROY_URL,
-		     twitter_http_post, ic, 1, args, 2);
-}
-
-void twitter_status_destroy(struct im_connection *ic, guint64 id)
-{
-	char *url;
-
-	url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s",
-	                      TWITTER_STATUS_DESTROY_URL, id, ".json");
-	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0,
-	               TWITTER_HTTP_USER_ACK);
-	g_free(url);
-}
-
-void twitter_status_retweet(struct im_connection *ic, guint64 id)
-{
-	char *url;
-
-	url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s",
-	                      TWITTER_STATUS_RETWEET_URL, id, ".json");
-	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0,
-	               TWITTER_HTTP_USER_ACK);
-	g_free(url);
-}
-
-/**
- * Report a user for sending spam.
- */
-void twitter_report_spam(struct im_connection *ic, char *screen_name)
-{
-	char *args[2] = {
-		"screen_name",
-		NULL,
-	};
-
-	args[1] = screen_name;
-	twitter_http_f(ic, TWITTER_REPORT_SPAM_URL, twitter_http_post,
-	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK);
-}
-
-/**
- * Favourite a tweet.
- */
-void twitter_favourite_tweet(struct im_connection *ic, guint64 id)
-{
-	char *args[2] = {
-		"id",
-		NULL,
-	};
-
-	args[1] = g_strdup_printf("%" G_GUINT64_FORMAT, id);
-	twitter_http_f(ic, TWITTER_FAVORITE_CREATE_URL, twitter_http_post,
-	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK);
-	g_free(args[1]);
-}
-
-static void twitter_http_status_show_url(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed, *id;
-	const char *name;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	/* for the parson branch:
-	name = json_object_dotget_string(json_object(parsed), "user.screen_name");
-	id = json_object_get_integer(json_object(parsed), "id");
-	*/
-
-	name = json_o_str(json_o_get(parsed, "user"), "screen_name");
-	id = json_o_get(parsed, "id");
-
-	if (name && id && id->type == json_integer) {
-		twitter_log(ic, "https://twitter.com/%s/status/%" G_GUINT64_FORMAT, name, id->u.integer);
-	} else {
-		twitter_log(ic, "Error: could not fetch tweet url.");
-	}
-
-	json_value_free(parsed);
-}
-
-void twitter_status_show_url(struct im_connection *ic, guint64 id)
-{
-	char *url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s", TWITTER_STATUS_SHOW_URL, id, ".json");
-	twitter_http(ic, url, twitter_http_status_show_url, ic, 0, NULL, 0);
-	g_free(url);
-}
Index: protocols/twitter/twitter_lib.h
===================================================================
--- protocols/twitter/twitter_lib.h	(revision a3ca3ddb8b3ba10b1a88854f1ab73341d15abc6b)
+++ 	(revision )
@@ -1,109 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  This library is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
-*  Lesser General Public License for more details.                          *
-*                                                                           *
-*  You should have received a copy of the GNU Lesser General Public License *
-*  along with this library; if not, write to the Free Software Foundation,  *
-*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-
-#ifndef _TWITTER_LIB_H
-#define _TWITTER_LIB_H
-
-#include "nogaim.h"
-#include "twitter_http.h"
-
-#define TWITTER_API_URL "https://api.twitter.com/1.1"
-
-/* Status URLs */
-#define TWITTER_STATUS_UPDATE_URL "/statuses/update.json"
-#define TWITTER_STATUS_SHOW_URL "/statuses/show/"
-#define TWITTER_STATUS_DESTROY_URL "/statuses/destroy/"
-#define TWITTER_STATUS_RETWEET_URL "/statuses/retweet/"
-
-/* Timeline URLs */
-#define TWITTER_PUBLIC_TIMELINE_URL "/statuses/public_timeline.json"
-#define TWITTER_FEATURED_USERS_URL "/statuses/featured.json"
-#define TWITTER_FRIENDS_TIMELINE_URL "/statuses/friends_timeline.json"
-#define TWITTER_HOME_TIMELINE_URL "/statuses/home_timeline.json"
-#define TWITTER_MENTIONS_URL "/statuses/mentions_timeline.json"
-#define TWITTER_USER_TIMELINE_URL "/statuses/user_timeline.json"
-
-/* Users URLs */
-#define TWITTER_USERS_LOOKUP_URL "/users/lookup.json"
-
-/* Direct messages URLs */
-#define TWITTER_DIRECT_MESSAGES_URL "/direct_messages.json"
-#define TWITTER_DIRECT_MESSAGES_NEW_URL "/direct_messages/new.json"
-#define TWITTER_DIRECT_MESSAGES_SENT_URL "/direct_messages/sent.json"
-#define TWITTER_DIRECT_MESSAGES_DESTROY_URL "/direct_messages/destroy/"
-
-/* Friendships URLs */
-#define TWITTER_FRIENDSHIPS_CREATE_URL "/friendships/create.json"
-#define TWITTER_FRIENDSHIPS_DESTROY_URL "/friendships/destroy.json"
-#define TWITTER_FRIENDSHIPS_SHOW_URL "/friendships/show.json"
-
-/* Social graphs URLs */
-#define TWITTER_FRIENDS_IDS_URL "/friends/ids.json"
-#define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json"
-#define TWITTER_MUTES_IDS_URL "/mutes/users/ids.json"
-#define TWITTER_NORETWEETS_IDS_URL "/friendships/no_retweets/ids.json"
-
-/* Account URLs */
-#define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status.json"
-
-/* Favorites URLs */
-#define TWITTER_FAVORITES_GET_URL "/favorites.json"
-#define TWITTER_FAVORITE_CREATE_URL "/favorites/create.json"
-#define TWITTER_FAVORITE_DESTROY_URL "/favorites/destroy.json"
-
-/* Block URLs */
-#define TWITTER_BLOCKS_CREATE_URL "/blocks/create/"
-#define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/"
-
-/* Mute URLs */
-#define TWITTER_MUTES_CREATE_URL "/mutes/users/create.json"
-#define TWITTER_MUTES_DESTROY_URL "/mutes/users/destroy.json"
-
-/* Report spam */
-#define TWITTER_REPORT_SPAM_URL "/users/report_spam.json"
-
-/* Stream URLs */
-#define TWITTER_USER_STREAM_URL "https://userstream.twitter.com/1.1/user.json"
-#define TWITTER_FILTER_STREAM_URL "https://stream.twitter.com/1.1/statuses/filter.json"
-
-gboolean twitter_open_stream(struct im_connection *ic);
-gboolean twitter_open_filter_stream(struct im_connection *ic);
-gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);
-
-void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to, gboolean auto_populate_reply_metadata);
-void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);
-void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create);
-void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create);
-void twitter_status_destroy(struct im_connection *ic, guint64 id);
-void twitter_status_retweet(struct im_connection *ic, guint64 id);
-void twitter_report_spam(struct im_connection *ic, char *screen_name);
-void twitter_favourite_tweet(struct im_connection *ic, guint64 id);
-void twitter_status_show_url(struct im_connection *ic, guint64 id);
-
-#endif //_TWITTER_LIB_H
-
Index: protocols/util.c
===================================================================
--- protocols/util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,415 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/*
+ * nogaim
+ *
+ * Gaim without gaim - for BitlBee
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *                          (and possibly other members of the Gaim team)
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
+ */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+/* Parts from util.c from gaim needed by nogaim */
+#define BITLBEE_CORE
+#include "nogaim.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <time.h>
+
+char *utf8_to_str(const char *in)
+{
+	int n = 0, i = 0;
+	int inlen;
+	char *result;
+
+	if (!in)
+		return NULL;
+
+	inlen = strlen(in);
+
+	result = g_malloc(inlen + 1);
+
+	while (n <= inlen - 1) {
+		long c = (long)in[n];
+		if (c < 0x80)
+			result[i++] = (char)c;
+		else {
+			if ((c & 0xC0) == 0xC0)
+				result[i++] =
+				    (char)(((c & 0x03) << 6) | (((unsigned char)in[++n]) & 0x3F));
+			else if ((c & 0xE0) == 0xE0) {
+				if (n + 2 <= inlen) {
+					result[i] =
+					    (char)(((c & 0xF) << 4) | (((unsigned char)in[++n]) & 0x3F));
+					result[i] =
+					    (char)(((unsigned char)result[i]) |
+						   (((unsigned char)in[++n]) & 0x3F));
+					i++;
+				} else
+					n += 2;
+			} else if ((c & 0xF0) == 0xF0)
+				n += 3;
+			else if ((c & 0xF8) == 0xF8)
+				n += 4;
+			else if ((c & 0xFC) == 0xFC)
+				n += 5;
+		}
+		n++;
+	}
+	result[i] = '\0';
+
+	return result;
+}
+
+char *str_to_utf8(const char *in)
+{
+	int n = 0, i = 0;
+	int inlen;
+	char *result = NULL;
+
+	if (!in)
+		return NULL;
+
+	inlen = strlen(in);
+
+	result = g_malloc(inlen * 2 + 1);
+
+	while (n < inlen) {
+		long c = (long)in[n];
+		if (c == 27) {
+			n += 2;
+			if (in[n] == 'x')
+				n++;
+			if (in[n] == '3')
+				n++;
+			n += 2;
+			continue;
+		}
+		/* why are we removing newlines and carriage returns?
+		if ((c == 0x0D) || (c == 0x0A)) {
+			n++;
+			continue;
+		}
+		*/
+		if (c < 128)
+			result[i++] = (char)c;
+		else {
+			result[i++] = (char)((c >> 6) | 192);
+			result[i++] = (char)((c & 63) | 128);
+		}
+		n++;
+	}
+	result[i] = '\0';
+
+	return result;
+}
+
+void strip_linefeed(gchar *text)
+{
+	int i, j;
+	gchar *text2 = g_malloc(strlen(text) + 1);
+
+	for (i = 0, j = 0; text[i]; i++)
+		if (text[i] != '\r')
+			text2[j++] = text[i];
+	text2[j] = '\0';
+
+	strcpy(text, text2);
+	g_free(text2);
+}
+
+char *add_cr(char *text)
+{
+	char *ret = NULL;
+	int count = 0, j;
+	unsigned int i;
+
+	if (text[0] == '\n')
+		count++;
+	for (i = 1; i < strlen(text); i++)
+		if (text[i] == '\n' && text[i - 1] != '\r')
+			count++;
+
+	if (count == 0)
+		return g_strdup(text);
+
+	ret = g_malloc0(strlen(text) + count + 1);
+
+	i = 0; j = 0;
+	if (text[i] == '\n')
+		ret[j++] = '\r';
+	ret[j++] = text[i++];
+	for (; i < strlen(text); i++) {
+		if (text[i] == '\n' && text[i - 1] != '\r')
+			ret[j++] = '\r';
+		ret[j++] = text[i];
+	}
+
+	return ret;
+}
+
+static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/";
+
+/* XXX Find bug */
+char *tobase64(const char *text)
+{
+	char *out = NULL;
+	const char *c;
+	unsigned int tmp = 0;
+	int len = 0, n = 0;
+
+	c = text;
+
+	while (*c) {
+		tmp = tmp << 8;
+		tmp += *c;
+		n++;
+
+		if (n == 3) {
+			out = g_realloc(out, len + 4);
+			out[len] = alphabet[(tmp >> 18) & 0x3f];
+			out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+			out[len + 2] = alphabet[(tmp >> 6) & 0x3f];
+			out[len + 3] = alphabet[tmp & 0x3f];
+			len += 4;
+			tmp = 0;
+			n = 0;
+		}
+		c++;
+	}
+	switch (n) {
+
+	case 2:
+		tmp <<= 8;
+		out = g_realloc(out, len + 5);
+		out[len] = alphabet[(tmp >> 18) & 0x3f];
+		out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+		out[len + 2] = alphabet[(tmp >> 6) & 0x3f];
+		out[len + 3] = '=';
+		out[len + 4] = 0;
+		break;
+	case 1:
+		tmp <<= 16;
+		out = g_realloc(out, len + 5);
+		out[len] = alphabet[(tmp >> 18) & 0x3f];
+		out[len + 1] = alphabet[(tmp >> 12) & 0x3f];
+		out[len + 2] = '=';
+		out[len + 3] = '=';
+		out[len + 4] = 0;
+		break;
+	case 0:
+		out = g_realloc(out, len + 1);
+		out[len] = 0;
+		break;
+	}
+	return out;
+}
+
+char *normalize(const char *s)
+{
+	static char buf[BUF_LEN];
+	char *t, *u;
+	int x = 0;
+
+	g_return_val_if_fail((s != NULL), NULL);
+
+	u = t = g_strdup(s);
+
+	strcpy(t, s);
+	g_strdown(t);
+
+	while (*t && (x < BUF_LEN - 1)) {
+		if (*t != ' ') {
+			buf[x] = *t;
+			x++;
+		}
+		t++;
+	}
+	buf[x] = '\0';
+	g_free(u);
+	return buf;
+}
+
+time_t get_time(int year, int month, int day, int hour, int min, int sec)
+{
+	struct tm tm;
+
+	tm.tm_year = year - 1900;
+	tm.tm_mon = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min = min;
+	tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
+	return mktime(&tm);
+}
+
+typedef struct htmlentity
+{
+	char code[8];
+	char is;
+} htmlentity_t;
+
+/* FIXME: This is ISO8859-1(5) centric, so might cause problems with other charsets. */
+
+static htmlentity_t ent[] =
+{
+	{ "lt",     '<' },
+	{ "gt",     '>' },
+	{ "amp",    '&' },
+	{ "quot",   '"' },
+	{ "aacute", 'á' },
+	{ "eacute", 'é' },
+	{ "iacute", 'é' },
+	{ "oacute", 'ó' },
+	{ "uacute", 'ú' },
+	{ "agrave", 'à' },
+	{ "egrave", 'è' },
+	{ "igrave", 'ì' },
+	{ "ograve", 'ò' },
+	{ "ugrave", 'ù' },
+	{ "acirc",  'â' },
+	{ "ecirc",  'ê' },
+	{ "icirc",  'î' },
+	{ "ocirc",  'ô' },
+	{ "ucirc",  'û' },
+	{ "nbsp",   ' ' },
+	{ "",        0  }
+};
+
+void strip_html( char *in )
+{
+	char *start = in;
+	char *out = g_malloc( strlen( in ) + 1 );
+	char *s = out, *cs;
+	int i, matched;
+	
+	memset( out, 0, strlen( in ) + 1 );
+	
+	while( *in )
+	{
+		if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
+		{
+			/* If in points at a < and in+1 points at a letter or a slash, this is probably
+			   a HTML-tag. Try to find a closing > and continue there. If the > can't be
+			   found, assume that it wasn't a HTML-tag after all. */
+			
+			cs = in;
+			
+			while( *in && *in != '>' )
+				in ++;
+			
+			if( *in )
+			{
+				if( g_strncasecmp( cs+1, "br", 2) == 0 )
+					*(s++) = '\n';
+				in ++;
+			}
+			else
+			{
+				in = cs;
+				*(s++) = *(in++);
+			}
+		}
+		else if( *in == '&' )
+		{
+			cs = ++in;
+			while( *in && isalpha( *in ) )
+				in ++;
+			
+			if( *in == ';' ) in ++;
+			matched = 0;
+			
+			for( i = 0; *ent[i].code; i ++ )
+				if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
+				{
+					*(s++) = ent[i].is;
+					matched = 1;
+					break;
+				}
+
+			/* None of the entities were matched, so return the string */
+			if( !matched )
+			{
+				in = cs - 1;
+				*(s++) = *(in++);
+			}
+		}
+		else
+		{
+			*(s++) = *(in++);
+		}
+	}
+	
+	strcpy( start, out );
+	g_free( out );
+}
+
+char *escape_html( const char *html )
+{
+	const char *c = html;
+	GString *ret;
+	char *str;
+	
+	if( html == NULL )
+		return( NULL );
+	if( g_strncasecmp( html, "<html>", 6 ) == 0 )
+		return( g_strdup( html ) );
+	
+	ret = g_string_new( "" );
+	
+	while( *c )
+	{
+		switch( *c )
+		{
+			case '&':
+				ret = g_string_append( ret, "&amp;" );
+				break;
+			case '<':
+				ret = g_string_append( ret, "&lt;" );
+				break;
+			case '>':
+				ret = g_string_append( ret, "&gt;" );
+				break;
+			case '"':
+				ret = g_string_append( ret, "&quot;" );
+				break;
+			default:
+				ret = g_string_append_c( ret, *c );
+		}
+		c ++;
+	}
+	
+	str = ret->str;
+	g_string_free( ret, FALSE );
+	return( str );
+}
+
+void info_string_append(GString *str, char *newline, char *name, char *value)
+{
+	if( value && value[0] )
+		g_string_sprintfa( str, "%s%s: %s", newline, name, value );
+}
Index: protocols/yahoo/Makefile
===================================================================
--- protocols/yahoo/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee  ##
+##                       ##
+## Copyright 2002 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../../Makefile.settings
+
+# [SH] Program variables
+objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_list.o yahoo_util.o
+
+CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB
+LFLAGS += -r
+
+# [SH] Phony targets
+all: yahooo.o
+
+.PHONY: all clean distclean
+
+clean:
+	rm -f *.o core
+
+distclean: clean
+
+### MAIN PROGRAM
+
+$(objects): ../../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+	@echo '*' Compiling $<
+	@$(CC) -c $(CFLAGS) $< -o $@
+
+yahooo.o: $(objects)
+	@echo '*' Linking yahooo.o
+	@$(LD) $(LFLAGS) $(objects) -o yahooo.o
Index: protocols/yahoo/crypt.c
===================================================================
--- protocols/yahoo/crypt.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/crypt.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,207 @@
+/* One way encryption based on MD5 sum.
+   Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+/* warmenhoven took this file and made it work with the md5.[ch] we
+ * already had. isn't that lovely. people should just use linux or
+ * freebsd, crypt works properly on those systems. i hate solaris */
+
+#if HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#if HAVE_STRING_H
+#  include <string.h>
+#elif HAVE_STRINGS_H
+#  include <strings.h>
+#endif
+
+#include <stdlib.h>
+#include "yahoo_util.h"
+
+#include "md5.h"
+
+/* Define our magic string to mark salt for MD5 "encryption"
+   replacement.  This is meant to be the same as for other MD5 based
+   encryption implementations.  */
+static const char md5_salt_prefix[] = "$1$";
+
+/* Table with characters for base64 transformation.  */
+static const char b64t[64] =
+"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+char *yahoo_crypt(char *key, char *salt)
+{
+	char *buffer = NULL;
+	int buflen = 0;
+	int needed = 3 + strlen (salt) + 1 + 26 + 1;
+
+	md5_byte_t alt_result[16];
+	md5_state_t ctx;
+	md5_state_t alt_ctx;
+	size_t salt_len;
+	size_t key_len;
+	size_t cnt;
+	char *cp;
+
+	if (buflen < needed) {
+		buflen = needed;
+		if ((buffer = realloc(buffer, buflen)) == NULL)
+			return NULL;
+	}
+
+	/* Find beginning of salt string.  The prefix should normally always
+	   be present.  Just in case it is not.  */
+	if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0)
+		/* Skip salt prefix.  */
+		salt += sizeof (md5_salt_prefix) - 1;
+
+	salt_len = MIN (strcspn (salt, "$"), 8);
+	key_len = strlen (key);
+
+	/* Prepare for the real work.  */
+	md5_init(&ctx);
+
+	/* Add the key string.  */
+	md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+	/* Because the SALT argument need not always have the salt prefix we
+	   add it separately.  */
+	md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1);
+
+	/* The last part is the salt string.  This must be at most 8
+	   characters and it ends at the first `$' character (for
+	   compatibility which existing solutions).  */
+	md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+	/* Compute alternate MD5 sum with input KEY, SALT, and KEY.  The
+	   final result will be added to the first context.  */
+	md5_init(&alt_ctx);
+
+	/* Add key.  */
+	md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+	/* Add salt.  */
+	md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len);
+
+	/* Add key again.  */
+	md5_append(&alt_ctx, (md5_byte_t *)key, key_len);
+
+	/* Now get result of this (16 bytes) and add it to the other
+	   context.  */
+	md5_finish(&alt_ctx, alt_result);
+
+	/* Add for any character in the key one byte of the alternate sum.  */
+	for (cnt = key_len; cnt > 16; cnt -= 16)
+		md5_append(&ctx, alt_result, 16);
+	md5_append(&ctx, alt_result, cnt);
+
+	/* For the following code we need a NUL byte.  */
+	alt_result[0] = '\0';
+
+	/* The original implementation now does something weird: for every 1
+	   bit in the key the first 0 is added to the buffer, for every 0
+	   bit the first character of the key.  This does not seem to be
+	   what was intended but we have to follow this to be compatible.  */
+	for (cnt = key_len; cnt > 0; cnt >>= 1)
+		md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1);
+
+	/* Create intermediate result.  */
+	md5_finish(&ctx, alt_result);
+
+	/* Now comes another weirdness.  In fear of password crackers here
+	   comes a quite long loop which just processes the output of the
+	   previous round again.  We cannot ignore this here.  */
+	for (cnt = 0; cnt < 1000; ++cnt) {
+		/* New context.  */
+		md5_init(&ctx);
+
+		/* Add key or last result.  */
+		if ((cnt & 1) != 0)
+			md5_append(&ctx, (md5_byte_t *)key, key_len);
+		else
+			md5_append(&ctx, alt_result, 16);
+
+		/* Add salt for numbers not divisible by 3.  */
+		if (cnt % 3 != 0)
+			md5_append(&ctx, (md5_byte_t *)salt, salt_len);
+
+		/* Add key for numbers not divisible by 7.  */
+		if (cnt % 7 != 0)
+			md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+		/* Add key or last result.  */
+		if ((cnt & 1) != 0)
+			md5_append(&ctx, alt_result, 16);
+		else
+			md5_append(&ctx, (md5_byte_t *)key, key_len);
+
+		/* Create intermediate result.  */
+		md5_finish(&ctx, alt_result);
+	}
+
+	/* Now we can construct the result string.  It consists of three
+	   parts.  */
+
+	strncpy(buffer, md5_salt_prefix, MAX (0, buflen));
+	cp = buffer + strlen(buffer);
+	buflen -= sizeof (md5_salt_prefix);
+
+	strncpy(cp, salt, MIN ((size_t) buflen, salt_len));
+	cp = cp + strlen(cp);
+	buflen -= MIN ((size_t) buflen, salt_len);
+
+	if (buflen > 0) {
+		*cp++ = '$';
+		--buflen;
+	}
+
+#define b64_from_24bit(B2, B1, B0, N) \
+	do { \
+		unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
+		int n = (N); \
+		while (n-- > 0 && buflen > 0) { \
+			*cp++ = b64t[w & 0x3f]; \
+			--buflen; \
+			w >>= 6; \
+		}\
+	} while (0)
+
+	b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4);
+	b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4);
+	b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4);
+	b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4);
+	b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4);
+	b64_from_24bit (0, 0, alt_result[11], 2);
+	if (buflen <= 0) {
+		FREE(buffer);
+	} else
+		*cp = '\0';	/* Terminate the string.  */
+
+	/* Clear the buffer for the intermediate result so that people
+	   attaching to processes or reading core dumps cannot get any
+	   information.  We do it in this way to clear correct_words[]
+	   inside the MD5 implementation as well.  */
+	md5_init(&ctx);
+	md5_finish(&ctx, alt_result);
+	memset (&ctx, '\0', sizeof (ctx));
+	memset (&alt_ctx, '\0', sizeof (alt_ctx));
+
+	return buffer;
+}
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/libyahoo2.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,4591 @@
+/*
+ * libyahoo2: libyahoo2.c
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua>
+ *
+ * Much of this code was taken and adapted from the yahoo module for
+ * gaim released under the GNU GPL.  This code is also released under the 
+ * GNU GPL.
+ *
+ * This code is derivitive of Gaim <http://gaim.sourceforge.net>
+ * copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *	       1998-1999, Adam Fritzler <afritz@marko.net>
+ *	       1998-2002, Rob Flynn <rob@marko.net>
+ *	       2000-2002, Eric Warmenhoven <eric@warmenhoven.org>
+ *	       2001-2002, Brian Macke <macke@strangelove.net>
+ *		    2001, Anand Biligiri S <abiligiri@users.sf.net>
+ *		    2001, Valdis Kletnieks
+ *		    2002, Sean Egan <bj91704@binghamton.edu>
+ *		    2002, Toby Gray <toby.gray@ntlworld.com>
+ *
+ * This library also uses code from other libraries, namely:
+ *     Portions from libfaim copyright 1998, 1999 Adam Fritzler
+ *     <afritz@auk.cx>
+ *     Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto
+ *     <hiro-y@kcn.ne.jp>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+#  define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include <sys/types.h>
+
+#ifdef __MINGW32__
+# include <winsock2.h>
+# define write(a,b,c) send(a,b,c,0)
+# define read(a,b,c)  recv(a,b,c,0)
+#endif
+
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "sha.h"
+#include "md5.h"
+#include "yahoo2.h"
+#include "yahoo_httplib.h"
+#include "yahoo_util.h"
+#include "yahoo_fn.h"
+
+#include "yahoo2_callbacks.h"
+#include "yahoo_debug.h"
+#if defined(__MINGW32__) && !defined(HAVE_GLIB)
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+#ifdef USE_STRUCT_CALLBACKS
+struct yahoo_callbacks *yc=NULL;
+
+void yahoo_register_callbacks(struct yahoo_callbacks * tyc)
+{
+	yc = tyc;
+}
+
+#define YAHOO_CALLBACK(x)	yc->x
+#else
+#define YAHOO_CALLBACK(x)	x
+#endif
+
+int yahoo_log_message(char * fmt, ...)
+{
+	char out[1024];
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(out, sizeof(out), fmt, ap);
+	va_end(ap);
+	return YAHOO_CALLBACK(ext_yahoo_log)("%s", out);
+}
+
+int yahoo_connect(char * host, int port)
+{
+	return YAHOO_CALLBACK(ext_yahoo_connect)(host, port);
+}
+
+static enum yahoo_log_level log_level = YAHOO_LOG_NONE;
+
+enum yahoo_log_level yahoo_get_log_level()
+{
+	return log_level;
+}
+
+int yahoo_set_log_level(enum yahoo_log_level level)
+{
+	enum yahoo_log_level l = log_level;
+	log_level = level;
+	return l;
+}
+
+/* default values for servers */
+static char pager_host[] = "scs.msg.yahoo.com"; 
+static int pager_port = 5050;
+static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0};
+static char filetransfer_host[]="filetransfer.msg.yahoo.com";
+static int filetransfer_port=80;
+static char webcam_host[]="webcam.yahoo.com";
+static int webcam_port=5100;
+static char webcam_description[]="";
+static char local_host[]="";
+static int conn_type=Y_WCM_DSL;
+
+static char profile_url[] = "http://profiles.yahoo.com/";
+
+enum yahoo_service { /* these are easier to see in hex */
+	YAHOO_SERVICE_LOGON = 1,
+	YAHOO_SERVICE_LOGOFF,
+	YAHOO_SERVICE_ISAWAY,
+	YAHOO_SERVICE_ISBACK,
+	YAHOO_SERVICE_IDLE, /* 5 (placemarker) */
+	YAHOO_SERVICE_MESSAGE,
+	YAHOO_SERVICE_IDACT,
+	YAHOO_SERVICE_IDDEACT,
+	YAHOO_SERVICE_MAILSTAT,
+	YAHOO_SERVICE_USERSTAT, /* 0xa */
+	YAHOO_SERVICE_NEWMAIL,
+	YAHOO_SERVICE_CHATINVITE,
+	YAHOO_SERVICE_CALENDAR,
+	YAHOO_SERVICE_NEWPERSONALMAIL,
+	YAHOO_SERVICE_NEWCONTACT,
+	YAHOO_SERVICE_ADDIDENT, /* 0x10 */
+	YAHOO_SERVICE_ADDIGNORE,
+	YAHOO_SERVICE_PING,
+	YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */
+	YAHOO_SERVICE_SYSMESSAGE = 0x14,
+	YAHOO_SERVICE_PASSTHROUGH2 = 0x16,
+	YAHOO_SERVICE_CONFINVITE = 0x18,
+	YAHOO_SERVICE_CONFLOGON,
+	YAHOO_SERVICE_CONFDECLINE,
+	YAHOO_SERVICE_CONFLOGOFF,
+	YAHOO_SERVICE_CONFADDINVITE,
+	YAHOO_SERVICE_CONFMSG,
+	YAHOO_SERVICE_CHATLOGON,
+	YAHOO_SERVICE_CHATLOGOFF,
+	YAHOO_SERVICE_CHATMSG = 0x20,
+	YAHOO_SERVICE_GAMELOGON = 0x28,
+	YAHOO_SERVICE_GAMELOGOFF,
+	YAHOO_SERVICE_GAMEMSG = 0x2a,
+	YAHOO_SERVICE_FILETRANSFER = 0x46,
+	YAHOO_SERVICE_VOICECHAT = 0x4A,
+	YAHOO_SERVICE_NOTIFY,
+	YAHOO_SERVICE_VERIFY,
+	YAHOO_SERVICE_P2PFILEXFER,
+	YAHOO_SERVICE_PEERTOPEER = 0x4F,	/* Checks if P2P possible */
+	YAHOO_SERVICE_WEBCAM,
+	YAHOO_SERVICE_AUTHRESP = 0x54,
+	YAHOO_SERVICE_LIST,
+	YAHOO_SERVICE_AUTH = 0x57,
+	YAHOO_SERVICE_ADDBUDDY = 0x83,
+	YAHOO_SERVICE_REMBUDDY,
+	YAHOO_SERVICE_IGNORECONTACT,	/* > 1, 7, 13 < 1, 66, 13, 0*/
+	YAHOO_SERVICE_REJECTCONTACT,
+	YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */
+	YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/
+	YAHOO_SERVICE_CHATGOTO,
+	YAHOO_SERVICE_CHATJOIN,	/* > 1 104-room 129-1600326591 62-2 */
+	YAHOO_SERVICE_CHATLEAVE,
+	YAHOO_SERVICE_CHATEXIT = 0x9b,
+	YAHOO_SERVICE_CHATLOGOUT = 0xa0,
+	YAHOO_SERVICE_CHATPING,
+	YAHOO_SERVICE_COMMENT = 0xa8
+};
+
+struct yahoo_pair {
+	int key;
+	char *value;
+};
+
+struct yahoo_packet {
+	unsigned short int service;
+	unsigned int status;
+	unsigned int id;
+	YList *hash;
+};
+
+struct yahoo_search_state {
+	int   lsearch_type;
+	char  *lsearch_text;
+	int   lsearch_gender;
+	int   lsearch_agerange;
+	int   lsearch_photo;
+	int   lsearch_yahoo_only;
+	int   lsearch_nstart;
+	int   lsearch_nfound;
+	int   lsearch_ntotal;
+};
+
+struct data_queue {
+	unsigned char *queue;
+	int len;
+};
+
+struct yahoo_input_data {
+	struct yahoo_data *yd;
+	struct yahoo_webcam *wcm;
+	struct yahoo_webcam_data *wcd;
+	struct yahoo_search_state *ys;
+
+	int   fd;
+	enum yahoo_connection_type type;
+	
+	unsigned char	*rxqueue;
+	int   rxlen;
+	int   read_tag;
+
+	YList *txqueues;
+	int   write_tag;
+};
+
+struct yahoo_server_settings {
+	char *pager_host;
+	int   pager_port;
+	char *filetransfer_host;
+	int   filetransfer_port;
+	char *webcam_host;
+	int   webcam_port;
+	char *webcam_description;
+	char *local_host;
+	int   conn_type;
+};
+
+static void * _yahoo_default_server_settings()
+{
+	struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1);
+
+	yss->pager_host = strdup(pager_host);
+	yss->pager_port = pager_port;
+	yss->filetransfer_host = strdup(filetransfer_host);
+	yss->filetransfer_port = filetransfer_port;
+	yss->webcam_host = strdup(webcam_host);
+	yss->webcam_port = webcam_port;
+	yss->webcam_description = strdup(webcam_description);
+	yss->local_host = strdup(local_host);
+	yss->conn_type = conn_type;
+
+	return yss;
+}
+
+static void * _yahoo_assign_server_settings(va_list ap)
+{
+	struct yahoo_server_settings *yss = _yahoo_default_server_settings();
+	char *key;
+	char *svalue;
+	int   nvalue;
+
+	while(1) {
+		key = va_arg(ap, char *);
+		if(key == NULL)
+			break;
+
+		if(!strcmp(key, "pager_host")) {
+			svalue = va_arg(ap, char *);
+			free(yss->pager_host);
+			yss->pager_host = strdup(svalue);
+		} else if(!strcmp(key, "pager_port")) {
+			nvalue = va_arg(ap, int);
+			yss->pager_port = nvalue;
+		} else if(!strcmp(key, "filetransfer_host")) {
+			svalue = va_arg(ap, char *);
+			free(yss->filetransfer_host);
+			yss->filetransfer_host = strdup(svalue);
+		} else if(!strcmp(key, "filetransfer_port")) {
+			nvalue = va_arg(ap, int);
+			yss->filetransfer_port = nvalue;
+		} else if(!strcmp(key, "webcam_host")) {
+			svalue = va_arg(ap, char *);
+			free(yss->webcam_host);
+			yss->webcam_host = strdup(svalue);
+		} else if(!strcmp(key, "webcam_port")) {
+			nvalue = va_arg(ap, int);
+			yss->webcam_port = nvalue;
+		} else if(!strcmp(key, "webcam_description")) {
+			svalue = va_arg(ap, char *);
+			free(yss->webcam_description);
+			yss->webcam_description = strdup(svalue);
+		} else if(!strcmp(key, "local_host")) {
+			svalue = va_arg(ap, char *);
+			free(yss->local_host);
+			yss->local_host = strdup(svalue);
+		} else if(!strcmp(key, "conn_type")) {
+			nvalue = va_arg(ap, int);
+			yss->conn_type = nvalue;
+		} else {
+			WARNING(("Unknown key passed to yahoo_init, "
+				"perhaps you didn't terminate the list "
+				"with NULL"));
+		}
+	}
+
+	return yss;
+}
+
+static void yahoo_free_server_settings(struct yahoo_server_settings *yss)
+{
+	if(!yss)
+		return;
+
+	free(yss->pager_host);
+	free(yss->filetransfer_host);
+	free(yss->webcam_host);
+	free(yss->webcam_description);
+	free(yss->local_host);
+
+	free(yss);
+}
+
+static YList *conns=NULL;
+static YList *inputs=NULL;
+static int last_id=0;
+
+static void add_to_list(struct yahoo_data *yd)
+{
+	conns = y_list_prepend(conns, yd);
+}
+static struct yahoo_data * find_conn_by_id(int id)
+{
+	YList *l;
+	for(l = conns; l; l = y_list_next(l)) {
+		struct yahoo_data *yd = l->data;
+		if(yd->client_id == id)
+			return yd;
+	}
+	return NULL;
+}
+static void del_from_list(struct yahoo_data *yd)
+{
+	conns = y_list_remove(conns, yd);
+}
+
+/* call repeatedly to get the next one */
+/*
+static struct yahoo_input_data * find_input_by_id(int id)
+{
+	YList *l;
+	for(l = inputs; l; l = y_list_next(l)) {
+		struct yahoo_input_data *yid = l->data;
+		if(yid->yd->client_id == id)
+			return yid;
+	}
+	return NULL;
+}
+*/
+
+static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who)
+{
+	YList *l;
+	LOG(("find_input_by_id_and_webcam_user"));
+	for(l = inputs; l; l = y_list_next(l)) {
+		struct yahoo_input_data *yid = l->data;
+		if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id 
+				&& yid->wcm && 
+				((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) ||
+				 !(yid->wcm->user && !who)))
+			return yid;
+	}
+	return NULL;
+}
+
+static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type)
+{
+	YList *l;
+	LOG(("find_input_by_id_and_type"));
+	for(l = inputs; l; l = y_list_next(l)) {
+		struct yahoo_input_data *yid = l->data;
+		if(yid->type == type && yid->yd->client_id == id)
+			return yid;
+	}
+	return NULL;
+}
+
+static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd)
+{
+	YList *l;
+	LOG(("find_input_by_id_and_fd"));
+	for(l = inputs; l; l = y_list_next(l)) {
+		struct yahoo_input_data *yid = l->data;
+		if(yid->fd == fd && yid->yd->client_id == id)
+			return yid;
+	}
+	return NULL;
+}
+
+static int count_inputs_with_id(int id)
+{
+	int c=0;
+	YList *l;
+	LOG(("counting %d", id));
+	for(l = inputs; l; l = y_list_next(l)) {
+		struct yahoo_input_data *yid = l->data;
+		if(yid->yd->client_id == id)
+			c++;
+	}
+	LOG(("%d", c));
+	return c;
+}
+
+
+extern char *yahoo_crypt(char *, char *);
+
+/* Free a buddy list */
+static void yahoo_free_buddies(YList * list)
+{
+	YList *l;
+
+	for(l = list; l; l = l->next)
+	{
+		struct yahoo_buddy *bud = l->data;
+		if(!bud)
+			continue;
+
+		FREE(bud->group);
+		FREE(bud->id);
+		FREE(bud->real_name);
+		if(bud->yab_entry) {
+			FREE(bud->yab_entry->fname);
+			FREE(bud->yab_entry->lname);
+			FREE(bud->yab_entry->nname);
+			FREE(bud->yab_entry->id);
+			FREE(bud->yab_entry->email);
+			FREE(bud->yab_entry->hphone);
+			FREE(bud->yab_entry->wphone);
+			FREE(bud->yab_entry->mphone);
+			FREE(bud->yab_entry);
+		}
+		FREE(bud);
+		l->data = bud = NULL;
+	}
+
+	y_list_free(list);
+}
+
+/* Free an identities list */
+static void yahoo_free_identities(YList * list)
+{
+	while (list) {
+		YList *n = list;
+		FREE(list->data);
+		list = y_list_remove_link(list, list);
+		y_list_free_1(n);
+	}
+}
+
+/* Free webcam data */
+static void yahoo_free_webcam(struct yahoo_webcam *wcm)
+{
+	if (wcm) {
+		FREE(wcm->user);
+		FREE(wcm->server);
+		FREE(wcm->key);
+		FREE(wcm->description);
+		FREE(wcm->my_ip);
+	}
+	FREE(wcm);
+}
+
+static void yahoo_free_data(struct yahoo_data *yd)
+{
+	FREE(yd->user);
+	FREE(yd->password);
+	FREE(yd->cookie_y);
+	FREE(yd->cookie_t);
+	FREE(yd->cookie_c);
+	FREE(yd->login_cookie);
+	FREE(yd->login_id);
+
+	yahoo_free_buddies(yd->buddies);
+	yahoo_free_buddies(yd->ignore);
+	yahoo_free_identities(yd->identities);
+
+	yahoo_free_server_settings(yd->server_settings);
+
+	FREE(yd);
+}
+
+#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4)
+
+static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, 
+		enum yahoo_status status, int id)
+{
+	struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1);
+
+	pkt->service = service;
+	pkt->status = status;
+	pkt->id = id;
+
+	return pkt;
+}
+
+static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value)
+{
+	struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
+	pair->key = key;
+	pair->value = strdup(value);
+	pkt->hash = y_list_append(pkt->hash, pair);
+}
+
+static int yahoo_packet_length(struct yahoo_packet *pkt)
+{
+	YList *l;
+
+	int len = 0;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		int tmp = pair->key;
+		do {
+			tmp /= 10;
+			len++;
+		} while (tmp);
+		len += 2;
+		len += strlen(pair->value);
+		len += 2;
+	}
+
+	return len;
+}
+
+#define yahoo_put16(buf, data) ( \
+		(*(buf) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+1) = (unsigned char)(data)&0xff),  \
+		2)
+#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff))
+#define yahoo_put32(buf, data) ( \
+		(*((buf)) = (unsigned char)((data)>>24)&0xff), \
+		(*((buf)+1) = (unsigned char)((data)>>16)&0xff), \
+		(*((buf)+2) = (unsigned char)((data)>>8)&0xff), \
+		(*((buf)+3) = (unsigned char)(data)&0xff), \
+		4)
+#define yahoo_get32(buf) ((((*(buf)   )&0xff)<<24) + \
+			 (((*((buf)+1))&0xff)<<16) + \
+			 (((*((buf)+2))&0xff)<< 8) + \
+			 (((*((buf)+3))&0xff)))
+
+static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len)
+{
+	int pos = 0;
+
+	while (pos + 1 < len) {
+		char *key, *value = NULL;
+		int accept;
+		int x;
+
+		struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1);
+
+		key = malloc(len + 1);
+		x = 0;
+		while (pos + 1 < len) {
+			if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
+				break;
+			key[x++] = data[pos++];
+		}
+		key[x] = 0;
+		pos += 2;
+		pair->key = strtol(key, NULL, 10);
+		free(key);
+		
+		/* Libyahoo2 developer(s) don't seem to have the time to fix
+		   this problem, so for now try to work around it:
+		   
+		   Sometimes we receive an invalid packet with not any more
+		   data at this point. I don't know how to handle this in a
+		   clean way, but let's hope this is clean enough: */
+		
+		if (pos + 1 < len) {
+			accept = x; 
+			/* if x is 0 there was no key, so don't accept it */
+			if (accept)
+				value = malloc(len - pos + 1);
+			x = 0;
+			while (pos + 1 < len) {
+				if (data[pos] == 0xc0 && data[pos + 1] == 0x80)
+					break;
+				if (accept)
+					value[x++] = data[pos++];
+			}
+			if (accept)
+				value[x] = 0;
+			pos += 2;
+		} else {
+			accept = 0;
+		}
+		
+		if (accept) {
+			pair->value = strdup(value);
+			FREE(value);
+			pkt->hash = y_list_append(pkt->hash, pair);
+			DEBUG_MSG(("Key: %d  \tValue: %s", pair->key, pair->value));
+		} else {
+			FREE(pair);
+		}
+	}
+}
+
+static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data)
+{
+	YList *l;
+	int pos = 0;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		unsigned char buf[100];
+
+		snprintf((char *)buf, sizeof(buf), "%d", pair->key);
+		strcpy((char *)data + pos, (char *)buf);
+		pos += strlen((char *)buf);
+		data[pos++] = 0xc0;
+		data[pos++] = 0x80;
+
+		strcpy((char *)data + pos, pair->value);
+		pos += strlen(pair->value);
+		data[pos++] = 0xc0;
+		data[pos++] = 0x80;
+	}
+}
+
+static void yahoo_dump_unhandled(struct yahoo_packet *pkt)
+{
+	YList *l;
+
+	NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status));
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		NOTICE(("\t%d => %s", pair->key, pair->value));
+	}
+}
+
+
+static void yahoo_packet_dump(unsigned char *data, int len)
+{
+	if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) {
+		int i;
+		for (i = 0; i < len; i++) {
+			if ((i % 8 == 0) && i)
+				YAHOO_CALLBACK(ext_yahoo_log)(" ");
+			if ((i % 16 == 0) && i)
+				YAHOO_CALLBACK(ext_yahoo_log)("\n");
+			YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]);
+		}
+		YAHOO_CALLBACK(ext_yahoo_log)("\n");
+		for (i = 0; i < len; i++) {
+			if ((i % 8 == 0) && i)
+				YAHOO_CALLBACK(ext_yahoo_log)(" ");
+			if ((i % 16 == 0) && i)
+				YAHOO_CALLBACK(ext_yahoo_log)("\n");
+			if (isprint(data[i]))
+				YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]);
+			else
+				YAHOO_CALLBACK(ext_yahoo_log)(" . ");
+		}
+		YAHOO_CALLBACK(ext_yahoo_log)("\n");
+	}
+}
+
+static char base64digits[] = 	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+				"abcdefghijklmnopqrstuvwxyz"
+				"0123456789._";
+static void to_y64(unsigned char *out, const unsigned char *in, int inlen)
+/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */
+{
+	for (; inlen >= 3; inlen -= 3)
+		{
+			*out++ = base64digits[in[0] >> 2];
+			*out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+			*out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)];
+			*out++ = base64digits[in[2] & 0x3f];
+			in += 3;
+		}
+	if (inlen > 0)
+		{
+			unsigned char fragment;
+
+			*out++ = base64digits[in[0] >> 2];
+			fragment = (in[0] << 4) & 0x30;
+			if (inlen > 1)
+				fragment |= in[1] >> 4;
+			*out++ = base64digits[fragment];
+			*out++ = (inlen < 2) ? '-' 
+					: base64digits[(in[1] << 2) & 0x3c];
+			*out++ = '-';
+		}
+	*out = '\0';
+}
+
+static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length)
+{
+	struct data_queue *tx = y_new0(struct data_queue, 1);
+	tx->queue = y_new0(unsigned char, length);
+	tx->len = length;
+	memcpy(tx->queue, data, length);
+
+	yid->txqueues = y_list_append(yid->txqueues, tx);
+
+	if(!yid->write_tag)
+		yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid);
+}
+
+static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad)
+{
+	int pktlen = yahoo_packet_length(pkt);
+	int len = YAHOO_PACKET_HDRLEN + pktlen;
+
+	unsigned char *data;
+	int pos = 0;
+
+	if (yid->fd < 0)
+		return;
+
+	data = y_new0(unsigned char, len + 1);
+
+	memcpy(data + pos, "YMSG", 4); pos += 4;
+	pos += yahoo_put16(data + pos, 0x0a00);
+	pos += yahoo_put16(data + pos, 0x0000);
+	pos += yahoo_put16(data + pos, pktlen + extra_pad);
+	pos += yahoo_put16(data + pos, pkt->service);
+	pos += yahoo_put32(data + pos, pkt->status);
+	pos += yahoo_put32(data + pos, pkt->id);
+
+	yahoo_packet_write(pkt, data + pos);
+
+	yahoo_packet_dump(data, len);
+	
+	yahoo_add_to_send_queue(yid, data, len);
+	FREE(data);
+}
+
+static void yahoo_packet_free(struct yahoo_packet *pkt)
+{
+	while (pkt->hash) {
+		struct yahoo_pair *pair = pkt->hash->data;
+		YList *tmp;
+		FREE(pair->value);
+		FREE(pair);
+		tmp = pkt->hash;
+		pkt->hash = y_list_remove_link(pkt->hash, pkt->hash);
+		y_list_free_1(tmp);
+	}
+	FREE(pkt);
+}
+
+static int yahoo_send_data(int fd, void *data, int len)
+{
+	int ret;
+	int e;
+
+	if (fd < 0)
+		return -1;
+
+	yahoo_packet_dump(data, len);
+
+	do {
+		ret = write(fd, data, len);
+	} while(ret == -1 && errno==EINTR);
+	e=errno;
+
+	if (ret == -1)  {
+		LOG(("wrote data: ERR %s", strerror(errno)));
+	} else {
+		LOG(("wrote data: OK"));
+	}
+
+	errno=e;
+	return ret;
+}
+
+void yahoo_close(int id) 
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return;
+
+	del_from_list(yd);
+
+	yahoo_free_data(yd);
+	if(id == last_id)
+		last_id--;
+}
+
+static void yahoo_input_close(struct yahoo_input_data *yid) 
+{
+	inputs = y_list_remove(inputs, yid);
+
+	LOG(("yahoo_input_close(read)")); 
+	YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag);
+	LOG(("yahoo_input_close(write)")); 
+	YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag);
+	yid->read_tag = yid->write_tag = 0;
+	if(yid->fd)
+		close(yid->fd);
+	yid->fd = 0;
+	FREE(yid->rxqueue);
+	if(count_inputs_with_id(yid->yd->client_id) == 0) {
+		LOG(("closing %d", yid->yd->client_id));
+		yahoo_close(yid->yd->client_id);
+	}
+	yahoo_free_webcam(yid->wcm);
+	if(yid->wcd)
+		FREE(yid->wcd);
+	if(yid->ys) {
+		FREE(yid->ys->lsearch_text);
+		FREE(yid->ys);
+	}
+	FREE(yid);
+}
+
+static int is_same_bud(const void * a, const void * b) {
+	const struct yahoo_buddy *subject = a;
+	const struct yahoo_buddy *object = b;
+
+	return strcmp(subject->id, object->id);
+}
+
+static YList * bud_str2list(char *rawlist)
+{
+	YList * l = NULL;
+
+	char **lines;
+	char **split;
+	char **buddies;
+	char **tmp, **bud;
+
+	lines = y_strsplit(rawlist, "\n", -1);
+	for (tmp = lines; *tmp; tmp++) {
+		struct yahoo_buddy *newbud;
+
+		split = y_strsplit(*tmp, ":", 2);
+		if (!split)
+			continue;
+		if (!split[0] || !split[1]) {
+			y_strfreev(split);
+			continue;
+		}
+		buddies = y_strsplit(split[1], ",", -1);
+
+		for (bud = buddies; bud && *bud; bud++) {
+			newbud = y_new0(struct yahoo_buddy, 1);
+			newbud->id = strdup(*bud);
+			newbud->group = strdup(split[0]);
+
+			if(y_list_find_custom(l, newbud, is_same_bud)) {
+				FREE(newbud->id);
+				FREE(newbud->group);
+				FREE(newbud);
+				continue;
+			}
+
+			newbud->real_name = NULL;
+
+			l = y_list_append(l, newbud);
+
+			NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group));
+		}
+
+		y_strfreev(buddies);
+		y_strfreev(split);
+	}
+	y_strfreev(lines);
+
+	return l;
+}
+
+static char * getcookie(char *rawcookie)
+{
+	char * cookie=NULL;
+	char * tmpcookie; 
+	char * cookieend;
+
+	if (strlen(rawcookie) < 2) 
+		return NULL;
+
+	tmpcookie = strdup(rawcookie+2);
+	cookieend = strchr(tmpcookie, ';');
+
+	if(cookieend)
+		*cookieend = '\0';
+
+	cookie = strdup(tmpcookie);
+	FREE(tmpcookie);
+	/* cookieend=NULL;  not sure why this was there since the value is not preserved in the stack -dd */
+
+	return cookie;
+}
+
+static char * getlcookie(char *cookie)
+{
+	char *tmp;
+	char *tmpend;
+	char *login_cookie = NULL;
+
+	tmpend = strstr(cookie, "n=");
+	if(tmpend) {
+		tmp = strdup(tmpend+2);
+		tmpend = strchr(tmp, '&');
+		if(tmpend)
+			*tmpend='\0';
+		login_cookie = strdup(tmp);
+		FREE(tmp);
+	}
+
+	return login_cookie;
+}
+
+static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *msg = NULL;
+	char *from = NULL;
+	int stat = 0;
+	int accept = 0;
+	char *ind = NULL;
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4)
+			from = pair->value;
+		if (pair->key == 49)
+			msg = pair->value;
+		if (pair->key == 13)
+			stat = atoi(pair->value);
+		if (pair->key == 14)
+			ind = pair->value;
+		if (pair->key == 16) {	/* status == -1 */
+			NOTICE((pair->value));
+			return;
+		}
+
+	}
+
+	if (!msg)
+		return;
+	
+	if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) 
+		YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, from, stat);
+	else if (!strncasecmp(msg, "GAME", strlen("GAME"))) 
+		YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, from, stat);
+	else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) 
+	{
+		if (!strcmp(ind, " ")) {
+			YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, from);
+		} else {
+			accept = atoi(ind);
+			/* accept the invitation (-1 = deny 1 = accept) */
+			if (accept < 0)
+				accept = 0;
+			YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, from, accept);
+		}
+	}
+	else
+		LOG(("Got unknown notification: %s", msg));
+}
+
+static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *from=NULL;
+	char *to=NULL;
+	char *msg=NULL;
+	char *url=NULL;
+	long expires=0;
+
+	char *service=NULL;
+
+	char *filename=NULL;
+	unsigned long filesize=0L;
+
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4)
+			from = pair->value;
+		if (pair->key == 5)
+			to = pair->value;
+		if (pair->key == 14)
+			msg = pair->value;
+		if (pair->key == 20)
+			url = pair->value;
+		if (pair->key == 38)
+			expires = atol(pair->value);
+
+		if (pair->key == 27)
+			filename = pair->value;
+		if (pair->key == 28)
+			filesize = atol(pair->value);
+
+		if (pair->key == 49)
+			service = pair->value;
+	}
+
+	if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) {
+		if(strcmp("FILEXFER", service) != 0) {
+			WARNING(("unhandled service 0x%02x", pkt->service));
+			yahoo_dump_unhandled(pkt);
+			return;
+		}
+	}
+
+	if(msg) {
+		char *tmp;
+		tmp = strchr(msg, '\006');
+		if(tmp)
+			*tmp = '\0';
+	}
+	if(url && from)
+		YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, from, url, expires, msg, filename, filesize);
+
+}
+
+static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *msg = NULL;
+	char *host = NULL;
+	char *who = NULL;
+	char *room = NULL;
+	char *id = NULL;
+	int  utf8 = 0;
+	YList *members = NULL;
+	YList *l;
+	
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 50)
+			host = pair->value;
+		
+		if (pair->key == 52) {		/* invite */
+			members = y_list_append(members, strdup(pair->value));
+		}
+		if (pair->key == 53)		/* logon */
+			who = pair->value;
+		if (pair->key == 54)		/* decline */
+			who = pair->value;
+		if (pair->key == 55)		/* unavailable (status == 2) */
+			who = pair->value;
+		if (pair->key == 56)		/* logoff */
+			who = pair->value;
+
+		if (pair->key == 57)
+			room = pair->value;
+
+		if (pair->key == 58)		/* join message */
+			msg = pair->value;
+		if (pair->key == 14)		/* decline/conf message */
+			msg = pair->value;
+
+		if (pair->key == 13)
+			;
+		if (pair->key == 16)		/* error */
+			msg = pair->value;
+
+		if (pair->key == 1)		/* my id */
+			id = pair->value;
+		if (pair->key == 3)		/* message sender */
+			who = pair->value;
+
+		if (pair->key == 97)
+			utf8 = atoi(pair->value);
+	}
+
+	if(!room)
+		return;
+
+	if(host) {
+		for(l = members; l; l = l->next) {
+			char * w = l->data;
+			if(!strcmp(w, host))
+				break;
+		}
+		if(!l)
+			members = y_list_append(members, strdup(host));
+	}
+	/* invite, decline, join, left, message -> status == 1 */
+
+	switch(pkt->service) {
+	case YAHOO_SERVICE_CONFINVITE:
+		if(pkt->status == 2)
+			;
+		else if(members)
+			YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
+		else if(msg)
+			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0);
+		break;
+	case YAHOO_SERVICE_CONFADDINVITE:
+		if(pkt->status == 2)
+			;
+		else
+			YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members);
+		break;
+	case YAHOO_SERVICE_CONFDECLINE:
+		if(who)
+			YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, who, room, msg);
+		break;
+	case YAHOO_SERVICE_CONFLOGON:
+		if(who)
+			YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, who, room);
+		break;
+	case YAHOO_SERVICE_CONFLOGOFF:
+		if(who)
+			YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, who, room);
+		break;
+	case YAHOO_SERVICE_CONFMSG:
+		if(who)
+			YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, who, room, msg, utf8);
+		break;
+	}
+}
+
+static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char *msg = NULL;
+	char *who = NULL;
+	char *room = NULL;
+	char *topic = NULL;
+	YList *members = NULL;
+	struct yahoo_chat_member *currentmember = NULL;
+	int  msgtype = 1;
+	int  utf8 = 0;
+	int  firstjoin = 0;
+	int  membercount = 0;
+	int  chaterr=0;
+	YList *l;
+	
+	yahoo_dump_unhandled(pkt);
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+
+		if (pair->key == 104) {
+			/* Room name */
+			room = pair->value;
+		}
+
+		if (pair->key == 105) {
+			/* Room topic */
+			topic = pair->value;
+		}
+
+		if (pair->key == 108) {
+			/* Number of members in this packet */
+			membercount = atoi(pair->value);
+		}
+
+		if (pair->key == 109) {
+			/* message sender */
+			who = pair->value;
+
+			if (pkt->service == YAHOO_SERVICE_CHATJOIN) {
+				currentmember = y_new0(struct yahoo_chat_member, 1);
+				currentmember->id = strdup(pair->value);
+				members = y_list_append(members, currentmember);
+			}
+		}
+
+		if (pair->key == 110) {
+			/* age */
+			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+				currentmember->age = atoi(pair->value);
+		}
+
+		if (pair->key == 113) {
+			/* attribs */
+			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+				currentmember->attribs = atoi(pair->value);
+		}
+
+		if (pair->key == 141) {
+			/* alias */
+			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+				currentmember->alias = strdup(pair->value);
+		}
+
+		if (pair->key == 142) {
+			/* location */
+			if (pkt->service == YAHOO_SERVICE_CHATJOIN)
+				currentmember->location = strdup(pair->value);
+		}
+
+
+		if (pair->key == 130) {
+			/* first join */
+			firstjoin = 1;
+		}
+
+		if (pair->key == 117) {
+			/* message */
+			msg = pair->value;
+		}
+
+		if (pair->key == 124) {
+			/* Message type */
+			msgtype = atoi(pair->value);
+		}
+		if (pair->key == 114) {
+			/* message error not sure what all the pair values mean */
+			/* but -1 means no session in room */
+			chaterr= atoi(pair->value);
+		}
+	}
+
+	if(!room) {
+		if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */
+			YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id);
+			return ;
+		}
+		if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr)  {
+			YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id);
+			return ;
+		}
+
+		WARNING(("We didn't get a room name, ignoring packet"));
+		return;
+	}
+
+	switch(pkt->service) {
+	case YAHOO_SERVICE_CHATJOIN:
+		if(y_list_length(members) != membercount) {
+			WARNING(("Count of members doesn't match No. of members we got"));
+		}
+		if(firstjoin && members) {
+			YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, room, topic, members, yid->fd);
+		} else if(who) {
+			if(y_list_length(members) != 1) {
+				WARNING(("Got more than 1 member on a normal join"));
+			}
+			/* this should only ever have one, but just in case */
+			while(members) {
+				YList *n = members->next;
+				currentmember = members->data;
+				YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, room, currentmember);
+				y_list_free_1(members);
+				members=n;
+			}
+		}
+		break;
+	case YAHOO_SERVICE_CHATEXIT:
+		if(who) {
+			YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, room, who);
+		}
+		break;
+	case YAHOO_SERVICE_COMMENT:
+		if(who) {
+			YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, who, room, msg, msgtype, utf8);
+		}
+		break;
+	}
+}
+
+static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	YList *l;
+	YList * messages = NULL;
+
+	struct m {
+		int  i_31;
+		int  i_32;
+		char *to;
+		char *from;
+		long tm;
+		char *msg;
+		int  utf8;
+	} *message = y_new0(struct m, 1);
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 1 || pair->key == 4)
+		{
+			if(!message->from)
+				message->from = pair->value;
+		}
+		else if (pair->key == 5)
+			message->to = pair->value;
+		else if (pair->key == 15)
+			message->tm = strtol(pair->value, NULL, 10);
+		else if (pair->key == 97)
+			message->utf8 = atoi(pair->value);
+			/* user message */  /* sys message */
+		else if (pair->key == 14 || pair->key == 16)
+			message->msg = pair->value;
+		else if (pair->key == 31) {
+			if(message->i_31) {
+				messages = y_list_append(messages, message);
+				message = y_new0(struct m, 1);
+			}
+			message->i_31 = atoi(pair->value);
+		}
+		else if (pair->key == 32)
+			message->i_32 = atoi(pair->value);
+		else
+			LOG(("yahoo_process_message: status: %d, key: %d, value: %s",
+					pkt->status, pair->key, pair->value));
+	}
+
+	messages = y_list_append(messages, message);
+
+	for (l = messages; l; l=l->next) {
+		message = l->data;
+		if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) {
+			YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg);
+		} else if (pkt->status <= 2 || pkt->status == 5) {
+			YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->from, message->msg, message->tm, pkt->status, message->utf8);
+		} else if (pkt->status == 0xffffffff) {
+			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0);
+		}
+		free(message);
+	}
+
+	y_list_free(messages);
+}
+
+
+static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	YList *l;
+	struct yahoo_data *yd = yid->yd;
+	char *name = NULL;
+	int state = 0;
+	int away = 0;
+	int idle = 0;
+	char *msg = NULL;
+	
+	if(pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL);
+		return;
+	}
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+
+		switch (pair->key) {
+		case 0: /* we won't actually do anything with this */
+			NOTICE(("key %d:%s", pair->key, pair->value));
+			break;
+		case 1: /* we don't get the full buddy list here. */
+			if (!yd->logged_in) {
+				yd->logged_in = TRUE;
+				if(yd->current_status < 0)
+					yd->current_status = yd->initial_status;
+				YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+			}
+			break;
+		case 8: /* how many online buddies we have */
+			NOTICE(("key %d:%s", pair->key, pair->value));
+			break;
+		case 7: /* the current buddy */
+			name = pair->value;
+			break;
+		case 10: /* state */
+			state = strtol(pair->value, NULL, 10);
+			break;
+		case 19: /* custom status message */
+			msg = pair->value;
+			break;
+		case 47: /* is it an away message or not */
+			away = atoi(pair->value);
+			break;
+		case 137: /* seconds idle */
+			idle = atoi(pair->value);
+			break;
+		case 11: /* what is this? */
+			NOTICE(("key %d:%s", pair->key, pair->value));
+			break;
+		case 17: /* in chat? */
+			break;
+		case 13: /* in pager? */
+			if (pkt->service == YAHOO_SERVICE_LOGOFF || strtol(pair->value, NULL, 10) == 0) {
+				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, YAHOO_STATUS_OFFLINE, NULL, 1);
+				break;
+			}
+			if (state == YAHOO_STATUS_AVAILABLE) {
+				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, 0);
+			} else if (state == YAHOO_STATUS_CUSTOM) {
+				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
+			} else {
+				YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, idle);
+			}
+
+			break;
+		case 60: 
+			/* sometimes going offline makes this 2, but invisible never sends it */
+			NOTICE(("key %d:%s", pair->key, pair->value));
+			 break;
+		case 16: /* Custom error message */
+			YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0);
+			break;
+		default:
+			WARNING(("unknown status key %d:%s", pair->key, pair->value));
+			break;
+		}
+	}
+}
+
+static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	YList *l;
+
+	if (!yd->logged_in) {
+		yd->logged_in = TRUE;
+		if(yd->current_status < 0)
+			yd->current_status = yd->initial_status;
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL);
+	}
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+
+		switch(pair->key) {
+		case 87: /* buddies */
+			if(!yd->rawbuddylist)
+				yd->rawbuddylist = strdup(pair->value);
+			else {
+				yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value);
+			}
+			break;
+
+		case 88: /* ignore list */
+			if(!yd->ignorelist)
+				yd->ignorelist = strdup("Ignore:");
+			yd->ignorelist = y_string_append(yd->ignorelist, pair->value);
+			break;
+
+		case 89: /* identities */
+			{
+			char **identities = y_strsplit(pair->value, ",", -1);
+			int i;
+			for(i=0; identities[i]; i++)
+				yd->identities = y_list_append(yd->identities, 
+						strdup(identities[i]));
+			y_strfreev(identities);
+			}
+			YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities);
+			break;
+		case 59: /* cookies */
+			if(yd->ignorelist) {
+				yd->ignore = bud_str2list(yd->ignorelist);
+				FREE(yd->ignorelist);
+				YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore);
+			}
+			if(yd->rawbuddylist) {
+				yd->buddies = bud_str2list(yd->rawbuddylist);
+				FREE(yd->rawbuddylist);
+				YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+			}
+
+			if(pair->value[0]=='Y') {
+				FREE(yd->cookie_y);
+				FREE(yd->login_cookie);
+
+				yd->cookie_y = getcookie(pair->value);
+				yd->login_cookie = getlcookie(yd->cookie_y);
+
+			} else if(pair->value[0]=='T') {
+				FREE(yd->cookie_t);
+				yd->cookie_t = getcookie(pair->value);
+
+			} else if(pair->value[0]=='C') {
+				FREE(yd->cookie_c);
+				yd->cookie_c = getcookie(pair->value);
+			} 
+
+			if(yd->cookie_y && yd->cookie_t && yd->cookie_c)
+				YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id);
+
+			break;
+		case 3: /* my id */
+		case 90: /* 1 */
+		case 100: /* 0 */
+		case 101: /* NULL */
+		case 102: /* NULL */
+		case 93: /* 86400/1440 */
+			break;
+		}
+	}
+}
+
+static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+
+	if(pkt->status != 0x01) {
+		DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status));
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, "");
+		return;
+	}
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+
+}
+
+static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid, 
+		const char *seed, const char *sn)
+{
+	struct yahoo_data *yd = yid->yd;
+	
+	/* So, Yahoo has stopped supporting its older clients in India, and 
+	 * undoubtedly will soon do so in the rest of the world.
+	 * 
+	 * The new clients use this authentication method.  I warn you in 
+	 * advance, it's bizzare, convoluted, inordinately complicated.  
+	 * It's also no more secure than crypt() was.  The only purpose this 
+	 * scheme could serve is to prevent third part clients from connecting 
+	 * to their servers.
+	 *
+	 * Sorry, Yahoo.
+	 */
+
+	struct yahoo_packet *pack;
+	
+	md5_byte_t result[16];
+	md5_state_t ctx;
+	char *crypt_result;
+	unsigned char *password_hash = malloc(25);
+	unsigned char *crypt_hash = malloc(25);
+	unsigned char *hash_string_p = malloc(50 + strlen(sn));
+	unsigned char *hash_string_c = malloc(50 + strlen(sn));
+	
+	char checksum;
+	
+	int sv;
+	
+	unsigned char *result6 = malloc(25);
+	unsigned char *result96 = malloc(25);
+
+	sv = seed[15];
+	sv = (sv % 8) % 5;
+
+	md5_init(&ctx);
+	md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password));
+	md5_finish(&ctx, result);
+	to_y64(password_hash, result, 16);
+	
+	md5_init(&ctx);
+	crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");  
+	md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
+	md5_finish(&ctx, result);
+	to_y64(crypt_hash, result, 16);
+	free(crypt_result);
+
+	switch (sv) {
+	case 0:
+		checksum = seed[seed[7] % 16];
+		snprintf((char *)hash_string_p, strlen(sn) + 50,
+			"%c%s%s%s", checksum, password_hash, yd->user, seed);
+		snprintf((char *)hash_string_c, strlen(sn) + 50,
+			"%c%s%s%s", checksum, crypt_hash, yd->user, seed);
+		break;
+	case 1:
+		checksum = seed[seed[9] % 16];
+		snprintf((char *)hash_string_p, strlen(sn) + 50,
+			"%c%s%s%s", checksum, yd->user, seed, password_hash);
+		snprintf((char *)hash_string_c, strlen(sn) + 50,
+			"%c%s%s%s", checksum, yd->user, seed, crypt_hash);
+		break;
+	case 2:
+		checksum = seed[seed[15] % 16];
+		snprintf((char *)hash_string_p, strlen(sn) + 50,
+			"%c%s%s%s", checksum, seed, password_hash, yd->user);
+		snprintf((char *)hash_string_c, strlen(sn) + 50,
+			"%c%s%s%s", checksum, seed, crypt_hash, yd->user);
+		break;
+	case 3:
+		checksum = seed[seed[1] % 16];
+		snprintf((char *)hash_string_p, strlen(sn) + 50,
+			"%c%s%s%s", checksum, yd->user, password_hash, seed);
+		snprintf((char *)hash_string_c, strlen(sn) + 50,
+			"%c%s%s%s", checksum, yd->user, crypt_hash, seed);
+		break;
+	case 4:
+		checksum = seed[seed[3] % 16];
+		snprintf((char *)hash_string_p, strlen(sn) + 50,
+			"%c%s%s%s", checksum, password_hash, seed, yd->user);
+		snprintf((char *)hash_string_c, strlen(sn) + 50,
+			"%c%s%s%s", checksum, crypt_hash, seed, yd->user);
+		break;
+	}
+		
+	md5_init(&ctx);  
+	md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p));
+	md5_finish(&ctx, result);
+	to_y64(result6, result, 16);
+
+	md5_init(&ctx);  
+	md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c));
+	md5_finish(&ctx, result);
+	to_y64(result96, result, 16);
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+	yahoo_packet_hash(pack, 0, yd->user);
+	yahoo_packet_hash(pack, 6, (char *)result6);
+	yahoo_packet_hash(pack, 96, (char *)result96);
+	yahoo_packet_hash(pack, 1, yd->user);
+		
+	yahoo_send_packet(yid, pack, 0);
+		
+	FREE(result6);
+	FREE(result96);
+	FREE(password_hash);
+	FREE(crypt_hash);
+	FREE(hash_string_p);
+	FREE(hash_string_c);
+
+	yahoo_packet_free(pack);
+
+}
+
+/*
+ * New auth protocol cracked by Cerulean Studios and sent in to Gaim
+ */
+static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn)
+{
+	struct yahoo_packet *pack = NULL;
+	struct yahoo_data *yd = yid->yd;
+
+	md5_byte_t         result[16];
+	md5_state_t        ctx;
+
+	SHA_CTX            ctx1;
+	SHA_CTX            ctx2;
+
+	char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ";
+	char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop";
+
+	char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5";
+	char *operand_lookup = "+|&%/*^-";
+	char *delimit_lookup = ",;";
+
+	unsigned char *password_hash = malloc(25);
+	unsigned char *crypt_hash = malloc(25);
+	char *crypt_result = NULL;
+	unsigned char pass_hash_xor1[64];
+	unsigned char pass_hash_xor2[64];
+	unsigned char crypt_hash_xor1[64];
+	unsigned char crypt_hash_xor2[64];
+	unsigned char chal[7];
+	char resp_6[100];
+	char resp_96[100];
+
+	unsigned char digest1[20];
+	unsigned char digest2[20];
+	unsigned char magic_key_char[4];
+	const unsigned char *magic_ptr;
+
+	unsigned int  magic[64];
+	unsigned int  magic_work=0;
+
+	char comparison_src[20];
+
+	int x, j, i;
+	int cnt = 0;
+	int magic_cnt = 0;
+	int magic_len;
+	int depth =0, table =0;
+
+	memset(&pass_hash_xor1, 0, 64);
+	memset(&pass_hash_xor2, 0, 64);
+	memset(&crypt_hash_xor1, 0, 64);
+	memset(&crypt_hash_xor2, 0, 64);
+	memset(&digest1, 0, 20);
+	memset(&digest2, 0, 20);
+	memset(&magic, 0, 64);
+	memset(&resp_6, 0, 100);
+	memset(&resp_96, 0, 100);
+	memset(&magic_key_char, 0, 4);
+
+	/* 
+	 * Magic: Phase 1.  Generate what seems to be a 30 
+	 * byte value (could change if base64
+	 * ends up differently?  I don't remember and I'm 
+	 * tired, so use a 64 byte buffer.
+	 */
+
+	magic_ptr = (unsigned char *)seed;
+
+	while (*magic_ptr != (int)NULL) {
+		char *loc;
+
+		/* Ignore parentheses.  */
+
+		if (*magic_ptr == '(' || *magic_ptr == ')') {
+			magic_ptr++;
+			continue;
+		}
+
+		/* Characters and digits verify against 
+		   the challenge lookup.
+		*/
+
+		if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) {
+			loc = strchr(challenge_lookup, *magic_ptr);
+			if (!loc) {
+				/* This isn't good */
+				continue;
+			}
+
+			/* Get offset into lookup table and lsh 3. */
+
+			magic_work = loc - challenge_lookup;
+			magic_work <<= 3;
+
+			magic_ptr++;
+			continue;
+		} else {
+			unsigned int local_store;
+
+			loc = strchr(operand_lookup, *magic_ptr);
+			if (!loc) {
+				/* Also not good. */
+				continue;
+			}
+
+			local_store = loc - operand_lookup;
+
+			/* Oops; how did this happen? */
+			if (magic_cnt >= 64) 
+				break;
+
+			magic[magic_cnt++] = magic_work | local_store;
+			magic_ptr++;
+			continue;
+		}
+	}
+
+	magic_len = magic_cnt;
+	magic_cnt = 0;
+
+	/* Magic: Phase 2.  Take generated magic value and 
+	 * sprinkle fairy dust on the values. */
+
+	for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) {
+		unsigned char byte1;
+		unsigned char byte2;
+
+		/* Bad.  Abort.
+		 */
+		if (magic_cnt >= magic_len) {
+			WARNING(("magic_cnt(%d)  magic_len(%d)", magic_cnt, magic_len))
+			break;
+		}
+
+		byte1 = magic[magic_cnt];
+		byte2 = magic[magic_cnt+1];
+
+		byte1 *= 0xcd;
+		byte1 ^= byte2;
+
+		magic[magic_cnt+1] = byte1;
+	}
+
+	/* Magic: Phase 3.  This computes 20 bytes.  The first 4 bytes are used as our magic 
+	 * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key 
+	 * plus 3 bytes.  The 3 bytes are found by looping, and they represent the offsets 
+	 * into particular functions we'll later call to potentially alter the magic key. 
+	 * 
+	 * %-) 
+	 */ 
+	
+	magic_cnt = 1; 
+	x = 0; 
+	
+	do { 
+		unsigned int     bl = 0;  
+		unsigned int     cl = magic[magic_cnt++]; 
+		
+		if (magic_cnt >= magic_len) 
+			break; 
+		
+		if (cl > 0x7F) { 
+			if (cl < 0xe0)  
+				bl = cl = (cl & 0x1f) << 6;  
+			else { 
+				bl = magic[magic_cnt++];  
+                              cl = (cl & 0x0f) << 6;  
+                              bl = ((bl & 0x3f) + cl) << 6;  
+			}  
+			
+			cl = magic[magic_cnt++];  
+			bl = (cl & 0x3f) + bl;  
+		} else 
+			bl = cl;  
+		
+		comparison_src[x++] = (bl & 0xff00) >> 8;  
+		comparison_src[x++] = bl & 0xff;  
+	} while (x < 20); 
+
+	/* Dump magic key into a char for SHA1 action. */
+	
+		
+	for(x = 0; x < 4; x++) 
+		magic_key_char[x] = comparison_src[x];
+
+	/* Compute values for recursive function table! */
+	memcpy( chal, magic_key_char, 4 );
+        x = 1;
+	for( i = 0; i < 0xFFFF && x; i++ )
+	{
+		for( j = 0; j < 5 && x; j++ )
+		{
+			chal[4] = i;
+			chal[5] = i >> 8;
+			chal[6] = j;
+			md5_init( &ctx );
+			md5_append( &ctx, chal, 7 );
+			md5_finish( &ctx, result );
+			if( memcmp( comparison_src + 4, result, 16 ) == 0 )
+			{
+				depth = i;
+				table = j;
+				x = 0;
+			}
+		}
+	}
+
+	/* Transform magic_key_char using transform table */
+	x = magic_key_char[3] << 24  | magic_key_char[2] << 16 
+		| magic_key_char[1] << 8 | magic_key_char[0];
+	x = yahoo_xfrm( table, depth, x );
+	x = yahoo_xfrm( table, depth, x );
+	magic_key_char[0] = x & 0xFF;
+	magic_key_char[1] = x >> 8 & 0xFF;
+	magic_key_char[2] = x >> 16 & 0xFF;
+	magic_key_char[3] = x >> 24 & 0xFF;
+
+	/* Get password and crypt hashes as per usual. */
+	md5_init(&ctx);
+	md5_append(&ctx, (md5_byte_t *)yd->password,  strlen(yd->password));
+	md5_finish(&ctx, result);
+	to_y64(password_hash, result, 16);
+
+	md5_init(&ctx);
+	crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$");  
+	md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result));
+	md5_finish(&ctx, result);
+	to_y64(crypt_hash, result, 16);
+	free(crypt_result);
+
+	/* Our first authentication response is based off 
+	 * of the password hash. */
+
+	for (x = 0; x < (int)strlen((char *)password_hash); x++) 
+		pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36;
+
+	if (cnt < 64) 
+		memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt);
+
+	cnt = 0;
+
+	for (x = 0; x < (int)strlen((char *)password_hash); x++) 
+		pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c;
+
+	if (cnt < 64) 
+		memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+	shaInit(&ctx1);
+	shaInit(&ctx2);
+
+	/* The first context gets the password hash XORed 
+	 * with 0x36 plus a magic value
+	 * which we previously extrapolated from our 
+	 * challenge. */
+
+	shaUpdate(&ctx1, pass_hash_xor1, 64);
+	if (j >= 3 )
+		ctx1.sizeLo = 0x1ff;
+	shaUpdate(&ctx1, magic_key_char, 4);
+	shaFinal(&ctx1, digest1);
+
+	 /* The second context gets the password hash XORed 
+	  * with 0x5c plus the SHA-1 digest
+	  * of the first context. */
+
+	shaUpdate(&ctx2, pass_hash_xor2, 64);
+	shaUpdate(&ctx2, digest1, 20);
+	shaFinal(&ctx2, digest2);
+
+	/* Now that we have digest2, use it to fetch 
+	 * characters from an alphabet to construct
+	 * our first authentication response. */
+
+	for (x = 0; x < 20; x += 2) {
+		unsigned int    val = 0;
+		unsigned int    lookup = 0;
+		char            byte[6];
+
+		memset(&byte, 0, 6);
+
+		/* First two bytes of digest stuffed 
+		 *  together.
+		 */
+
+		val = digest2[x];
+		val <<= 8;
+		val += digest2[x+1];
+
+		lookup = (val >> 0x0b);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet1))
+			break;
+		sprintf(byte, "%c", alphabet1[lookup]);
+		strcat(resp_6, byte);
+		strcat(resp_6, "=");
+
+		lookup = (val >> 0x06);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet2))
+			break;
+		sprintf(byte, "%c", alphabet2[lookup]);
+		strcat(resp_6, byte);
+
+		lookup = (val >> 0x01);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet2))
+			break;
+		sprintf(byte, "%c", alphabet2[lookup]);
+		strcat(resp_6, byte);
+
+		lookup = (val & 0x01);
+		if (lookup >= strlen(delimit_lookup))
+			break;
+		sprintf(byte, "%c", delimit_lookup[lookup]);
+		strcat(resp_6, byte);
+	}
+
+	/* Our second authentication response is based off 
+	 * of the crypto hash. */
+
+	cnt = 0;
+	memset(&digest1, 0, 20);
+	memset(&digest2, 0, 20);
+
+	for (x = 0; x < (int)strlen((char *)crypt_hash); x++) 
+		crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36;
+
+	if (cnt < 64) 
+		memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt);
+
+	cnt = 0;
+
+	for (x = 0; x < (int)strlen((char *)crypt_hash); x++) 
+		crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c;
+
+	if (cnt < 64) 
+		memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt);
+
+	shaInit(&ctx1);
+	shaInit(&ctx2);
+
+	/* The first context gets the password hash XORed 
+	 * with 0x36 plus a magic value
+	 * which we previously extrapolated from our 
+	 * challenge. */
+
+	shaUpdate(&ctx1, crypt_hash_xor1, 64);
+	if (j >= 3 )
+		ctx1.sizeLo = 0x1ff;
+	shaUpdate(&ctx1, magic_key_char, 4);
+	shaFinal(&ctx1, digest1);
+
+	/* The second context gets the password hash XORed 
+	 * with 0x5c plus the SHA-1 digest
+	 * of the first context. */
+
+	shaUpdate(&ctx2, crypt_hash_xor2, 64);
+	shaUpdate(&ctx2, digest1, 20);
+	shaFinal(&ctx2, digest2);
+
+	/* Now that we have digest2, use it to fetch 
+	 * characters from an alphabet to construct
+	 * our first authentication response.  */
+
+	for (x = 0; x < 20; x += 2) {
+		unsigned int val = 0;
+		unsigned int lookup = 0;
+
+		char byte[6];
+
+		memset(&byte, 0, 6);
+
+		/* First two bytes of digest stuffed 
+		 *  together. */
+
+		val = digest2[x];
+		val <<= 8;
+		val += digest2[x+1];
+
+		lookup = (val >> 0x0b);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet1))
+			break;
+		sprintf(byte, "%c", alphabet1[lookup]);
+		strcat(resp_96, byte);
+		strcat(resp_96, "=");
+
+		lookup = (val >> 0x06);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet2))
+			break;
+		sprintf(byte, "%c", alphabet2[lookup]);
+		strcat(resp_96, byte);
+
+		lookup = (val >> 0x01);
+		lookup &= 0x1f;
+		if (lookup >= strlen(alphabet2))
+			break;
+		sprintf(byte, "%c", alphabet2[lookup]);
+		strcat(resp_96, byte);
+
+		lookup = (val & 0x01);
+		if (lookup >= strlen(delimit_lookup))
+			break;
+		sprintf(byte, "%c", delimit_lookup[lookup]);
+		strcat(resp_96, byte);
+	}
+
+	pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id);
+	yahoo_packet_hash(pack, 0, sn);
+	yahoo_packet_hash(pack, 6, resp_6);
+	yahoo_packet_hash(pack, 96, resp_96);
+	yahoo_packet_hash(pack, 1, sn);
+	yahoo_send_packet(yid, pack, 0);
+	yahoo_packet_free(pack);
+
+	free(password_hash);
+	free(crypt_hash);
+}
+
+static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char *seed = NULL;
+	char *sn   = NULL;
+	YList *l = pkt->hash;
+	int m = 0;
+
+	while (l) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 94)
+			seed = pair->value;
+		if (pair->key == 1)
+			sn = pair->value;
+		if (pair->key == 13)
+			m = atoi(pair->value);
+		l = l->next;
+	}
+
+	if (!seed) 
+		return;
+
+	switch (m) {
+		case 0:
+			yahoo_process_auth_pre_0x0b(yid, seed, sn);
+			break;
+		case 1:
+			yahoo_process_auth_0x0b(yid, seed, sn);
+			break;
+		default:
+			/* call error */
+			WARNING(("unknown auth type %d", m));
+			yahoo_process_auth_0x0b(yid, seed, sn);
+			break;
+	}
+}
+
+static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *login_id;
+	char *handle;
+	char *url=NULL;
+	int  login_status=0;
+
+	YList *l;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 0)
+			login_id = pair->value;
+		else if (pair->key == 1)
+			handle = pair->value;
+		else if (pair->key == 20)
+			url = pair->value;
+		else if (pair->key == 66)
+			login_status = atoi(pair->value);
+	}
+
+	if(pkt->status == 0xffffffff) {
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url);
+	/*	yahoo_logoff(yd->client_id);*/
+	}
+}
+
+static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *who = NULL;
+	char *email = NULL;
+	char *subj = NULL;
+	int count = 0;
+	YList *l;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 9)
+			count = strtol(pair->value, NULL, 10);
+		else if (pair->key == 43)
+			who = pair->value;
+		else if (pair->key == 42)
+			email = pair->value;
+		else if (pair->key == 18)
+			subj = pair->value;
+		else
+			LOG(("key: %d => value: %s", pair->key, pair->value));
+	}
+
+	if (who && email && subj) {
+		char from[1024];
+		snprintf(from, sizeof(from), "%s (%s)", who, email);
+		YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count);
+	} else if(count > 0)
+		YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count);
+}
+
+static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *id = NULL;
+	char *who = NULL;
+	char *msg = NULL;
+	char *name = NULL;
+	long tm = 0L;
+	int state = YAHOO_STATUS_AVAILABLE;
+	int online = FALSE;
+	int away = 0;
+
+	YList *l;
+
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 1)
+			id = pair->value;
+		else if (pair->key == 3)
+			who = pair->value;
+		else if (pair->key == 14)
+			msg = pair->value;
+		else if (pair->key == 7)
+			name = pair->value;
+		else if (pair->key == 10)
+			state = strtol(pair->value, NULL, 10);
+		else if (pair->key == 15)
+			tm = strtol(pair->value, NULL, 10);
+		else if (pair->key == 13)
+			online = strtol(pair->value, NULL, 10);
+		else if (pair->key == 47)
+			away = strtol(pair->value, NULL, 10);
+	}
+
+	if (id)
+		YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg);
+	else if (name)
+		YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away);
+	else if(pkt->status == 0x07)
+		YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg);
+}
+
+static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *who = NULL;
+	char *where = NULL;
+	int status = 0;
+	char *me = NULL;
+
+	struct yahoo_buddy *bud=NULL;
+
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 1)
+			me = pair->value;
+		if (pair->key == 7)
+			who = pair->value;
+		if (pair->key == 65)
+			where = pair->value;
+		if (pair->key == 66)
+			status = strtol(pair->value, NULL, 10);
+	}
+
+	yahoo_dump_unhandled(pkt);
+
+	if(!who)
+		return;
+	if(!where)
+		where = "Unknown";
+
+	bud = y_new0(struct yahoo_buddy, 1);
+	bud->id = strdup(who);
+	bud->group = strdup(where);
+	bud->real_name = NULL;
+
+	yd->buddies = y_list_append(yd->buddies, bud);
+
+/*	YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */
+}
+
+static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	struct yahoo_data *yd = yid->yd;
+	char *who = NULL;
+	char *where = NULL;
+	int unk_66 = 0;
+	char *me = NULL;
+	struct yahoo_buddy *bud;
+
+	YList *buddy;
+
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 1)
+			me = pair->value;
+		else if (pair->key == 7)
+			who = pair->value;
+		else if (pair->key == 65)
+			where = pair->value;
+		else if (pair->key == 66)
+			unk_66 = strtol(pair->value, NULL, 10);
+		else
+			DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value));
+	}
+
+	if(!who || !where)
+		return;
+	
+	bud = y_new0(struct yahoo_buddy, 1);
+	bud->id = strdup(who);
+	bud->group = strdup(where);
+
+	buddy = y_list_find_custom(yd->buddies, bud, is_same_bud);
+
+	FREE(bud->id);
+	FREE(bud->group);
+	FREE(bud);
+
+	if(buddy) {
+		bud = buddy->data;
+		yd->buddies = y_list_remove_link(yd->buddies, buddy);
+		y_list_free_1(buddy);
+
+		FREE(bud->id);
+		FREE(bud->group);
+		FREE(bud->real_name);
+		FREE(bud);
+
+		bud=NULL;
+	}
+}
+
+static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char *who = NULL;
+	int  status = 0;
+	char *me = NULL;
+	int  un_ignore = 0;
+
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 0)
+			who = pair->value;
+		if (pair->key == 1)
+			me = pair->value;
+		if (pair->key == 13) /* 1 == ignore, 2 == unignore */ 
+			un_ignore = strtol(pair->value, NULL, 10);
+		if (pair->key == 66) 
+			status = strtol(pair->value, NULL, 10);
+	}
+
+
+	/*
+	 * status
+	 * 	0  - ok
+	 * 	2  - already in ignore list, could not add
+	 * 	3  - not in ignore list, could not delete
+	 * 	12 - is a buddy, could not add
+	 */
+
+/*	if(status)
+		YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, status, who, 0);
+*/	
+}
+
+static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char *who = NULL;
+	char *me = NULL;
+	char *room = NULL;
+	char *voice_room = NULL;
+
+	YList *l;
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 4)
+			who = pair->value;
+		if (pair->key == 5)
+			me = pair->value;
+		if (pair->key == 13)
+			voice_room=pair->value;
+		if (pair->key == 57) 
+			room=pair->value;
+	}
+
+	NOTICE(("got voice chat invite from %s in %s", who, room));
+	/* 
+	 * send: s:0 1:me 5:who 57:room 13:1
+	 * ????  s:4 5:who 10:99 19:-1615114531
+	 * gotr: s:4 5:who 10:99 19:-1615114615
+	 * ????  s:1 5:me 4:who 57:room 13:3room
+	 * got:  s:1 5:me 4:who 57:room 13:1room
+	 * rej:  s:0 1:me 5:who 57:room 13:3
+	 * rejr: s:4 5:who 10:99 19:-1617114599
+	 */
+}
+
+static void _yahoo_webcam_get_server_connected(int fd, int error, void *d)
+{
+	struct yahoo_input_data *yid = d;
+	char *who = yid->wcm->user;
+	char *data = NULL;
+	char *packet = NULL;
+	unsigned char magic_nr[] = {0, 1, 0};
+	unsigned char header_len = 8;
+	unsigned int len = 0;
+	unsigned int pos = 0;
+
+	if(error || fd <= 0) {
+		FREE(who);
+		FREE(yid);
+		return;
+	}
+
+	yid->fd = fd;
+	inputs = y_list_prepend(inputs, yid);
+	
+	/* send initial packet */
+	if (who)
+		data = strdup("<RVWCFG>");
+	else
+		data = strdup("<RUPCFG>");
+	yahoo_add_to_send_queue(yid, data, strlen(data));
+	FREE(data);
+
+	/* send data */
+	if (who)
+	{
+		data = strdup("g=");
+		data = y_string_append(data, who);
+		data = y_string_append(data, "\r\n");
+	} else {
+		data = strdup("f=1\r\n");
+	}
+	len = strlen(data);
+	packet = y_new0(char, header_len + len);
+	packet[pos++] = header_len;
+	memcpy(packet + pos, magic_nr, sizeof(magic_nr));
+	pos += sizeof(magic_nr);
+	pos += yahoo_put32(packet + pos, len);
+	memcpy(packet + pos, data, len);
+	pos += len;
+	yahoo_add_to_send_queue(yid, packet, pos);
+	FREE(packet);
+	FREE(data);
+
+	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
+}
+
+static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key)
+{
+	struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1);
+	struct yahoo_server_settings *yss = y->yd->server_settings;
+
+	yid->type = YAHOO_CONNECTION_WEBCAM_MASTER;
+	yid->yd = y->yd;
+	yid->wcm = y_new0(struct yahoo_webcam, 1);
+	yid->wcm->user = who?strdup(who):NULL;
+	yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD;
+	yid->wcm->key = strdup(key);
+
+	YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port, 
+			_yahoo_webcam_get_server_connected, yid);
+
+}
+
+static YList *webcam_queue=NULL;
+static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	char *me = NULL;
+	char *key = NULL;
+	char *who = NULL;
+
+	YList *l;
+	yahoo_dump_unhandled(pkt);
+	for (l = pkt->hash; l; l = l->next) {
+		struct yahoo_pair *pair = l->data;
+		if (pair->key == 5)
+			me = pair->value;
+		if (pair->key == 61) 
+			key=pair->value;
+	}
+
+	l = webcam_queue;
+	if(!l)
+		return;
+	who = l->data;
+	webcam_queue = y_list_remove_link(webcam_queue, webcam_queue);
+	y_list_free_1(l);
+	yahoo_webcam_get_server(yid, who, key);
+	FREE(who);
+}
+
+static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt)
+{
+	DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service));
+	switch (pkt->service)
+	{
+	case YAHOO_SERVICE_USERSTAT:
+	case YAHOO_SERVICE_LOGON:
+	case YAHOO_SERVICE_LOGOFF:
+	case YAHOO_SERVICE_ISAWAY:
+	case YAHOO_SERVICE_ISBACK:
+	case YAHOO_SERVICE_GAMELOGON:
+	case YAHOO_SERVICE_GAMELOGOFF:
+	case YAHOO_SERVICE_IDACT:
+	case YAHOO_SERVICE_IDDEACT:
+		yahoo_process_status(yid, pkt);
+		break;
+	case YAHOO_SERVICE_NOTIFY:
+		yahoo_process_notify(yid, pkt);
+		break;
+	case YAHOO_SERVICE_MESSAGE:
+	case YAHOO_SERVICE_GAMEMSG:
+	case YAHOO_SERVICE_SYSMESSAGE:
+		yahoo_process_message(yid, pkt);
+		break;
+	case YAHOO_SERVICE_NEWMAIL:
+		yahoo_process_mail(yid, pkt);
+		break;
+	case YAHOO_SERVICE_NEWCONTACT:
+		yahoo_process_contact(yid, pkt);
+		break;
+	case YAHOO_SERVICE_LIST:
+		yahoo_process_list(yid, pkt);
+		break;
+	case YAHOO_SERVICE_VERIFY:
+		yahoo_process_verify(yid, pkt);
+		break;
+	case YAHOO_SERVICE_AUTH:
+		yahoo_process_auth(yid, pkt);
+		break;
+	case YAHOO_SERVICE_AUTHRESP:
+		yahoo_process_auth_resp(yid, pkt);
+		break;
+	case YAHOO_SERVICE_CONFINVITE:
+	case YAHOO_SERVICE_CONFADDINVITE:
+	case YAHOO_SERVICE_CONFDECLINE:
+	case YAHOO_SERVICE_CONFLOGON:
+	case YAHOO_SERVICE_CONFLOGOFF:
+	case YAHOO_SERVICE_CONFMSG:
+		yahoo_process_conference(yid, pkt);
+		break;
+	case YAHOO_SERVICE_CHATONLINE:
+	case YAHOO_SERVICE_CHATGOTO:
+	case YAHOO_SERVICE_CHATJOIN:
+	case YAHOO_SERVICE_CHATLEAVE:
+	case YAHOO_SERVICE_CHATEXIT:
+	case YAHOO_SERVICE_CHATLOGOUT:
+	case YAHOO_SERVICE_CHATPING:
+	case YAHOO_SERVICE_COMMENT:
+		yahoo_process_chat(yid, pkt);
+		break;
+	case YAHOO_SERVICE_P2PFILEXFER:
+	case YAHOO_SERVICE_FILETRANSFER:
+		yahoo_process_filetransfer(yid, pkt);
+		break;
+	case YAHOO_SERVICE_ADDBUDDY:
+		yahoo_process_buddyadd(yid, pkt);
+		break;
+	case YAHOO_SERVICE_REMBUDDY:
+		yahoo_process_buddydel(yid, pkt);
+		break;
+	case YAHOO_SERVICE_IGNORECONTACT:
+		yahoo_process_ignore(yid, pkt);
+		break;
+	case YAHOO_SERVICE_VOICECHAT:
+		yahoo_process_voicechat(yid, pkt);
+		break;
+	case YAHOO_SERVICE_WEBCAM:
+		yahoo_process_webcam_key(yid, pkt);
+		break;
+	case YAHOO_SERVICE_IDLE:
+	case YAHOO_SERVICE_MAILSTAT:
+	case YAHOO_SERVICE_CHATINVITE:
+	case YAHOO_SERVICE_CALENDAR:
+	case YAHOO_SERVICE_NEWPERSONALMAIL:
+	case YAHOO_SERVICE_ADDIDENT:
+	case YAHOO_SERVICE_ADDIGNORE:
+	case YAHOO_SERVICE_PING:
+	case YAHOO_SERVICE_GOTGROUPRENAME:
+	case YAHOO_SERVICE_GROUPRENAME:
+	case YAHOO_SERVICE_PASSTHROUGH2:
+	case YAHOO_SERVICE_CHATLOGON:
+	case YAHOO_SERVICE_CHATLOGOFF:
+	case YAHOO_SERVICE_CHATMSG:
+	case YAHOO_SERVICE_REJECTCONTACT:
+	case YAHOO_SERVICE_PEERTOPEER:
+		WARNING(("unhandled service 0x%02x", pkt->service));
+		yahoo_dump_unhandled(pkt);
+		break;
+	default:
+		WARNING(("unknown service 0x%02x", pkt->service));
+		yahoo_dump_unhandled(pkt);
+		break;
+	}
+}
+
+static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid)
+{
+	struct yahoo_packet *pkt;
+	struct yahoo_data *yd = yid->yd;
+	int pos = 0;
+	int pktlen;
+
+	if(!yd)
+		return NULL;
+
+	DEBUG_MSG(("rxlen is %d", yid->rxlen));
+	if (yid->rxlen < YAHOO_PACKET_HDRLEN) {
+		DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN"));
+		return NULL;
+	}
+
+	pos += 4; /* YMSG */
+	pos += 2;
+	pos += 2;
+
+	pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2;
+	DEBUG_MSG(("%d bytes to read, rxlen is %d", 
+			pktlen, yid->rxlen));
+
+	if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) {
+		DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen"));
+		return NULL;
+	}
+
+	LOG(("reading packet"));
+	yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen);
+
+	pkt = yahoo_packet_new(0, 0, 0);
+
+	pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2;
+	pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4;
+	DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service,
+				pkt->status));
+	pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4;
+
+	yd->session_id = pkt->id;
+
+	yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen);
+
+	yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen;
+	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	if (yid->rxlen>0) {
+		unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN 
+				+ pktlen, yid->rxlen);
+		FREE(yid->rxqueue);
+		yid->rxqueue = tmp;
+		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	} else {
+		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+		FREE(yid->rxqueue);
+	}
+
+	return pkt;
+}
+
+static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len)
+{
+	char *st, *en;
+	char *data = (char *)d;
+	data[len]='\0';
+
+	DEBUG_MSG(("Got yab: %s", data));
+	st = en = strstr(data, "userid=\"");
+	if(st) {
+		st += strlen("userid=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->id = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "fname=\"");
+	if(st) {
+		st += strlen("fname=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->fname = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "lname=\"");
+	if(st) {
+		st += strlen("lname=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->lname = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "nname=\"");
+	if(st) {
+		st += strlen("nname=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->nname = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "email=\"");
+	if(st) {
+		st += strlen("email=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->email = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "hphone=\"");
+	if(st) {
+		st += strlen("hphone=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->hphone = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "wphone=\"");
+	if(st) {
+		st += strlen("wphone=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->wphone = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "mphone=\"");
+	if(st) {
+		st += strlen("mphone=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->mphone = yahoo_xmldecode(st);
+	}
+
+	st = strstr(en, "dbid=\"");
+	if(st) {
+		st += strlen("dbid=\"");
+		en = strchr(st, '"'); *en++ = '\0';
+		yab->dbid = atoi(st);
+	}
+}
+
+static struct yab * yahoo_getyab(struct yahoo_input_data *yid)
+{
+	struct yab *yab = NULL;
+	int pos = 0, end=0;
+	struct yahoo_data *yd = yid->yd;
+
+	if(!yd)
+		return NULL;
+
+	DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+	if(yid->rxlen <= strlen("<record"))
+		return NULL;
+
+	/* start with <record */
+	while(pos < yid->rxlen-strlen("<record")+1 
+			&& memcmp(yid->rxqueue + pos, "<record", strlen("<record")))
+		pos++;
+
+	if(pos >= yid->rxlen-1)
+		return NULL;
+
+	end = pos+2;
+	/* end with /> */
+	while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>")))
+		end++;
+
+	if(end >= yid->rxlen-1)
+		return NULL;
+
+	yab = y_new0(struct yab, 1);
+	yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos);
+	
+
+	yid->rxlen -= end+1;
+	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	if (yid->rxlen>0) {
+		unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen);
+		FREE(yid->rxqueue);
+		yid->rxqueue = tmp;
+		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	} else {
+		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+		FREE(yid->rxqueue);
+	}
+
+
+	return yab;
+}
+
+static char * yahoo_getwebcam_master(struct yahoo_input_data *yid)
+{
+	unsigned int pos=0;
+	unsigned int len=0;
+	unsigned int status=0;
+	char *server=NULL;
+	struct yahoo_data *yd = yid->yd;
+
+	if(!yid || !yd)
+		return NULL;
+
+	DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+	len = yid->rxqueue[pos++];
+	if (yid->rxlen < len)
+		return NULL;
+
+	/* extract status (0 = ok, 6 = webcam not online) */
+	status = yid->rxqueue[pos++];
+
+	if (status == 0)
+	{
+		pos += 2; /* skip next 2 bytes */
+		server =  y_memdup(yid->rxqueue+pos, 16);
+		pos += 16;
+	}
+	else if (status == 6)
+	{
+		YAHOO_CALLBACK(ext_yahoo_webcam_closed)
+			(yd->client_id, yid->wcm->user, 4);
+	}
+
+	/* skip rest of the data */
+
+	yid->rxlen -= len;
+	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	if (yid->rxlen>0) {
+		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
+		FREE(yid->rxqueue);
+		yid->rxqueue = tmp;
+		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	} else {
+		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+		FREE(yid->rxqueue);
+	}
+
+	return server;
+}
+
+static int yahoo_get_webcam_data(struct yahoo_input_data *yid)
+{
+	unsigned char reason=0;
+	unsigned int pos=0;
+	unsigned int begin=0;
+	unsigned int end=0;
+	unsigned int closed=0;
+	unsigned char header_len=0;
+	char *who;
+	int connect=0;
+	struct yahoo_data *yd = yid->yd;
+
+	if(!yd)
+		return -1;
+
+	if(!yid->wcm || !yid->wcd || !yid->rxlen)
+		return -1;
+
+	DEBUG_MSG(("rxlen is %d", yid->rxlen));
+
+	/* if we are not reading part of image then read header */
+	if (!yid->wcd->to_read)
+	{
+		header_len=yid->rxqueue[pos++];
+		yid->wcd->packet_type=0;
+
+		if (yid->rxlen < header_len)
+			return 0;
+
+		if (header_len >= 8)
+		{
+			reason = yid->rxqueue[pos++];
+			/* next 2 bytes should always be 05 00 */
+			pos += 2;
+			yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos);
+			pos += 4;
+			yid->wcd->to_read = yid->wcd->data_size;
+		}
+		if (header_len >= 13)
+		{
+			yid->wcd->packet_type = yid->rxqueue[pos++];
+			yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos);
+			pos += 4;
+		}
+
+		/* skip rest of header */
+		pos = header_len;
+	}
+
+	begin = pos;
+	pos += yid->wcd->to_read;
+	if (pos > yid->rxlen) pos = yid->rxlen;
+
+	/* if it is not an image then make sure we have the whole packet */
+	if (yid->wcd->packet_type != 0x02) {
+		if ((pos - begin) != yid->wcd->data_size) {
+			yid->wcd->to_read = 0;
+			return 0;
+		} else {
+			yahoo_packet_dump(yid->rxqueue + begin, pos - begin);
+		}
+	}
+
+	DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type,
+		yid->wcd->data_size));
+
+	/* find out what kind of packet we got */
+	switch (yid->wcd->packet_type)
+	{
+		case 0x00:
+			/* user requests to view webcam (uploading) */
+			if (yid->wcd->data_size &&
+				yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) {
+				end = begin;
+				while (end <= yid->rxlen &&
+					yid->rxqueue[end++] != 13);
+				if (end > begin)
+				{
+					who = y_memdup(yid->rxqueue + begin, end - begin);
+					who[end - begin - 1] = 0;
+					YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2);
+					FREE(who);
+				}
+			}
+
+			if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) {
+				/* timestamp/status field */
+				/* 0 = declined viewing permission */
+				/* 1 = accepted viewing permission */
+				if (yid->wcd->timestamp == 0) {
+					YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3);
+				}
+			}
+			break;
+		case 0x01: /* status packets?? */
+			/* timestamp contains status info */
+			/* 00 00 00 01 = we have data?? */
+			break;
+		case 0x02: /* image data */
+			YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id, 
+					yid->wcm->user, yid->rxqueue + begin,
+					yid->wcd->data_size, pos - begin,
+					yid->wcd->timestamp);
+			break;
+		case 0x05: /* response packets when uploading */
+			if (!yid->wcd->data_size) {
+				YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp);
+			}
+			break;
+		case 0x07: /* connection is closing */
+			switch(reason)
+			{
+				case 0x01: /* user closed connection */
+					closed = 1;
+					break;
+				case 0x0F: /* user cancelled permission */
+					closed = 2;
+					break;
+			}
+			YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed);
+			break;
+		case 0x0C: /* user connected */
+		case 0x0D: /* user disconnected */
+			if (yid->wcd->data_size) {
+				who = y_memdup(yid->rxqueue + begin, pos - begin + 1);
+				who[pos - begin] = 0;
+				if (yid->wcd->packet_type == 0x0C)
+					connect=1;
+				else
+					connect=0;
+				YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect);
+				FREE(who);
+			}
+			break;
+		case 0x13: /* user data */
+			/* i=user_ip (ip of the user we are viewing) */
+			/* j=user_ext_ip (external ip of the user we */
+ 			/*                are viewing) */
+			break;
+		case 0x17: /* ?? */
+			break;
+	}
+	yid->wcd->to_read -= pos - begin;
+
+	yid->rxlen -= pos;
+	DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	if (yid->rxlen>0) {
+		unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen);
+		FREE(yid->rxqueue);
+		yid->rxqueue = tmp;
+		DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue));
+	} else {
+		DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue));
+		FREE(yid->rxqueue);
+	}
+
+	/* If we read a complete packet return success */
+	if (!yid->wcd->to_read)
+		return 1;
+
+	return 0;
+}
+
+int yahoo_write_ready(int id, int fd, void *data)
+{
+	struct yahoo_input_data *yid = data;
+	int len;
+	struct data_queue *tx;
+
+	LOG(("write callback: id=%d fd=%d data=%p", id, fd, data));
+	if(!yid || !yid->txqueues)
+		return -2;
+	
+	tx = yid->txqueues->data;
+	LOG(("writing %d bytes", tx->len));
+	len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len));
+
+	if(len == -1 && errno == EAGAIN)
+		return 1;
+
+	if(len <= 0) {
+		int e = errno;
+		DEBUG_MSG(("len == %d (<= 0)", len));
+		while(yid->txqueues) {
+			YList *l=yid->txqueues;
+			tx = l->data;
+			free(tx->queue);
+			free(tx);
+			yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
+			y_list_free_1(l);
+		}
+		LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd));
+		YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
+		yid->write_tag = 0;
+		errno=e;
+		return 0;
+	}
+
+
+	tx->len -= len;
+	if(tx->len > 0) {
+		unsigned char *tmp = y_memdup(tx->queue + len, tx->len);
+		FREE(tx->queue);
+		tx->queue = tmp;
+	} else {
+		YList *l=yid->txqueues;
+		free(tx->queue);
+		free(tx);
+		yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues);
+		y_list_free_1(l);
+		/*
+		if(!yid->txqueues) 
+			LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
+		*/
+		if(!yid->txqueues) {
+			LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd));
+			YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag);
+			yid->write_tag = 0;
+		}
+	}
+
+	return 1;
+}
+
+static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over)
+{
+	struct yahoo_packet *pkt;
+	struct yahoo_data *yd = yid->yd;
+	int id = yd->client_id;
+
+	if(over)
+		return;
+
+	while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) 
+			&& (pkt = yahoo_getdata(yid)) != NULL) {
+
+		yahoo_packet_process(yid, pkt);
+
+		yahoo_packet_free(pkt);
+	}
+}
+
+static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over)
+{
+}
+
+static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over)
+{
+	if(over)
+		return;
+
+	if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "</content>")) {
+		YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue);
+	}
+}
+
+static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over)
+{
+	struct yahoo_data *yd = yid->yd;
+	struct yab *yab;
+	YList *buds;
+	int changed=0;
+	int id = yd->client_id;
+
+	if(over)
+		return;
+
+	while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) 
+			&& (yab = yahoo_getyab(yid)) != NULL) {
+		if(!yab->id)
+			continue;
+		changed=1;
+		for(buds = yd->buddies; buds; buds=buds->next) {
+			struct yahoo_buddy * bud = buds->data;
+			if(!strcmp(bud->id, yab->id)) {
+				bud->yab_entry = yab;
+				if(yab->nname) {
+					bud->real_name = strdup(yab->nname);
+				} else if(yab->fname && yab->lname) {
+					bud->real_name = y_new0(char, 
+							strlen(yab->fname)+
+							strlen(yab->lname)+2
+							);
+					sprintf(bud->real_name, "%s %s",
+							yab->fname, yab->lname);
+				} else if(yab->fname) {
+					bud->real_name = strdup(yab->fname);
+				}
+				break; /* for */
+			}
+		}
+	}
+
+	if(changed)
+		YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies);
+}
+
+static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over)
+{
+	struct yahoo_found_contact *yct=NULL;
+	char *p = (char *)yid->rxqueue, *np, *cp;
+	int k, n;
+	int start=0, found=0, total=0;
+	YList *contacts=NULL;
+	struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER);
+
+	if(!over || !pyid)
+		return;
+
+	if(p && (p=strstr(p, "\r\n\r\n"))) {
+		p += 4;
+
+		for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) {
+			p++;
+			n = atoi(p);
+			switch(k) {
+				case 0: found = pyid->ys->lsearch_nfound = n; break;
+				case 2: start = pyid->ys->lsearch_nstart = n; break;
+				case 3: total = pyid->ys->lsearch_ntotal = n; break;
+			}
+		}
+
+		if(p)
+			p++;
+
+		k=0;
+		while(p && *p) {
+			cp = p;
+			np = strchr(p, 4);
+
+			if(!np)
+				break;
+			*np = 0;
+			p = np+1;
+
+			switch(k++) {
+				case 1:
+					if(strlen(cp) > 2 && y_list_length(contacts) < total) {
+						yct = y_new0(struct yahoo_found_contact, 1);
+						contacts = y_list_append(contacts, yct);
+						yct->id = cp+2;
+					} else {
+						*p = 0;
+					}
+					break;
+				case 2: 
+					yct->online = !strcmp(cp, "2") ? 1 : 0;
+					break;
+				case 3: 
+					yct->gender = cp;
+					break;
+				case 4: 
+					yct->age = atoi(cp);
+					break;
+				case 5: 
+					if(cp != "\005")
+						yct->location = cp;
+					k = 0;
+					break;
+			}
+		}
+	}
+
+	YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts);
+
+	while(contacts) {
+		YList *node = contacts;
+		contacts = y_list_remove_link(contacts, node);
+		free(node->data);
+		y_list_free_1(node);
+	}
+}
+
+static void _yahoo_webcam_connected(int fd, int error, void *d)
+{
+	struct yahoo_input_data *yid = d;
+	struct yahoo_webcam *wcm = yid->wcm;
+	struct yahoo_data *yd = yid->yd;
+	char conn_type[100];
+	char *data=NULL;
+	char *packet=NULL;
+	unsigned char magic_nr[] = {1, 0, 0, 0, 1};
+	unsigned header_len=0;
+	unsigned int len=0;
+	unsigned int pos=0;
+
+	if(error || fd <= 0) {
+		FREE(yid);
+		return;
+	}
+
+	yid->fd = fd;
+	inputs = y_list_prepend(inputs, yid);
+
+	LOG(("Connected"));
+	/* send initial packet */
+	switch (wcm->direction)
+	{
+		case YAHOO_WEBCAM_DOWNLOAD:
+			data = strdup("<REQIMG>");
+			break;
+		case YAHOO_WEBCAM_UPLOAD:	
+			data = strdup("<SNDIMG>");
+			break;
+		default:
+			return;
+	}
+	yahoo_add_to_send_queue(yid, data, strlen(data));
+	FREE(data);
+
+	/* send data */
+	switch (wcm->direction)
+	{
+		case YAHOO_WEBCAM_DOWNLOAD:
+			header_len = 8;
+			data = strdup("a=2\r\nc=us\r\ne=21\r\nu=");
+			data = y_string_append(data, yd->user);
+			data = y_string_append(data, "\r\nt=");
+			data = y_string_append(data, wcm->key);
+			data = y_string_append(data, "\r\ni=");
+			data = y_string_append(data, wcm->my_ip);
+			data = y_string_append(data, "\r\ng=");
+			data = y_string_append(data, wcm->user);
+			data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
+			snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
+			data = y_string_append(data, conn_type);
+			data = y_string_append(data, "\r\n");
+			break;
+		case YAHOO_WEBCAM_UPLOAD:
+			header_len = 13;
+			data = strdup("a=2\r\nc=us\r\nu=");
+			data = y_string_append(data, yd->user);
+			data = y_string_append(data, "\r\nt=");
+			data = y_string_append(data, wcm->key);
+			data = y_string_append(data, "\r\ni=");
+			data = y_string_append(data, wcm->my_ip);
+			data = y_string_append(data, "\r\no=w-2-5-1\r\np=");
+			snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type);
+			data = y_string_append(data, conn_type);
+			data = y_string_append(data, "\r\nb=");
+			data = y_string_append(data, wcm->description);
+			data = y_string_append(data, "\r\n");
+			break;
+	}
+
+	len = strlen(data);
+	packet = y_new0(char, header_len + len);
+	packet[pos++] = header_len;
+	packet[pos++] = 0;
+	switch (wcm->direction)
+	{
+		case YAHOO_WEBCAM_DOWNLOAD:
+			packet[pos++] = 1;
+			packet[pos++] = 0;
+			break;
+		case YAHOO_WEBCAM_UPLOAD:
+			packet[pos++] = 5;
+			packet[pos++] = 0;
+			break;
+	}
+
+	pos += yahoo_put32(packet + pos, len);
+	if (wcm->direction == YAHOO_WEBCAM_UPLOAD)
+	{
+		memcpy(packet + pos, magic_nr, sizeof(magic_nr));
+		pos += sizeof(magic_nr);
+	}
+	memcpy(packet + pos, data, len);
+	yahoo_add_to_send_queue(yid, packet, header_len + len);
+	FREE(packet);
+	FREE(data);
+
+	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
+}
+
+static void yahoo_webcam_connect(struct yahoo_input_data *y)
+{
+	struct yahoo_webcam *wcm = y->wcm;
+	struct yahoo_input_data *yid;
+	struct yahoo_server_settings *yss;
+
+	if (!wcm || !wcm->server || !wcm->key)
+		return;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->type = YAHOO_CONNECTION_WEBCAM;
+	yid->yd = y->yd;
+
+	/* copy webcam data to new connection */
+	yid->wcm = y->wcm;
+	y->wcm = NULL;
+
+	yss = y->yd->server_settings;
+
+	yid->wcd = y_new0(struct yahoo_webcam_data, 1);
+
+	LOG(("Connecting to: %s:%d", wcm->server, wcm->port));
+	YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port,
+			_yahoo_webcam_connected, yid);
+
+}
+
+static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over)
+{
+	char* server;
+	struct yahoo_server_settings *yss;
+
+	if(over)
+		return;
+
+	server = yahoo_getwebcam_master(yid);
+
+	if (server)
+	{
+		yss = yid->yd->server_settings;
+		yid->wcm->server = strdup(server);
+		yid->wcm->port = yss->webcam_port;
+		yid->wcm->conn_type = yss->conn_type;
+		yid->wcm->my_ip = strdup(yss->local_host);
+		if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD)
+			yid->wcm->description = strdup(yss->webcam_description);
+		yahoo_webcam_connect(yid);
+		FREE(server);
+	}
+}
+
+static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over)
+{
+	int id = yid->yd->client_id;
+	int fd = yid->fd;
+
+	if(over)
+		return;
+
+	/* as long as we still have packets available keep processing them */
+	while (find_input_by_id_and_fd(id, fd) 
+			&& yahoo_get_webcam_data(yid) == 1);
+}
+
+static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = {
+	yahoo_process_pager_connection,
+	yahoo_process_ft_connection,
+	yahoo_process_yab_connection,
+	yahoo_process_webcam_master_connection,
+	yahoo_process_webcam_connection,
+	yahoo_process_chatcat_connection,
+	yahoo_process_search_connection
+};
+
+int yahoo_read_ready(int id, int fd, void *data)
+{
+	struct yahoo_input_data *yid = data;
+	char buf[1024];
+	int len;
+
+	LOG(("read callback: id=%d fd=%d data=%p", id, fd, data));
+	if(!yid)
+		return -2;
+
+	
+	do {
+		len = read(fd, buf, sizeof(buf));
+	} while(len == -1 && errno == EINTR);
+
+	if(len == -1 && errno == EAGAIN)	/* we'll try again later */
+		return 1;
+
+	if (len <= 0) {
+		int e = errno;
+		DEBUG_MSG(("len == %d (<= 0)", len));
+
+		if(yid->type == YAHOO_CONNECTION_PAGER) {
+			YAHOO_CALLBACK(ext_yahoo_login_response)(yid->yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+		}
+
+		yahoo_process_connection[yid->type](yid, 1);
+		yahoo_input_close(yid);
+
+		/* no need to return an error, because we've already fixed it */
+		if(len == 0)
+			return 1;
+
+		errno=e;
+		LOG(("read error: %s", strerror(errno)));
+		return -1;
+	}
+
+	yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen);
+	memcpy(yid->rxqueue + yid->rxlen, buf, len);
+	yid->rxlen += len;
+
+	yahoo_process_connection[yid->type](yid, 0);
+
+	return len;
+}
+
+int yahoo_init_with_attributes(const char *username, const char *password, ...)
+{
+	va_list ap;
+	struct yahoo_data *yd;
+
+	yd = y_new0(struct yahoo_data, 1);
+
+	if(!yd)
+		return 0;
+
+	yd->user = strdup(username);
+	yd->password = strdup(password);
+
+	yd->initial_status = -1;
+	yd->current_status = -1;
+
+	yd->client_id = ++last_id;
+
+	add_to_list(yd);
+
+	va_start(ap, password);
+	yd->server_settings = _yahoo_assign_server_settings(ap);
+	va_end(ap);
+
+	return yd->client_id;
+}
+
+int yahoo_init(const char *username, const char *password)
+{
+	return yahoo_init_with_attributes(username, password, NULL);
+}
+
+struct connect_callback_data {
+	struct yahoo_data *yd;
+	int tag;
+	int i;
+};
+
+static void yahoo_connected(int fd, int error, void *data)
+{
+	struct connect_callback_data *ccd = data;
+	struct yahoo_data *yd = ccd->yd;
+	struct yahoo_packet *pkt;
+	struct yahoo_input_data *yid;
+	struct yahoo_server_settings *yss = yd->server_settings;
+
+	if(error) {
+		if(fallback_ports[ccd->i]) {
+			int tag;
+			yss->pager_port = fallback_ports[ccd->i++];
+			tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host,
+					yss->pager_port, yahoo_connected, ccd);
+
+			if(tag > 0)
+				ccd->tag=tag;
+		} else {
+			FREE(ccd);
+			YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+		}
+		return;
+	}
+
+	FREE(ccd);
+
+	/* fd < 0 && error == 0 means connect was cancelled */
+	if(fd < 0)
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	NOTICE(("Sending initial packet"));
+
+	yahoo_packet_hash(pkt, 1, yd->user);
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->yd = yd;
+	yid->fd = fd;
+	inputs = y_list_prepend(inputs, yid);
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+
+	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid);
+}
+
+void yahoo_login(int id, int initial)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct connect_callback_data *ccd;
+	struct yahoo_server_settings *yss;
+	int tag;
+
+	if(!yd)
+		return;
+
+	yss = yd->server_settings;
+
+	yd->initial_status = initial;
+
+	ccd = y_new0(struct connect_callback_data, 1);
+	ccd->yd = yd;
+	tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port, 
+			yahoo_connected, ccd);
+
+	/*
+	 * if tag <= 0, then callback has already been called
+	 * so ccd will have been freed
+	 */
+	if(tag > 0)
+		ccd->tag = tag;
+	else if(tag < 0)
+		YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL);
+}
+
+
+int yahoo_get_fd(int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	if(!yid)
+		return 0;
+	else
+		return yid->fd;
+}
+
+void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_packet *pkt = NULL;
+	struct yahoo_data *yd;
+
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id);
+
+	if(from && strcmp(from, yd->user))
+		yahoo_packet_hash(pkt, 0, yd->user);
+	yahoo_packet_hash(pkt, 1, from?from:yd->user);
+	yahoo_packet_hash(pkt, 5, who);
+	yahoo_packet_hash(pkt, 14, what);
+
+	if(utf8)
+		yahoo_packet_hash(pkt, 97, "1");
+
+	yahoo_packet_hash(pkt, 63, ";0");	/* imvironment name; or ;0 */
+	yahoo_packet_hash(pkt, 64, "0");
+
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_send_typing(int id, const char *from, const char *who, int typ)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id);
+
+	yahoo_packet_hash(pkt, 5, who);
+	yahoo_packet_hash(pkt, 4, from?from:yd->user);
+	yahoo_packet_hash(pkt, 14, " ");
+	yahoo_packet_hash(pkt, 13, typ ? "1" : "0");
+	yahoo_packet_hash(pkt, 49, "TYPING");
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+	int service;
+	char s[4];
+
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+
+	if (msg) {
+		yd->current_status = YAHOO_STATUS_CUSTOM;
+	} else {
+		yd->current_status = state;
+	}
+
+	if (yd->current_status == YAHOO_STATUS_AVAILABLE)
+		service = YAHOO_SERVICE_ISBACK;
+	else
+		service = YAHOO_SERVICE_ISAWAY;
+	pkt = yahoo_packet_new(service, yd->current_status, yd->session_id);
+	snprintf(s, sizeof(s), "%d", yd->current_status);
+	yahoo_packet_hash(pkt, 10, s);
+	if (yd->current_status == YAHOO_STATUS_CUSTOM) {
+		yahoo_packet_hash(pkt, 19, msg);
+		yahoo_packet_hash(pkt, 47, away?"1":"0");
+	}
+
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_logoff(int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	LOG(("yahoo_logoff: current status: %d", yd->current_status));
+
+	if(yd->current_status != -1) {
+		pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
+		yd->current_status = -1;
+
+		if (pkt) {
+			yahoo_send_packet(yid, pkt, 0);
+			yahoo_packet_free(pkt);
+		}
+	}
+
+	
+/*	do {
+		yahoo_input_close(yid);
+	} while((yid = find_input_by_id(id)));*/
+	
+}
+
+void yahoo_get_list(int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	if (pkt) {
+		yahoo_send_packet(yid, pkt, 0);
+		yahoo_packet_free(pkt);
+	}
+}
+
+static void _yahoo_http_connected(int id, int fd, int error, void *data)
+{
+	struct yahoo_input_data *yid = data;
+	if(fd <= 0) {
+		inputs = y_list_remove(inputs, yid);
+		FREE(yid);
+		return;
+	}
+
+	yid->fd = fd;
+	yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid);
+}
+
+void yahoo_get_yab(int id)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct yahoo_input_data *yid;
+	char url[1024];
+	char buff[1024];
+
+	if(!yd)
+		return;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->yd = yd;
+	yid->type = YAHOO_CONNECTION_YAB;
+
+	snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0");
+
+	snprintf(buff, sizeof(buff), "Y=%s; T=%s",
+			yd->cookie_y, yd->cookie_t);
+
+	inputs = y_list_prepend(inputs, yid);
+
+	yahoo_http_get(yid->yd->client_id, url, buff, 
+			_yahoo_http_connected, yid);
+}
+
+void yahoo_set_yab(int id, struct yab * yab)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct yahoo_input_data *yid;
+	char url[1024];
+	char buff[1024];
+	char *temp;
+	int size = sizeof(url)-1;
+
+	if(!yd)
+		return;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->type = YAHOO_CONNECTION_YAB;
+	yid->yd = yd;
+
+	strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size);
+
+	if(yab->dbid) {
+		/* change existing yab */
+		char tmp[32];
+		strncat(url, "&ee=1&ow=1&id=", size - strlen(url));
+		snprintf(tmp, sizeof(tmp), "%d", yab->dbid);
+		strncat(url, tmp, size - strlen(url));
+	}
+
+	if(yab->fname) {
+		strncat(url, "&fn=", size - strlen(url));
+		temp = yahoo_urlencode(yab->fname);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	if(yab->lname) {
+		strncat(url, "&ln=", size - strlen(url));
+		temp = yahoo_urlencode(yab->lname);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	strncat(url, "&yid=", size - strlen(url));
+	temp = yahoo_urlencode(yab->id);
+	strncat(url, temp, size - strlen(url));
+	free(temp);
+	if(yab->nname) {
+		strncat(url, "&nn=", size - strlen(url));
+		temp = yahoo_urlencode(yab->nname);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	if(yab->email) {
+		strncat(url, "&e=", size - strlen(url));
+		temp = yahoo_urlencode(yab->email);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	if(yab->hphone) {
+		strncat(url, "&hp=", size - strlen(url));
+		temp = yahoo_urlencode(yab->hphone);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	if(yab->wphone) {
+		strncat(url, "&wp=", size - strlen(url));
+		temp = yahoo_urlencode(yab->wphone);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	if(yab->mphone) {
+		strncat(url, "&mp=", size - strlen(url));
+		temp = yahoo_urlencode(yab->mphone);
+		strncat(url, temp, size - strlen(url));
+		free(temp);
+	}
+	strncat(url, "&pp=0", size - strlen(url));
+
+	snprintf(buff, sizeof(buff), "Y=%s; T=%s",
+			yd->cookie_y, yd->cookie_t);
+
+	inputs = y_list_prepend(inputs, yid);
+
+	yahoo_http_get(yid->yd->client_id, url, buff, 
+			_yahoo_http_connected, yid);
+}
+
+void yahoo_set_identity_status(int id, const char * identity, int active)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT,
+			YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 3, identity);
+	if (pkt) {
+		yahoo_send_packet(yid, pkt, 0);
+		yahoo_packet_free(pkt);
+	}
+}
+
+void yahoo_refresh(int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	if (pkt) {
+		yahoo_send_packet(yid, pkt, 0);
+		yahoo_packet_free(pkt);
+	}
+}
+
+void yahoo_keepalive(int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt=NULL;
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_chat_keepalive (int id)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if (!yid)
+	    return;
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_send_packet (yid, pkt, 0);
+	yahoo_packet_free (pkt);
+}
+
+void yahoo_add_buddy(int id, const char *who, const char *group)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	if (!yd->logged_in)
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 65, group);
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_remove_buddy(int id, const char *who, const char *group)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 65, group);
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_reject_buddy(int id, const char *who, const char *msg)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	if (!yd->logged_in)
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 14, msg);
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_ignore_buddy(int id, const char *who, int unignore)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	if (!yd->logged_in)
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 13, unignore?"2":"1");
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 65, new_group);
+	yahoo_packet_hash(pkt, 14, " ");
+
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 7, who);
+	yahoo_packet_hash(pkt, 65, old_group);
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_group_rename(int id, const char *old_group, const char *new_group)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt = NULL;
+
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id);
+	yahoo_packet_hash(pkt, 1, yd->user);
+	yahoo_packet_hash(pkt, 65, old_group);
+	yahoo_packet_hash(pkt, 67, new_group);
+
+	yahoo_send_packet(yid, pkt, 0);
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	yahoo_packet_hash(pkt, 51, who);
+	yahoo_packet_hash(pkt, 57, room);
+	yahoo_packet_hash(pkt, 58, msg);
+	yahoo_packet_hash(pkt, 13, "0");
+	for(; members; members = members->next) {
+		yahoo_packet_hash(pkt, 52, (char *)members->data);
+		yahoo_packet_hash(pkt, 53, (char *)members->data);
+	}
+	/* 52, 53 -> other members? */
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	yahoo_packet_hash(pkt, 50, yd->user);
+	for(; who; who = who->next) {
+		yahoo_packet_hash(pkt, 52, (char *)who->data);
+	}
+	yahoo_packet_hash(pkt, 57, room);
+	yahoo_packet_hash(pkt, 58, msg);
+	yahoo_packet_hash(pkt, 13, "0");
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_logon(int id, const char *from, YList *who, const char *room)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	for(; who; who = who->next) {
+		yahoo_packet_hash(pkt, 3, (char *)who->data);
+	}
+	yahoo_packet_hash(pkt, 57, room);
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	for(; who; who = who->next) {
+		yahoo_packet_hash(pkt, 3, (char *)who->data);
+	}
+	yahoo_packet_hash(pkt, 57, room);
+	yahoo_packet_hash(pkt, 14, msg);
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	for(; who; who = who->next) {
+		yahoo_packet_hash(pkt, 3, (char *)who->data);
+	}
+	yahoo_packet_hash(pkt, 57, room);
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	for(; who; who = who->next) {
+		yahoo_packet_hash(pkt, 53, (char *)who->data);
+	}
+	yahoo_packet_hash(pkt, 57, room);
+	yahoo_packet_hash(pkt, 14, msg);
+
+	if(utf8)
+		yahoo_packet_hash(pkt, 97, "1");
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_get_chatrooms(int id, int chatroomid)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct yahoo_input_data *yid;
+	char url[1024];
+	char buff[1024];
+
+	if(!yd)
+		return;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->yd = yd;
+	yid->type = YAHOO_CONNECTION_CHATCAT;
+
+	if (chatroomid == 0) {
+		snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0");
+	} else {
+		snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid);
+	}
+
+	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+
+	inputs = y_list_prepend(inputs, yid);
+
+	yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
+}
+
+void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	yahoo_packet_hash(pkt, 109, yd->user);
+	yahoo_packet_hash(pkt, 6, "abcde");
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	yahoo_packet_hash(pkt, 104, room);
+	yahoo_packet_hash(pkt, 129, roomid);
+	yahoo_packet_hash(pkt, 62, "2"); /* ??? */
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+
+void  yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+	char buf[2];
+		
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+	yahoo_packet_hash(pkt, 104, room);
+	yahoo_packet_hash(pkt, 117, msg);
+	
+	snprintf(buf, sizeof(buf), "%d", msgtype);
+	yahoo_packet_hash(pkt, 124, buf);
+
+	if(utf8)
+		yahoo_packet_hash(pkt, 97, "1");
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+
+void yahoo_chat_logoff(int id, const char *from)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, (from?from:yd->user));
+
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_webcam_close_feed(int id, const char *who)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who);
+
+	if(yid)
+		yahoo_input_close(yid);
+}
+
+void yahoo_webcam_get_feed(int id, const char *who)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_data *yd;
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+
+	/* 
+	 * add the user to the queue.  this is a dirty hack, since
+	 * the yahoo server doesn't tell us who's key it's returning,
+	 * we have to just hope that it sends back keys in the same 
+	 * order that we request them.
+	 * The queue is popped in yahoo_process_webcam_key
+	 */
+	webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL);
+
+	yd = yid->yd;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	yahoo_packet_hash(pkt, 1, yd->user);
+	if (who != NULL)
+		yahoo_packet_hash(pkt, 5, who);
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
+	unsigned char *packet;
+	unsigned char header_len = 13;
+	unsigned int pos = 0;
+
+	if (!yid)
+		return;
+
+	packet = y_new0(unsigned char, header_len);
+
+	packet[pos++] = header_len;
+	packet[pos++] = 0;
+	packet[pos++] = 5; /* version byte?? */
+	packet[pos++] = 0;
+	pos += yahoo_put32(packet + pos, length);
+	packet[pos++] = 2; /* packet type, image */
+	pos += yahoo_put32(packet + pos, timestamp);
+	yahoo_add_to_send_queue(yid, packet, header_len);
+	FREE(packet);
+
+	if (length)
+		yahoo_add_to_send_queue(yid, image, length);
+}
+
+void yahoo_webcam_accept_viewer(int id, const char* who, int accept)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM);
+	char *packet = NULL;
+	char *data = NULL;
+	unsigned char header_len = 13;
+	unsigned int pos = 0;
+	unsigned int len = 0;
+
+	if (!yid)
+		return;
+
+	data = strdup("u=");
+	data = y_string_append(data, (char*)who);
+	data = y_string_append(data, "\r\n");
+	len = strlen(data);
+
+	packet = y_new0(char, header_len + len);
+	packet[pos++] = header_len;
+	packet[pos++] = 0;
+	packet[pos++] = 5; /* version byte?? */
+	packet[pos++] = 0;
+	pos += yahoo_put32(packet + pos, len);
+	packet[pos++] = 0; /* packet type */
+	pos += yahoo_put32(packet + pos, accept);
+	memcpy(packet + pos, data, len);
+	FREE(data);
+	yahoo_add_to_send_queue(yid, packet, header_len + len);
+	FREE(packet);
+}
+
+void yahoo_webcam_invite(int id, const char *who)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_packet *pkt;
+		
+	if(!yid)
+		return;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id);
+
+	yahoo_packet_hash(pkt, 49, "WEBCAMINVITE");
+	yahoo_packet_hash(pkt, 14, " ");
+	yahoo_packet_hash(pkt, 13, "0");
+	yahoo_packet_hash(pkt, 1, yid->yd->user);
+	yahoo_packet_hash(pkt, 5, who);
+	yahoo_send_packet(yid, pkt, 0);
+
+	yahoo_packet_free(pkt);
+}
+
+static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct yahoo_input_data *yid;
+	char url[1024];
+	char buff[1024];
+	char *ctext, *p;
+
+	if(!yd)
+		return;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->yd = yd;
+	yid->type = YAHOO_CONNECTION_SEARCH;
+
+	/*
+	age range
+	.ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+
+	*/
+
+	snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos);
+
+	ctext = strdup(text);
+	while((p = strchr(ctext, ' ')))
+		*p = '+';
+
+	snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s",
+			ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "",
+			startpos ? buff : "");
+
+	FREE(ctext);
+
+	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+
+	inputs = y_list_prepend(inputs, yid);
+	yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid);
+}
+
+void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, 
+		int photo, int yahoo_only)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_search_state *yss;
+
+	if(!yid)
+		return;
+
+	if(!yid->ys)
+		yid->ys = y_new0(struct yahoo_search_state, 1);
+
+	yss = yid->ys;
+
+	FREE(yss->lsearch_text);
+	yss->lsearch_type = t;
+	yss->lsearch_text = strdup(text);
+	yss->lsearch_gender = g;
+	yss->lsearch_agerange = ar;
+	yss->lsearch_photo = photo;
+	yss->lsearch_yahoo_only = yahoo_only;
+
+	yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0);
+}
+
+void yahoo_search_again(int id, int start)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER);
+	struct yahoo_search_state *yss;
+
+	if(!yid || !yid->ys)
+		return;
+
+	yss = yid->ys;
+
+	if(start == -1)
+		start = yss->lsearch_nstart + yss->lsearch_nfound;
+
+	yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, 
+			yss->lsearch_gender, yss->lsearch_agerange, 
+			yss->lsearch_photo, yss->lsearch_yahoo_only, 
+			start, yss->lsearch_ntotal);
+}
+
+struct send_file_data {
+	struct yahoo_packet *pkt;
+	yahoo_get_fd_callback callback;
+	void *user_data;
+};
+
+static void _yahoo_send_file_connected(int id, int fd, int error, void *data)
+{
+	struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT);
+	struct send_file_data *sfd = data;
+	struct yahoo_packet *pkt = sfd->pkt;
+	unsigned char buff[1024];
+
+	if(fd <= 0) {
+		sfd->callback(id, fd, error, sfd->user_data);
+		FREE(sfd);
+		yahoo_packet_free(pkt);
+		inputs = y_list_remove(inputs, yid);
+		FREE(yid);
+		return;
+	}
+
+	yid->fd = fd;
+	yahoo_send_packet(yid, pkt, 8);
+	yahoo_packet_free(pkt);
+
+	snprintf((char *)buff, sizeof(buff), "29");
+	buff[2] = 0xc0;
+	buff[3] = 0x80;
+	
+	write(yid->fd, buff, 4);
+
+/*	YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */
+
+	sfd->callback(id, fd, error, sfd->user_data);
+	FREE(sfd);
+	inputs = y_list_remove(inputs, yid);
+	/*
+	while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) {
+		if(!strcmp(buff, ""))
+			break;
+	}
+
+	*/
+	yahoo_input_close(yid);
+}
+
+void yahoo_send_file(int id, const char *who, const char *msg, 
+		const char *name, unsigned long size, 
+		yahoo_get_fd_callback callback, void *data)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	struct yahoo_input_data *yid;
+	struct yahoo_server_settings *yss;
+	struct yahoo_packet *pkt = NULL;
+	char size_str[10];
+	long content_length=0;
+	unsigned char buff[1024];
+	char url[255];
+	struct send_file_data *sfd;
+
+	if(!yd)
+		return;
+
+	yss = yd->server_settings;
+
+	yid = y_new0(struct yahoo_input_data, 1);
+	yid->yd = yd;
+	yid->type = YAHOO_CONNECTION_FT;
+
+	pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id);
+
+	snprintf(size_str, sizeof(size_str), "%ld", size);
+
+	yahoo_packet_hash(pkt, 0, yd->user);
+	yahoo_packet_hash(pkt, 5, who);
+	yahoo_packet_hash(pkt, 14, msg);
+	yahoo_packet_hash(pkt, 27, name);
+	yahoo_packet_hash(pkt, 28, size_str);
+
+	content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt);
+
+	snprintf(url, sizeof(url), "http://%s:%d/notifyft", 
+			yss->filetransfer_host, yss->filetransfer_port);
+	snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s",
+			yd->cookie_y, yd->cookie_t);
+	inputs = y_list_prepend(inputs, yid);
+
+	sfd = y_new0(struct send_file_data, 1);
+	sfd->pkt = pkt;
+	sfd->callback = callback;
+	sfd->user_data = data;
+	yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size,
+			_yahoo_send_file_connected, sfd);
+}
+
+
+enum yahoo_status yahoo_current_status(int id)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return YAHOO_STATUS_OFFLINE;
+	return yd->current_status;
+}
+
+const YList * yahoo_get_buddylist(int id)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return NULL;
+	return yd->buddies;
+}
+
+const YList * yahoo_get_ignorelist(int id)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return NULL;
+	return yd->ignore;
+}
+
+const YList * yahoo_get_identities(int id)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return NULL;
+	return yd->identities;
+}
+
+const char * yahoo_get_cookie(int id, const char *which)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return NULL;
+	if(!strncasecmp(which, "y", 1))
+		return yd->cookie_y;
+	if(!strncasecmp(which, "t", 1))
+		return yd->cookie_t;
+	if(!strncasecmp(which, "c", 1))
+		return yd->cookie_c;
+	if(!strncasecmp(which, "login", 5))
+		return yd->login_cookie;
+	return NULL;
+}
+
+void yahoo_get_url_handle(int id, const char *url, 
+		yahoo_get_url_handle_callback callback, void *data)
+{
+	struct yahoo_data *yd = find_conn_by_id(id);
+	if(!yd)
+		return;
+
+	yahoo_get_url_fd(id, url, yd, callback, data);
+}
+
+const char * yahoo_get_profile_url( void )
+{
+	return profile_url;
+}
+
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,958 @@
+/*
+ * libyahoo2 wrapper to BitlBee
+ *
+ * Mostly Copyright 2004 Wilmer van der Gaast <wilmer@gaast.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include "nogaim.h"
+#include "yahoo2.h"
+#include "yahoo2_callbacks.h"
+
+#define BYAHOO_DEFAULT_GROUP "Buddies"
+
+/* A hack to handle removal of buddies not in the group "Buddies" correctly */
+struct byahoo_buddygroups
+{
+	char *buddy;
+	char *group;
+};
+
+struct byahoo_data
+{
+	int y2_id;
+	int current_status;
+	gboolean logged_in;
+	GSList *buddygroups;
+};
+
+struct byahoo_input_data
+{
+	int h;
+	void *d;
+};
+
+struct byahoo_conf_invitation
+{
+	char *name;
+	struct conversation *c;
+	int yid;
+	YList *members;
+	struct gaim_connection *gc;
+};
+
+static char *yahoo_name()
+{
+	return "Yahoo";
+}
+
+static struct prpl *my_protocol = NULL;
+static GSList *byahoo_inputs = NULL;
+static int byahoo_chat_id = 0;
+
+static char *byahoo_strip( char *in )
+{
+	int len;
+	
+	/* This should get rid of HTML tags at the beginning of the string. */
+	while( *in )
+	{
+		if( g_strncasecmp( in, "<font", 5 ) == 0 ||
+		    g_strncasecmp( in, "<fade", 5 ) == 0 ||
+		    g_strncasecmp( in, "<alt", 4 ) == 0 )
+		{
+			char *s = strchr( in, '>' );
+			if( !s )
+				break;
+			
+			in = s + 1;
+		}
+		else if( strncmp( in, "\e[", 2 ) == 0 )
+		{
+			char *s;
+			
+			for( s = in + 2; *s && *s != 'm'; s ++ );
+			
+			if( *s != 'm' )
+				break;
+			
+			in = s + 1;
+		}
+		else
+		{
+			break;
+		}
+	}
+	
+	/* This is supposed to get rid of the closing HTML tags at the end of the line. */
+	len = strlen( in );
+	while( len > 0 && in[len-1] == '>' )
+	{
+		int blen = len;
+		
+		len --;
+		while( len > 0 && ( in[len] != '<' || in[len+1] != '/' ) )
+			len --;
+		
+		if( len == 0 && ( in[len] != '<' || in[len+1] != '/' ) )
+		{
+			len = blen;
+			break;
+		}
+	}
+	
+	return( g_strndup( in, len ) );
+}
+
+static void byahoo_login( struct aim_user *user )
+{
+	struct gaim_connection *gc = new_gaim_conn( user );
+	struct byahoo_data *yd = gc->proto_data = g_new0( struct byahoo_data, 1 );
+	
+	yd->logged_in = FALSE;
+	yd->current_status = YAHOO_STATUS_AVAILABLE;
+	
+	set_login_progress( gc, 1, "Connecting" );
+	yd->y2_id = yahoo_init( user->username, user->password );
+	yahoo_login( yd->y2_id, yd->current_status );
+}
+
+static void byahoo_close( struct gaim_connection *gc )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	GSList *l;
+	
+	while( gc->conversations )
+		serv_got_chat_left( gc, gc->conversations->id );
+	
+	for( l = yd->buddygroups; l; l = l->next )
+	{
+		struct byahoo_buddygroups *bg = l->data;
+		
+		g_free( bg->buddy );
+		g_free( bg->group );
+		g_free( bg );
+	}
+	g_slist_free( yd->buddygroups );
+	
+	if( yd->logged_in )
+		yahoo_logoff( yd->y2_id );
+	else
+		yahoo_close( yd->y2_id );
+	
+	g_free( yd );
+}
+
+static void byahoo_get_info(struct gaim_connection *gc, char *who) 
+{
+	/* Just make an URL and let the user fetch the info */
+	serv_got_crap(gc, "%s\n%s: %s%s", _("User Info"), 
+			_("For now, fetch yourself"), yahoo_get_profile_url(),
+			who);
+}
+
+static int byahoo_send_im( struct gaim_connection *gc, char *who, char *what, int len, int flags )
+{
+	struct byahoo_data *yd = gc->proto_data;
+	
+	yahoo_send_im( yd->y2_id, NULL, who, what, 1 );
+	
+	return 1;
+}
+
+static int byahoo_send_typing( struct gaim_connection *gc, char *who, int typing )
+{
+	struct byahoo_data *yd = gc->proto_data;
+	
+	yahoo_send_typing( yd->y2_id, NULL, who, typing );
+	
+	return 1;
+}
+
+static void byahoo_set_away( struct gaim_connection *gc, char *state, char *msg )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+
+	gc->away = NULL;
+
+	if (msg)
+	{
+		yd->current_status = YAHOO_STATUS_CUSTOM;
+		gc->away = "";
+	}
+	else if (state)
+	{
+		gc->away = "";
+		if( g_strcasecmp(state, "Available" ) == 0 )
+		{
+			yd->current_status = YAHOO_STATUS_AVAILABLE;
+			gc->away = NULL;
+		}
+		else if( g_strcasecmp( state, "Be Right Back" ) == 0 )
+			yd->current_status = YAHOO_STATUS_BRB;
+		else if( g_strcasecmp( state, "Busy" ) == 0 )
+			yd->current_status = YAHOO_STATUS_BUSY;
+		else if( g_strcasecmp( state, "Not At Home" ) == 0 )
+			yd->current_status = YAHOO_STATUS_NOTATHOME;
+		else if( g_strcasecmp( state, "Not At Desk" ) == 0 )
+			yd->current_status = YAHOO_STATUS_NOTATDESK;
+		else if( g_strcasecmp( state, "Not In Office" ) == 0 )
+			yd->current_status = YAHOO_STATUS_NOTINOFFICE;
+		else if( g_strcasecmp( state, "On Phone" ) == 0 )
+			yd->current_status = YAHOO_STATUS_ONPHONE;
+		else if( g_strcasecmp( state, "On Vacation" ) == 0 )
+			yd->current_status = YAHOO_STATUS_ONVACATION;
+		else if( g_strcasecmp( state, "Out To Lunch" ) == 0 )
+			yd->current_status = YAHOO_STATUS_OUTTOLUNCH;
+		else if( g_strcasecmp( state, "Stepped Out" ) == 0 )
+			yd->current_status = YAHOO_STATUS_STEPPEDOUT;
+		else if( g_strcasecmp( state, "Invisible" ) == 0 )
+			yd->current_status = YAHOO_STATUS_INVISIBLE;
+		else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 )
+		{
+			if (gc->is_idle)
+				yd->current_status = YAHOO_STATUS_IDLE;
+			else
+				yd->current_status = YAHOO_STATUS_AVAILABLE;
+			
+			gc->away = NULL;
+		}
+	}
+	else if ( gc->is_idle )
+		yd->current_status = YAHOO_STATUS_IDLE;
+	else
+		yd->current_status = YAHOO_STATUS_AVAILABLE;
+	
+	yahoo_set_away( yd->y2_id, yd->current_status, msg, gc->away != NULL );
+}
+
+static GList *byahoo_away_states( struct gaim_connection *gc )
+{
+	GList *m = NULL;
+
+	m = g_list_append( m, "Available" );
+	m = g_list_append( m, "Be Right Back" );
+	m = g_list_append( m, "Busy" );
+	m = g_list_append( m, "Not At Home" );
+	m = g_list_append( m, "Not At Desk" );
+	m = g_list_append( m, "Not In Office" );
+	m = g_list_append( m, "On Phone" );
+	m = g_list_append( m, "On Vacation" );
+	m = g_list_append( m, "Out To Lunch" );
+	m = g_list_append( m, "Stepped Out" );
+	m = g_list_append( m, "Invisible" );
+	m = g_list_append( m, GAIM_AWAY_CUSTOM );
+	
+	return m;
+}
+
+static void byahoo_keepalive( struct gaim_connection *gc )
+{
+	struct byahoo_data *yd = gc->proto_data;
+	
+	yahoo_keepalive( yd->y2_id );
+}
+
+static void byahoo_add_buddy( struct gaim_connection *gc, char *who )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	
+	yahoo_add_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP );
+}
+
+static void byahoo_remove_buddy( struct gaim_connection *gc, char *who, char *group )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	GSList *bgl;
+	
+	yahoo_remove_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP );
+	
+	for( bgl = yd->buddygroups; bgl; bgl = bgl->next )
+	{
+		struct byahoo_buddygroups *bg = bgl->data;
+		
+		if( g_strcasecmp( bg->buddy, who ) == 0 )
+			yahoo_remove_buddy( yd->y2_id, who, bg->group );
+	}
+}
+
+static char *byahoo_get_status_string( struct gaim_connection *gc, int stat )
+{
+	enum yahoo_status a = stat >> 1;
+	
+	switch (a)
+	{
+	case YAHOO_STATUS_BRB:
+		return "Be Right Back";
+	case YAHOO_STATUS_BUSY:
+		return "Busy";
+	case YAHOO_STATUS_NOTATHOME:
+		return "Not At Home";
+	case YAHOO_STATUS_NOTATDESK:
+		return "Not At Desk";
+	case YAHOO_STATUS_NOTINOFFICE:
+		return "Not In Office";
+	case YAHOO_STATUS_ONPHONE:
+		return "On Phone";
+	case YAHOO_STATUS_ONVACATION:
+		return "On Vacation";
+	case YAHOO_STATUS_OUTTOLUNCH:
+		return "Out To Lunch";
+	case YAHOO_STATUS_STEPPEDOUT:
+		return "Stepped Out";
+	case YAHOO_STATUS_INVISIBLE:
+		return "Invisible";
+	case YAHOO_STATUS_CUSTOM:
+		return "Away";
+	case YAHOO_STATUS_IDLE:
+		return "Idle";
+	case YAHOO_STATUS_OFFLINE:
+		return "Offline";
+	case YAHOO_STATUS_NOTIFY:
+		return "Notify";
+	default:
+		return "Away";
+	}
+}
+
+static int byahoo_chat_send( struct gaim_connection *gc, int id, char *message )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && c->id != id; c = c->next );
+
+	yahoo_conference_message( yd->y2_id, NULL, c->data, c->title, message, 1 );
+	
+	return( 0 );
+}
+
+static void byahoo_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && c->id != id; c = c->next );
+	
+	yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg );
+}
+
+static void byahoo_chat_leave( struct gaim_connection *gc, int id )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && c->id != id; c = c->next );
+	
+	yahoo_conference_logoff( yd->y2_id, NULL, c->data, c->title );
+	serv_got_chat_left( gc, c->id );
+}
+
+static int byahoo_chat_open( struct gaim_connection *gc, char *who )
+{
+	struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data;
+	struct conversation *c;
+	char *roomname;
+	YList *members;
+	
+	roomname = g_new0( char, strlen( gc->username ) + 16 );
+	g_snprintf( roomname, strlen( gc->username ) + 16, "%s-Bee-%d", gc->username, byahoo_chat_id );
+	
+	c = serv_got_joined_chat( gc, ++byahoo_chat_id, roomname );
+	add_chat_buddy( c, gc->username );
+	
+	/* FIXME: Free this thing when the chat's destroyed. We can't *always*
+	          do this because it's not always created here. */
+	c->data = members = g_new0( YList, 1 );
+	members->data = g_strdup( who );
+	
+	yahoo_conference_invite( yd->y2_id, NULL, members, roomname, "Please join my groupchat..." );
+	
+	g_free( roomname );
+	
+	return( 1 );
+}
+
+void byahoo_init( struct prpl *ret )
+{
+	ret->protocol = PROTO_YAHOO;
+	ret->name = yahoo_name;
+	
+	ret->login = byahoo_login;
+	ret->close = byahoo_close;
+	ret->send_im = byahoo_send_im;
+	ret->send_typing = byahoo_send_typing;
+	ret->get_info = byahoo_get_info;
+	ret->away_states = byahoo_away_states;
+	ret->set_away = byahoo_set_away;
+	ret->keepalive = byahoo_keepalive;
+	ret->add_buddy = byahoo_add_buddy;
+	ret->remove_buddy = byahoo_remove_buddy;
+	ret->get_status_string = byahoo_get_status_string;
+	
+	ret->chat_send = byahoo_chat_send;
+	ret->chat_invite = byahoo_chat_invite;
+	ret->chat_leave = byahoo_chat_leave;
+	ret->chat_open = byahoo_chat_open;
+	
+	my_protocol = ret;
+}
+
+static struct gaim_connection *byahoo_get_gc_by_id( int id )
+{
+	GSList *l;
+	struct gaim_connection *gc;
+	struct byahoo_data *yd;
+	
+	for( l = get_connections(); l; l = l->next )
+	{
+		gc = l->data;
+		yd = gc->proto_data;
+		
+		if( gc->protocol == PROTO_YAHOO && yd->y2_id == id )
+			return( gc );
+	}
+	
+	return( NULL );
+}
+
+
+/* Now it's callback time! */
+
+struct byahoo_connect_callback_data
+{
+	int fd;
+	yahoo_connect_callback callback;
+	gpointer data;
+	int id;
+};
+
+void byahoo_connect_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct byahoo_connect_callback_data *d = data;
+	
+	if( !byahoo_get_gc_by_id( d->id ) )
+	{
+		g_free( d );
+		return;
+	}
+	
+	d->callback( d->fd, 0, d->data );
+	g_free( d );
+}
+
+struct byahoo_read_ready_data
+{
+	int id;
+	int fd;
+	int tag;
+	gpointer data;
+};
+
+void byahoo_read_ready_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct byahoo_read_ready_data *d = data;
+	
+	if( !byahoo_get_gc_by_id( d->id ) )
+	{
+		/* WTF doesn't libyahoo clean this up? */
+		ext_yahoo_remove_handler( d->id, d->tag );
+		return;
+	}
+	
+	yahoo_read_ready( d->id, d->fd, d->data );
+}
+
+struct byahoo_write_ready_data
+{
+	int id;
+	int fd;
+	int tag;
+	gpointer data;
+};
+
+void byahoo_write_ready_callback( gpointer data, gint source, GaimInputCondition cond )
+{
+	struct byahoo_write_ready_data *d = data;
+	
+	if( !byahoo_get_gc_by_id( d->id ) )
+	{
+		/* WTF doesn't libyahoo clean this up? */
+		ext_yahoo_remove_handler( d->id, d->tag );
+		return;
+	}
+	
+	yahoo_write_ready( d->id, d->fd, d->data );
+}
+
+void ext_yahoo_login_response( int id, int succ, char *url )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	struct byahoo_data *yd = NULL;
+	
+	if( gc == NULL )
+	{
+		/* libyahoo2 seems to call this one twice when something
+		   went wrong sometimes. Don't know why. Because we clean
+		   up the connection on the first failure, the second
+		   should be ignored. */
+		
+		return;
+	}
+	
+	yd = (struct byahoo_data *) gc->proto_data;
+	
+	if( succ == YAHOO_LOGIN_OK )
+	{
+		account_online( gc );
+		
+		yd->logged_in = TRUE;
+	}
+	else
+	{
+		char *errstr;
+		char *s;
+		
+		yd->logged_in = FALSE;
+		
+		if( succ == YAHOO_LOGIN_UNAME )
+			errstr = "Incorrect Yahoo! username";
+		else if( succ == YAHOO_LOGIN_PASSWD )
+			errstr = "Incorrect Yahoo! password";
+		else if( succ == YAHOO_LOGIN_LOCK )
+			errstr = "Yahoo! account locked";
+		else if( succ == YAHOO_LOGIN_DUPL )
+		{
+			errstr = "Logged in on a different machine or device";
+			gc->wants_to_die = TRUE;
+		}
+		else if( succ == YAHOO_LOGIN_SOCK )
+			errstr = "Socket problem";
+		else
+			errstr = "Unknown error";
+		
+		if( url && *url )
+		{
+			s = g_malloc( strlen( "Error %d (%s). See %s for more information." ) + strlen( url ) + strlen( errstr ) + 16 );
+			sprintf( s, "Error %d (%s). See %s for more information.", succ, errstr, url );
+		}
+		else
+		{
+			s = g_malloc( strlen( "Error %d (%s)" ) + strlen( errstr ) + 16 );
+			sprintf( s, "Error %d (%s)", succ, errstr );
+		}
+		
+		if( yd->logged_in )
+			hide_login_progress_error( gc, s );
+		else
+			hide_login_progress( gc, s );
+		
+		g_free( s );
+		
+		signoff( gc );
+	}
+}
+
+void ext_yahoo_got_buddies( int id, YList *buds )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	struct byahoo_data *yd = gc->proto_data;
+	YList *bl = buds;
+	
+	while( bl )
+	{
+		struct yahoo_buddy *b = bl->data;
+		struct byahoo_buddygroups *bg;
+		
+		if( strcmp( b->group, BYAHOO_DEFAULT_GROUP ) != 0 )
+		{
+			bg = g_new0( struct byahoo_buddygroups, 1 );
+			
+			bg->buddy = g_strdup( b->id );
+			bg->group = g_strdup( b->group );
+			yd->buddygroups = g_slist_append( yd->buddygroups, bg );
+		}
+		
+		add_buddy( gc, b->group, b->id, b->real_name );
+		bl = bl->next;
+	}
+}
+
+void ext_yahoo_got_ignore( int id, YList *igns )
+{
+}
+
+void ext_yahoo_got_identities( int id, YList *ids )
+{
+}
+
+void ext_yahoo_got_cookies( int id )
+{
+}
+
+void ext_yahoo_status_changed( int id, char *who, int stat, char *msg, int away )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_update( gc, who, stat != YAHOO_STATUS_OFFLINE, 0, 0, 0,
+	                 ( stat != YAHOO_STATUS_AVAILABLE ) | ( stat << 1 ), 0 );
+}
+
+void ext_yahoo_got_im( int id, char *who, char *msg, long tm, int stat, int utf8 )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	char *m = byahoo_strip( msg );
+	
+	serv_got_im( gc, who, m, 0, 0, strlen( m ) );
+	g_free( m );
+}
+
+void ext_yahoo_got_file( int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_crap( gc, "Got a file transfer (file = %s) from %s. Ignoring for now due to lack of support.", fname, who );
+}
+
+void ext_yahoo_typing_notify( int id, char *who, int stat )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_typing( gc, who, 1 );
+}
+
+void ext_yahoo_system_message( int id, char *msg )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_crap( gc, "Yahoo! system message: %s", msg );
+}
+
+void ext_yahoo_webcam_invite( int id, char *from )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_crap( gc, "Got a webcam invitation from %s. IRC+webcams is a no-no though...", from );
+}
+
+void ext_yahoo_error( int id, char *err, int fatal )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	if( fatal )
+	{
+		hide_login_progress_error( gc, err );
+		signoff( gc );
+	}
+	else
+	{
+		do_error_dialog( gc, err, "Yahoo! error" );
+	}
+}
+
+/* TODO: Clear up the mess of inp and d structures */
+int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *data )
+{
+	struct byahoo_input_data *inp = g_new0( struct byahoo_input_data, 1 );
+	
+	if( cond == YAHOO_INPUT_READ )
+	{
+		struct byahoo_read_ready_data *d = g_new0( struct byahoo_read_ready_data, 1 );
+		
+		d->id = id;
+		d->fd = fd;
+		d->data = data;
+		
+		inp->d = d;
+		d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_READ, (GaimInputFunction) byahoo_read_ready_callback, (gpointer) d );
+	}
+	else if( cond == YAHOO_INPUT_WRITE )
+	{
+		struct byahoo_write_ready_data *d = g_new0( struct byahoo_write_ready_data, 1 );
+		
+		d->id = id;
+		d->fd = fd;
+		d->data = data;
+		
+		inp->d = d;
+		d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_WRITE, (GaimInputFunction) byahoo_write_ready_callback, (gpointer) d );
+	}
+	else
+	{
+		g_free( inp );
+		return( -1 );
+		/* Panic... */
+	}
+	
+	byahoo_inputs = g_slist_append( byahoo_inputs, inp );
+	return( inp->h );
+}
+
+void ext_yahoo_remove_handler( int id, int tag )
+{
+	struct byahoo_input_data *inp;
+	GSList *l = byahoo_inputs;
+	
+	while( l )
+	{
+		inp = l->data;
+		if( inp->h == tag )
+		{
+			g_free( inp->d );
+			g_free( inp );
+			byahoo_inputs = g_slist_remove( byahoo_inputs, inp );
+			break;
+		}
+		l = l->next;
+	}
+	
+	gaim_input_remove( tag );
+}
+
+int ext_yahoo_connect_async( int id, char *host, int port, yahoo_connect_callback callback, void *data )
+{
+	struct byahoo_connect_callback_data *d;
+	int fd;
+	
+	d = g_new0( struct byahoo_connect_callback_data, 1 );
+	if( ( fd = proxy_connect( host, port, (GaimInputFunction) byahoo_connect_callback, (gpointer) d ) ) < 0 )
+	{
+		g_free( d );
+		return( fd );
+	}
+	d->fd = fd;
+	d->callback = callback;
+	d->data = data;
+	d->id = id;
+	
+	return( fd );
+}
+
+/* Because we don't want asynchronous connects in BitlBee, and because
+   libyahoo doesn't seem to use this one anyway, this one is now defunct. */
+int ext_yahoo_connect(char *host, int port)
+{
+#if 0
+	struct sockaddr_in serv_addr;
+	static struct hostent *server;
+	static char last_host[256];
+	int servfd;
+	char **p;
+
+	if(last_host[0] || g_strcasecmp(last_host, host)!=0) {
+		if(!(server = gethostbyname(host))) {
+			return -1;
+		}
+		strncpy(last_host, host, 255);
+	}
+
+	if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		return -1;
+	}
+
+	for (p = server->h_addr_list; *p; p++)
+	{
+		memset(&serv_addr, 0, sizeof(serv_addr));
+		serv_addr.sin_family = AF_INET;
+		memcpy(&serv_addr.sin_addr.s_addr, *p, server->h_length);
+		serv_addr.sin_port = htons(port);
+
+		if(connect(servfd, (struct sockaddr *) &serv_addr, 
+					sizeof(serv_addr)) == -1) {
+			return -1;
+		} else {
+			return servfd;
+		}
+	}
+
+	closesocket(servfd);
+#endif
+	return -1;
+}
+
+static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
+{
+	yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
+	add_chat_buddy( inv->c, inv->gc->username );
+	g_free( inv->name );
+	g_free( inv );
+}
+
+static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
+{
+	yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
+	serv_got_chat_left( inv->gc, inv->c->id );
+	g_free( inv->name );
+	g_free( inv );
+}
+
+void ext_yahoo_got_conf_invite( int id, char *who, char *room, char *msg, YList *members )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	struct byahoo_conf_invitation *inv;
+	char txt[1024];
+	YList *m;
+	
+	inv = g_malloc( sizeof( struct byahoo_conf_invitation ) );
+	memset( inv, 0, sizeof( struct byahoo_conf_invitation ) );
+	inv->name = g_strdup( room );
+	inv->c = serv_got_joined_chat( gc, ++byahoo_chat_id, room );
+	inv->c->data = members;
+	inv->yid = id;
+	inv->members = members;
+	inv->gc = gc;
+	
+	for( m = members; m; m = m->next )
+		if( g_strcasecmp( m->data, gc->username ) != 0 )
+			add_chat_buddy( inv->c, m->data );
+	
+	g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", room, who, msg );
+	
+	do_ask_dialog( gc, txt, inv, byahoo_accept_conf, byahoo_reject_conf );
+}
+
+void ext_yahoo_conf_userdecline( int id, char *who, char *room, char *msg )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	serv_got_crap( gc, "Invite to chatroom %s rejected by %s: %s", room, who, msg );
+}
+
+void ext_yahoo_conf_userjoin( int id, char *who, char *room )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+	
+	if( c )
+		add_chat_buddy( c, who );
+	else if( set_getint( gc->irc, "debug" ) )
+		serv_got_crap( gc, "Got ext_yahoo_conf_userjoin() from %s for unknown conference %s", who, room );
+}
+
+void ext_yahoo_conf_userleave( int id, char *who, char *room )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+	
+	if( c )
+		remove_chat_buddy( c, who, "" );
+	else if( set_getint( gc->irc, "debug" ) )
+		serv_got_crap( gc, "Got ext_yahoo_conf_userleave() from %s for unknown conference %s", who, room );
+}
+
+void ext_yahoo_conf_message( int id, char *who, char *room, char *msg, int utf8 )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	char *m = byahoo_strip( msg );
+	struct conversation *c;
+	
+	for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next );
+	
+	serv_got_chat_in( gc, c ? c->id : 0, who, 0, m, 0 );
+	g_free( m );
+}
+
+void ext_yahoo_chat_cat_xml( int id, char *xml )
+{
+}
+
+void ext_yahoo_chat_join( int id, char *room, char *topic, YList *members, int fd )
+{
+}
+
+void ext_yahoo_chat_userjoin( int id, char *room, struct yahoo_chat_member *who )
+{
+}
+
+void ext_yahoo_chat_userleave( int id, char *room, char *who )
+{
+}
+
+void ext_yahoo_chat_message( int id, char *who, char *room, char *msg, int msgtype, int utf8 )
+{
+}
+
+void ext_yahoo_chat_yahoologout( int id )
+{
+}
+
+void ext_yahoo_chat_yahooerror( int id )
+{
+}
+
+void ext_yahoo_contact_added( int id, char *myid, char *who, char *msg )
+{
+}
+
+void ext_yahoo_rejected( int id, char *who, char *msg )
+{
+}
+
+void ext_yahoo_game_notify( int id, char *who, int stat )
+{
+}
+
+void ext_yahoo_mail_notify( int id, char *from, char *subj, int cnt )
+{
+	struct gaim_connection *gc = byahoo_get_gc_by_id( id );
+	
+	if( from && subj )
+		serv_got_crap( gc, "Received e-mail message from %s with subject `%s'", from, subj );
+	else if( cnt > 0 )
+		serv_got_crap( gc, "Received %d new e-mails", cnt );
+}
+
+void ext_yahoo_webcam_invite_reply( int id, char *from, int accept )
+{
+}
+
+void ext_yahoo_webcam_closed( int id, char *who, int reason )
+{
+}
+
+void ext_yahoo_got_search_result( int id, int found, int start, int total, YList *contacts )
+{
+}
+
+void ext_yahoo_webcam_viewer( int id, char *who, int connect )
+{
+}
+
+void ext_yahoo_webcam_data_request( int id, int send )
+{
+}
+
+int ext_yahoo_log( char *fmt, ... )
+{
+	return( 0 );
+}
+
+void ext_yahoo_got_webcam_image( int id, const char * who, const unsigned char *image, unsigned int image_size, unsigned int real_size, unsigned int timestamp )
+{
+}
Index: protocols/yahoo/yahoo2.h
===================================================================
--- protocols/yahoo/yahoo2.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,222 @@
+/*
+ * libyahoo2: yahoo2.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef YAHOO2_H
+#define YAHOO2_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* *** BitlBee: *** */
+#include "bitlbee.h"
+#undef free
+#define free( x )		g_free( x )
+#undef malloc
+#define malloc( x )		g_malloc( x )
+#undef calloc
+#define calloc( x, y )		g_calloc( x, y )
+#undef realloc
+#define realloc( x, y )		g_realloc( x, y )
+#undef strdup
+#define strdup( x )		g_strdup( x )
+#undef strndup
+#define strndup( x,y )		g_strndup( x,y )
+#undef snprintf
+// #define snprintf( x... )	g_snprintf( x )
+#undef strcasecmp
+#define strcasecmp( x,y )	g_strcasecmp( x,y )
+#undef strncasecmp
+#define strncasecmp( x,y,z )	g_strncasecmp( x,y,z )
+
+
+#include "yahoo2_types.h"
+
+/* returns the socket descriptor for a given pager connection. shouldn't be needed */
+int  yahoo_get_fd(int id);
+
+/* says how much logging to do */
+/* see yahoo2_types.h for the different values */
+int  yahoo_set_log_level(enum yahoo_log_level level);
+enum yahoo_log_level  yahoo_get_log_level( void );
+
+/* these functions should be self explanatory */
+/* who always means the buddy you're acting on */
+/* id is the successful value returned by yahoo_init */
+
+
+/* init returns a connection id used to identify the connection hereon */
+/* or 0 on failure */
+/* you must call init before calling any other function */
+/*
+ * The optional parameters to init are key/value pairs that specify 
+ * server settings to use.  This list must be NULL terminated - even
+ * if the list is empty.  If a parameter isn't set, a default value
+ * will be used.  Parameter keys are strings, parameter values are
+ * either strings or ints, depending on the key.  Values passed in
+ * are copied, so you can use const/auto/static/pointers/whatever
+ * you want.  Parameters are:
+ * 	NAME			TYPE		DEFAULT
+ *	pager_host		char *		scs.msg.yahoo.com
+ *	pager_port		int		5050
+ *	filetransfer_host	char *		filetransfer.msg.yahoo.com
+ *	filetransfer_port	int		80
+ *	webcam_host		char *		webcam.yahoo.com
+ *	webcam_port		int		5100
+ *	webcam_description	char *		""
+ *	local_host		char *		""
+ *	conn_type		int		Y_WCM_DSL
+ *
+ * You should set at least local_host if you intend to use webcams
+ */
+int  yahoo_init_with_attributes(const char *username, const char *password, ...);
+
+/* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults
+ * for all attributes */
+int  yahoo_init(const char *username, const char *password);
+
+
+
+/* release all resources held by this session */
+/* you need to call yahoo_close for a session only if
+ * yahoo_logoff is never called for it (ie, it was never logged in) */
+void yahoo_close(int id);
+/* login logs in to the server */
+/* initial is of type enum yahoo_status.  see yahoo2_types.h */
+void yahoo_login(int id, int initial);
+void yahoo_logoff(int id);
+/* reloads status of all buddies */
+void yahoo_refresh(int id);
+/* activates/deactivates an identity */
+void yahoo_set_identity_status(int id, const char * identity, int active);
+/* regets the entire buddy list from the server */
+void yahoo_get_list(int id);
+/* download buddy contact information from your yahoo addressbook */
+void yahoo_get_yab(int id);
+/* add/modify an address book entry.  if yab->dbid is set, it will */
+/* modify that entry else it creates a new entry */
+void yahoo_set_yab(int id, struct yab * yab);
+void yahoo_keepalive(int id);
+void yahoo_chat_keepalive(int id);
+
+/* from is the identity you're sending from.  if NULL, the default is used */
+/* utf8 is whether msg is a utf8 string or not. */
+void yahoo_send_im(int id, const char *from, const char *who, const char *msg, int utf8);
+/* if type is true, send typing notice, else send stopped typing notice */
+void yahoo_send_typing(int id, const char *from, const char *who, int typ);
+
+/* used to set away/back status. */
+/* away says whether the custom message is an away message or a sig */
+void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away);
+
+void yahoo_add_buddy(int id, const char *who, const char *group);
+void yahoo_remove_buddy(int id, const char *who, const char *group);
+void yahoo_reject_buddy(int id, const char *who, const char *msg);
+/* if unignore is true, unignore, else ignore */
+void yahoo_ignore_buddy(int id, const char *who, int unignore);
+void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group);
+void yahoo_group_rename(int id, const char *old_group, const char *new_group);
+
+void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg);
+void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg);
+void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg);
+void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8);
+void yahoo_conference_logon(int id, const char * from, YList *who, const char *room);
+void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room);
+
+/* Get a list of chatrooms */
+void yahoo_get_chatrooms(int id,int chatroomid);
+/* join room with specified roomname and roomid */
+void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid);
+/* Send message "msg" to room with specified roomname, msgtype is 1-normal message or 2-/me mesage */
+void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8);
+/* Log off chat */
+void yahoo_chat_logoff(int id, const char *from);
+
+/* requests a webcam feed */
+/* who is the person who's webcam you would like to view */
+/* if who is null, then you're the broadcaster */
+void yahoo_webcam_get_feed(int id, const char *who);
+void yahoo_webcam_close_feed(int id, const char *who);
+
+/* sends an image when uploading */
+/* image points to a JPEG-2000 image, length is the length of the image */
+/* in bytes. The timestamp is the time in milliseconds since we started the */
+/* webcam. */
+void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp);
+
+/* this function should be called if we want to allow a user to watch the */
+/* webcam. Who is the user we want to accept. */
+/* Accept user (accept = 1), decline user (accept = 0) */
+void yahoo_webcam_accept_viewer(int id, const char* who, int accept);
+
+/* send an invitation to a user to view your webcam */
+void yahoo_webcam_invite(int id, const char *who);
+
+/* will set up a connection and initiate file transfer.
+ * callback will be called with the fd that you should write
+ * the file data to
+ */
+void yahoo_send_file(int id, const char *who, const char *msg, const char *name, unsigned long size,
+		yahoo_get_fd_callback callback, void *data);
+
+/* send a search request
+ */
+void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar,
+		int photo, int yahoo_only);
+
+/* continue last search
+ * should be called if only (start+found >= total)
+ *
+ * where the above three are passed to ext_yahoo_got_search_result
+ */
+void yahoo_search_again(int id, int start);
+
+/* returns a socket fd to a url for downloading a file. */
+void yahoo_get_url_handle(int id, const char *url, 
+		yahoo_get_url_handle_callback callback, void *data);
+
+/* these should be called when input is available on a fd */
+/* registered by ext_yahoo_add_handler */
+/* if these return negative values, errno may be set */
+int  yahoo_read_ready(int id, int fd, void *data);
+int  yahoo_write_ready(int id, int fd, void *data);
+
+/* utility functions. these do not hit the server */
+enum yahoo_status yahoo_current_status(int id);
+const YList * yahoo_get_buddylist(int id);
+const YList * yahoo_get_ignorelist(int id);
+const YList * yahoo_get_identities(int id);
+/* 'which' could be y, t, c or login.  This may change in later versions. */
+const char  * yahoo_get_cookie(int id, const char *which);
+
+/* returns the url used to get user profiles - you must append the user id */
+/* as of now this is http://profiles.yahoo.com/ */
+/* You'll have to do urlencoding yourself, but see yahoo_httplib.h first */
+const char  * yahoo_get_profile_url( void );
+
+#include "yahoo_httplib.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: protocols/yahoo/yahoo2_callbacks.h
===================================================================
--- protocols/yahoo/yahoo2_callbacks.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2_callbacks.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,697 @@
+/*
+ * libyahoo2: yahoo2_callbacks.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * The functions in this file *must* be defined in your client program
+ * If you want to use a callback structure instead of direct functions,
+ * then you must define USE_STRUCT_CALLBACKS in all files that #include
+ * this one.
+ *
+ * Register the callback structure by calling yahoo_register_callbacks -
+ * declared in this file and defined in libyahoo2.c
+ */
+
+
+
+#ifndef YAHOO2_CALLBACKS_H
+#define YAHOO2_CALLBACKS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yahoo2_types.h"
+
+/*
+ * yahoo2_callbacks.h
+ *
+ * Callback interface for libyahoo2
+ */
+
+typedef enum {
+	YAHOO_INPUT_READ = 1 << 0,
+	YAHOO_INPUT_WRITE = 1 << 1,
+	YAHOO_INPUT_EXCEPTION = 1 << 2
+} yahoo_input_condition;
+
+/*
+ * A callback function called when an asynchronous connect completes.
+ * 
+ * Params:
+ *     fd    - The file descriptor that has been connected, or -1 on error
+ *     error - The value of errno set by the call to connect or 0 if no error
+ *	       Set both fd and error to 0 if the connect was cancelled by the
+ *	       user
+ *     callback_data - the callback_data passed to the ext_yahoo_connect_async
+ *	       function
+ */
+typedef void (*yahoo_connect_callback)(int fd, int error, void *callback_data);
+
+
+
+/*
+ * The following functions need to be implemented in the client
+ * interface.  They will be called by the library when each
+ * event occurs.
+ */
+
+/* 
+ * should we use a callback structure or directly call functions
+ * if you want the structure, you *must* define USE_STRUCT_CALLBACKS
+ * both when you compile the library, and when you compile your code
+ * that uses the library
+ */
+
+#ifdef USE_STRUCT_CALLBACKS
+#define YAHOO_CALLBACK_TYPE(x)	(*x)
+struct yahoo_callbacks {
+#else
+#define YAHOO_CALLBACK_TYPE(x)	x
+#endif
+
+/*
+ * Name: ext_yahoo_login_response
+ * 	Called when the login process is complete
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	succ - enum yahoo_login_status
+ * 	url  - url to reactivate account if locked
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, char *url);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_buddies
+ * 	Called when the contact list is got from the server
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	buds - the buddy list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_ignore
+ * 	Called when the ignore list is got from the server
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	igns - the ignore list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_identities
+ * 	Called when the contact list is got from the server
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	ids  - the identity list
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_cookies
+ * 	Called when the cookie list is got from the server
+ * Params:
+ * 	id   - the id that identifies the server connection
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id);
+
+
+
+
+/*
+ * Name: ext_yahoo_status_changed
+ * 	Called when remote user's status changes.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the handle of the remote user
+ * 	stat - status code (enum yahoo_status)
+ * 	msg  - the message if stat == YAHOO_STATUS_CUSTOM
+ * 	away - whether the contact is away or not (YAHOO_STATUS_CUSTOM)
+ * 	       for YAHOO_STATUS_IDLE, this is the number of seconds he is idle
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, char *who, int stat, char *msg, int away);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_im
+ * 	Called when remote user sends you a message.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the handle of the remote user
+ * 	msg  - the message - NULL if stat == 2
+ * 	tm   - timestamp of message if offline
+ * 	stat - message status - 0
+ * 				1
+ * 				2 == error sending message
+ * 				5
+ * 	utf8 - whether the message is encoded as utf8 or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, char *who, char *msg, long tm, int stat, int utf8);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_conf_invite
+ * 	Called when remote user sends you a conference invitation.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user inviting you
+ * 	room - the room to join
+ * 	msg  - the message
+ *	members - the initial members of the conference (null terminated list)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, char *who, char *room, char *msg, YList *members);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userdecline
+ * 	Called when someone declines to join the conference.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who has declined
+ * 	room - the room
+ * 	msg  - the declining message
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, char *who, char *room, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userjoin
+ * 	Called when someone joins the conference.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who has joined
+ * 	room - the room joined
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, char *who, char *room);
+
+
+
+
+/*
+ * Name: ext_yahoo_conf_userleave
+ * 	Called when someone leaves the conference.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who has left
+ * 	room - the room left
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, char *who, char *room);
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_cat_xml
+ * 	Called when joining the chatroom.
+ * Params:
+ * 	id      - the id that identifies the server connection
+ * 	room    - the room joined, used in all other chat calls, freed by 
+ * 	          library after call
+ * 	topic   - the topic of the room, freed by library after call
+ *	members - the initial members of the chatroom (null terminated YList of 
+ *	          yahoo_chat_member's) Must be freed by the client
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, char *xml);
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_join
+ * 	Called when joining the chatroom.
+ * Params:
+ * 	id      - the id that identifies the server connection
+ * 	room    - the room joined, used in all other chat calls, freed by 
+ * 	          library after call
+ * 	topic   - the topic of the room, freed by library after call
+ *	members - the initial members of the chatroom (null terminated YList 
+ *	          of yahoo_chat_member's) Must be freed by the client
+ *	fd	- the socket where the connection is coming from (for tracking)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, char *room, char *topic, YList *members, int fd);
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_userjoin
+ * 	Called when someone joins the chatroom.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	room - the room joined
+ * 	who  - the user who has joined, Must be freed by the client
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, char *room, struct yahoo_chat_member *who);
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_userleave
+ * 	Called when someone leaves the chatroom.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	room - the room left
+ * 	who  - the user who has left (Just the User ID)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, char *room, char *who);
+
+
+
+
+/*
+ * Name: ext_yahoo_chat_message
+ * 	Called when someone messages in the chatroom.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	room - the room
+ * 	who  - the user who messaged (Just the user id)
+ * 	msg  - the message
+ * 	msgtype  - 1 = Normal message
+ * 		   2 = /me type message
+ * 	utf8 - whether the message is utf8 encoded or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, char *who, char *room, char *msg, int msgtype, int utf8);
+
+/*
+ *
+ * Name: ext_yahoo_chat_yahoologout
+ *	called when yahoo disconnects your chat session
+ *	Note this is called whenver a disconnect happens, client or server
+ *	requested. Care should be taken to make sure you know the origin 
+ *	of the disconnect request before doing anything here (auto-join's etc)
+ * Params:
+ *	id   - the id that identifies this connection
+ * Returns:
+ *	nothing.
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id);
+
+/*
+ *
+ * Name: ext_yahoo_chat_yahooerror
+ *	called when yahoo sends back an error to you
+ *	Note this is called whenver chat message is sent into a room
+ *	in error (fd not connected, room doesn't exists etc)
+ *	Care should be taken to make sure you know the origin 
+ *	of the error before doing anything about it.
+ * Params:
+ *	id   - the id that identifies this connection
+ * Returns:
+ *	nothing.
+ */
+
+void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id);
+
+/*
+ * Name: ext_yahoo_conf_message
+ * 	Called when someone messages in the conference.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who messaged
+ * 	room - the room
+ * 	msg  - the message
+ * 	utf8 - whether the message is utf8 encoded or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, char *who, char *room, char *msg, int utf8);
+
+
+
+
+/*
+ * Name: ext_yahoo_got_file
+ * 	Called when someone sends you a file
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who sent the file
+ * 	url  - the file url
+ * 	expires  - the expiry date of the file on the server (timestamp)
+ * 	msg  - the message
+ * 	fname- the file name if direct transfer
+ * 	fsize- the file size if direct transfer
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize);
+
+
+
+
+/*
+ * Name: ext_yahoo_contact_added
+ * 	Called when a contact is added to your list
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	myid - the identity he was added to
+ * 	who  - who was added
+ * 	msg  - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, char *myid, char *who, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_rejected
+ * 	Called when a contact rejects your add
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - who rejected you
+ * 	msg  - any message sent
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, char *who, char *msg);
+
+
+
+
+/*
+ * Name: ext_yahoo_typing_notify
+ * 	Called when remote user starts or stops typing.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the handle of the remote user
+ * 	stat - 1 if typing, 0 if stopped typing
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, char *who, int stat);
+
+
+
+
+/*
+ * Name: ext_yahoo_game_notify
+ * 	Called when remote user starts or stops a game.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the handle of the remote user
+ * 	stat - 1 if game, 0 if stopped gaming
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, char *who, int stat);
+
+
+
+
+/*
+ * Name: ext_yahoo_mail_notify
+ * 	Called when you receive mail, or with number of messages
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	from - who the mail is from - NULL if only mail count
+ * 	subj - the subject of the mail - NULL if only mail count
+ * 	cnt  - mail count - 0 if new mail notification
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify)(int id, char *from, char *subj, int cnt);
+
+
+
+
+/*
+ * Name: ext_yahoo_system_message
+ * 	System message
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	msg  - the message
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, char *msg);
+
+
+
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_got_webcam_image
+ * 	Called when you get a webcam update
+ *	An update can either be receiving an image, a part of an image or
+ *	just an update with a timestamp
+ * Params:
+ * 	id         - the id that identifies the server connection
+ * 	who        - the user who's webcam we're viewing
+ *	image      - image data
+ *	image_size - length of the image in bytes
+ *	real_size  - actual length of image data
+ *	timestamp  - milliseconds since the webcam started
+ *
+ *	If the real_size is smaller then the image_size then only part of
+ *	the image has been read. This function will keep being called till
+ *	the total amount of bytes in image_size has been read. The image
+ *	received is in JPEG-2000 Code Stream Syntax (ISO/IEC 15444-1).
+ *	The size of the image will be either 160x120 or 320x240.
+ *	Each webcam image contains a timestamp. This timestamp should be
+ *	used to keep the image in sync since some images can take longer
+ *	to transport then others. When image_size is 0 we can still receive
+ *	a timestamp to stay in sync
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who,
+		const unsigned char *image, unsigned int image_size, unsigned int real_size,
+		unsigned int timestamp);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_invite
+ * 	Called when you get a webcam invitation
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	from - who the invitation is from
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, char *from);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_invite_reply
+ * 	Called when you get a response to a webcam invitation
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	from - who the invitation response is from
+ *	accept - 0 (decline), 1 (accept)
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, char *from, int accept);
+
+
+
+/*
+ * Name: ext_yahoo_webcam_closed
+ * 	Called when the webcam connection closed
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	who  - the user who we where connected to
+ *	reason - reason why the connection closed
+ *	         1 = user stopped broadcasting
+ *	         2 = user cancelled viewing permission
+ *	         3 = user declines permission
+ *	         4 = user does not have webcam online
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, char *who, int reason);
+
+
+/*
+ * Name: ext_yahoo_got_search_result
+ *      Called when the search result received from server
+ * Params:
+ *      id  	 - the id that identifies the server connection
+ * 	found	 - total number of results returned in the current result set
+ * 	start	 - offset from where the current result set starts
+ * 	total	 - total number of results available (start + found <= total)
+ * 	contacts - the list of results as a YList of yahoo_found_contact
+ * 		   these will be freed after this function returns, so
+ * 		   if you need to use the information, make a copy
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int start, int total, YList *contacts);
+
+
+
+/*
+ * Name: ext_yahoo_error
+ * 	Called on error.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	err  - the error message
+ * 	fatal- whether this error is fatal to the connection or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, char *err, int fatal);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_viewer
+ *	Called when a viewer disconnects/connects/requests to connect
+ * Params:
+ *	id  - the id that identifies the server connection
+ *	who - the viewer
+ *	connect - 0=disconnect 1=connect 2=request
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, char *who, int connect);
+
+
+
+
+/*
+ * Name: ext_yahoo_webcam_data_request
+ *	Called when you get a request for webcam images
+ * Params:
+ *	id   - the id that identifies the server connection
+ *	send - whether to send images or not
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send);
+
+
+
+
+/*
+ * Name: ext_yahoo_log
+ * 	Called to log a message.
+ * Params:
+ * 	fmt  - the printf formatted message
+ * Returns:
+ * 	0
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(char *fmt, ...);
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_add_handler
+ * 	Add a listener for the fd.  Must call yahoo_read_ready
+ * 	when a YAHOO_INPUT_READ fd is ready and yahoo_write_ready
+ * 	when a YAHOO_INPUT_WRITE fd is ready.
+ * Params:
+ * 	id   - the id that identifies the server connection
+ * 	fd   - the fd on which to listen
+ * 	cond - the condition on which to call the callback
+ * 	data - callback data to pass to yahoo_*_ready
+ * 	
+ * Returns: a tag to be used when removing the handler
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condition cond, void *data);
+
+
+
+
+/*
+ * Name: ext_yahoo_remove_handler
+ * 	Remove the listener for the fd.
+ * Params:
+ * 	id   - the id that identifies the connection
+ * 	tag  - the handler tag to remove
+ */
+void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag);
+
+
+
+
+
+/*
+ * Name: ext_yahoo_connect
+ * 	Connect to a host:port
+ * Params:
+ * 	host - the host to connect to
+ * 	port - the port to connect on
+ * Returns:
+ * 	a unix file descriptor to the socket
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(char *host, int port);
+
+
+
+
+
+
+
+
+/*
+ * Name: ext_yahoo_connect_async
+ * 	Connect to a host:port asynchronously.  This function should return
+ * 	immediately returing a tag used to identify the connection handler,
+ * 	or a pre-connect error (eg: host name lookup failure).
+ * 	Once the connect completes (successfully or unsuccessfully), callback
+ * 	should be called (see the signature for yahoo_connect_callback).
+ * 	The callback may safely be called before this function returns, but
+ * 	it should not be called twice.
+ * Params:
+ * 	id   - the id that identifies this connection
+ * 	host - the host to connect to
+ * 	port - the port to connect on
+ * 	callback - function to call when connect completes
+ * 	callback_data - data to pass to the callback function
+ * Returns:
+ * 	a unix file descriptor to the socket
+ */
+int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, char *host, int port, 
+		yahoo_connect_callback callback, void *callback_data);
+
+#ifdef USE_STRUCT_CALLBACKS
+};
+
+/*
+ * if using a callback structure, call yahoo_register_callbacks
+ * before doing anything else
+ */
+void yahoo_register_callbacks(struct yahoo_callbacks * tyc);
+	
+#undef YAHOO_CALLBACK_TYPE
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: protocols/yahoo/yahoo2_types.h
===================================================================
--- protocols/yahoo/yahoo2_types.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2_types.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,241 @@
+/*
+ * libyahoo2: yahoo2_types.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef YAHOO2_TYPES_H
+#define YAHOO2_TYPES_H
+
+#include "yahoo_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum yahoo_status {
+	YAHOO_STATUS_AVAILABLE = 0,
+	YAHOO_STATUS_BRB,
+	YAHOO_STATUS_BUSY,
+	YAHOO_STATUS_NOTATHOME,
+	YAHOO_STATUS_NOTATDESK,
+	YAHOO_STATUS_NOTINOFFICE,
+	YAHOO_STATUS_ONPHONE,
+	YAHOO_STATUS_ONVACATION,
+	YAHOO_STATUS_OUTTOLUNCH,
+	YAHOO_STATUS_STEPPEDOUT,
+	YAHOO_STATUS_INVISIBLE = 12,
+	YAHOO_STATUS_CUSTOM = 99,
+	YAHOO_STATUS_IDLE = 999,
+	YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */
+	YAHOO_STATUS_NOTIFY = 0x16
+};
+#define YAHOO_STATUS_GAME	0x2 		/* Games don't fit into the regular status model */
+
+enum yahoo_login_status {
+	YAHOO_LOGIN_OK = 0,
+	YAHOO_LOGIN_UNAME = 3,
+	YAHOO_LOGIN_PASSWD = 13,
+	YAHOO_LOGIN_LOCK = 14,
+	YAHOO_LOGIN_DUPL = 99,
+	YAHOO_LOGIN_SOCK = -1
+};
+
+enum yahoo_error {
+	E_CUSTOM = 0,
+
+	/* responses from ignore buddy */
+	E_IGNOREDUP = 2,
+	E_IGNORENONE = 3,
+	E_IGNORECONF = 12,
+	
+	/* conference */
+	E_CONFNOTAVAIL = 20
+};
+
+enum yahoo_log_level {
+	YAHOO_LOG_NONE = 0,
+	YAHOO_LOG_FATAL,
+	YAHOO_LOG_ERR,
+	YAHOO_LOG_WARNING,
+	YAHOO_LOG_NOTICE,
+	YAHOO_LOG_INFO,
+	YAHOO_LOG_DEBUG
+};
+
+
+/* Yahoo style/color directives */
+#define YAHOO_COLOR_BLACK "\033[30m"
+#define YAHOO_COLOR_BLUE "\033[31m"
+#define YAHOO_COLOR_LIGHTBLUE "\033[32m"
+#define YAHOO_COLOR_GRAY "\033[33m"
+#define YAHOO_COLOR_GREEN "\033[34m"
+#define YAHOO_COLOR_PINK "\033[35m"
+#define YAHOO_COLOR_PURPLE "\033[36m"
+#define YAHOO_COLOR_ORANGE "\033[37m"
+#define YAHOO_COLOR_RED "\033[38m"
+#define YAHOO_COLOR_OLIVE "\033[39m"
+#define YAHOO_COLOR_ANY "\033[#"
+#define YAHOO_STYLE_ITALICON "\033[2m"
+#define YAHOO_STYLE_ITALICOFF "\033[x2m"
+#define YAHOO_STYLE_BOLDON "\033[1m"
+#define YAHOO_STYLE_BOLDOFF "\033[x1m"
+#define YAHOO_STYLE_UNDERLINEON "\033[4m"
+#define YAHOO_STYLE_UNDERLINEOFF "\033[x4m"
+#define YAHOO_STYLE_URLON "\033[lm"
+#define YAHOO_STYLE_URLOFF "\033[xlm"
+
+enum yahoo_connection_type {
+	YAHOO_CONNECTION_PAGER=0,
+	YAHOO_CONNECTION_FT,
+	YAHOO_CONNECTION_YAB,
+	YAHOO_CONNECTION_WEBCAM_MASTER,
+	YAHOO_CONNECTION_WEBCAM,
+	YAHOO_CONNECTION_CHATCAT,
+	YAHOO_CONNECTION_SEARCH
+};
+
+enum yahoo_webcam_direction_type {
+        YAHOO_WEBCAM_DOWNLOAD=0,
+        YAHOO_WEBCAM_UPLOAD
+};
+
+/* chat member attribs */
+#define YAHOO_CHAT_MALE 0x8000
+#define YAHOO_CHAT_FEMALE 0x10000
+#define YAHOO_CHAT_FEMALE 0x10000
+#define YAHOO_CHAT_DUNNO 0x400
+#define YAHOO_CHAT_WEBCAM 0x10
+
+enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 };
+
+struct yahoo_webcam {
+	int direction;     /* Uploading or downloading */
+	int conn_type;     /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */
+
+	char *user;        /* user we are viewing */
+	char *server;      /* webcam server to connect to */
+	int   port;        /* webcam port to connect on */
+	char *key;         /* key to connect to the server with */
+	char *description; /* webcam description */
+	char *my_ip;       /* own ip number */
+};
+
+struct yahoo_webcam_data {
+	unsigned int data_size;
+	unsigned int to_read;
+	unsigned int timestamp;
+	unsigned char packet_type;
+};
+
+struct yahoo_data {
+	char  *user;
+	char  *password;
+
+	char  *cookie_y;
+	char  *cookie_t;
+	char  *cookie_c;
+	char  *login_cookie;
+
+	YList *buddies;
+	YList *ignore;
+	YList *identities;
+	char  *login_id;
+
+	int   current_status;
+	int   initial_status;
+	int   logged_in;
+
+	int   session_id;
+
+	int   client_id;
+
+	char  *rawbuddylist;
+	char  *ignorelist;
+
+	void  *server_settings;
+};
+
+struct yab {
+	char *id;
+	char *fname;
+	char *lname;
+	char *nname;
+	char *email;
+	char *hphone;
+	char *wphone;
+	char *mphone;
+	int  dbid;
+};
+
+struct yahoo_buddy {
+	char *group;
+	char *id;
+	char *real_name;
+	struct yab *yab_entry;
+};
+
+enum yahoo_search_type {
+	YAHOO_SEARCH_KEYWORD = 0,
+	YAHOO_SEARCH_YID,
+	YAHOO_SEARCH_NAME
+};
+
+enum yahoo_search_gender {
+	YAHOO_GENDER_NONE = 0,
+	YAHOO_GENDER_MALE,
+	YAHOO_GENDER_FEMALE
+};
+
+enum yahoo_search_agerange {
+	YAHOO_AGERANGE_NONE = 0
+};
+
+struct yahoo_found_contact {
+	char *id;
+	char *gender;
+	char *location;
+	int age;
+	int online;
+};
+
+/*
+ * Function pointer to be passed to http get/post and send file
+ */
+typedef void (*yahoo_get_fd_callback)(int id, int fd, int error, void *data);
+
+/*
+ * Function pointer to be passed to yahoo_get_url_handle
+ */
+typedef void (*yahoo_get_url_handle_callback)(int id, int fd, int error,
+		const char *filename, unsigned long size, void *data);
+
+
+struct yahoo_chat_member {
+	char *id;
+	int  age;
+	int  attribs;
+	char *alias;
+	char *location;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: protocols/yahoo/yahoo_debug.h
===================================================================
--- protocols/yahoo/yahoo_debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,38 @@
+/*
+ * libyahoo2: yahoo_debug.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+extern int yahoo_log_message(char *fmt, ...);
+
+#define NOTICE(x) if(yahoo_get_log_level() >= YAHOO_LOG_NOTICE) { yahoo_log_message x; yahoo_log_message("\n"); }
+
+#define LOG(x) if(yahoo_get_log_level() >= YAHOO_LOG_INFO) { yahoo_log_message("%s:%d: ", __FILE__, __LINE__); \
+	yahoo_log_message x; \
+	yahoo_log_message("\n"); }
+
+#define WARNING(x) if(yahoo_get_log_level() >= YAHOO_LOG_WARNING) { yahoo_log_message("%s:%d: warning: ", __FILE__, __LINE__); \
+	yahoo_log_message x; \
+	yahoo_log_message("\n"); }
+
+#define DEBUG_MSG(x) if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { yahoo_log_message("%s:%d: debug: ", __FILE__, __LINE__); \
+	yahoo_log_message x; \
+	yahoo_log_message("\n"); }
+
+
Index: protocols/yahoo/yahoo_fn.c
===================================================================
--- protocols/yahoo/yahoo_fn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_fn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,4622 @@
+/*
+ * libyahoo2 - originally from gaim patches by Amatus
+ *
+ * Copyright (C) 2003-2004
+ *
+ * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "yahoo_fn.h"
+
+unsigned char table_0[256] = {
+   0x5A, 0x41, 0x11, 0x77, 0x29, 0x9C, 0x31, 0xAD,
+   0x4A, 0x32, 0x1A, 0x6D, 0x56, 0x9F, 0x39, 0xA6,
+   0x0C, 0xE8, 0x49, 0x40, 0xA4, 0x21, 0xE9, 0x01,
+   0x91, 0x86, 0x2F, 0xB9, 0xED, 0x80, 0x51, 0xAB,
+   0x7F, 0x92, 0xF2, 0x73, 0xCD, 0xD9, 0x75, 0x2A,
+   0x70, 0x34, 0x35, 0x8D, 0xA8, 0x72, 0x7D, 0x9B,
+   0x2E, 0xC5, 0x2D, 0x76, 0x1E, 0xBB, 0xE7, 0x37,
+   0xBA, 0xB7, 0xB2, 0x03, 0x20, 0x17, 0x8A, 0x07,
+   0xD6, 0x96, 0x13, 0x95, 0xE5, 0xF1, 0x18, 0x3B,
+   0xA5, 0x62, 0x33, 0xC1, 0x44, 0x3D, 0x6C, 0xA7,
+   0xBF, 0x1C, 0x60, 0xFF, 0x5B, 0xF5, 0x8E, 0xE6,
+   0x5C, 0xCC, 0xF7, 0x69, 0x15, 0x0F, 0x0B, 0xBD,
+   0x12, 0x9D, 0xB3, 0x65, 0x53, 0xB1, 0x14, 0xF4,
+   0x19, 0x3E, 0xB6, 0x45, 0xCB, 0xA2, 0x7A, 0xD3,
+   0xF8, 0xD1, 0x61, 0xEE, 0xBC, 0xC6, 0xB0, 0x5D,
+   0x4B, 0x09, 0x26, 0xE1, 0x1D, 0x6E, 0xC3, 0xFB,
+   0x68, 0x4C, 0x42, 0x52, 0x5F, 0xDE, 0xFD, 0xEF,
+   0x81, 0x04, 0x6F, 0xE0, 0xF0, 0x1F, 0x0D, 0x7C,
+   0x58, 0x4F, 0x1B, 0x30, 0xCF, 0x9A, 0x2B, 0x05,
+   0xF6, 0x3F, 0x78, 0xAC, 0xD8, 0xEC, 0xE2, 0x25,
+   0x93, 0xDA, 0x84, 0x8C, 0x4E, 0xD5, 0x38, 0x0A,
+   0x06, 0x7E, 0xD4, 0x59, 0x98, 0xE3, 0x36, 0xC2,
+   0xD2, 0xA3, 0x10, 0x79, 0xFA, 0xC9, 0x16, 0x27,
+   0x66, 0x89, 0xFE, 0x57, 0xF3, 0x83, 0xB8, 0x28,
+   0x3C, 0xC7, 0xCE, 0x71, 0xC8, 0xDB, 0x22, 0xE4,
+   0xDD, 0xDF, 0x02, 0x8F, 0x5E, 0xEB, 0x48, 0x2C,
+   0x08, 0xC4, 0x43, 0xEA, 0x50, 0x55, 0x90, 0x54,
+   0x87, 0xCA, 0x00, 0x24, 0x6B, 0x85, 0x97, 0xD7,
+   0xDC, 0x6A, 0x67, 0xD0, 0x88, 0xA1, 0x9E, 0xC0,
+   0x46, 0xAE, 0x64, 0x74, 0x4D, 0xA0, 0x99, 0xB5,
+   0x0E, 0x8B, 0xAA, 0x3A, 0xB4, 0xFC, 0xA9, 0x94,
+   0x7B, 0xBE, 0xF9, 0xAF, 0x82, 0x63, 0x47, 0x23 };
+
+unsigned char table_1[256] = {
+   0x08, 0xCB, 0x54, 0xCF, 0x97, 0x53, 0x59, 0xF1,
+   0x66, 0xEC, 0xDB, 0x1B, 0xB1, 0xE2, 0x36, 0xEB,
+   0xB3, 0x8F, 0x71, 0xA8, 0x90, 0x7D, 0xDA, 0xDC,
+   0x2C, 0x2F, 0xE8, 0x6A, 0x73, 0x37, 0xAE, 0xCC,
+   0xA1, 0x16, 0xE6, 0xFC, 0x9C, 0xA9, 0x2A, 0x3F,
+   0x58, 0xFD, 0x56, 0x4C, 0xA5, 0xF2, 0x33, 0x99,
+   0x1A, 0xB7, 0xFE, 0xA6, 0x1E, 0x32, 0x9E, 0x48,
+   0x03, 0x4A, 0x78, 0xEE, 0xCA, 0xC3, 0x88, 0x7A,
+   0xAC, 0x23, 0xAA, 0xBD, 0xDE, 0xD3, 0x67, 0x43,
+   0xFF, 0x64, 0x8A, 0xF9, 0x04, 0xD0, 0x7B, 0xC2,
+   0xBC, 0xF3, 0x89, 0x0E, 0xDD, 0xAB, 0x9D, 0x84,
+   0x5A, 0x62, 0x7F, 0x6D, 0x82, 0x68, 0xA3, 0xED,
+   0x2E, 0x07, 0x41, 0xEF, 0x2D, 0x70, 0x4F, 0x69,
+   0x8E, 0xE7, 0x0F, 0x11, 0x19, 0xAF, 0x31, 0xFB,
+   0x8D, 0x4B, 0x5F, 0x96, 0x75, 0x42, 0x6C, 0x46,
+   0xE4, 0x55, 0xD6, 0x3B, 0xE1, 0xD1, 0xB0, 0xB5,
+   0x45, 0x29, 0xC0, 0x94, 0x9F, 0xD4, 0x15, 0x17,
+   0x3C, 0x47, 0xC8, 0xD9, 0xC6, 0x76, 0xB9, 0x02,
+   0xE0, 0xC9, 0xB2, 0x01, 0xC1, 0x5D, 0x4E, 0x14,
+   0xF4, 0xAD, 0xB6, 0x00, 0x72, 0xF0, 0x49, 0x0D,
+   0xD8, 0x5E, 0x6F, 0x2B, 0x8C, 0x51, 0x83, 0xC5,
+   0x0A, 0x85, 0xE5, 0x38, 0x7E, 0x26, 0xEA, 0x22,
+   0x6B, 0x06, 0xD5, 0x8B, 0xBF, 0xC7, 0x35, 0x1D,
+   0xF6, 0x24, 0x28, 0xCE, 0x9B, 0x77, 0x20, 0x60,
+   0xF5, 0x87, 0x3D, 0x65, 0x86, 0x0C, 0xDF, 0xBA,
+   0x12, 0xA4, 0x3A, 0x34, 0xD7, 0xA0, 0xF8, 0x63,
+   0x52, 0x27, 0xB8, 0x18, 0xA7, 0x13, 0x91, 0x09,
+   0x93, 0x5C, 0x10, 0x9A, 0xB4, 0xE9, 0x44, 0xC4,
+   0x21, 0x57, 0x1C, 0x0B, 0xA2, 0x74, 0x4D, 0xBE,
+   0xD2, 0x1F, 0xCD, 0xE3, 0x6E, 0x7C, 0x40, 0x50,
+   0x39, 0x80, 0x98, 0xFA, 0x25, 0x92, 0x30, 0x5B,
+   0x05, 0x95, 0xBB, 0x79, 0x61, 0x3E, 0x81, 0xF7 };
+
+unsigned char table_2[32] = {
+   0x19, 0x05, 0x09, 0x1C, 0x0B, 0x1A, 0x12, 0x03,
+   0x06, 0x04, 0x0D, 0x1D, 0x15, 0x0E, 0x1B, 0x18,
+   0x00, 0x07, 0x08, 0x02, 0x13, 0x1F, 0x0C, 0x1E,
+   0x16, 0x0A, 0x10, 0x0F, 0x01, 0x14, 0x11, 0x17 };
+
+unsigned char table_3[256] = {
+   0xBC, 0x1B, 0xCC, 0x1E, 0x5B, 0x59, 0x4F, 0xA8,
+   0x62, 0xC6, 0xC1, 0xBB, 0x83, 0x2D, 0xA3, 0xA6,
+   0x5A, 0xDC, 0xE5, 0x93, 0xFB, 0x5C, 0xD6, 0x2A,
+   0x97, 0xC7, 0x1C, 0x73, 0x08, 0x45, 0xD2, 0x89,
+   0x4A, 0xD4, 0xCF, 0x0C, 0x1D, 0xD8, 0xCD, 0x26,
+   0x8F, 0x11, 0x55, 0x8B, 0xD3, 0x53, 0xCE, 0x00,
+   0xB5, 0x3B, 0x2E, 0x39, 0x88, 0x7B, 0x85, 0x46,
+   0x54, 0xA5, 0x31, 0x40, 0x3E, 0x0A, 0x4C, 0x68,
+   0x70, 0x0F, 0xBA, 0x0E, 0x75, 0x8A, 0xEB, 0x44,
+   0x60, 0x6C, 0x05, 0xC9, 0xF0, 0xDD, 0x0D, 0x66,
+   0xAB, 0xA1, 0xAD, 0xF2, 0x12, 0x6A, 0xE6, 0x27,
+   0xF6, 0x9F, 0xDB, 0xB8, 0xF4, 0x56, 0x5E, 0x2C,
+   0xDA, 0xFE, 0x34, 0x86, 0xF5, 0xC2, 0xB0, 0xF1,
+   0xCB, 0xF3, 0x78, 0x9B, 0x7F, 0xB4, 0xD7, 0x58,
+   0x74, 0x07, 0x72, 0x96, 0x02, 0xCA, 0xAC, 0xE8,
+   0x5D, 0xA7, 0x32, 0xBD, 0x81, 0x43, 0x18, 0xF8,
+   0x15, 0x0B, 0xE9, 0x76, 0x30, 0xBF, 0x3A, 0x22,
+   0x9E, 0xD1, 0x79, 0x37, 0xBE, 0x8C, 0x7A, 0x98,
+   0x21, 0x95, 0x10, 0x8D, 0xDF, 0xC0, 0x69, 0xC8,
+   0x03, 0x6E, 0x4B, 0x36, 0xFC, 0x6F, 0xA9, 0x48,
+   0x63, 0xE1, 0xB9, 0x24, 0x87, 0x13, 0xB2, 0xA4,
+   0x84, 0x06, 0x14, 0x61, 0x3D, 0x92, 0xB1, 0x41,
+   0xE2, 0x71, 0xAF, 0x16, 0xDE, 0x25, 0x82, 0xD9,
+   0x2B, 0x33, 0x51, 0xA2, 0x4E, 0x7D, 0x94, 0xFF,
+   0xFD, 0x5F, 0x80, 0xED, 0x64, 0xE7, 0x50, 0x6D,
+   0xD0, 0x3C, 0x6B, 0x65, 0x77, 0x17, 0x1A, 0xEC,
+   0xD5, 0xAA, 0xF9, 0xC4, 0x9C, 0x35, 0xE3, 0x42,
+   0xE4, 0x19, 0x52, 0x67, 0xB7, 0x9D, 0x28, 0xC5,
+   0x47, 0x38, 0x91, 0x57, 0xAE, 0x3F, 0x29, 0x9A,
+   0x2F, 0xF7, 0x90, 0x04, 0xEE, 0xFA, 0x20, 0xB6,
+   0xEA, 0x49, 0x23, 0x4D, 0xB3, 0x8E, 0xC3, 0x1F,
+   0x7C, 0xEF, 0xE0, 0x99, 0x09, 0xA0, 0x01, 0x7E };
+
+unsigned char table_4[32] = {
+   0x1F, 0x0B, 0x00, 0x1E, 0x03, 0x0E, 0x15, 0x01,
+   0x1A, 0x17, 0x1D, 0x1B, 0x11, 0x0F, 0x0A, 0x12,
+   0x13, 0x18, 0x02, 0x04, 0x09, 0x06, 0x0D, 0x07,
+   0x08, 0x05, 0x10, 0x19, 0x0C, 0x14, 0x16, 0x1C };
+
+unsigned char table_5[256] = {
+   0x9A, 0xAB, 0x61, 0x28, 0x0A, 0x23, 0xFC, 0xBA,
+   0x90, 0x22, 0xB7, 0x62, 0xD9, 0x09, 0x91, 0xF4,
+   0x7B, 0x5D, 0x6B, 0x80, 0xAC, 0x9E, 0x21, 0x72,
+   0x64, 0x2D, 0xFF, 0x66, 0xEB, 0x5B, 0x05, 0xC8,
+   0x1B, 0xD1, 0x55, 0xF5, 0x97, 0x08, 0xAE, 0xC7,
+   0x00, 0xDE, 0xE1, 0x78, 0xD8, 0xB6, 0xF0, 0x17,
+   0xE4, 0x32, 0xCD, 0x76, 0x07, 0x14, 0x7F, 0x7A,
+   0xBF, 0xB4, 0x1D, 0x94, 0x48, 0x75, 0xFA, 0xA7,
+   0x99, 0x7E, 0x65, 0x38, 0x29, 0x51, 0xC3, 0x83,
+   0x7C, 0x0D, 0xA0, 0xCC, 0xF1, 0xDD, 0xE2, 0x49,
+   0xF8, 0xD2, 0x25, 0x54, 0x9B, 0x0E, 0xB9, 0xFE,
+   0x67, 0xC4, 0xCE, 0x13, 0xD4, 0xE7, 0xB8, 0x41,
+   0x77, 0xDB, 0xA6, 0xB0, 0x11, 0x6A, 0x5E, 0x68,
+   0x8D, 0xF9, 0x36, 0xD3, 0xC2, 0x3A, 0xAA, 0x59,
+   0x03, 0xE0, 0xE3, 0xF3, 0x42, 0x2C, 0x04, 0x47,
+   0xE6, 0x93, 0xCB, 0x6E, 0x20, 0xCA, 0x01, 0xA1,
+   0x40, 0x2B, 0x2F, 0x5F, 0x87, 0xD0, 0xEC, 0x88,
+   0x27, 0x58, 0xC6, 0x3E, 0xDF, 0x26, 0x5C, 0xE9,
+   0x1F, 0x0F, 0x95, 0x1C, 0xFB, 0xA5, 0x12, 0x39,
+   0x1E, 0x3C, 0x33, 0x43, 0x56, 0xE8, 0x82, 0xF7,
+   0x7D, 0x89, 0xF2, 0xD7, 0x50, 0x92, 0x60, 0x4C,
+   0x2A, 0x86, 0x16, 0x6C, 0x37, 0xC0, 0xAD, 0xB3,
+   0x24, 0x45, 0xB1, 0xA2, 0x71, 0xA4, 0xA3, 0xED,
+   0xC9, 0x5A, 0x4D, 0x84, 0x0C, 0x3F, 0xC5, 0x9D,
+   0x63, 0x19, 0x79, 0x57, 0x96, 0x30, 0x74, 0xBB,
+   0xDA, 0x1A, 0x9F, 0x44, 0xC1, 0x98, 0xE5, 0x81,
+   0xD6, 0x18, 0x8F, 0xFD, 0x8E, 0x06, 0x6F, 0xF6,
+   0x2E, 0x3B, 0xB5, 0x85, 0x8A, 0x9C, 0x53, 0x4A,
+   0xA9, 0x52, 0x3D, 0x4E, 0xBE, 0xAF, 0xBC, 0xA8,
+   0x4F, 0x6D, 0x15, 0x35, 0x8C, 0xBD, 0x34, 0x8B,
+   0xDC, 0x0B, 0xCF, 0x31, 0xEA, 0xB2, 0x70, 0x4B,
+   0x46, 0x73, 0x69, 0xD5, 0x10, 0xEE, 0x02, 0xEF };
+
+unsigned char table_6[32] = {
+   0x1A, 0x1C, 0x0F, 0x0C, 0x00, 0x02, 0x13, 0x09,
+   0x11, 0x05, 0x0D, 0x12, 0x18, 0x0B, 0x04, 0x10,
+   0x14, 0x1B, 0x1E, 0x16, 0x07, 0x08, 0x03, 0x17,
+   0x19, 0x1F, 0x01, 0x0E, 0x15, 0x06, 0x0A, 0x1D };
+
+unsigned char table_7[256] = {
+   0x52, 0x11, 0x72, 0xD0, 0x76, 0xD7, 0xAE, 0x03,
+   0x7F, 0x19, 0xF4, 0xB8, 0xB3, 0x5D, 0xCA, 0x2D,
+   0x5C, 0x30, 0x53, 0x1A, 0x57, 0xF6, 0xAD, 0x83,
+   0x29, 0x79, 0xD5, 0xF0, 0x0F, 0xC3, 0x8B, 0xD3,
+   0x8E, 0x37, 0x01, 0xA6, 0xF1, 0x10, 0x04, 0x71,
+   0xCC, 0xC6, 0xE7, 0xC2, 0x85, 0x94, 0xBD, 0x6F,
+   0xCB, 0xEA, 0xFC, 0xA1, 0x38, 0x5E, 0x08, 0x2E,
+   0x35, 0x42, 0x67, 0xD4, 0x56, 0x6D, 0x7C, 0xE5,
+   0x0E, 0x7D, 0x12, 0x65, 0xF5, 0x33, 0x82, 0xC4,
+   0x1D, 0xD2, 0x16, 0x58, 0xEC, 0xCD, 0xA8, 0xBF,
+   0xAB, 0x07, 0x45, 0x55, 0xB7, 0x6A, 0x70, 0xF2,
+   0xBE, 0x05, 0x6B, 0x9D, 0xEB, 0x13, 0x0D, 0x9F,
+   0xE8, 0xA7, 0xC8, 0x31, 0x3C, 0xB6, 0x21, 0xC0,
+   0x20, 0x60, 0x6C, 0xE2, 0xCE, 0x8C, 0xFD, 0x95,
+   0xE3, 0x4A, 0xB5, 0xB2, 0x40, 0xB1, 0xF3, 0x17,
+   0xF9, 0x24, 0x06, 0x22, 0x2F, 0x25, 0x93, 0x8A,
+   0x2A, 0x7E, 0x28, 0x3D, 0x47, 0xF8, 0x89, 0xA5,
+   0x7B, 0x9B, 0xC5, 0x84, 0x59, 0x46, 0x90, 0x74,
+   0x69, 0xC7, 0xAA, 0xEE, 0x6E, 0xD6, 0xB0, 0x18,
+   0x66, 0xA0, 0x7A, 0x1E, 0xFB, 0xDB, 0x4E, 0x51,
+   0x92, 0xE4, 0xE0, 0x3E, 0xB4, 0xD8, 0x23, 0x3B,
+   0xC1, 0x5F, 0xFE, 0x98, 0x99, 0x73, 0x09, 0xA9,
+   0xA3, 0xDF, 0x14, 0x5A, 0x26, 0x8F, 0x0B, 0xAF,
+   0x4C, 0x97, 0x54, 0xE1, 0x63, 0x48, 0xED, 0xBA,
+   0xCF, 0xBB, 0x1F, 0xDC, 0xA4, 0xFA, 0x64, 0x75,
+   0xDE, 0x81, 0x9A, 0xFF, 0x49, 0x41, 0x27, 0x62,
+   0x02, 0x15, 0xD9, 0x86, 0xAC, 0x3F, 0x0C, 0x61,
+   0xD1, 0x77, 0x2B, 0x1B, 0x96, 0xDA, 0x68, 0x1C,
+   0x44, 0x32, 0xBC, 0xA2, 0x87, 0xF7, 0x91, 0x8D,
+   0x80, 0xDD, 0x0A, 0x50, 0x34, 0x4B, 0x00, 0xB9,
+   0x36, 0xE6, 0x78, 0x4F, 0xC9, 0xE9, 0x2C, 0x43,
+   0x88, 0x9E, 0x9C, 0x5B, 0x4D, 0x3A, 0x39, 0xEF };
+
+unsigned char table_8[32] = {
+   0x13, 0x08, 0x1E, 0x1D, 0x17, 0x16, 0x07, 0x1F,
+   0x0E, 0x03, 0x1A, 0x19, 0x01, 0x12, 0x11, 0x10,
+   0x09, 0x0C, 0x0F, 0x14, 0x0B, 0x05, 0x00, 0x04,
+   0x1C, 0x18, 0x0A, 0x15, 0x02, 0x1B, 0x06, 0x0D };
+
+unsigned char table_9[256] = {
+   0x20, 0x2A, 0xDA, 0xFE, 0x76, 0x0D, 0xED, 0x39,
+   0x51, 0x4C, 0x46, 0x9A, 0xF1, 0xB0, 0x10, 0xC7,
+   0xD1, 0x6F, 0x18, 0x24, 0xB9, 0x7A, 0x4F, 0x47,
+   0xE0, 0x4E, 0x88, 0x09, 0x8A, 0xBA, 0x60, 0xBD,
+   0xC2, 0x27, 0x93, 0x7D, 0x94, 0x40, 0xCB, 0x80,
+   0xB8, 0x41, 0x84, 0x5D, 0xC1, 0x0F, 0x5E, 0x78,
+   0x2B, 0x48, 0x28, 0x29, 0xEE, 0x81, 0x90, 0x86,
+   0x50, 0x9C, 0xF3, 0xB2, 0x35, 0x52, 0x0C, 0x9D,
+   0xFC, 0x69, 0xD6, 0xA6, 0x06, 0xD7, 0xC6, 0xFF,
+   0x1C, 0x14, 0x57, 0x33, 0xE2, 0x1F, 0x83, 0xA8,
+   0xF7, 0x99, 0xC5, 0xDC, 0x70, 0x9E, 0xF4, 0x6B,
+   0x0A, 0x77, 0x95, 0x4A, 0x2E, 0x53, 0xF2, 0x62,
+   0x98, 0xF8, 0x96, 0xDB, 0xE6, 0x32, 0x3C, 0x58,
+   0xD5, 0x6D, 0xE7, 0x4B, 0xCE, 0x91, 0x43, 0xD8,
+   0xFA, 0xE3, 0x4D, 0xD9, 0x68, 0xDE, 0xEC, 0x01,
+   0x08, 0xD3, 0x8F, 0x19, 0xC4, 0xA7, 0x6E, 0x3E,
+   0x63, 0x12, 0x72, 0x42, 0x9F, 0xB4, 0x04, 0x1B,
+   0x7E, 0x11, 0x17, 0x73, 0xB5, 0x22, 0x56, 0xA1,
+   0x89, 0xDD, 0xF5, 0x3F, 0x49, 0x26, 0x8D, 0x15,
+   0x85, 0x75, 0x5F, 0x65, 0x82, 0xB6, 0xF6, 0xD2,
+   0xA4, 0x55, 0x37, 0xC8, 0xA0, 0xCC, 0x66, 0x5C,
+   0xC9, 0x25, 0x36, 0x67, 0x7C, 0xE1, 0xA3, 0xCF,
+   0xA9, 0x59, 0x2F, 0xFB, 0xBB, 0x07, 0x87, 0xA2,
+   0x44, 0x92, 0x13, 0x00, 0x16, 0x61, 0x38, 0xEB,
+   0xAE, 0xD4, 0x1E, 0x64, 0x6A, 0xE4, 0xCA, 0x1D,
+   0x6C, 0xDF, 0xAB, 0x5B, 0x03, 0x7B, 0x9B, 0x8C,
+   0x5A, 0xFD, 0xC3, 0xB3, 0x0B, 0xAA, 0xAC, 0x8B,
+   0xBE, 0xBC, 0x3D, 0x97, 0xCD, 0x05, 0x21, 0x8E,
+   0xAD, 0xEA, 0x54, 0x30, 0xAF, 0x02, 0xB1, 0x34,
+   0x0E, 0xA5, 0x3B, 0x45, 0x1A, 0x23, 0xE8, 0x7F,
+   0xEF, 0xB7, 0x31, 0xD0, 0xBF, 0x3A, 0x79, 0xE5,
+   0xF9, 0xF0, 0x2C, 0x74, 0xE9, 0x71, 0xC0, 0x2D };
+
+unsigned char table_10[32] = {
+   0x1D, 0x12, 0x11, 0x0D, 0x1E, 0x19, 0x16, 0x1B,
+   0x18, 0x13, 0x07, 0x17, 0x0C, 0x02, 0x00, 0x15,
+   0x0E, 0x08, 0x05, 0x01, 0x10, 0x06, 0x04, 0x0F,
+   0x1F, 0x1A, 0x0B, 0x09, 0x0A, 0x14, 0x1C, 0x03 };
+
+unsigned char table_11[256] = {
+   0x6B, 0x1D, 0xC6, 0x0A, 0xB7, 0xAC, 0xB2, 0x11,
+   0x29, 0xD3, 0xA2, 0x4D, 0xCB, 0x03, 0xEF, 0xA6,
+   0xC1, 0x5D, 0x75, 0x48, 0x35, 0x6C, 0xE2, 0x84,
+   0xAB, 0xAA, 0xD8, 0x2C, 0x0E, 0x95, 0x25, 0x27,
+   0x7D, 0x0B, 0xD0, 0xFB, 0x14, 0xE5, 0xF2, 0x4E,
+   0x7F, 0x2A, 0x63, 0x3C, 0xC9, 0xF6, 0xDC, 0x07,
+   0x26, 0x55, 0xCF, 0x2B, 0xCD, 0xA7, 0x17, 0xD2,
+   0x9A, 0x7B, 0x93, 0x78, 0x9E, 0xE6, 0x2F, 0x49,
+   0x1E, 0xFD, 0xF0, 0xFE, 0x7C, 0x33, 0x92, 0xA3,
+   0xC8, 0xA0, 0xA9, 0xC4, 0xA1, 0x94, 0x6D, 0x44,
+   0x0C, 0x90, 0x3A, 0x8C, 0x8E, 0x85, 0xAF, 0x40,
+   0x36, 0xA4, 0xD1, 0xB9, 0x19, 0x6F, 0xF4, 0xBA,
+   0x1A, 0x73, 0xD9, 0xB5, 0xB4, 0x7A, 0xF9, 0x83,
+   0x58, 0xAD, 0xCE, 0x60, 0x98, 0xDB, 0x1C, 0x1B,
+   0x52, 0xB8, 0xF3, 0x96, 0xED, 0xDE, 0xB3, 0xEE,
+   0x4F, 0xBD, 0x10, 0xD4, 0x43, 0xEA, 0xE7, 0x37,
+   0x12, 0x3D, 0xA8, 0x22, 0x65, 0xEC, 0x5B, 0x08,
+   0x9D, 0x0D, 0x5C, 0xB6, 0x8A, 0x79, 0x3F, 0x04,
+   0xD6, 0x01, 0xE1, 0xBE, 0xDD, 0x50, 0xFA, 0x41,
+   0x13, 0x91, 0xF7, 0xDA, 0x18, 0xB0, 0x45, 0x81,
+   0x4C, 0xF5, 0x32, 0x23, 0x56, 0x5A, 0xEB, 0x97,
+   0x34, 0x00, 0x77, 0x71, 0x4B, 0x70, 0xD5, 0x31,
+   0x72, 0x05, 0xDF, 0xE8, 0x15, 0x3B, 0x54, 0x16,
+   0x89, 0xE4, 0xF1, 0xD7, 0x80, 0x82, 0x4A, 0xE3,
+   0x39, 0x06, 0x47, 0x28, 0xC2, 0x86, 0x87, 0xB1,
+   0x62, 0x74, 0x53, 0x21, 0x67, 0x38, 0x42, 0xCA,
+   0x9B, 0xC3, 0x51, 0x99, 0x8B, 0x1F, 0x24, 0x8D,
+   0xF8, 0x68, 0x3E, 0x59, 0xBB, 0x61, 0x5F, 0xBC,
+   0x09, 0x6E, 0x8F, 0x0F, 0x2D, 0xC0, 0xE0, 0x46,
+   0x66, 0x69, 0xA5, 0xE9, 0x30, 0x9C, 0x5E, 0xAE,
+   0xBF, 0xC7, 0x20, 0x7E, 0x6A, 0xC5, 0x88, 0xFC,
+   0x64, 0x76, 0xFF, 0x9F, 0x2E, 0x02, 0xCC, 0x57 };
+
+unsigned char table_12[32] = {
+   0x14, 0x1B, 0x18, 0x00, 0x1F, 0x15, 0x17, 0x07,
+   0x11, 0x1A, 0x0E, 0x13, 0x12, 0x06, 0x01, 0x03,
+   0x1C, 0x0C, 0x0B, 0x1D, 0x10, 0x0F, 0x09, 0x19,
+   0x0D, 0x1E, 0x04, 0x05, 0x08, 0x16, 0x0A, 0x02 };
+
+unsigned char table_13[256] = {
+   0x37, 0x8A, 0x1B, 0x91, 0xA5, 0x2B, 0x2D, 0x88,
+   0x8E, 0xFE, 0x0E, 0xD3, 0xF3, 0xE9, 0x7D, 0xD1,
+   0x24, 0xEA, 0xB1, 0x8B, 0x5C, 0xA4, 0x44, 0x7E,
+   0x8C, 0x2C, 0x73, 0xD5, 0x50, 0x3E, 0xD7, 0x18,
+   0xB9, 0xD6, 0xBA, 0x94, 0x0C, 0xFC, 0xCB, 0xB4,
+   0x0D, 0x63, 0x4C, 0xDE, 0x77, 0x16, 0xFD, 0x81,
+   0x3C, 0x11, 0x45, 0x36, 0xF6, 0x67, 0x95, 0x6D,
+   0x6A, 0x1A, 0xA3, 0xC5, 0x92, 0x10, 0x28, 0x84,
+   0x48, 0xA6, 0x23, 0xE3, 0x4B, 0xE1, 0xF5, 0x19,
+   0xE0, 0x2E, 0x00, 0x61, 0x74, 0xCC, 0xF7, 0xB0,
+   0x68, 0xC8, 0x40, 0x6F, 0x59, 0x52, 0x26, 0x99,
+   0xC9, 0xF9, 0xC4, 0x53, 0x9B, 0xEC, 0x03, 0x17,
+   0xE2, 0x06, 0x30, 0x7B, 0xBE, 0xCD, 0x1D, 0x3B,
+   0xD2, 0x5B, 0x65, 0x21, 0x49, 0xB7, 0x79, 0xCF,
+   0x82, 0x86, 0xC7, 0x62, 0xEE, 0x8D, 0xFF, 0xD4,
+   0xC3, 0x85, 0xA7, 0xFA, 0xA9, 0x6B, 0xF2, 0x69,
+   0x9C, 0x38, 0x78, 0xBD, 0x7F, 0xDD, 0xCE, 0xA1,
+   0x33, 0xC2, 0x43, 0xEB, 0xD8, 0xE6, 0x2A, 0xE4,
+   0x76, 0x6C, 0xAA, 0x46, 0x05, 0xE7, 0xA0, 0x0A,
+   0x71, 0x98, 0x41, 0x5F, 0x0F, 0xEF, 0x51, 0xAD,
+   0xF0, 0xED, 0x96, 0x5A, 0x42, 0x3F, 0xBF, 0x6E,
+   0xBC, 0x5D, 0xC1, 0x15, 0x70, 0x54, 0x4D, 0x14,
+   0xB5, 0xCA, 0x27, 0x80, 0x87, 0x39, 0x60, 0x47,
+   0x9D, 0x2F, 0x56, 0x1F, 0xBB, 0x31, 0xF1, 0xE8,
+   0xB3, 0x9E, 0x5E, 0x7C, 0xD0, 0xC6, 0xB2, 0x57,
+   0x83, 0xAC, 0x09, 0x8F, 0xA2, 0x90, 0x13, 0x25,
+   0x01, 0x08, 0x64, 0xB6, 0x02, 0xDB, 0x55, 0x32,
+   0xAF, 0x9A, 0xC0, 0x1C, 0x12, 0x29, 0x0B, 0x72,
+   0x4F, 0xDA, 0xAB, 0x35, 0xF8, 0x22, 0xD9, 0x4E,
+   0x3D, 0x1E, 0xDC, 0x58, 0x20, 0x34, 0xAE, 0x66,
+   0x75, 0x93, 0x9F, 0x3A, 0x07, 0xE5, 0x89, 0xDF,
+   0x97, 0x4A, 0xB8, 0x7A, 0xF4, 0xFB, 0x04, 0xA8 };
+
+unsigned char table_14[32] = {
+   0x04, 0x14, 0x13, 0x15, 0x1A, 0x1B, 0x0F, 0x16,
+   0x02, 0x0D, 0x0C, 0x06, 0x10, 0x17, 0x01, 0x0B,
+   0x1E, 0x08, 0x1C, 0x18, 0x19, 0x0A, 0x1F, 0x05,
+   0x11, 0x09, 0x1D, 0x07, 0x0E, 0x12, 0x03, 0x00 };
+
+unsigned char table_15[256] = {
+   0x61, 0x48, 0x58, 0x41, 0x7F, 0x88, 0x43, 0x42,
+   0xD9, 0x80, 0x81, 0xFE, 0xC6, 0x49, 0xD7, 0x2C,
+   0xE6, 0x5B, 0xEE, 0xFF, 0x2A, 0x6F, 0xBF, 0x98,
+   0xD6, 0x20, 0xB9, 0xB1, 0x5D, 0x95, 0x72, 0x1E,
+   0x82, 0x96, 0xDE, 0xC1, 0x40, 0xD8, 0x70, 0xA3,
+   0xD1, 0x1F, 0xF0, 0x9F, 0x2D, 0xDC, 0x3F, 0xF9,
+   0x5E, 0x0D, 0x15, 0x2F, 0x67, 0x31, 0x9D, 0x84,
+   0x97, 0x0C, 0xF6, 0x79, 0xC2, 0xA7, 0xC0, 0x32,
+   0xB3, 0xEB, 0xED, 0x71, 0x30, 0xCC, 0x4B, 0xA0,
+   0xF5, 0xC4, 0xCD, 0x27, 0xFA, 0x11, 0x25, 0xDB,
+   0x4F, 0xE2, 0x7E, 0xA6, 0xAF, 0x34, 0x69, 0x63,
+   0x8F, 0x08, 0x1C, 0x85, 0xF1, 0x57, 0x78, 0xC8,
+   0xA2, 0x83, 0xB5, 0x68, 0xF7, 0x64, 0x45, 0x26,
+   0x3B, 0x03, 0xAD, 0x3C, 0x50, 0xD5, 0x77, 0xFC,
+   0xFB, 0x18, 0xC9, 0xD2, 0x9C, 0xBB, 0xBA, 0x76,
+   0x23, 0x55, 0xD3, 0x5A, 0x01, 0xE9, 0x87, 0x07,
+   0x19, 0x09, 0x39, 0x8A, 0x91, 0x93, 0x12, 0xDF,
+   0x22, 0xA8, 0xCF, 0x4E, 0x4D, 0x65, 0xB0, 0x0F,
+   0x13, 0x53, 0x21, 0x8C, 0xE5, 0xB7, 0x0B, 0x0E,
+   0x6C, 0x44, 0xCA, 0x7B, 0xC5, 0x6E, 0xCE, 0xE3,
+   0x14, 0x29, 0xAC, 0x2E, 0xE7, 0x59, 0xE8, 0x0A,
+   0xEA, 0x66, 0x7C, 0x94, 0x6D, 0x05, 0x9E, 0x9A,
+   0x2B, 0x38, 0x6A, 0xCB, 0x51, 0xEF, 0x06, 0xDA,
+   0xFD, 0x47, 0x92, 0x1D, 0xA5, 0x37, 0x33, 0xEC,
+   0xB4, 0x52, 0x56, 0xC3, 0xF4, 0xF8, 0x8B, 0xD0,
+   0xA4, 0x5F, 0x28, 0x89, 0x75, 0xC7, 0x04, 0x00,
+   0xE4, 0x86, 0x36, 0x3A, 0x99, 0x16, 0x7D, 0xE0,
+   0x7A, 0x4C, 0x54, 0x46, 0x73, 0xB2, 0xF3, 0xE1,
+   0x62, 0xBE, 0x90, 0x4A, 0x24, 0x6B, 0x3E, 0xAA,
+   0x1B, 0xF2, 0x60, 0xD4, 0xA9, 0x9B, 0x1A, 0xB8,
+   0xA1, 0x35, 0xAE, 0xB6, 0x10, 0x5C, 0x17, 0xBC,
+   0xAB, 0x8D, 0x02, 0x74, 0xBD, 0x3D, 0x8E, 0xDD };
+
+unsigned char table_16[256] = {
+   0x3F, 0x9C, 0x17, 0xC1, 0x59, 0xC6, 0x23, 0x93,
+   0x4B, 0xDF, 0xCB, 0x55, 0x2B, 0xDE, 0xCD, 0xAD,
+   0xB3, 0xE7, 0x42, 0x2F, 0x02, 0x5A, 0x7B, 0x5C,
+   0x8F, 0xD1, 0x11, 0xCE, 0xEC, 0xF6, 0xA4, 0xE6,
+   0x58, 0x98, 0x6A, 0x99, 0xFB, 0x9B, 0x53, 0x21,
+   0x8A, 0x09, 0x2E, 0x3C, 0x22, 0x38, 0xAC, 0x07,
+   0x91, 0x46, 0xA9, 0x95, 0xC3, 0x14, 0x84, 0xDB,
+   0x36, 0x68, 0x1D, 0xDD, 0xF9, 0x12, 0xE0, 0x3D,
+   0x8D, 0x4D, 0x05, 0x86, 0x69, 0xC0, 0xD3, 0xD5,
+   0xA5, 0xC9, 0xE5, 0x67, 0x6D, 0xE2, 0x7F, 0xFE,
+   0xB2, 0x0F, 0x62, 0xCF, 0x37, 0x35, 0xF3, 0x28,
+   0x16, 0xA6, 0x50, 0x76, 0x80, 0x00, 0x31, 0x97,
+   0x39, 0x7C, 0x25, 0x0C, 0x64, 0xF2, 0x52, 0x1A,
+   0x92, 0x4F, 0x2A, 0x56, 0x03, 0x4C, 0xBD, 0x10,
+   0xB7, 0x2C, 0x8C, 0xAE, 0x73, 0xB9, 0xE9, 0xF7,
+   0xA7, 0xE1, 0x75, 0xBC, 0xC5, 0x1C, 0x3A, 0x63,
+   0x7A, 0x4A, 0x29, 0xD2, 0x71, 0xE8, 0x08, 0xA1,
+   0xD4, 0xFD, 0x13, 0xFA, 0xA0, 0x27, 0x41, 0x72,
+   0x82, 0x18, 0x51, 0x60, 0x5E, 0x66, 0x0D, 0xAA,
+   0xD8, 0x1F, 0xAF, 0x45, 0xD0, 0xF1, 0x9F, 0x6B,
+   0xE4, 0x44, 0x89, 0xEE, 0xC4, 0x0B, 0x6C, 0xCC,
+   0x83, 0x77, 0xA2, 0x87, 0x0A, 0xA8, 0xED, 0x90,
+   0x74, 0x6E, 0xF5, 0xAB, 0xA3, 0xB6, 0x5F, 0x0E,
+   0x04, 0x9A, 0xB4, 0x8E, 0xF0, 0xFF, 0x88, 0xB5,
+   0xF8, 0xBF, 0x8B, 0x6F, 0x4E, 0x79, 0x40, 0xCA,
+   0x24, 0x26, 0xDC, 0x33, 0xEB, 0x2D, 0x5B, 0x1B,
+   0x9D, 0xC7, 0x49, 0x48, 0x54, 0x85, 0xEF, 0xD7,
+   0xC2, 0xB8, 0xC8, 0x5D, 0xD9, 0x3B, 0x15, 0xBB,
+   0x65, 0xE3, 0xD6, 0x30, 0x3E, 0x1E, 0x32, 0x9E,
+   0x57, 0x81, 0x34, 0x06, 0xFC, 0xBA, 0x7D, 0x20,
+   0x70, 0xDA, 0x7E, 0x47, 0x94, 0x61, 0xB0, 0x78,
+   0xF4, 0xBE, 0xEA, 0x19, 0x43, 0x01, 0xB1, 0x96 };
+
+unsigned char table_17[256] = {
+   0x7E, 0xF1, 0xD3, 0x75, 0x87, 0xA6, 0xED, 0x9E,
+   0xA9, 0xD5, 0xC6, 0xBF, 0xE6, 0x6A, 0xEE, 0x4B,
+   0x34, 0xDF, 0x4C, 0x7D, 0xDD, 0xFE, 0x3F, 0xAF,
+   0x66, 0x2D, 0x74, 0x6F, 0xFC, 0x4F, 0x5F, 0x88,
+   0x29, 0x7B, 0xC7, 0x2A, 0x70, 0xE8, 0x1D, 0xDE,
+   0xD0, 0x55, 0x71, 0x81, 0xC4, 0x0D, 0x50, 0x4E,
+   0x58, 0x00, 0x96, 0x97, 0xBB, 0xD7, 0x53, 0x15,
+   0x6C, 0x40, 0x17, 0xC9, 0xFF, 0x8F, 0x94, 0xFB,
+   0x19, 0x9A, 0x3E, 0xB5, 0x5A, 0x5E, 0x86, 0x24,
+   0xB8, 0x77, 0xBA, 0x85, 0x51, 0x18, 0xBE, 0x59,
+   0x79, 0xF3, 0xD4, 0xC3, 0xAB, 0x28, 0xFD, 0x25,
+   0x41, 0x91, 0x07, 0x8D, 0xAE, 0x49, 0xF5, 0x80,
+   0x35, 0xA1, 0x9C, 0x3C, 0xE2, 0x65, 0xB3, 0xE0,
+   0x16, 0xCB, 0x12, 0x6B, 0xF7, 0xB1, 0x93, 0x8A,
+   0xCE, 0x54, 0x4D, 0xF8, 0x13, 0xA2, 0x95, 0x46,
+   0xEA, 0x61, 0x57, 0x9D, 0x27, 0x8B, 0x3D, 0x60,
+   0x36, 0x68, 0x06, 0x56, 0xB6, 0x1B, 0xD2, 0x89,
+   0x10, 0xA7, 0xC5, 0x1A, 0x0B, 0x2C, 0xBD, 0x14,
+   0x0A, 0xDC, 0x23, 0xA8, 0xE1, 0x04, 0x02, 0xC0,
+   0xB2, 0x9B, 0xE3, 0x2E, 0x33, 0x7C, 0x32, 0xAC,
+   0x7A, 0x39, 0xB0, 0xF9, 0x98, 0x5B, 0x3A, 0x48,
+   0x21, 0x90, 0xB9, 0x20, 0xF0, 0xA0, 0x09, 0x1F,
+   0x2F, 0xEF, 0xEB, 0x22, 0x78, 0x82, 0x37, 0xD6,
+   0xD1, 0x84, 0x76, 0x01, 0xDB, 0x43, 0xC2, 0xB7,
+   0x7F, 0xA4, 0xE5, 0xC1, 0x1C, 0x69, 0x05, 0xEC,
+   0xD8, 0x38, 0x67, 0x42, 0x72, 0xBC, 0x73, 0xAD,
+   0xA3, 0xE9, 0x4A, 0x8E, 0x47, 0x1E, 0xC8, 0x6E,
+   0xDA, 0x5D, 0x2B, 0xF6, 0x30, 0x63, 0xCC, 0xF4,
+   0xCD, 0x8C, 0x0F, 0x3B, 0xE7, 0xD9, 0xCF, 0xB4,
+   0x03, 0x92, 0x0E, 0x31, 0xE4, 0x08, 0xF2, 0x45,
+   0xCA, 0x83, 0x26, 0x5C, 0xA5, 0x44, 0x64, 0x6D,
+   0x9F, 0x99, 0x62, 0xAA, 0xFA, 0x11, 0x0C, 0x52 };
+
+unsigned char table_18[256] = {
+   0x0F, 0x42, 0x3D, 0x86, 0x3E, 0x66, 0xFE, 0x5C,
+   0x52, 0xE2, 0xA3, 0xB3, 0xCE, 0x16, 0xCC, 0x95,
+   0xB0, 0x8B, 0x82, 0x3B, 0x93, 0x7D, 0x62, 0x08,
+   0x1C, 0x6E, 0xBB, 0xCB, 0x1D, 0x88, 0x69, 0xD4,
+   0xC9, 0x40, 0x1F, 0xBE, 0x27, 0xBC, 0xDB, 0x38,
+   0xE5, 0xA1, 0x71, 0xBA, 0x8A, 0x5E, 0xFD, 0x36,
+   0x8F, 0x26, 0x6B, 0xE4, 0x20, 0x6D, 0xC5, 0xDE,
+   0xE0, 0x83, 0x7C, 0xD5, 0xD9, 0x4D, 0xDC, 0xE3,
+   0x0D, 0x32, 0xED, 0x0E, 0x2F, 0x21, 0xA7, 0x79,
+   0xA0, 0xD3, 0x8C, 0x14, 0x6F, 0xB7, 0xF8, 0x85,
+   0x5D, 0x37, 0x24, 0xD6, 0x25, 0xD2, 0x8E, 0xA5,
+   0xB8, 0xCD, 0x5A, 0x9F, 0x05, 0xAD, 0x65, 0x9E,
+   0x4F, 0x5B, 0x56, 0xF0, 0xAA, 0xC2, 0x28, 0xA8,
+   0x6A, 0x01, 0x99, 0x2E, 0xA6, 0x77, 0x74, 0x64,
+   0x76, 0x15, 0x90, 0x75, 0xAF, 0xE8, 0x39, 0x48,
+   0x09, 0x11, 0xE1, 0x2D, 0xEC, 0xB5, 0x7A, 0xB1,
+   0x94, 0x13, 0x41, 0x4C, 0x02, 0xA9, 0x97, 0xDF,
+   0xC3, 0x8D, 0xEA, 0x3A, 0x9C, 0xD1, 0xA2, 0x9A,
+   0xD7, 0x59, 0xD8, 0x18, 0xDA, 0x47, 0x89, 0x81,
+   0xC7, 0xF5, 0xFC, 0x98, 0xCA, 0x91, 0x06, 0x68,
+   0xC8, 0x07, 0x4A, 0x84, 0x0A, 0xE7, 0x33, 0x2C,
+   0xEB, 0xDD, 0x5F, 0xAC, 0x23, 0x1A, 0x35, 0x70,
+   0x43, 0x80, 0x61, 0xAE, 0xC1, 0xD0, 0x7B, 0x92,
+   0x49, 0x51, 0x53, 0xC4, 0x34, 0x30, 0x0C, 0x4B,
+   0x00, 0x04, 0x10, 0xFF, 0x63, 0x44, 0xB4, 0x0B,
+   0x57, 0x72, 0xF1, 0x9D, 0x19, 0xF6, 0xB2, 0x87,
+   0x1B, 0xEE, 0x46, 0x2A, 0xF3, 0xBF, 0x12, 0x96,
+   0x58, 0x2B, 0xF9, 0xB6, 0xCF, 0x22, 0x3C, 0xAB,
+   0x1E, 0x6C, 0x31, 0xC6, 0xF7, 0x78, 0x45, 0x17,
+   0xE9, 0x7E, 0x73, 0xF2, 0x55, 0xFB, 0x3F, 0x9B,
+   0xF4, 0xBD, 0xA4, 0x29, 0x60, 0x03, 0xB9, 0x50,
+   0xFA, 0x4E, 0xEF, 0x54, 0xE6, 0x7F, 0xC0, 0x67 };
+
+unsigned char table_19[256] = {
+   0xEA, 0xE7, 0x13, 0x14, 0xB9, 0xC0, 0xC4, 0x42,
+   0x49, 0x6E, 0x2A, 0xA6, 0x65, 0x3C, 0x6A, 0x40,
+   0x07, 0xCD, 0x4F, 0xFE, 0xF2, 0x2D, 0xC8, 0x30,
+   0x9D, 0xBE, 0x1B, 0x9B, 0x4A, 0x7E, 0x9F, 0xA7,
+   0x78, 0xAB, 0x4D, 0x1D, 0xF1, 0x96, 0x32, 0x84,
+   0xFB, 0x80, 0x88, 0xE8, 0x41, 0x97, 0xDC, 0xD0,
+   0x4E, 0x33, 0xA4, 0x3B, 0xE0, 0xDD, 0x36, 0xC9,
+   0x72, 0x48, 0x8A, 0x2F, 0x35, 0xF0, 0xDF, 0x21,
+   0xE1, 0xE5, 0x6C, 0x9A, 0x60, 0x8F, 0xB7, 0x24,
+   0xE4, 0x9E, 0x8C, 0x0F, 0x3D, 0x28, 0xBB, 0xD6,
+   0x69, 0xA0, 0x66, 0xC7, 0xE3, 0xD8, 0x11, 0x27,
+   0xD9, 0x37, 0xF4, 0xF5, 0x8E, 0xD4, 0x76, 0xE2,
+   0xDB, 0x15, 0xA2, 0x5C, 0x9C, 0xEE, 0x44, 0xED,
+   0x2B, 0xB3, 0x75, 0x74, 0x71, 0x8B, 0x3A, 0x91,
+   0x06, 0x19, 0xC1, 0x57, 0x89, 0xCC, 0x82, 0x10,
+   0x17, 0xB2, 0x08, 0x70, 0x39, 0xCA, 0xBA, 0xB5,
+   0xAA, 0xBF, 0x02, 0xBD, 0x26, 0x58, 0x04, 0x54,
+   0x23, 0x4B, 0x90, 0x51, 0x6D, 0x98, 0xD5, 0xB0,
+   0xAF, 0x22, 0xDA, 0xB4, 0x87, 0xFC, 0x7D, 0x18,
+   0x6F, 0x64, 0x59, 0x09, 0x0C, 0xA5, 0x5D, 0x03,
+   0x0A, 0xD3, 0xCE, 0x99, 0x8D, 0xC2, 0xC3, 0x62,
+   0xD2, 0x83, 0x1A, 0xAC, 0x7C, 0x93, 0xD7, 0xA9,
+   0x16, 0xF7, 0x77, 0xE6, 0x3E, 0x05, 0x73, 0x55,
+   0x43, 0x95, 0x7A, 0x6B, 0x38, 0x67, 0x3F, 0xC6,
+   0xAD, 0x0E, 0x29, 0x46, 0x45, 0xFA, 0xBC, 0xEC,
+   0x5B, 0x7F, 0x0B, 0x1C, 0x01, 0x12, 0x85, 0x50,
+   0xF9, 0xEF, 0x25, 0x34, 0x79, 0x2E, 0xEB, 0x00,
+   0x5F, 0x86, 0xF8, 0x4C, 0xA8, 0x56, 0xB6, 0x5A,
+   0xF3, 0x31, 0x94, 0x92, 0xB1, 0xB8, 0x52, 0xD1,
+   0xCF, 0xCB, 0xA1, 0x81, 0x68, 0x47, 0xFF, 0xC5,
+   0xFD, 0x1F, 0xDE, 0x53, 0xA3, 0x2C, 0x20, 0xF6,
+   0x1E, 0x0D, 0xAE, 0x7B, 0x5E, 0x61, 0xE9, 0x63 };
+
+unsigned char table_20[32] = {
+   0x0D, 0x0B, 0x11, 0x02, 0x05, 0x1B, 0x08, 0x1D,
+   0x04, 0x14, 0x01, 0x09, 0x00, 0x19, 0x1E, 0x15,
+   0x1F, 0x0A, 0x0F, 0x1C, 0x10, 0x16, 0x0C, 0x07,
+   0x13, 0x1A, 0x06, 0x17, 0x0E, 0x12, 0x18, 0x03 };
+
+unsigned char table_21[256] = {
+   0x4C, 0x94, 0xAD, 0x66, 0x9E, 0x69, 0x04, 0xA8,
+   0x61, 0xE0, 0xE1, 0x3D, 0xFD, 0x9C, 0xFB, 0x19,
+   0x1E, 0x80, 0x8C, 0xA0, 0xFC, 0x27, 0x26, 0x3B,
+   0x48, 0x6D, 0x07, 0xE4, 0xEA, 0x17, 0x64, 0x9B,
+   0xD0, 0xE2, 0xD1, 0x13, 0x39, 0xF5, 0x73, 0xD3,
+   0x0C, 0x3A, 0x6E, 0x77, 0xFA, 0xE3, 0x2F, 0x44,
+   0x7E, 0x72, 0x30, 0x43, 0xD4, 0x7F, 0x36, 0xD9,
+   0xBD, 0x3E, 0x3F, 0x91, 0xBE, 0x54, 0x79, 0xA6,
+   0x7C, 0x0E, 0xC5, 0x7A, 0x70, 0xC4, 0xD7, 0xCE,
+   0xDA, 0xAA, 0x68, 0x8F, 0xBC, 0x96, 0x1B, 0x16,
+   0xA2, 0xC6, 0x67, 0x09, 0x45, 0x9F, 0xCF, 0x41,
+   0xC8, 0x60, 0x74, 0x99, 0x5D, 0x85, 0x5F, 0x50,
+   0x33, 0x52, 0x22, 0xA9, 0xB5, 0x2D, 0x98, 0x87,
+   0x15, 0x9A, 0xAC, 0x2C, 0xDE, 0xC0, 0xB8, 0x37,
+   0x88, 0x1F, 0xC1, 0x4F, 0x65, 0x0F, 0x3C, 0x84,
+   0x4B, 0x1A, 0xAB, 0xA4, 0x23, 0xCB, 0xB1, 0xC7,
+   0xDB, 0xEF, 0x40, 0x0D, 0x46, 0xE8, 0xF4, 0x71,
+   0x38, 0x01, 0x5C, 0x0B, 0x5E, 0xC9, 0xAF, 0xC3,
+   0xF6, 0xB6, 0x10, 0x1D, 0xE5, 0x8A, 0x90, 0xA7,
+   0xA3, 0x05, 0x4E, 0x14, 0x63, 0x25, 0x34, 0xEC,
+   0x6B, 0x95, 0x21, 0x55, 0xF2, 0xF0, 0x47, 0x9D,
+   0xF8, 0x8E, 0x02, 0x0A, 0xED, 0x97, 0xAE, 0x00,
+   0x2A, 0xEB, 0xB2, 0xA5, 0x32, 0x06, 0x2E, 0xFE,
+   0x8D, 0x7B, 0x7D, 0x35, 0x5A, 0xD2, 0xF1, 0xE9,
+   0xF9, 0x62, 0xB7, 0xB9, 0x53, 0x75, 0x5B, 0x8B,
+   0xCC, 0x6C, 0x18, 0x49, 0x89, 0x31, 0xB0, 0x92,
+   0x6F, 0xDF, 0x03, 0x57, 0xF3, 0x58, 0xCA, 0x2B,
+   0x93, 0xA1, 0xD6, 0x24, 0x29, 0xCD, 0x59, 0x1C,
+   0x83, 0xB3, 0x42, 0xBF, 0x82, 0xB4, 0x11, 0x4A,
+   0x08, 0xEE, 0x76, 0x4D, 0x12, 0xDC, 0xE6, 0xC2,
+   0x56, 0xBA, 0x86, 0x28, 0x6A, 0x20, 0x51, 0xF7,
+   0xFF, 0xD8, 0xE7, 0xDD, 0xBB, 0x78, 0xD5, 0x81 };
+
+unsigned char table_22[32] = {
+   0x0B, 0x15, 0x1C, 0x0C, 0x06, 0x0A, 0x1D, 0x16,
+   0x12, 0x0E, 0x04, 0x11, 0x1F, 0x0F, 0x07, 0x02,
+   0x17, 0x13, 0x19, 0x18, 0x0D, 0x10, 0x1A, 0x05,
+   0x03, 0x00, 0x01, 0x08, 0x09, 0x14, 0x1B, 0x1E };
+
+unsigned char table_23[256] = {
+   0x36, 0x53, 0x2D, 0xD0, 0x7A, 0xF0, 0xD5, 0x1C,
+   0x50, 0x61, 0x9A, 0x90, 0x0B, 0x29, 0x20, 0x77,
+   0xF1, 0x82, 0xFE, 0xC1, 0xA7, 0xB6, 0x78, 0x87,
+   0x02, 0x05, 0xCB, 0x28, 0xAE, 0xD6, 0x17, 0x1A,
+   0x91, 0x5D, 0xB9, 0xE2, 0xDE, 0x6A, 0x4E, 0x07,
+   0xAC, 0x38, 0x13, 0x3B, 0x46, 0xFD, 0xB7, 0xD1,
+   0x79, 0xFB, 0x58, 0x76, 0x08, 0x47, 0x95, 0xA6,
+   0x99, 0x9E, 0x12, 0x67, 0xC2, 0xED, 0x9C, 0x1B,
+   0x89, 0x71, 0xB5, 0x4A, 0xAA, 0x5F, 0x34, 0x85,
+   0x40, 0x2B, 0x9F, 0x37, 0x7C, 0x0F, 0xD4, 0x75,
+   0x48, 0x27, 0x2E, 0xC9, 0xEB, 0x06, 0xDF, 0x8C,
+   0x14, 0xAF, 0xEE, 0xA2, 0x74, 0x45, 0x8D, 0x70,
+   0x6B, 0xD7, 0x56, 0xCF, 0xBC, 0x7B, 0x01, 0xC8,
+   0x54, 0xB0, 0x3C, 0x39, 0xFA, 0x81, 0xDC, 0xBB,
+   0x0D, 0xB2, 0xAD, 0x93, 0xC7, 0x8A, 0x73, 0x6C,
+   0xC3, 0x04, 0x2F, 0xEF, 0x52, 0x33, 0x9D, 0x1E,
+   0xC5, 0x65, 0x23, 0xD8, 0xB1, 0xD2, 0xE5, 0x25,
+   0x2C, 0xE6, 0x92, 0xB4, 0xF7, 0xF4, 0x8F, 0x6E,
+   0xE8, 0x5A, 0x8E, 0x7D, 0x4C, 0xB3, 0xFF, 0x41,
+   0x26, 0xE3, 0x30, 0x69, 0xF8, 0x80, 0x57, 0x4F,
+   0xA0, 0x7F, 0x66, 0x68, 0xE1, 0x7E, 0x0E, 0x31,
+   0xE7, 0xEA, 0x3E, 0x8B, 0x4B, 0x94, 0xE9, 0xCD,
+   0x19, 0x35, 0xA3, 0x98, 0xD9, 0x5B, 0x44, 0x2A,
+   0xE0, 0x6D, 0xF3, 0xE4, 0x72, 0x18, 0x03, 0x59,
+   0x84, 0x09, 0xA1, 0x9B, 0xBD, 0xDA, 0x4D, 0x63,
+   0xCC, 0x3A, 0x10, 0xFC, 0x3F, 0x0A, 0x88, 0x24,
+   0xF5, 0x21, 0xC4, 0x6F, 0x1F, 0x42, 0x62, 0x64,
+   0x51, 0xDD, 0xCA, 0xF9, 0x22, 0xCE, 0xA8, 0x86,
+   0xBA, 0xB8, 0x5C, 0xAB, 0x32, 0x00, 0x0C, 0xF2,
+   0x83, 0xDB, 0xF6, 0x60, 0x3D, 0x16, 0xEC, 0x11,
+   0xA4, 0xBE, 0x96, 0x5E, 0x97, 0xD3, 0xA5, 0x55,
+   0x1D, 0x15, 0xC6, 0xBF, 0xA9, 0x43, 0xC0, 0x49 };
+
+unsigned char table_24[256] = {
+   0xDC, 0x5A, 0xE6, 0x59, 0x64, 0xDA, 0x58, 0x40,
+   0x95, 0xF8, 0x2A, 0xE0, 0x39, 0x7E, 0x32, 0x89,
+   0x09, 0x93, 0xED, 0x55, 0xC3, 0x5B, 0x1A, 0xD1,
+   0xA5, 0x8B, 0x0F, 0x13, 0xC9, 0xE1, 0x34, 0xD0,
+   0xB6, 0xA2, 0xD9, 0x52, 0x57, 0x83, 0xFD, 0xE9,
+   0xAC, 0x73, 0x6E, 0x21, 0xF1, 0x0E, 0x25, 0xCC,
+   0x36, 0xFB, 0xF7, 0x92, 0x15, 0x30, 0x54, 0x91,
+   0xD6, 0x9E, 0xAA, 0x35, 0x70, 0xB2, 0xC0, 0x27,
+   0xFE, 0x04, 0xBC, 0xC7, 0x02, 0xFA, 0x7D, 0xE3,
+   0xBE, 0x62, 0x79, 0x2B, 0x31, 0x6A, 0x8F, 0x7F,
+   0x56, 0xF0, 0xB4, 0x0C, 0x1F, 0x68, 0xB7, 0xB9,
+   0x0B, 0x14, 0x3E, 0xA9, 0x4B, 0x03, 0x10, 0xEE,
+   0x2C, 0xAB, 0x8A, 0x77, 0xB1, 0xE7, 0xCA, 0xD4,
+   0x98, 0x01, 0xAD, 0x1E, 0x50, 0x26, 0x82, 0x44,
+   0xF3, 0xBF, 0xD3, 0x6B, 0x33, 0x0A, 0x3C, 0x5D,
+   0xCE, 0x81, 0xC5, 0x78, 0x9F, 0xB8, 0x23, 0xDB,
+   0x4E, 0xA1, 0x41, 0x76, 0xAE, 0x51, 0x86, 0x06,
+   0x7A, 0x66, 0xA0, 0x5E, 0x29, 0x17, 0x84, 0x4A,
+   0xB0, 0x3B, 0x3D, 0x71, 0x07, 0x7B, 0x0D, 0x9A,
+   0x6F, 0x9B, 0x5C, 0x88, 0xB3, 0xD7, 0x24, 0xD5,
+   0x48, 0xF5, 0xE8, 0xE4, 0xCF, 0x16, 0xA4, 0xC8,
+   0xEF, 0x42, 0x22, 0xEC, 0x47, 0x69, 0x90, 0x63,
+   0xE2, 0x1B, 0x87, 0x85, 0x3F, 0xDE, 0x8C, 0x60,
+   0x99, 0xE5, 0x8E, 0x4F, 0xF4, 0xBA, 0xB5, 0x9C,
+   0x37, 0x67, 0xBD, 0xA6, 0x97, 0xDD, 0xCB, 0x43,
+   0x45, 0x19, 0x49, 0x1C, 0x75, 0xC1, 0xBB, 0xF2,
+   0x46, 0xFC, 0x53, 0x9D, 0xD8, 0xA3, 0xDF, 0x2F,
+   0xEB, 0x72, 0x94, 0xA8, 0x6D, 0xC6, 0x28, 0x4C,
+   0x00, 0x38, 0xC2, 0x65, 0x05, 0x2E, 0xD2, 0x12,
+   0xFF, 0x18, 0x61, 0x6C, 0x7C, 0x11, 0xAF, 0x96,
+   0xCD, 0x20, 0x74, 0x08, 0x1D, 0xC4, 0xF9, 0x4D,
+   0xEA, 0x8D, 0x2D, 0x5F, 0xF6, 0xA7, 0x80, 0x3A };
+
+unsigned char table_25[32] = {
+   0x0A, 0x11, 0x17, 0x03, 0x05, 0x0B, 0x18, 0x13,
+   0x09, 0x02, 0x00, 0x1C, 0x0C, 0x08, 0x1B, 0x14,
+   0x06, 0x0E, 0x01, 0x0D, 0x16, 0x1E, 0x1D, 0x19,
+   0x0F, 0x1A, 0x10, 0x04, 0x12, 0x15, 0x07, 0x1F };
+
+unsigned char table_26[32] = {
+   0x19, 0x13, 0x1B, 0x01, 0x1C, 0x0D, 0x0C, 0x15,
+   0x0B, 0x00, 0x1A, 0x0F, 0x12, 0x16, 0x08, 0x0A,
+   0x03, 0x06, 0x14, 0x10, 0x18, 0x04, 0x11, 0x1D,
+   0x1F, 0x07, 0x17, 0x05, 0x02, 0x0E, 0x1E, 0x09 };
+
+unsigned char table_27[256] = {
+   0x72, 0xF0, 0x14, 0xCB, 0x61, 0xA5, 0xB2, 0x02,
+   0x75, 0x22, 0xC3, 0x9D, 0x5A, 0x63, 0xFA, 0x5F,
+   0xD9, 0x55, 0x58, 0x43, 0x24, 0x7D, 0x77, 0x93,
+   0xBA, 0x50, 0x1D, 0xF7, 0x49, 0x18, 0xB0, 0x42,
+   0xBB, 0xEC, 0x52, 0x38, 0xDC, 0xC8, 0x16, 0x54,
+   0x17, 0x19, 0x89, 0x67, 0x33, 0x3C, 0x0A, 0xAD,
+   0xC9, 0xDE, 0x81, 0xED, 0xBD, 0x0E, 0x0B, 0x6D,
+   0x46, 0x30, 0x35, 0x2B, 0x8C, 0xA0, 0x1C, 0x0D,
+   0xFD, 0xA1, 0x70, 0xC6, 0xD8, 0x41, 0xB3, 0xC0,
+   0x44, 0xEB, 0x92, 0xBE, 0x6B, 0x98, 0x1A, 0x76,
+   0x71, 0xC5, 0x51, 0x56, 0x80, 0xFC, 0x01, 0x53,
+   0x4B, 0xD0, 0x8B, 0xD2, 0x7B, 0xE7, 0x15, 0x5D,
+   0xE5, 0xA6, 0x8A, 0xD3, 0x9B, 0xF4, 0x69, 0x23,
+   0xE8, 0xB6, 0xC7, 0xE2, 0x73, 0x9F, 0x88, 0xDF,
+   0xB4, 0x28, 0xEE, 0xC2, 0x94, 0xB8, 0xF9, 0x7F,
+   0x4A, 0x57, 0x06, 0xF6, 0xBF, 0xC1, 0xAB, 0xFB,
+   0xA4, 0x8E, 0xD1, 0xD7, 0xF5, 0x7C, 0xA3, 0x1E,
+   0x3B, 0x32, 0x03, 0xAA, 0x90, 0x5C, 0x48, 0xE0,
+   0xE3, 0xCF, 0xD4, 0xEF, 0x59, 0xD5, 0x1B, 0x34,
+   0x1F, 0x95, 0xCE, 0x7A, 0x20, 0x26, 0x87, 0xB7,
+   0x78, 0x9C, 0x4F, 0xA2, 0x12, 0x97, 0x27, 0x3F,
+   0xFF, 0x07, 0x84, 0x96, 0x04, 0xAF, 0xA8, 0xEA,
+   0x2C, 0x6C, 0xAE, 0x37, 0x91, 0xA9, 0x10, 0xDB,
+   0xCD, 0xDA, 0x08, 0x99, 0xF1, 0x4D, 0xCC, 0x68,
+   0x79, 0x2E, 0xB1, 0x39, 0x9E, 0xE9, 0x2F, 0x6A,
+   0x3D, 0x0F, 0x85, 0x8D, 0xCA, 0x29, 0x86, 0xD6,
+   0xDD, 0x05, 0x25, 0x3A, 0x40, 0x21, 0x45, 0xAC,
+   0x11, 0xF3, 0xA7, 0x09, 0x2A, 0x31, 0xE4, 0x0C,
+   0xF8, 0x6E, 0x3E, 0xB5, 0x82, 0xFE, 0x74, 0x13,
+   0x65, 0xE1, 0x2D, 0x8F, 0xE6, 0xC4, 0x00, 0x5B,
+   0x4E, 0xB9, 0x66, 0xF2, 0x62, 0x36, 0x4C, 0x83,
+   0x5E, 0x6F, 0x47, 0x64, 0xBC, 0x9A, 0x60, 0x7E };
+
+unsigned char table_28[32] = {
+   0x15, 0x05, 0x08, 0x19, 0x02, 0x18, 0x1E, 0x07,
+   0x0D, 0x0C, 0x1A, 0x06, 0x17, 0x03, 0x10, 0x09,
+   0x01, 0x11, 0x1C, 0x04, 0x0F, 0x1F, 0x12, 0x0B,
+   0x1B, 0x13, 0x0A, 0x16, 0x0E, 0x00, 0x1D, 0x14 };
+
+unsigned char table_29[256] = {
+   0x34, 0x59, 0x05, 0x13, 0x09, 0x1D, 0xDF, 0x77,
+   0x11, 0xA5, 0x92, 0x27, 0xCD, 0x7B, 0x5E, 0x80,
+   0xF9, 0x50, 0x18, 0x24, 0xD4, 0x70, 0x4A, 0x39,
+   0x66, 0xA4, 0xDB, 0xE9, 0xED, 0x48, 0xD9, 0xE7,
+   0x32, 0xDA, 0x53, 0x8F, 0x72, 0xE1, 0xF6, 0xFE,
+   0xD3, 0xAD, 0xA6, 0x1F, 0xB9, 0xD1, 0x0F, 0x4C,
+   0x23, 0x90, 0x68, 0xBC, 0x4B, 0x9B, 0x3D, 0xAB,
+   0xF0, 0x94, 0x4F, 0x1C, 0x07, 0x65, 0x7F, 0x01,
+   0x5C, 0xD7, 0x21, 0x8C, 0xBF, 0x8E, 0xB8, 0x86,
+   0x6C, 0x33, 0x36, 0xC1, 0x06, 0x74, 0x37, 0x84,
+   0x41, 0xAE, 0x67, 0x29, 0xB4, 0x85, 0xCE, 0x2A,
+   0xCB, 0x1E, 0x61, 0x9E, 0x7A, 0x44, 0x3E, 0x89,
+   0x14, 0x20, 0x19, 0xBB, 0xE0, 0xAA, 0xCF, 0x83,
+   0xA8, 0x93, 0x43, 0xF2, 0xAC, 0x0E, 0xD2, 0xCC,
+   0xDD, 0x47, 0x58, 0xC9, 0xCA, 0x1B, 0x54, 0x6E,
+   0x8A, 0x79, 0xF8, 0xC4, 0xFB, 0xD5, 0x91, 0xDE,
+   0x12, 0x31, 0x99, 0xFA, 0x6D, 0xC8, 0x57, 0xEC,
+   0xB7, 0x28, 0x0C, 0x52, 0xF1, 0x0D, 0xB1, 0x9A,
+   0x26, 0x98, 0x16, 0x7D, 0xD0, 0x2E, 0x8B, 0xD8,
+   0xE6, 0xE8, 0x30, 0xFD, 0x7C, 0x64, 0x5A, 0xBD,
+   0x87, 0xE2, 0xA1, 0x3F, 0xC3, 0x38, 0x96, 0xA3,
+   0x2D, 0xF3, 0x3A, 0xEE, 0xC0, 0x10, 0xEA, 0x6F,
+   0x8D, 0x03, 0xF4, 0x51, 0x97, 0x7E, 0x56, 0x42,
+   0x3C, 0x5D, 0x5F, 0xF5, 0x6A, 0xAF, 0xE4, 0xBE,
+   0xBA, 0x78, 0xA0, 0x5B, 0x49, 0xA7, 0xC7, 0x9C,
+   0x63, 0x6B, 0x00, 0x17, 0x69, 0x75, 0x3B, 0x40,
+   0xEF, 0x45, 0xB5, 0x2B, 0x2F, 0x02, 0xC6, 0x22,
+   0x9F, 0xFC, 0x73, 0x08, 0x81, 0xB2, 0x2C, 0x71,
+   0x35, 0xA2, 0xE3, 0xB3, 0x9D, 0xC5, 0x0A, 0xC2,
+   0x25, 0x82, 0xDC, 0x88, 0xA9, 0xE5, 0xF7, 0xEB,
+   0xD6, 0x60, 0x76, 0x55, 0x0B, 0x4E, 0xFF, 0x1A,
+   0x46, 0x62, 0xB6, 0xB0, 0x15, 0x04, 0x95, 0x4D };
+
+unsigned char table_30[32] = {
+   0x00, 0x1C, 0x0E, 0x0C, 0x06, 0x16, 0x09, 0x12,
+   0x01, 0x13, 0x0B, 0x14, 0x11, 0x08, 0x04, 0x18,
+   0x10, 0x1B, 0x15, 0x03, 0x02, 0x19, 0x1A, 0x17,
+   0x1E, 0x1F, 0x0F, 0x07, 0x0D, 0x05, 0x1D, 0x0A };
+
+unsigned char table_31[256] = {
+   0xDF, 0xD8, 0x3F, 0xBC, 0x5F, 0xC9, 0x8E, 0x4C,
+   0x0B, 0x3C, 0xE5, 0xBF, 0x39, 0xD5, 0x30, 0xDD,
+   0x23, 0xC7, 0x72, 0x63, 0x1F, 0xF8, 0x96, 0x31,
+   0x70, 0xD6, 0x9E, 0xE8, 0x9D, 0xF5, 0xEF, 0x65,
+   0xC2, 0x50, 0x62, 0x77, 0xD3, 0x6C, 0x1A, 0x91,
+   0xBB, 0xFF, 0xCD, 0x9B, 0xB6, 0xBA, 0xB8, 0x7A,
+   0x14, 0xA7, 0x74, 0x89, 0xD4, 0x6E, 0x19, 0x69,
+   0xAB, 0x01, 0x15, 0x0E, 0x87, 0x55, 0x79, 0x1C,
+   0x18, 0xBE, 0xA8, 0xDB, 0x52, 0xD2, 0x8F, 0x7E,
+   0x81, 0xAF, 0xFD, 0x5C, 0x3E, 0x1B, 0xB9, 0xB2,
+   0xB7, 0x51, 0x57, 0x8C, 0xCF, 0x5B, 0xA4, 0x75,
+   0xDE, 0x22, 0x8B, 0x10, 0x12, 0xC8, 0x35, 0x2D,
+   0x45, 0xB5, 0xF0, 0x47, 0x88, 0x16, 0xEB, 0x67,
+   0xD9, 0x0C, 0xF1, 0xC1, 0x34, 0x33, 0xC6, 0x78,
+   0xB3, 0x26, 0xE3, 0xBD, 0x5D, 0x4E, 0x66, 0xE4,
+   0xD7, 0xC4, 0xE6, 0xA1, 0xB0, 0x95, 0x2B, 0x9A,
+   0x4A, 0x3A, 0xCB, 0x40, 0xE1, 0x60, 0x49, 0xCC,
+   0x03, 0xAC, 0xF4, 0x97, 0x32, 0x0F, 0x38, 0x17,
+   0xF9, 0xE0, 0xD1, 0xFB, 0x04, 0x5E, 0x68, 0x06,
+   0xAE, 0xFA, 0xAA, 0xED, 0x24, 0x0D, 0x00, 0x61,
+   0x20, 0xA3, 0x7B, 0x6B, 0x76, 0x27, 0xEA, 0xCE,
+   0x6A, 0x82, 0x9F, 0x6D, 0x9C, 0x64, 0xA2, 0x11,
+   0x37, 0x2A, 0xCA, 0x84, 0x25, 0x7C, 0x2F, 0x8D,
+   0x90, 0xE7, 0x09, 0x93, 0xF3, 0x43, 0x71, 0xEC,
+   0xA9, 0x7D, 0x94, 0xA6, 0x3D, 0x7F, 0x54, 0x44,
+   0x99, 0x80, 0x41, 0xC0, 0xA0, 0x8A, 0x1E, 0xDC,
+   0x08, 0xD0, 0x2E, 0x42, 0x05, 0x85, 0x86, 0xFE,
+   0x3B, 0x59, 0xC3, 0x58, 0x13, 0xB4, 0x36, 0xA5,
+   0x73, 0x28, 0x29, 0xDA, 0x4F, 0x1D, 0xB1, 0x53,
+   0x46, 0x2C, 0xF2, 0x4D, 0xAD, 0xFC, 0x83, 0x02,
+   0x6F, 0x07, 0xE9, 0xEE, 0x21, 0x98, 0x5A, 0xC5,
+   0x92, 0x48, 0xF7, 0x0A, 0xF6, 0xE2, 0x4B, 0x56 };
+
+unsigned char table_32[256] = {
+   0x7B, 0x0F, 0x56, 0x2F, 0x1E, 0x2A, 0x7A, 0xD1,
+   0x02, 0x91, 0x4E, 0x37, 0x6C, 0x10, 0xA7, 0xF2,
+   0x38, 0xAC, 0x9E, 0x2B, 0x5E, 0x23, 0xE3, 0x19,
+   0x9B, 0xF6, 0xB0, 0x59, 0x14, 0xB9, 0xA9, 0x46,
+   0x84, 0x1D, 0xC0, 0x98, 0xF3, 0xE1, 0xE8, 0x94,
+   0x52, 0x35, 0xBA, 0xD8, 0x07, 0xEF, 0x31, 0xF8,
+   0x03, 0x76, 0x9C, 0xD7, 0xE4, 0x8B, 0xAF, 0x60,
+   0xDD, 0x51, 0x00, 0xDF, 0x11, 0x7F, 0x1C, 0xED,
+   0x49, 0xC9, 0xF4, 0x87, 0x64, 0xFC, 0x5D, 0xAD,
+   0x88, 0x85, 0xF7, 0x5A, 0x92, 0xDB, 0x72, 0x1A,
+   0x83, 0x15, 0x30, 0x24, 0x9F, 0xFF, 0x5B, 0xF1,
+   0xD2, 0xFD, 0xC2, 0xB5, 0x25, 0x22, 0x18, 0x3D,
+   0xCD, 0x97, 0x8C, 0xCC, 0x78, 0x90, 0xAA, 0x5F,
+   0x0A, 0x57, 0x05, 0x61, 0xD4, 0xA0, 0x3A, 0xDE,
+   0x3B, 0xF9, 0x65, 0x68, 0x4F, 0x28, 0xFA, 0xEB,
+   0x63, 0x2D, 0x8D, 0xD0, 0xA1, 0xFE, 0x12, 0x96,
+   0x3C, 0x42, 0x29, 0xD6, 0xA4, 0x34, 0xBD, 0x70,
+   0x89, 0xBE, 0xF5, 0x79, 0xAB, 0x8F, 0x32, 0xB4,
+   0xEE, 0xE7, 0x2C, 0x04, 0x4B, 0xD5, 0xB1, 0x54,
+   0xF0, 0xDA, 0x16, 0x77, 0xA6, 0x53, 0xB2, 0xE2,
+   0x73, 0xBF, 0x17, 0xA8, 0x75, 0x26, 0xE0, 0xBC,
+   0x0C, 0x71, 0xFB, 0x6D, 0x7E, 0xC5, 0xEA, 0x21,
+   0x9D, 0x95, 0x8E, 0xA5, 0x48, 0xB8, 0x7D, 0xCB,
+   0x01, 0x99, 0xE5, 0xBB, 0x82, 0xC4, 0xCA, 0xC1,
+   0x58, 0x6E, 0x5C, 0x7C, 0xDC, 0x33, 0xB6, 0xC3,
+   0x09, 0xC7, 0x1F, 0x0D, 0x43, 0x6F, 0xE9, 0x86,
+   0x27, 0xC8, 0x44, 0xB3, 0xD3, 0xCF, 0x08, 0x66,
+   0x1B, 0x20, 0x4D, 0xD9, 0xC6, 0x36, 0x40, 0x74,
+   0x62, 0x6A, 0x55, 0xEC, 0x06, 0x2E, 0xE6, 0x80,
+   0x13, 0x93, 0x50, 0xCE, 0x69, 0x3E, 0x67, 0x4A,
+   0x81, 0x4C, 0x0B, 0x3F, 0xB7, 0x0E, 0x39, 0xAE,
+   0x47, 0x6B, 0x8A, 0xA2, 0x9A, 0xA3, 0x45, 0x41 };
+
+unsigned char table_33[256] = {
+   0xDE, 0xD3, 0x79, 0x67, 0x13, 0x5C, 0x04, 0xF2,
+   0xD9, 0x9F, 0x65, 0x56, 0xCC, 0x3B, 0xA4, 0x9A,
+   0x08, 0xBF, 0x26, 0xB2, 0xA7, 0x5E, 0xAA, 0xCA,
+   0xBB, 0x2B, 0x38, 0x3F, 0xD8, 0x87, 0xFA, 0x5D,
+   0x73, 0x8E, 0x1E, 0x93, 0x05, 0xAF, 0x3E, 0x4E,
+   0x90, 0xDB, 0x0B, 0x33, 0x0D, 0x2F, 0x86, 0x4F,
+   0xFD, 0xD0, 0x39, 0xB1, 0x8A, 0x1A, 0x20, 0xE6,
+   0xCF, 0xA2, 0x82, 0xDF, 0x42, 0x9C, 0x30, 0x40,
+   0xE3, 0xB0, 0x88, 0x5A, 0xEC, 0x25, 0xE2, 0xC4,
+   0x12, 0x54, 0x50, 0x97, 0x96, 0x21, 0x23, 0x7B,
+   0x1D, 0x61, 0x52, 0x34, 0x7D, 0x69, 0x16, 0xC3,
+   0x31, 0xF8, 0x48, 0x19, 0x95, 0x01, 0x29, 0x8C,
+   0x15, 0xAC, 0x84, 0x74, 0xAB, 0x70, 0xDA, 0x36,
+   0xD6, 0x8F, 0xFE, 0x35, 0xD7, 0x2E, 0x89, 0x07,
+   0x62, 0x17, 0xDC, 0x92, 0x45, 0x83, 0xB5, 0xE5,
+   0x8B, 0xC0, 0x27, 0x85, 0x7C, 0x9D, 0x55, 0x81,
+   0x71, 0xCD, 0xC9, 0x00, 0x02, 0xC1, 0x0A, 0x37,
+   0xED, 0xEA, 0xC2, 0x98, 0x49, 0x06, 0x1C, 0x78,
+   0x64, 0xCE, 0x9E, 0x4C, 0x7A, 0xB4, 0x43, 0x0F,
+   0xE0, 0x7E, 0xBC, 0x5B, 0x51, 0xE7, 0x18, 0xF9,
+   0x11, 0xA1, 0xF5, 0xC7, 0xCB, 0x4D, 0x6A, 0x0E,
+   0x57, 0xF1, 0xFB, 0xB3, 0x99, 0xF0, 0x32, 0xD5,
+   0xA9, 0x4B, 0x6F, 0x6D, 0xA8, 0xC5, 0xDD, 0x7F,
+   0xEB, 0xBE, 0xFC, 0x2C, 0x22, 0x58, 0x03, 0x9B,
+   0x77, 0xF7, 0xBD, 0xBA, 0xD2, 0x6B, 0xAD, 0x5F,
+   0x10, 0x6E, 0x09, 0xD1, 0x1B, 0x24, 0xEF, 0x72,
+   0x3D, 0x59, 0x28, 0xE1, 0xB7, 0x44, 0x8D, 0xB8,
+   0xAE, 0x2D, 0x60, 0xA6, 0xC8, 0x0C, 0xF4, 0x41,
+   0xA3, 0x68, 0x46, 0x6C, 0x76, 0xA0, 0xB6, 0x66,
+   0xE4, 0x1F, 0x75, 0x4A, 0xFF, 0x2A, 0x94, 0xD4,
+   0xF3, 0xE9, 0x91, 0x63, 0xA5, 0xB9, 0xE8, 0x14,
+   0x80, 0x3C, 0xEE, 0x47, 0xC6, 0x3A, 0x53, 0xF6 };
+
+unsigned char table_34[256] = {
+   0xF0, 0xE9, 0x3E, 0xD6, 0x89, 0xC8, 0xC7, 0x23,
+   0x75, 0x26, 0x5F, 0x9C, 0x57, 0xB8, 0x2A, 0x29,
+   0xE5, 0xB5, 0x68, 0xA4, 0x92, 0x46, 0x40, 0x7F,
+   0xF2, 0xBC, 0x6A, 0xE0, 0x8F, 0x0F, 0xE4, 0x3A,
+   0xE1, 0x30, 0x84, 0x6E, 0x82, 0x8E, 0x56, 0xC5,
+   0x32, 0x85, 0xFB, 0x59, 0x43, 0x41, 0xC2, 0xF6,
+   0x67, 0x5A, 0x7C, 0x34, 0xA1, 0xD0, 0x4B, 0xAC,
+   0x61, 0x72, 0x6B, 0xAF, 0xC4, 0x20, 0x9A, 0xD4,
+   0x74, 0x8D, 0x87, 0x83, 0xE2, 0x62, 0x6D, 0xE6,
+   0xE7, 0xF9, 0x76, 0xCB, 0x18, 0x90, 0x4F, 0xFF,
+   0xD3, 0x3C, 0x08, 0x79, 0x93, 0x2D, 0x95, 0xA3,
+   0xDD, 0x5B, 0xDA, 0x7A, 0x39, 0x4D, 0xC1, 0x2E,
+   0xCC, 0x53, 0xE8, 0xA2, 0xCF, 0x15, 0x78, 0x1C,
+   0xEB, 0x9B, 0x7B, 0xAD, 0x31, 0x2F, 0xE3, 0xC9,
+   0x3B, 0xEC, 0x2C, 0x49, 0x02, 0x52, 0x28, 0xBA,
+   0x0C, 0x19, 0x24, 0xF7, 0x97, 0x09, 0xA6, 0xA0,
+   0xDF, 0xD1, 0xD2, 0xDC, 0x51, 0xA5, 0x94, 0xFD,
+   0x71, 0xF5, 0x50, 0x0A, 0x69, 0x25, 0x88, 0x5C,
+   0x91, 0xD5, 0x47, 0x0B, 0x27, 0x13, 0x96, 0xD9,
+   0xF1, 0xA9, 0x70, 0xC3, 0xBE, 0x42, 0x4E, 0x4A,
+   0xB1, 0x07, 0xA7, 0x54, 0xFE, 0x48, 0x9F, 0x63,
+   0x17, 0xAE, 0xB9, 0x58, 0x21, 0x35, 0xED, 0x5D,
+   0x9D, 0x3D, 0xB4, 0xFC, 0xEA, 0x8C, 0x80, 0xA8,
+   0x1E, 0xB0, 0xDE, 0x0D, 0x11, 0x6F, 0x04, 0x12,
+   0xF4, 0x10, 0x64, 0x0E, 0xD7, 0x2B, 0xB3, 0x8B,
+   0xB7, 0x01, 0x86, 0xCA, 0xFA, 0x9E, 0xEE, 0x66,
+   0x37, 0x65, 0x81, 0x38, 0x1F, 0xAA, 0x73, 0xAB,
+   0xBD, 0xDB, 0x14, 0xCD, 0x00, 0xBB, 0x98, 0x44,
+   0x45, 0xB6, 0x99, 0x5E, 0xD8, 0x1D, 0x36, 0xF8,
+   0x55, 0x6C, 0x16, 0x7E, 0x77, 0x3F, 0x22, 0xEF,
+   0xF3, 0x7D, 0xC6, 0xCE, 0x8A, 0xB2, 0x33, 0x4C,
+   0x03, 0x05, 0xBF, 0x06, 0x1B, 0xC0, 0x1A, 0x60 };
+
+unsigned char table_35[256] = {
+   0xCC, 0x40, 0xEF, 0x1F, 0xDB, 0xE5, 0x71, 0x51,
+   0x3B, 0x0F, 0x7D, 0x9C, 0x83, 0x17, 0x6F, 0x8F,
+   0x13, 0xDC, 0x7F, 0xA9, 0xA5, 0xA2, 0x9D, 0xDF,
+   0xE7, 0x97, 0x2A, 0x30, 0xF2, 0x73, 0xCF, 0x87,
+   0x29, 0xB3, 0x86, 0x43, 0x09, 0xB0, 0x2E, 0x10,
+   0x8E, 0xBC, 0x57, 0xBA, 0x68, 0xF5, 0xCB, 0x89,
+   0x32, 0xC1, 0x6B, 0x1E, 0xAC, 0xB2, 0x2D, 0x6A,
+   0x50, 0xEB, 0x18, 0x06, 0xD8, 0xC7, 0x36, 0x31,
+   0xC5, 0xAF, 0x12, 0x15, 0xB7, 0x37, 0x4E, 0x01,
+   0x14, 0x21, 0x44, 0x5E, 0xF4, 0xB4, 0xE4, 0x65,
+   0xFE, 0x8A, 0xEA, 0x0D, 0xBB, 0x45, 0x8B, 0x25,
+   0x80, 0x35, 0x61, 0xA8, 0x4A, 0x47, 0xAB, 0x91,
+   0x1B, 0x1C, 0x05, 0x4D, 0x5A, 0xD4, 0xF1, 0x9B,
+   0x0E, 0x98, 0xCA, 0x96, 0x42, 0x7E, 0x03, 0x5F,
+   0xE2, 0x90, 0xBF, 0x82, 0xC9, 0x3D, 0xE0, 0x5C,
+   0xFA, 0x3E, 0x41, 0x11, 0x79, 0x58, 0x24, 0x2C,
+   0xC0, 0x28, 0x5D, 0xA3, 0xDE, 0x67, 0xFF, 0xA4,
+   0x63, 0xB1, 0x22, 0x04, 0xFD, 0x70, 0x39, 0x46,
+   0xAA, 0x0A, 0x34, 0x6C, 0xD7, 0x92, 0xA1, 0x3C,
+   0x19, 0xD5, 0xFC, 0xAD, 0x85, 0x07, 0x00, 0x23,
+   0xF8, 0x69, 0x56, 0x53, 0x55, 0x7A, 0xB8, 0xC8,
+   0xDA, 0xCE, 0xF3, 0x5B, 0x49, 0xE1, 0xBE, 0xEC,
+   0x1A, 0x88, 0x02, 0xBD, 0xF7, 0x1D, 0x64, 0xA0,
+   0x4F, 0xD9, 0xE3, 0x95, 0xC6, 0x48, 0x2B, 0xED,
+   0x9A, 0x9E, 0x26, 0x6E, 0xD1, 0x94, 0xB9, 0x93,
+   0xDD, 0xF6, 0xA6, 0xFB, 0xC2, 0xB6, 0x0C, 0xE9,
+   0x77, 0xF9, 0xCD, 0x08, 0xEE, 0x3F, 0xE6, 0x75,
+   0xD6, 0x84, 0x76, 0x8C, 0xF0, 0xAE, 0xD2, 0x78,
+   0x2F, 0x4B, 0x16, 0x4C, 0x27, 0x81, 0x6D, 0x99,
+   0x38, 0xD3, 0x54, 0x62, 0x74, 0x20, 0x60, 0xC3,
+   0x7C, 0x8D, 0x72, 0x0B, 0x52, 0xE8, 0xA7, 0x3A,
+   0x59, 0xC4, 0x9F, 0xD0, 0x66, 0x7B, 0x33, 0xB5 };
+
+unsigned char table_36[256] = {
+   0xDB, 0x6F, 0xFE, 0xB3, 0x5C, 0x1F, 0xB8, 0xBF,
+   0xA3, 0x71, 0x11, 0x56, 0x90, 0xE2, 0x63, 0x18,
+   0x83, 0x51, 0x21, 0xEB, 0x66, 0x08, 0xA6, 0xA5,
+   0x1C, 0xF5, 0x14, 0x24, 0x41, 0x33, 0xA7, 0xB5,
+   0xC7, 0x79, 0x57, 0x50, 0x85, 0xE1, 0x6D, 0xF7,
+   0x0E, 0xDE, 0x67, 0xAB, 0xA1, 0x0B, 0xD9, 0x4A,
+   0xCA, 0x36, 0xEA, 0xDA, 0x16, 0xEF, 0x9F, 0x0A,
+   0x09, 0x9A, 0x1D, 0xC5, 0xD7, 0x5F, 0x19, 0xDC,
+   0x15, 0x06, 0xE8, 0x94, 0x0C, 0x0D, 0xC9, 0x7C,
+   0xD6, 0x62, 0xBB, 0x49, 0xF9, 0x61, 0x07, 0x9B,
+   0x28, 0xC3, 0x9E, 0xF4, 0x38, 0x78, 0x20, 0x03,
+   0xA2, 0x7F, 0xC2, 0x9D, 0x5E, 0x65, 0x52, 0x17,
+   0x2E, 0x1B, 0xB0, 0x42, 0xBC, 0xFD, 0xF1, 0xD2,
+   0xF6, 0x60, 0xD3, 0x29, 0x97, 0x3D, 0x0F, 0xB1,
+   0x2F, 0x22, 0xDD, 0x80, 0x32, 0xF8, 0xAD, 0x70,
+   0xB9, 0x8F, 0x37, 0xCE, 0x46, 0x58, 0xB7, 0x30,
+   0xED, 0x7A, 0xE9, 0xC0, 0x7D, 0x13, 0x64, 0x23,
+   0x4E, 0xC8, 0xF0, 0xCC, 0x3B, 0x45, 0x68, 0x8D,
+   0xBE, 0x8B, 0xD8, 0x43, 0x02, 0x27, 0xE4, 0xAA,
+   0x10, 0xF2, 0x59, 0x72, 0x40, 0x26, 0x69, 0xE5,
+   0x05, 0x84, 0x4F, 0xE0, 0x6B, 0xC1, 0xAC, 0x4C,
+   0xFB, 0x31, 0x77, 0x8E, 0xD4, 0x12, 0xA9, 0xB4,
+   0xEC, 0x00, 0x76, 0x1E, 0x25, 0xAE, 0xE7, 0x3C,
+   0x35, 0x93, 0x9C, 0xC4, 0xFC, 0x2D, 0x91, 0x04,
+   0xAF, 0x53, 0x3F, 0xE6, 0xA4, 0xD0, 0x1A, 0xDF,
+   0x3A, 0x55, 0x99, 0x01, 0xCB, 0x6C, 0x82, 0x3E,
+   0x5D, 0xA8, 0x88, 0x54, 0x5B, 0x95, 0xCD, 0x8C,
+   0x81, 0x34, 0xD1, 0x39, 0xFF, 0xEE, 0xFA, 0x8A,
+   0x6E, 0x86, 0x92, 0x89, 0xF3, 0x6A, 0xBA, 0x2C,
+   0xD5, 0x44, 0xC6, 0x96, 0xBD, 0xB2, 0x2B, 0x87,
+   0x74, 0xA0, 0x73, 0x5A, 0x2A, 0x98, 0x75, 0x47,
+   0x4B, 0xB6, 0x7B, 0x4D, 0xCF, 0x7E, 0x48, 0xE3 };
+
+unsigned char table_37[256] = {
+   0x1F, 0xD6, 0xB1, 0xB3, 0x40, 0xAD, 0xDE, 0xB7,
+   0x19, 0xB4, 0xE7, 0x0B, 0x9C, 0x2D, 0xE0, 0xF5,
+   0xCF, 0x2C, 0x30, 0x65, 0x2F, 0xCD, 0x02, 0x91,
+   0xCE, 0x2B, 0xBF, 0x78, 0xE6, 0xFA, 0x51, 0x48,
+   0xFB, 0x4D, 0xBE, 0x71, 0x1A, 0x56, 0xFD, 0x81,
+   0x33, 0x75, 0x89, 0x96, 0x37, 0x82, 0x9E, 0x93,
+   0x41, 0x18, 0x5B, 0x2E, 0x22, 0x0F, 0xAF, 0x4B,
+   0xB9, 0xD5, 0xEE, 0x6C, 0xE4, 0x05, 0xCC, 0x99,
+   0xE5, 0x3B, 0x62, 0xBD, 0x7B, 0xAA, 0x4A, 0xE2,
+   0x34, 0x43, 0xF7, 0x39, 0xFE, 0x14, 0x1D, 0xE3,
+   0xF0, 0xA7, 0x77, 0xDF, 0xA0, 0xD3, 0xAC, 0xD9,
+   0xEA, 0x76, 0xDD, 0xA4, 0xC5, 0xC9, 0x61, 0xF3,
+   0xA8, 0xB0, 0x35, 0xE8, 0x68, 0xD4, 0x15, 0xF9,
+   0x97, 0xED, 0x25, 0x0A, 0x88, 0x8F, 0x06, 0xA3,
+   0x16, 0x36, 0x32, 0xA2, 0xC6, 0x64, 0xD7, 0x94,
+   0xD2, 0x6D, 0x74, 0xFC, 0x44, 0x27, 0x5C, 0xFF,
+   0x60, 0x1E, 0x58, 0x8B, 0x5E, 0xC7, 0x90, 0x17,
+   0x63, 0xAE, 0xC3, 0x12, 0x13, 0x84, 0xEC, 0x49,
+   0xA5, 0x9B, 0x31, 0x8D, 0xE1, 0x79, 0xF1, 0x00,
+   0x28, 0x3D, 0xC2, 0x55, 0x20, 0x52, 0x95, 0x7E,
+   0x42, 0x1C, 0x66, 0x92, 0x7D, 0xB6, 0xC4, 0xF4,
+   0x80, 0xB2, 0x72, 0x6E, 0x11, 0xF6, 0x0D, 0x5A,
+   0xEF, 0x9D, 0x69, 0x9A, 0x45, 0x67, 0x3F, 0xDA,
+   0x8E, 0x57, 0x09, 0x7C, 0x38, 0xA6, 0x83, 0x87,
+   0x7A, 0x08, 0x4C, 0x5F, 0x85, 0x7F, 0xD0, 0x04,
+   0x50, 0xCB, 0xB8, 0x07, 0x24, 0x26, 0x29, 0x46,
+   0x01, 0x03, 0xC1, 0xD8, 0xDC, 0x0E, 0x3C, 0x4F,
+   0x53, 0x4E, 0xB5, 0xF8, 0xC0, 0x8A, 0xF2, 0xBB,
+   0xE9, 0x5D, 0x2A, 0xBA, 0x0C, 0x1B, 0x3A, 0xA9,
+   0x21, 0x6A, 0x70, 0xBC, 0xEB, 0xA1, 0x54, 0x10,
+   0x98, 0x9F, 0x23, 0xD1, 0x6B, 0x59, 0x3E, 0xCA,
+   0x73, 0xC8, 0x86, 0x47, 0xDB, 0xAB, 0x6F, 0x8C };
+
+unsigned char table_38[256] = {
+   0xAA, 0x8D, 0x37, 0x94, 0x99, 0xDD, 0x70, 0x77,
+   0x78, 0xC9, 0x0F, 0xFA, 0xE2, 0x05, 0xC2, 0x16,
+   0x02, 0x4D, 0x44, 0x65, 0xAC, 0xB0, 0x39, 0xF8,
+   0x06, 0x60, 0xD8, 0xE1, 0x19, 0xB4, 0x36, 0x20,
+   0x59, 0x1D, 0xAD, 0xE4, 0xE8, 0xFF, 0x9D, 0x0D,
+   0x51, 0x28, 0xE7, 0x8C, 0x0E, 0x97, 0xE3, 0xAE,
+   0x6A, 0x27, 0x98, 0xDB, 0x26, 0xF6, 0xEC, 0xC6,
+   0xC0, 0xBD, 0x68, 0x61, 0x83, 0x86, 0xE0, 0x2C,
+   0xEE, 0x47, 0xF9, 0x5F, 0x6D, 0xBA, 0xE9, 0x72,
+   0x8A, 0xBB, 0x08, 0x29, 0xAF, 0x1C, 0xD3, 0x5D,
+   0xF7, 0x87, 0x6F, 0x9A, 0x2F, 0x11, 0xD9, 0x90,
+   0x66, 0x8E, 0xEB, 0xB1, 0x2E, 0xEA, 0xA3, 0x55,
+   0x2B, 0xCC, 0x4C, 0x4B, 0x48, 0x71, 0x3B, 0xFC,
+   0xA4, 0x45, 0x0A, 0x8F, 0x7A, 0x13, 0x01, 0x22,
+   0xC1, 0xF1, 0xA2, 0xB8, 0x7C, 0xF4, 0xB3, 0xB7,
+   0x5B, 0xE5, 0x07, 0x50, 0x7E, 0x18, 0xEF, 0x91,
+   0x5C, 0x15, 0x69, 0xBE, 0x0C, 0x93, 0x56, 0x35,
+   0x7B, 0xCF, 0x34, 0x74, 0x3E, 0x5E, 0x31, 0x21,
+   0x12, 0x63, 0x7F, 0x2A, 0x9B, 0xD4, 0x6B, 0xBC,
+   0x33, 0x62, 0x30, 0x75, 0x17, 0x23, 0xB2, 0xF0,
+   0x57, 0x67, 0x95, 0x3D, 0xCD, 0x10, 0xE6, 0xC8,
+   0x8B, 0xA9, 0x73, 0xC4, 0x43, 0xBF, 0xA7, 0xCA,
+   0xB5, 0xD5, 0xD6, 0x3F, 0x1A, 0x7D, 0x82, 0xA8,
+   0x40, 0x64, 0xAB, 0x04, 0xC3, 0x1F, 0xA0, 0x5A,
+   0x85, 0xF3, 0xDE, 0xFE, 0xDA, 0x1E, 0x81, 0x92,
+   0x9C, 0x2D, 0x9F, 0x32, 0xB9, 0xA1, 0x96, 0xD0,
+   0x4F, 0x38, 0x80, 0xCB, 0x6C, 0x14, 0x84, 0x1B,
+   0xD7, 0xC5, 0xED, 0xD2, 0x3A, 0x0B, 0x88, 0xFD,
+   0xDC, 0x49, 0x9E, 0xF5, 0xF2, 0x52, 0xA6, 0x24,
+   0xC7, 0xB6, 0x03, 0x3C, 0xD1, 0x54, 0x41, 0xDF,
+   0x89, 0x58, 0x79, 0xFB, 0x6E, 0xA5, 0x42, 0x25,
+   0x09, 0x76, 0x00, 0x46, 0x4E, 0x53, 0xCE, 0x4A };
+
+unsigned char table_39[32] = {
+   0x12, 0x18, 0x0E, 0x08, 0x16, 0x05, 0x06, 0x00,
+   0x11, 0x17, 0x15, 0x1B, 0x14, 0x01, 0x1F, 0x19,
+   0x04, 0x0D, 0x0A, 0x0F, 0x10, 0x07, 0x1D, 0x03,
+   0x0B, 0x13, 0x0C, 0x09, 0x1E, 0x02, 0x1A, 0x1C };
+
+unsigned char table_40[32] = {
+   0x16, 0x02, 0x06, 0x0E, 0x0D, 0x1C, 0x08, 0x0A,
+   0x0F, 0x13, 0x0B, 0x18, 0x07, 0x04, 0x14, 0x01,
+   0x1B, 0x05, 0x17, 0x1E, 0x11, 0x1A, 0x10, 0x1F,
+   0x12, 0x19, 0x1D, 0x03, 0x0C, 0x00, 0x09, 0x15 };
+
+unsigned char table_41[32] = {
+   0x13, 0x18, 0x04, 0x1F, 0x1D, 0x11, 0x03, 0x00,
+   0x10, 0x12, 0x06, 0x0A, 0x1C, 0x07, 0x15, 0x0E,
+   0x08, 0x05, 0x0C, 0x09, 0x01, 0x02, 0x16, 0x0B,
+   0x1A, 0x17, 0x14, 0x1E, 0x0D, 0x0F, 0x19, 0x1B };
+
+unsigned char table_42[32] = {
+   0x00, 0x08, 0x15, 0x1D, 0x05, 0x18, 0x06, 0x07,
+   0x1F, 0x01, 0x0B, 0x03, 0x19, 0x13, 0x02, 0x1C,
+   0x17, 0x11, 0x0E, 0x1E, 0x0C, 0x0F, 0x09, 0x1A,
+   0x1B, 0x16, 0x10, 0x0D, 0x0A, 0x14, 0x12, 0x04 };
+
+unsigned char table_43[256] = {
+   0x34, 0xB7, 0x36, 0x85, 0x5F, 0x93, 0x98, 0x70,
+   0x1E, 0x59, 0x83, 0x60, 0x6F, 0xBF, 0xF9, 0xD0,
+   0xB3, 0x22, 0x12, 0x38, 0xF5, 0x01, 0xC9, 0x5B,
+   0xEF, 0x1D, 0x81, 0x64, 0xFA, 0x8F, 0x7F, 0xBC,
+   0x05, 0x08, 0xE0, 0x8B, 0xE8, 0x86, 0x95, 0xCB,
+   0xCA, 0x5A, 0xEB, 0x10, 0x92, 0xE2, 0x7E, 0x28,
+   0xD9, 0xC7, 0x0D, 0x24, 0xA7, 0x02, 0x0B, 0xF1,
+   0x7B, 0xD3, 0xFE, 0x2B, 0x89, 0x0E, 0xAE, 0xAD,
+   0xC8, 0x82, 0x79, 0x43, 0x96, 0xDE, 0x0C, 0x9A,
+   0x57, 0x84, 0xB4, 0x19, 0xF8, 0xF0, 0xAF, 0xBE,
+   0x99, 0x9F, 0x46, 0xE4, 0x31, 0xDF, 0x30, 0x51,
+   0xD4, 0xE5, 0xFC, 0x32, 0x04, 0x56, 0x7D, 0x33,
+   0xF7, 0x18, 0x23, 0x4E, 0xC2, 0x7C, 0x6C, 0xD2,
+   0xB1, 0x9B, 0x40, 0xA2, 0x88, 0x00, 0xA1, 0xAB,
+   0xC6, 0x5C, 0x87, 0x3B, 0xD7, 0x27, 0x2E, 0x45,
+   0xDA, 0x8E, 0x61, 0x5E, 0xFB, 0x09, 0x5D, 0x6B,
+   0xA3, 0x29, 0x4F, 0xAC, 0xD1, 0x77, 0x4A, 0xA9,
+   0xC4, 0x7A, 0x15, 0xD8, 0xAA, 0x17, 0xB9, 0x2D,
+   0xE7, 0xBD, 0x2C, 0x62, 0x2F, 0xB2, 0xED, 0x3F,
+   0x48, 0x26, 0x1B, 0x35, 0x20, 0x72, 0x4D, 0xFF,
+   0xBB, 0x78, 0x1F, 0xCC, 0xEC, 0xA8, 0x9D, 0x90,
+   0x4B, 0x13, 0xE1, 0xBA, 0xF3, 0x3C, 0x42, 0x65,
+   0x14, 0xDD, 0x75, 0xE3, 0x4C, 0x74, 0x94, 0xCD,
+   0xF2, 0x66, 0x06, 0xE9, 0x49, 0xB8, 0x71, 0x41,
+   0xA0, 0x25, 0x55, 0x47, 0x97, 0x9E, 0x11, 0x54,
+   0x1A, 0xB0, 0x3E, 0x37, 0x39, 0x1C, 0x8D, 0x03,
+   0x6E, 0xF6, 0x80, 0x6D, 0x8C, 0x9C, 0xB6, 0xCF,
+   0xC3, 0x91, 0x63, 0xC0, 0x07, 0x67, 0xE6, 0xF4,
+   0xCE, 0x3D, 0xDB, 0x16, 0xFD, 0xEA, 0xD6, 0x68,
+   0xD5, 0xA6, 0x0F, 0x58, 0x44, 0x52, 0xB5, 0xDC,
+   0x0A, 0x69, 0xC5, 0xA5, 0xC1, 0x8A, 0x2A, 0xEE,
+   0x73, 0x76, 0x3A, 0x21, 0x53, 0xA4, 0x50, 0x6A };
+
+unsigned char table_44[32] = {
+   0x1A, 0x0E, 0x0A, 0x17, 0x1F, 0x08, 0x10, 0x14,
+   0x0C, 0x0F, 0x09, 0x1C, 0x06, 0x18, 0x1E, 0x12,
+   0x15, 0x00, 0x11, 0x13, 0x0D, 0x01, 0x0B, 0x03,
+   0x16, 0x19, 0x05, 0x1D, 0x02, 0x07, 0x04, 0x1B };
+
+unsigned char table_45[256] = {
+   0x5E, 0xD6, 0xE2, 0x54, 0x35, 0xC2, 0xAC, 0x9D,
+   0x92, 0x64, 0x57, 0x65, 0xC8, 0xAE, 0x21, 0xA9,
+   0x89, 0x48, 0x12, 0x59, 0xEC, 0xEF, 0x9F, 0xF7,
+   0x19, 0x03, 0x83, 0xC0, 0x79, 0x5D, 0x4A, 0x10,
+   0x8C, 0xEB, 0xFF, 0xB5, 0x3B, 0x51, 0x2D, 0xD1,
+   0x6B, 0xC5, 0x24, 0x5C, 0xE6, 0x11, 0x94, 0x3F,
+   0xD0, 0x2F, 0x0E, 0x95, 0x3C, 0xFE, 0x5B, 0x20,
+   0x23, 0xE0, 0x91, 0x6F, 0xCA, 0x56, 0x0C, 0x73,
+   0xDA, 0x67, 0x37, 0xA3, 0xA5, 0x70, 0x93, 0x1C,
+   0x18, 0xD9, 0x42, 0x5F, 0x44, 0xF0, 0xF2, 0x14,
+   0x58, 0x8A, 0x1D, 0x40, 0x4E, 0x0B, 0x74, 0x84,
+   0x52, 0xCB, 0x60, 0xED, 0xAD, 0x66, 0x43, 0x6C,
+   0x81, 0xA1, 0x27, 0xB9, 0xBA, 0x4D, 0xF5, 0x04,
+   0xB8, 0x96, 0xA6, 0xA2, 0x7D, 0xD4, 0xEA, 0x45,
+   0x4F, 0x55, 0xD3, 0x3E, 0x8E, 0x4C, 0xBF, 0x8B,
+   0x9A, 0x06, 0x7A, 0xF4, 0x02, 0x88, 0x80, 0x22,
+   0xF3, 0xBD, 0x78, 0xEE, 0xAF, 0xF8, 0x15, 0x09,
+   0x0F, 0xB0, 0xDD, 0x99, 0x72, 0xE7, 0x90, 0xE1,
+   0x25, 0x62, 0x8D, 0x9C, 0x13, 0x08, 0xC9, 0x28,
+   0x2A, 0x47, 0x69, 0xDE, 0x77, 0x87, 0xBB, 0xE9,
+   0xAA, 0x33, 0x05, 0x29, 0x34, 0x97, 0xFD, 0xA0,
+   0x1E, 0xFC, 0xBE, 0xB1, 0x71, 0x9B, 0x50, 0xDC,
+   0xB7, 0x31, 0x63, 0x3A, 0xDF, 0xC3, 0x1B, 0x7C,
+   0x0A, 0xD7, 0xF6, 0xDB, 0x49, 0x53, 0x7F, 0xD2,
+   0x30, 0xA4, 0xB3, 0x6E, 0xB2, 0x6D, 0xCD, 0x7E,
+   0x26, 0xE8, 0x76, 0xCF, 0xE5, 0xCE, 0x16, 0xF1,
+   0xC6, 0x68, 0x36, 0x46, 0x1F, 0x38, 0x0D, 0x41,
+   0x17, 0xBC, 0x86, 0x9E, 0x6A, 0x7B, 0xB4, 0x01,
+   0xCC, 0x2C, 0xE3, 0x5A, 0xB6, 0xFA, 0x00, 0x75,
+   0x39, 0xA7, 0xC1, 0xD5, 0x98, 0xAB, 0x1A, 0x85,
+   0xD8, 0xE4, 0xC4, 0xA8, 0x4B, 0x61, 0x2E, 0x3D,
+   0xF9, 0x2B, 0x32, 0x8F, 0xFB, 0xC7, 0x07, 0x82 };
+
+unsigned char table_46[256] = {
+   0x85, 0x78, 0xFE, 0x6C, 0x61, 0xA0, 0x71, 0xCC,
+   0x45, 0x54, 0x7A, 0xE6, 0x82, 0x1D, 0xA6, 0x02,
+   0x47, 0xD0, 0x23, 0x55, 0x62, 0xFA, 0x76, 0x3E,
+   0xE3, 0x66, 0x74, 0x10, 0x5D, 0x49, 0x69, 0x0B,
+   0x75, 0x12, 0x8D, 0x9F, 0xEE, 0x93, 0x50, 0x70,
+   0x32, 0xBC, 0x1E, 0xD3, 0xEF, 0x7B, 0xB4, 0x92,
+   0xFD, 0x16, 0xC2, 0xD8, 0xDE, 0x68, 0xD1, 0x64,
+   0xC3, 0xA3, 0xB3, 0xC9, 0x08, 0xFB, 0x84, 0xC1,
+   0x28, 0x53, 0xCF, 0xD2, 0x35, 0xD7, 0x4A, 0x01,
+   0x44, 0xA4, 0x07, 0xAC, 0x98, 0xF1, 0xB2, 0x9A,
+   0x94, 0x2D, 0xD4, 0x34, 0x27, 0x60, 0x1A, 0xB9,
+   0xAF, 0x89, 0xEB, 0x8F, 0x6A, 0x13, 0x05, 0xF0,
+   0x77, 0x5F, 0x4F, 0x58, 0x2C, 0xE7, 0xCE, 0xED,
+   0xC0, 0x0D, 0x3A, 0xA7, 0xE2, 0x38, 0x5B, 0xE9,
+   0x3D, 0xF2, 0xDF, 0x86, 0xE0, 0x72, 0xF7, 0x88,
+   0xAD, 0xB7, 0x11, 0xDB, 0x73, 0x87, 0xC5, 0x22,
+   0xE1, 0x5C, 0xD6, 0x57, 0x7E, 0x7D, 0xA2, 0xF9,
+   0xF5, 0x9C, 0x25, 0x6F, 0x26, 0x51, 0xC8, 0x80,
+   0x2B, 0xA8, 0x19, 0xD9, 0x65, 0xCD, 0x97, 0xEA,
+   0xFF, 0x5E, 0x24, 0x3B, 0x4D, 0xB1, 0x1C, 0x79,
+   0x39, 0x6B, 0xA5, 0x2A, 0x09, 0xCA, 0x04, 0xEC,
+   0xBA, 0x18, 0x31, 0x46, 0x20, 0xBE, 0x1F, 0x3C,
+   0x6D, 0xAA, 0xF6, 0xDD, 0xF4, 0x96, 0x03, 0x0A,
+   0x9E, 0x83, 0xA1, 0x9D, 0xD5, 0xB0, 0x17, 0xBF,
+   0x56, 0xAB, 0xAE, 0x1B, 0x52, 0xC6, 0x81, 0x4B,
+   0xDC, 0x90, 0x5A, 0x9B, 0xB6, 0x0F, 0xF3, 0x67,
+   0x30, 0x63, 0x7C, 0x40, 0x0E, 0x7F, 0x95, 0x36,
+   0xC4, 0x4E, 0x43, 0xCB, 0x15, 0xB8, 0x00, 0x91,
+   0x8A, 0x4C, 0x8E, 0x14, 0x06, 0x6E, 0xA9, 0x2E,
+   0x3F, 0x48, 0x2F, 0x0C, 0xB5, 0x21, 0xBB, 0xDA,
+   0x8B, 0x42, 0x29, 0x8C, 0x33, 0x59, 0xE8, 0xF8,
+   0xC7, 0xE4, 0x37, 0xE5, 0xFC, 0xBD, 0x99, 0x41 };
+
+unsigned char table_47[32] = {
+   0x18, 0x1D, 0x16, 0x10, 0x11, 0x04, 0x1E, 0x08,
+   0x19, 0x0E, 0x0F, 0x02, 0x14, 0x1C, 0x07, 0x17,
+   0x0D, 0x09, 0x12, 0x1A, 0x05, 0x01, 0x0B, 0x0A,
+   0x13, 0x15, 0x0C, 0x00, 0x06, 0x1F, 0x03, 0x1B };
+
+unsigned char table_48[32] = {
+   0x13, 0x08, 0x15, 0x01, 0x17, 0x10, 0x0F, 0x1F,
+   0x1D, 0x0D, 0x12, 0x03, 0x06, 0x0A, 0x1C, 0x19,
+   0x1A, 0x04, 0x1B, 0x02, 0x16, 0x1E, 0x11, 0x00,
+   0x14, 0x09, 0x0C, 0x18, 0x05, 0x07, 0x0E, 0x0B };
+
+unsigned char table_49[32] = {
+   0x1F, 0x0F, 0x19, 0x07, 0x18, 0x05, 0x1E, 0x1D,
+   0x15, 0x08, 0x17, 0x10, 0x0A, 0x0E, 0x0C, 0x1B,
+   0x02, 0x13, 0x03, 0x0D, 0x04, 0x1A, 0x06, 0x09,
+   0x12, 0x1C, 0x0B, 0x16, 0x14, 0x01, 0x11, 0x00 };
+
+unsigned char table_50[32] = {
+   0x16, 0x18, 0x1C, 0x0E, 0x12, 0x00, 0x04, 0x1B,
+   0x1F, 0x13, 0x17, 0x0A, 0x1E, 0x03, 0x0C, 0x01,
+   0x0F, 0x10, 0x02, 0x08, 0x14, 0x09, 0x19, 0x15,
+   0x06, 0x0D, 0x0B, 0x1D, 0x05, 0x07, 0x11, 0x1A };
+
+unsigned char table_51[32] = {
+   0x1C, 0x0D, 0x1B, 0x07, 0x17, 0x0E, 0x06, 0x01,
+   0x12, 0x19, 0x03, 0x0B, 0x10, 0x08, 0x00, 0x1E,
+   0x0A, 0x04, 0x1A, 0x1D, 0x0C, 0x18, 0x02, 0x13,
+   0x0F, 0x11, 0x05, 0x09, 0x15, 0x16, 0x1F, 0x14 };
+
+unsigned char table_52[256] = {
+   0x34, 0x0B, 0x47, 0xA3, 0x56, 0x30, 0x73, 0xD4,
+   0x4B, 0xF6, 0xA6, 0x80, 0x22, 0x95, 0xA5, 0xBB,
+   0xFE, 0xCD, 0x27, 0x88, 0x87, 0x18, 0x86, 0x6E,
+   0xB9, 0x07, 0x37, 0x52, 0x0A, 0x28, 0x2C, 0xC4,
+   0x75, 0xA1, 0x29, 0x54, 0x84, 0x08, 0x72, 0x51,
+   0xDD, 0xF1, 0x4E, 0x1A, 0x90, 0x57, 0x20, 0xAD,
+   0x68, 0x61, 0xAF, 0x50, 0x6B, 0x1B, 0x71, 0xEB,
+   0x63, 0xC9, 0xB0, 0x58, 0x26, 0x40, 0xC7, 0xD9,
+   0x70, 0xA2, 0x9A, 0x09, 0x3F, 0x92, 0x0D, 0x8C,
+   0xC1, 0x96, 0x9F, 0x77, 0x4D, 0x5A, 0xEA, 0x11,
+   0xD7, 0xF3, 0x33, 0x93, 0x10, 0xF2, 0x9D, 0x83,
+   0xFF, 0x7E, 0xD2, 0x41, 0x24, 0xB4, 0x8D, 0x5C,
+   0xCF, 0xEF, 0xE9, 0x64, 0x76, 0xD1, 0xDE, 0xE4,
+   0x91, 0x35, 0x89, 0x19, 0x02, 0x0E, 0xF4, 0x2A,
+   0x0F, 0xE1, 0xA8, 0x2D, 0x21, 0x23, 0xAA, 0x7C,
+   0x78, 0x45, 0xA9, 0xDC, 0x06, 0xF9, 0xDF, 0xF7,
+   0x03, 0xAB, 0xB5, 0x1C, 0x36, 0x7B, 0x97, 0xFA,
+   0xE5, 0x3B, 0x2F, 0x1F, 0x9E, 0xED, 0xA7, 0x55,
+   0x42, 0x6F, 0x1E, 0xB7, 0xE6, 0xFB, 0x12, 0xD5,
+   0x99, 0xC6, 0x66, 0x4A, 0xE8, 0x48, 0x60, 0xB1,
+   0x05, 0x53, 0x8A, 0xB6, 0x25, 0x8F, 0xA4, 0xD8,
+   0x9C, 0xC0, 0x59, 0x3A, 0xBD, 0xDB, 0x44, 0x5E,
+   0xE3, 0xDA, 0x1D, 0x32, 0xF5, 0xBA, 0x43, 0x13,
+   0x82, 0x4C, 0xE7, 0x17, 0x15, 0x3E, 0x69, 0x2E,
+   0xC3, 0xF0, 0x5F, 0xFD, 0xCE, 0xD3, 0xCA, 0x39,
+   0xD6, 0x79, 0x3D, 0xC8, 0x67, 0x8B, 0x31, 0x4F,
+   0xB3, 0xBC, 0x65, 0x00, 0x7A, 0x98, 0xC5, 0x6C,
+   0x2B, 0x94, 0x6D, 0x74, 0x14, 0xAC, 0xCC, 0xA0,
+   0x5B, 0xF8, 0xCB, 0x7F, 0xB2, 0xEC, 0xBF, 0x3C,
+   0xE0, 0xAE, 0xFC, 0x62, 0x04, 0x8E, 0x85, 0x49,
+   0x9B, 0xC2, 0x38, 0xD0, 0xEE, 0x81, 0x46, 0xE2,
+   0x01, 0x0C, 0x5D, 0x7D, 0xB8, 0xBE, 0x6A, 0x16 };
+
+unsigned char table_53[256] = {
+   0xE3, 0xF4, 0x8D, 0x72, 0x45, 0x32, 0x9D, 0xCE,
+   0x1F, 0x6B, 0xBC, 0xDC, 0xF1, 0xEC, 0x5A, 0x3B,
+   0xA5, 0xA2, 0x2B, 0xDD, 0x8A, 0xA3, 0x76, 0xE4,
+   0xAF, 0xE9, 0xE1, 0x21, 0xDB, 0x9F, 0x19, 0xD3,
+   0x26, 0x80, 0x15, 0xC2, 0x46, 0xB8, 0x17, 0x56,
+   0x99, 0x81, 0x08, 0xD7, 0xEF, 0x8E, 0x04, 0x05,
+   0x97, 0x2F, 0x78, 0xAD, 0xA1, 0x52, 0x36, 0x58,
+   0x53, 0x68, 0x22, 0x70, 0x0B, 0x79, 0xE6, 0xFA,
+   0xC3, 0x91, 0xE2, 0xF7, 0xF6, 0x75, 0x2D, 0x0A,
+   0x90, 0xEB, 0xA6, 0x35, 0xA7, 0x10, 0xB5, 0xFB,
+   0xE7, 0xAA, 0x1E, 0x43, 0xBB, 0x3C, 0x65, 0x25,
+   0x2C, 0x59, 0x62, 0x2A, 0xF9, 0x4B, 0x95, 0x5E,
+   0x20, 0x11, 0x42, 0x27, 0x44, 0xE8, 0x14, 0x6F,
+   0xD1, 0xD8, 0x00, 0x3A, 0x5B, 0x18, 0x89, 0x02,
+   0x61, 0xD6, 0xC5, 0x98, 0xD0, 0x5F, 0x34, 0x29,
+   0xFD, 0x31, 0x1A, 0xCD, 0x0F, 0x9E, 0xCA, 0x7B,
+   0xEA, 0x93, 0x71, 0x5C, 0x0E, 0x57, 0x33, 0xC4,
+   0x37, 0xF5, 0x83, 0xB0, 0xDF, 0x49, 0x74, 0x54,
+   0x1D, 0x24, 0xB9, 0x16, 0x1C, 0x28, 0xDE, 0x4A,
+   0xF0, 0x01, 0x86, 0x82, 0xCC, 0x12, 0x8C, 0x06,
+   0x30, 0xA8, 0x7A, 0x73, 0x66, 0x7C, 0xC6, 0xB6,
+   0xF2, 0x13, 0xBF, 0x40, 0x85, 0x77, 0x09, 0x3D,
+   0x67, 0x63, 0x3F, 0x7F, 0xF3, 0x87, 0x8F, 0xFF,
+   0x92, 0xC7, 0x4C, 0x23, 0xBA, 0xCB, 0xB1, 0xED,
+   0x0C, 0x60, 0x47, 0xFE, 0x38, 0x5D, 0xCF, 0x8B,
+   0x4D, 0xA9, 0x2E, 0xE5, 0xA4, 0x1B, 0x88, 0x3E,
+   0x7D, 0xF8, 0xC0, 0xD5, 0x6D, 0x6C, 0x48, 0xAC,
+   0x9B, 0x51, 0x7E, 0x6E, 0x50, 0x0D, 0x9A, 0xB3,
+   0xEE, 0x07, 0x4F, 0x69, 0x9C, 0x03, 0xD9, 0xD4,
+   0xB4, 0xD2, 0xAE, 0x4E, 0x55, 0xB7, 0xC9, 0x41,
+   0x39, 0x6A, 0xC8, 0xA0, 0xB2, 0xC1, 0x84, 0xFC,
+   0xAB, 0x64, 0xE0, 0xBE, 0xDA, 0xBD, 0x96, 0x94 };
+
+unsigned char table_54[32] = {
+   0x01, 0x02, 0x1D, 0x10, 0x0E, 0x11, 0x08, 0x14,
+   0x12, 0x09, 0x15, 0x17, 0x16, 0x04, 0x06, 0x1B,
+   0x07, 0x1A, 0x18, 0x13, 0x0A, 0x1E, 0x1C, 0x1F,
+   0x0C, 0x0B, 0x0D, 0x05, 0x0F, 0x00, 0x19, 0x03 };
+
+unsigned char table_55[32] = {
+   0x01, 0x12, 0x13, 0x09, 0x0B, 0x19, 0x03, 0x0E,
+   0x02, 0x1F, 0x1D, 0x1B, 0x1E, 0x11, 0x06, 0x05,
+   0x00, 0x16, 0x07, 0x0C, 0x15, 0x0D, 0x1A, 0x08,
+   0x18, 0x10, 0x0F, 0x17, 0x1C, 0x0A, 0x04, 0x14 };
+
+unsigned char table_56[256] = {
+   0xEF, 0x06, 0x5F, 0x11, 0x4B, 0x60, 0x13, 0xBB,
+   0x79, 0xD7, 0xE4, 0x6D, 0x22, 0xB4, 0x15, 0x50,
+   0x29, 0x17, 0xD2, 0xE3, 0x37, 0x8C, 0x46, 0x7C,
+   0xA2, 0xF5, 0x65, 0x16, 0xCB, 0x04, 0x3E, 0xDF,
+   0x8E, 0xDE, 0x53, 0xF1, 0xF4, 0xD1, 0x3B, 0xEE,
+   0x9A, 0x09, 0x9B, 0x6C, 0xF6, 0xCC, 0xFB, 0x40,
+   0xE0, 0xFD, 0x2B, 0x1D, 0x73, 0x18, 0xCD, 0x31,
+   0x3F, 0x9E, 0xAD, 0xC9, 0x43, 0x4E, 0x99, 0x3A,
+   0x8F, 0x92, 0x85, 0xFC, 0x12, 0x41, 0x20, 0xE8,
+   0x2A, 0xC0, 0x1C, 0x38, 0x74, 0x0B, 0xF3, 0x05,
+   0x0D, 0x1F, 0x94, 0x9C, 0xAC, 0x00, 0x59, 0x0C,
+   0xB3, 0x8D, 0xA8, 0x75, 0xB7, 0x68, 0x2F, 0x27,
+   0x6F, 0x69, 0x76, 0xD8, 0xEC, 0xA5, 0xB2, 0x6A,
+   0x19, 0x72, 0x1A, 0xB6, 0xE5, 0x77, 0xC6, 0x44,
+   0x9D, 0xCA, 0x82, 0x35, 0x36, 0x5E, 0xA9, 0x25,
+   0xFA, 0x5C, 0x24, 0x30, 0x39, 0x0E, 0x2C, 0x7D,
+   0xE6, 0x88, 0xA0, 0x63, 0xB8, 0x6B, 0x01, 0xDD,
+   0xDA, 0x9F, 0x45, 0x83, 0xE2, 0x7F, 0x1B, 0x56,
+   0xAF, 0x14, 0xC3, 0x49, 0xBF, 0x78, 0x70, 0x58,
+   0x23, 0xA3, 0xBD, 0x34, 0x47, 0x2D, 0x0A, 0xD4,
+   0x33, 0x03, 0x1E, 0xC1, 0x87, 0xAE, 0x3C, 0x95,
+   0xB0, 0x42, 0x91, 0xB9, 0x5A, 0x61, 0xAA, 0xCF,
+   0xF2, 0x51, 0xA6, 0xF8, 0xDC, 0x71, 0xAB, 0x48,
+   0x66, 0x90, 0x97, 0xC4, 0x08, 0xF9, 0xD0, 0x7B,
+   0xDB, 0xBA, 0x8B, 0xC2, 0xC5, 0x2E, 0xF7, 0x5B,
+   0xFF, 0x21, 0x81, 0x54, 0xD3, 0x62, 0x57, 0x4C,
+   0x6E, 0x02, 0x98, 0xFE, 0x7E, 0xE7, 0xBC, 0x07,
+   0x28, 0x5D, 0x86, 0xCE, 0xEA, 0x84, 0xF0, 0xE1,
+   0x93, 0x80, 0xE9, 0xC7, 0x4A, 0xED, 0xB1, 0x26,
+   0x89, 0x3D, 0x4F, 0xA7, 0xA1, 0xD6, 0xB5, 0x4D,
+   0x67, 0xA4, 0x55, 0x10, 0x0F, 0xD9, 0x52, 0x32,
+   0x96, 0xD5, 0xEB, 0x64, 0x8A, 0xC8, 0x7A, 0xBE };
+
+unsigned char table_57[256] = {
+   0xD1, 0x9B, 0x15, 0x06, 0xB4, 0xF6, 0x97, 0xF0,
+   0xC6, 0x5B, 0x88, 0x12, 0x25, 0xFA, 0x7B, 0x79,
+   0xD6, 0xAB, 0xDC, 0x47, 0x85, 0x61, 0x67, 0x0B,
+   0xF3, 0x20, 0x44, 0x53, 0x2A, 0x3B, 0x2D, 0xE8,
+   0x17, 0x71, 0xC3, 0xB7, 0x7F, 0x35, 0xEB, 0x10,
+   0x03, 0x0D, 0x60, 0x96, 0x27, 0xBB, 0x39, 0x50,
+   0x95, 0x55, 0xCC, 0xD4, 0x2F, 0x51, 0xB3, 0x05,
+   0xA5, 0xAD, 0xBC, 0x18, 0xE2, 0xAE, 0x07, 0x87,
+   0xC4, 0x8D, 0xBE, 0x77, 0xC2, 0x16, 0xFC, 0x33,
+   0x4C, 0x4F, 0xE6, 0xA6, 0x57, 0x9F, 0x37, 0x91,
+   0xED, 0x4A, 0xF7, 0xB5, 0x52, 0x7C, 0xBD, 0x30,
+   0xA0, 0x2C, 0x8C, 0xB0, 0x0C, 0xDA, 0x6F, 0x9E,
+   0xEE, 0x43, 0x40, 0x8F, 0x8B, 0x76, 0xA4, 0x68,
+   0xFF, 0x6D, 0x58, 0xC9, 0xF9, 0x6E, 0x3F, 0x56,
+   0xCA, 0x49, 0xC8, 0x5D, 0xCD, 0xC7, 0x99, 0xEC,
+   0x72, 0x38, 0x0A, 0xA9, 0xC5, 0x04, 0x64, 0xBF,
+   0xB6, 0x29, 0x80, 0x2E, 0x19, 0x0E, 0x82, 0x45,
+   0xBA, 0xD7, 0x1E, 0x86, 0xA8, 0xD8, 0x24, 0xDB,
+   0xCF, 0xE1, 0x54, 0xB2, 0x3E, 0x4D, 0x90, 0x42,
+   0x5F, 0x59, 0x0F, 0xCE, 0x8E, 0xA2, 0xA7, 0x1D,
+   0x22, 0xFD, 0x81, 0x63, 0xE5, 0x6A, 0xE7, 0x93,
+   0x41, 0x46, 0x66, 0x89, 0x13, 0xEA, 0x69, 0x1C,
+   0x83, 0xF2, 0x08, 0xB8, 0x01, 0x23, 0x26, 0xFB,
+   0x78, 0xAA, 0x31, 0x11, 0x1B, 0x98, 0xDD, 0xAC,
+   0xB9, 0xFE, 0x94, 0x74, 0xAF, 0x32, 0xD0, 0x5A,
+   0xA1, 0xF4, 0x6B, 0x8A, 0xE3, 0x65, 0xDE, 0xCB,
+   0x73, 0x3D, 0xA3, 0x7E, 0xDF, 0xD2, 0x6C, 0x7A,
+   0x36, 0xD9, 0x62, 0x4B, 0xEF, 0xC1, 0x1F, 0x00,
+   0x34, 0xB1, 0xF8, 0xE4, 0xD5, 0x09, 0x1A, 0x9A,
+   0x70, 0x48, 0x9D, 0xF1, 0xE0, 0x9C, 0xD3, 0x5C,
+   0x75, 0x02, 0x2B, 0x92, 0x21, 0x7D, 0xF5, 0x5E,
+   0x4E, 0x3C, 0x84, 0x14, 0x28, 0x3A, 0xE9, 0xC0 };
+
+unsigned char table_58[256] = {
+   0xE9, 0x81, 0x60, 0xA7, 0x18, 0xA0, 0x0F, 0x55,
+   0x2B, 0x52, 0xE0, 0x8B, 0x9D, 0x85, 0xD2, 0xA3,
+   0x3F, 0x6E, 0xB1, 0xAF, 0xE3, 0x36, 0xE2, 0x19,
+   0x56, 0xB0, 0x09, 0xB5, 0x79, 0x43, 0xE1, 0x06,
+   0x45, 0xB6, 0xC0, 0x22, 0xEE, 0x41, 0xEC, 0x01,
+   0x66, 0x2D, 0x87, 0x38, 0x16, 0x37, 0xFA, 0x29,
+   0x96, 0xA4, 0xC3, 0x23, 0x59, 0x7E, 0x92, 0x78,
+   0x10, 0x2A, 0x4C, 0x0E, 0x9B, 0x4A, 0x35, 0xF4,
+   0x42, 0x0C, 0xD8, 0xD7, 0x24, 0x2C, 0xDD, 0x8E,
+   0x5B, 0xF5, 0x33, 0x48, 0xEF, 0xDE, 0x4B, 0xBC,
+   0x51, 0xAB, 0x7C, 0xE4, 0x63, 0x70, 0x9A, 0xAC,
+   0x54, 0x1D, 0x25, 0xC5, 0xEA, 0xB3, 0x05, 0xF7,
+   0xC1, 0x1F, 0xE8, 0x97, 0xBB, 0x32, 0x6D, 0xC7,
+   0x28, 0x61, 0xDB, 0x4D, 0x77, 0x72, 0x65, 0x8C,
+   0x80, 0x3A, 0x76, 0x47, 0xA8, 0x03, 0x04, 0x12,
+   0xCE, 0xA9, 0x75, 0x3C, 0x49, 0xF8, 0x64, 0xDF,
+   0x57, 0xA2, 0x69, 0x44, 0xAD, 0x3E, 0x4F, 0x0B,
+   0x74, 0x67, 0xC9, 0x1A, 0x17, 0xAA, 0x02, 0x6F,
+   0xDA, 0xF2, 0xC6, 0x27, 0x53, 0xD6, 0xFD, 0xCA,
+   0x8D, 0x93, 0x89, 0xD5, 0x6B, 0x4E, 0x90, 0x82,
+   0x30, 0xE7, 0xC4, 0xD9, 0x8A, 0x7F, 0xB4, 0xFC,
+   0xCF, 0xA1, 0xAE, 0x1C, 0x39, 0x1B, 0x7B, 0x5E,
+   0x88, 0x7D, 0xD3, 0x71, 0x2E, 0x98, 0x13, 0x8F,
+   0xCC, 0x84, 0x73, 0xCD, 0x21, 0x0D, 0x5C, 0xA5,
+   0x3D, 0x9E, 0x99, 0xC2, 0xF3, 0x34, 0x14, 0x62,
+   0x46, 0x0A, 0x07, 0x08, 0xFF, 0xFB, 0xB7, 0xBF,
+   0x5D, 0x91, 0xB8, 0x83, 0xBE, 0x94, 0xBA, 0xF9,
+   0xEB, 0xE5, 0xCB, 0x95, 0x40, 0x31, 0xE6, 0x86,
+   0xD4, 0xFE, 0xD0, 0x7A, 0x26, 0xB9, 0xDC, 0x2F,
+   0xBD, 0xF0, 0x5F, 0x00, 0x9C, 0x6A, 0x5A, 0x3B,
+   0xF1, 0xC8, 0x9F, 0xED, 0x50, 0x20, 0x15, 0x11,
+   0x68, 0x1E, 0xF6, 0xA6, 0x6C, 0xB2, 0xD1, 0x58 };
+
+unsigned char table_59[256] = {
+   0x4C, 0x85, 0x2B, 0x14, 0xCC, 0x4D, 0x5F, 0xD7,
+   0xCE, 0x28, 0xC5, 0x0B, 0xA1, 0x99, 0x08, 0xDE,
+   0x42, 0xD1, 0x82, 0x5C, 0xC9, 0x8F, 0x72, 0x12,
+   0xCB, 0x0D, 0x04, 0xFA, 0xCD, 0xE5, 0x9A, 0x6F,
+   0xCF, 0x92, 0xB5, 0x88, 0x87, 0xBF, 0x90, 0x7C,
+   0xAC, 0xBE, 0x36, 0x21, 0x7D, 0x7F, 0xC7, 0x9F,
+   0x75, 0xBB, 0x61, 0x16, 0x17, 0x63, 0xAE, 0xC4,
+   0x23, 0x89, 0xE0, 0x37, 0x91, 0x5E, 0xC8, 0xE4,
+   0xFD, 0xD5, 0xA2, 0xC6, 0x5A, 0xEF, 0x9B, 0xD6,
+   0x27, 0xEE, 0x60, 0x1C, 0xDF, 0xDA, 0xF1, 0xD2,
+   0x1E, 0x01, 0x9D, 0x44, 0x03, 0xD8, 0x11, 0x53,
+   0x4F, 0x6C, 0x8B, 0xB7, 0x40, 0xF2, 0x79, 0x20,
+   0x74, 0x97, 0x3E, 0x3D, 0x05, 0xD4, 0x70, 0x30,
+   0x54, 0x59, 0xE7, 0x15, 0xE1, 0xEB, 0x71, 0x83,
+   0xFE, 0x66, 0xB1, 0xA6, 0xF7, 0x8E, 0x6A, 0xEA,
+   0x65, 0x7E, 0xA3, 0xCA, 0x2D, 0x4B, 0xB8, 0x9C,
+   0x35, 0xC3, 0xB6, 0x49, 0x32, 0x25, 0xB3, 0xB0,
+   0x76, 0xC0, 0xF5, 0x00, 0x8A, 0xAF, 0x19, 0xDB,
+   0xDD, 0x47, 0xDC, 0x07, 0xB2, 0x4A, 0x55, 0xE6,
+   0x69, 0xEC, 0xED, 0x06, 0x94, 0xB9, 0xA7, 0x56,
+   0x2C, 0xAA, 0xE3, 0x22, 0x3B, 0x98, 0x77, 0x52,
+   0x3C, 0x64, 0xF8, 0x13, 0x78, 0xFC, 0xFB, 0xF3,
+   0xD3, 0xF9, 0x29, 0x45, 0x51, 0x8C, 0xA0, 0x38,
+   0xD9, 0xA5, 0x62, 0x3A, 0x6E, 0xD0, 0xE8, 0x7A,
+   0x33, 0x1D, 0xB4, 0x73, 0x02, 0xFF, 0x10, 0x80,
+   0x6B, 0xF0, 0xA4, 0xBA, 0xF6, 0xC2, 0x0E, 0xE2,
+   0x81, 0x43, 0x84, 0x86, 0x1F, 0x31, 0x2F, 0xA9,
+   0x1B, 0x2A, 0x4E, 0xF4, 0x95, 0x5B, 0x3F, 0x34,
+   0x39, 0x7B, 0x0A, 0x26, 0x6D, 0x57, 0x50, 0x09,
+   0x9E, 0xA8, 0xBC, 0x24, 0x93, 0x67, 0x41, 0x96,
+   0x0C, 0x46, 0xBD, 0xE9, 0x68, 0x18, 0xAB, 0x2E,
+   0x5D, 0x1A, 0x8D, 0xC1, 0x58, 0x48, 0xAD, 0x0F };
+
+unsigned char table_60[32] = {
+   0x1C, 0x06, 0x1E, 0x10, 0x1D, 0x05, 0x00, 0x0E,
+   0x0C, 0x02, 0x11, 0x19, 0x15, 0x18, 0x16, 0x07,
+   0x1F, 0x0B, 0x14, 0x01, 0x0F, 0x09, 0x0D, 0x13,
+   0x03, 0x08, 0x12, 0x04, 0x1B, 0x0A, 0x17, 0x1A };
+
+unsigned char table_61[256] = {
+   0xC5, 0xA6, 0xF2, 0x6B, 0x4B, 0x58, 0xE0, 0x41,
+   0xC6, 0x2F, 0x13, 0xFE, 0xC1, 0x34, 0x3F, 0x24,
+   0x10, 0xBF, 0x8B, 0xC9, 0x26, 0x2E, 0x68, 0xBE,
+   0x28, 0x54, 0x93, 0x11, 0x21, 0x03, 0xFF, 0x50,
+   0x31, 0x71, 0x2C, 0x6C, 0x91, 0x8F, 0x3B, 0x40,
+   0x3E, 0xE5, 0xA5, 0x80, 0xEA, 0x7C, 0x9D, 0x18,
+   0x84, 0x5A, 0x73, 0x3A, 0x33, 0x43, 0xA1, 0x47,
+   0xB1, 0xEE, 0xFB, 0x79, 0x5E, 0xAF, 0xB9, 0x48,
+   0x0F, 0x88, 0x65, 0x67, 0x6F, 0xDB, 0x25, 0xE4,
+   0xB0, 0x87, 0xD0, 0x46, 0xB5, 0xB7, 0x53, 0xD4,
+   0x1E, 0x76, 0xB4, 0x90, 0xDD, 0xA3, 0xF7, 0x57,
+   0xD2, 0xCC, 0x5D, 0xE3, 0xB3, 0xD8, 0x5F, 0x2B,
+   0x69, 0x4A, 0x9B, 0x39, 0x1A, 0x8D, 0x05, 0x8A,
+   0x44, 0x15, 0xAE, 0xF3, 0xA8, 0x92, 0x02, 0xAB,
+   0xB8, 0xDA, 0x0A, 0x0C, 0xED, 0xD7, 0x77, 0x98,
+   0x3D, 0x19, 0x95, 0x36, 0xE7, 0x7F, 0x66, 0xEF,
+   0x86, 0xDC, 0xCB, 0x9C, 0x63, 0xE6, 0x1D, 0x14,
+   0x9A, 0x22, 0xBD, 0xD6, 0x89, 0x2D, 0xD1, 0xF9,
+   0xA2, 0xDE, 0xF5, 0x5C, 0x8E, 0x2A, 0x29, 0xCA,
+   0x7A, 0x8C, 0x38, 0x9F, 0xBB, 0xDF, 0xEC, 0x30,
+   0x00, 0xFC, 0xAC, 0x81, 0xB2, 0xE8, 0xC0, 0xA7,
+   0x7B, 0x07, 0x52, 0x74, 0x70, 0x0E, 0x51, 0x6A,
+   0x62, 0x0D, 0x85, 0x1B, 0x4F, 0x96, 0x55, 0x1C,
+   0x32, 0x6E, 0x01, 0xF6, 0x08, 0xFD, 0x17, 0x35,
+   0xF0, 0x16, 0xC8, 0x23, 0xE9, 0x59, 0x3C, 0x37,
+   0x5B, 0x42, 0xD3, 0x49, 0x7D, 0x83, 0x78, 0xAD,
+   0x94, 0x9E, 0x56, 0xB6, 0xF1, 0xC3, 0x75, 0xF8,
+   0xFA, 0x09, 0x4C, 0xD9, 0x97, 0xF4, 0x7E, 0x6D,
+   0xBC, 0x4D, 0x64, 0xCD, 0x12, 0x99, 0x45, 0xCE,
+   0x61, 0x20, 0x0B, 0xA0, 0x82, 0xD5, 0xE1, 0x72,
+   0xA9, 0x1F, 0x06, 0x27, 0xC7, 0x04, 0xE2, 0xBA,
+   0xCF, 0x60, 0xAA, 0xA4, 0xEB, 0xC4, 0x4E, 0xC2 };
+
+unsigned char table_62[256] = {
+   0x01, 0x59, 0xEC, 0xFC, 0x51, 0xD2, 0xE4, 0x9D,
+   0xAA, 0x61, 0xD5, 0xCA, 0x63, 0x5D, 0xCE, 0x36,
+   0xB9, 0x49, 0x76, 0xA9, 0x14, 0x4C, 0x90, 0x28,
+   0x66, 0x17, 0x4F, 0x1E, 0x1A, 0x47, 0x30, 0xE8,
+   0xFD, 0x86, 0x2E, 0x7B, 0x7E, 0xCC, 0x34, 0x13,
+   0x94, 0x45, 0x38, 0x74, 0x29, 0xB0, 0x37, 0xC3,
+   0x26, 0x6C, 0x39, 0xA3, 0x89, 0xEB, 0xA2, 0x20,
+   0x00, 0xE0, 0x73, 0xE7, 0xB5, 0xCB, 0xED, 0x3E,
+   0x79, 0x09, 0xFA, 0x32, 0x54, 0xBA, 0x05, 0x96,
+   0xDE, 0x23, 0xD0, 0xA1, 0xAB, 0xFE, 0xF2, 0x22,
+   0xB2, 0x9B, 0x7D, 0x44, 0x12, 0x3D, 0x40, 0x82,
+   0xA0, 0xA8, 0x33, 0xDC, 0xF7, 0xFB, 0xAC, 0x41,
+   0x8A, 0x9C, 0x60, 0x11, 0xC8, 0xF0, 0xEA, 0x57,
+   0x3A, 0x42, 0xCD, 0x1D, 0x3C, 0xC6, 0x97, 0x62,
+   0x55, 0x9F, 0xF3, 0x93, 0x91, 0xDA, 0x6A, 0xE5,
+   0x27, 0x8E, 0x4E, 0xFF, 0xA4, 0x80, 0x04, 0xE1,
+   0x2B, 0x5E, 0xC0, 0x64, 0xC2, 0xD8, 0x46, 0x8C,
+   0xD4, 0x0F, 0xC4, 0x43, 0xD9, 0x9E, 0x4B, 0x5C,
+   0x0A, 0x8B, 0xBF, 0xD7, 0x7A, 0x81, 0x3B, 0x4A,
+   0x58, 0xB6, 0x21, 0x1F, 0xC1, 0xBD, 0xB1, 0x77,
+   0x72, 0x1C, 0x4D, 0xBC, 0xA5, 0x65, 0xC7, 0xF5,
+   0xB4, 0x2D, 0x69, 0x71, 0xE6, 0x8F, 0xBB, 0x03,
+   0xAF, 0xD6, 0x08, 0x75, 0xB7, 0x31, 0xF4, 0x2A,
+   0x48, 0x70, 0x0C, 0x8D, 0xD1, 0x87, 0x2F, 0x16,
+   0x5A, 0x5B, 0x98, 0xA6, 0xC5, 0x99, 0x50, 0x07,
+   0xDD, 0x92, 0x25, 0x68, 0x0D, 0xBE, 0x78, 0x0B,
+   0xAD, 0x84, 0x6B, 0x19, 0x52, 0x7C, 0xF6, 0xB3,
+   0x56, 0x83, 0x88, 0xEE, 0x2C, 0x1B, 0x6E, 0x53,
+   0x67, 0xE2, 0x6F, 0x15, 0x06, 0x10, 0x18, 0x85,
+   0xF1, 0x6D, 0xF9, 0xC9, 0xAE, 0x3F, 0xB8, 0x95,
+   0x35, 0xDF, 0xEF, 0xA7, 0x7F, 0x24, 0xF8, 0xE3,
+   0xCF, 0xE9, 0xDB, 0xD3, 0x02, 0x9A, 0x0E, 0x5F };
+
+unsigned char table_63[256] = {
+   0x0C, 0x02, 0xEE, 0x94, 0x2D, 0x76, 0x96, 0x75,
+   0x21, 0xDC, 0x37, 0x03, 0xC0, 0xF7, 0xDF, 0xEF,
+   0xB1, 0x1D, 0xCF, 0x15, 0x5A, 0xB4, 0xCC, 0x81,
+   0x89, 0x6B, 0xA5, 0x2E, 0x6D, 0xD4, 0x08, 0x44,
+   0x2A, 0x60, 0x50, 0xBF, 0x40, 0x7D, 0x5F, 0x64,
+   0x93, 0x70, 0xA4, 0x7F, 0xC9, 0xEB, 0x0A, 0xF8,
+   0x9F, 0xA8, 0xBC, 0x25, 0xE5, 0xF3, 0x1B, 0xD7,
+   0x29, 0x13, 0x0D, 0x69, 0x20, 0x5C, 0x0F, 0x91,
+   0x4F, 0x62, 0x06, 0x26, 0x41, 0xED, 0xDA, 0x53,
+   0x65, 0xFF, 0xCD, 0x3F, 0xF6, 0x01, 0xCE, 0xA2,
+   0x04, 0xDE, 0x27, 0x87, 0xBA, 0x86, 0x24, 0x78,
+   0xAF, 0xE1, 0x3D, 0xD0, 0xC8, 0x1F, 0x4A, 0x2C,
+   0x9A, 0xF0, 0xCB, 0xAD, 0x0B, 0x59, 0xC5, 0x58,
+   0xEA, 0x8A, 0xA1, 0x45, 0xB7, 0x5D, 0xB5, 0x77,
+   0x2B, 0x47, 0x05, 0x00, 0xAC, 0x61, 0xFA, 0x33,
+   0x74, 0x31, 0xCA, 0x22, 0x42, 0x8B, 0xFE, 0x09,
+   0xB2, 0x6E, 0x1A, 0xBE, 0xAA, 0x7B, 0xEC, 0xF4,
+   0x51, 0x66, 0x28, 0x12, 0xFC, 0x5E, 0x67, 0xF5,
+   0xB9, 0x82, 0x90, 0x8E, 0x8D, 0x17, 0xE7, 0xE8,
+   0xB0, 0xC3, 0x16, 0xA0, 0x4B, 0xB6, 0xFB, 0x7E,
+   0xC4, 0x85, 0x4C, 0x1E, 0xC7, 0x39, 0x4E, 0xA9,
+   0xE3, 0x4D, 0x32, 0x72, 0x35, 0x80, 0xE0, 0x34,
+   0xB8, 0x73, 0x98, 0x49, 0x92, 0x30, 0xD5, 0xD2,
+   0xA3, 0x54, 0x7A, 0x84, 0x8F, 0x6C, 0xFD, 0x43,
+   0x3A, 0x36, 0x3B, 0xD9, 0x48, 0x6A, 0x14, 0x79,
+   0xD1, 0x57, 0x88, 0xDB, 0xE4, 0x9B, 0xF9, 0x99,
+   0x10, 0x71, 0xC1, 0x68, 0x9E, 0x11, 0xAB, 0xBD,
+   0x7C, 0x3E, 0x3C, 0x18, 0x9D, 0x97, 0xF2, 0xE6,
+   0xA6, 0xF1, 0x46, 0xC2, 0x19, 0xBB, 0x52, 0xD8,
+   0x95, 0xD3, 0x23, 0xAE, 0x07, 0x2F, 0xE9, 0x63,
+   0x1C, 0x55, 0x6F, 0x9C, 0x56, 0x38, 0xC6, 0x5B,
+   0x8C, 0xE2, 0x83, 0xA7, 0xD6, 0x0E, 0xB3, 0xDD };
+
+unsigned char table_64[32] = {
+   0x03, 0x05, 0x0D, 0x09, 0x1A, 0x16, 0x08, 0x10,
+   0x06, 0x1E, 0x1C, 0x15, 0x02, 0x04, 0x17, 0x0C,
+   0x18, 0x0B, 0x19, 0x11, 0x1B, 0x14, 0x13, 0x0A,
+   0x0E, 0x00, 0x1D, 0x1F, 0x01, 0x0F, 0x07, 0x12 };
+
+unsigned char table_65[32] = {
+   0x01, 0x0A, 0x1E, 0x14, 0x10, 0x1D, 0x0D, 0x17,
+   0x0E, 0x0C, 0x0F, 0x12, 0x04, 0x1A, 0x05, 0x02,
+   0x08, 0x1C, 0x09, 0x1F, 0x0B, 0x13, 0x19, 0x1B,
+   0x11, 0x00, 0x16, 0x06, 0x03, 0x18, 0x15, 0x07 };
+
+unsigned char table_66[32] = {
+   0x1C, 0x18, 0x0C, 0x09, 0x05, 0x03, 0x15, 0x12,
+   0x0D, 0x02, 0x08, 0x0E, 0x19, 0x07, 0x13, 0x17,
+   0x1E, 0x1D, 0x1F, 0x11, 0x06, 0x0A, 0x0B, 0x14,
+   0x0F, 0x10, 0x01, 0x1B, 0x00, 0x04, 0x1A, 0x16 };
+
+unsigned char table_67[256] = {
+   0x6B, 0x49, 0xC8, 0x86, 0xFF, 0xC0, 0x5D, 0xEF,
+   0xF7, 0x06, 0xE0, 0x98, 0xA9, 0x72, 0x71, 0xD5,
+   0xBA, 0x7F, 0x10, 0xD1, 0xBE, 0x41, 0x9C, 0x40,
+   0x28, 0x8E, 0xE5, 0x74, 0x47, 0x9E, 0x3E, 0x7C,
+   0xB5, 0xCD, 0x3F, 0x20, 0xF2, 0xA6, 0xDC, 0x97,
+   0x32, 0x6D, 0x52, 0xF5, 0x16, 0x05, 0xFE, 0x04,
+   0x3D, 0x53, 0x50, 0x23, 0x39, 0x77, 0x08, 0x60,
+   0x75, 0x18, 0x4A, 0xC6, 0xBB, 0xE7, 0xF1, 0xAB,
+   0xEB, 0x88, 0xB6, 0x82, 0x6E, 0x91, 0xF3, 0x34,
+   0x3A, 0x42, 0x1A, 0xDF, 0xA1, 0xB3, 0x92, 0xBF,
+   0xB7, 0x00, 0xD4, 0xDE, 0x31, 0xF0, 0x1C, 0xDA,
+   0x4F, 0x61, 0x67, 0x2C, 0x07, 0xF9, 0x15, 0xA4,
+   0x7A, 0x26, 0x45, 0x2A, 0x12, 0x9F, 0xF4, 0x14,
+   0x8C, 0x90, 0xFC, 0xC5, 0x4B, 0x87, 0xE2, 0xC7,
+   0xD0, 0x8A, 0xE8, 0xDD, 0xEE, 0x3C, 0x2F, 0x22,
+   0x6A, 0x54, 0x37, 0x9B, 0x84, 0x25, 0x8F, 0xE3,
+   0xD7, 0xD8, 0x4E, 0xAD, 0x0F, 0x4C, 0x56, 0xA2,
+   0xD3, 0xB0, 0x73, 0x0B, 0xAE, 0xEA, 0x1D, 0x01,
+   0x36, 0xB4, 0x2D, 0xC4, 0x19, 0x58, 0x1E, 0x62,
+   0xE9, 0xB2, 0x5B, 0x5A, 0xBD, 0xD6, 0x65, 0x94,
+   0x9A, 0x55, 0xCC, 0x99, 0x1B, 0x85, 0x2B, 0xBC,
+   0x8D, 0x46, 0x81, 0xB8, 0xA3, 0x29, 0x5F, 0x35,
+   0x5C, 0xB1, 0x1F, 0x13, 0x17, 0xCB, 0x51, 0x02,
+   0x09, 0x7E, 0xA7, 0x69, 0x6F, 0x95, 0x30, 0x7B,
+   0xCA, 0x48, 0xAF, 0xAA, 0x0E, 0x44, 0x38, 0xB9,
+   0x0D, 0x11, 0xA0, 0xD9, 0x0C, 0xDB, 0xF8, 0x68,
+   0x33, 0x79, 0x59, 0x66, 0x4D, 0x03, 0xE1, 0x89,
+   0xE4, 0x3B, 0x78, 0xC2, 0x64, 0x6C, 0x27, 0xC9,
+   0xCF, 0xAC, 0xED, 0xFA, 0x5E, 0x2E, 0x76, 0x57,
+   0x93, 0xEC, 0x80, 0xA8, 0xE6, 0xCE, 0xC1, 0xA5,
+   0x9D, 0xD2, 0xC3, 0x0A, 0x7D, 0x70, 0xF6, 0x63,
+   0x24, 0x43, 0x21, 0x83, 0xFB, 0xFD, 0x8B, 0x96 };
+
+unsigned char table_68[256] = {
+   0x93, 0xFF, 0x83, 0x70, 0x12, 0x2D, 0x1C, 0xD6,
+   0xF9, 0xEE, 0xCF, 0x94, 0x7B, 0xB5, 0xA4, 0x84,
+   0x99, 0xF7, 0x67, 0x32, 0xFC, 0x8A, 0xE3, 0xE4,
+   0xCE, 0xC6, 0x77, 0x7E, 0xDA, 0x42, 0x85, 0xF0,
+   0x7D, 0x48, 0x28, 0x79, 0xDE, 0x5B, 0xE2, 0x0F,
+   0x75, 0xC5, 0x2C, 0x4F, 0xF3, 0xEC, 0x14, 0x10,
+   0x9C, 0x6E, 0x59, 0x4A, 0x20, 0x34, 0xA3, 0x89,
+   0xE0, 0x4E, 0x52, 0x88, 0x81, 0x5F, 0x6F, 0x71,
+   0x17, 0x3B, 0x21, 0xB4, 0xCB, 0x9B, 0x18, 0x13,
+   0xE8, 0xE1, 0x02, 0x2E, 0xED, 0x00, 0xA7, 0x1B,
+   0x06, 0xF4, 0x27, 0xDC, 0x35, 0x2F, 0x08, 0x9D,
+   0x7C, 0xC0, 0x36, 0xA6, 0x6B, 0xDF, 0x4C, 0xBC,
+   0xFE, 0xDB, 0xA5, 0xA8, 0x8D, 0x73, 0x7F, 0xC7,
+   0x8E, 0x60, 0x31, 0x61, 0x4B, 0x29, 0xD7, 0xE9,
+   0xBD, 0xAB, 0xCC, 0xFA, 0xD9, 0xEF, 0xC2, 0xD4,
+   0x19, 0x11, 0x15, 0xC9, 0xB1, 0xD5, 0x64, 0x97,
+   0xE7, 0x8F, 0x05, 0x44, 0xF8, 0xF1, 0x58, 0x47,
+   0x2A, 0x03, 0x1F, 0xAF, 0x0D, 0x04, 0x23, 0xB8,
+   0x24, 0x51, 0xB2, 0x54, 0x41, 0x53, 0x5C, 0xAE,
+   0xB7, 0xB3, 0xB6, 0x3D, 0x37, 0x39, 0x55, 0xBF,
+   0x0B, 0x7A, 0x57, 0x3C, 0x0E, 0x40, 0x6A, 0xF5,
+   0x72, 0xDD, 0xBB, 0x8B, 0xAA, 0x46, 0xA0, 0x30,
+   0x56, 0x78, 0x38, 0xBA, 0x9E, 0x92, 0x87, 0xFB,
+   0x66, 0x90, 0x1E, 0xB9, 0x96, 0x65, 0xA2, 0x50,
+   0x1D, 0xC3, 0x26, 0x22, 0xD0, 0x0A, 0x43, 0xF2,
+   0xB0, 0xEB, 0xAC, 0x62, 0x98, 0x3F, 0xD3, 0x69,
+   0xA1, 0x9F, 0x16, 0x95, 0xE6, 0xF6, 0x2B, 0x25,
+   0x1A, 0xD2, 0xBE, 0x09, 0x5D, 0x45, 0xC4, 0xFD,
+   0x5A, 0x07, 0x0C, 0x82, 0x3E, 0x49, 0x74, 0x6C,
+   0x68, 0x5E, 0xCA, 0xEA, 0xCD, 0x9A, 0xAD, 0xD1,
+   0x33, 0x86, 0x76, 0x80, 0xE5, 0xC8, 0xD8, 0xA9,
+   0x8C, 0x6D, 0x91, 0x63, 0x3A, 0x4D, 0xC1, 0x01 };
+
+unsigned char table_69[256] = {
+   0x21, 0x6B, 0x9B, 0xAE, 0x11, 0x5A, 0x91, 0xC2,
+   0x47, 0x8E, 0x87, 0x86, 0x4F, 0xFC, 0x8F, 0x66,
+   0x97, 0x2F, 0x61, 0x9C, 0x5B, 0x4C, 0xB3, 0x14,
+   0x77, 0x48, 0x62, 0xE1, 0x54, 0x64, 0xDD, 0xCD,
+   0x30, 0xB7, 0x2D, 0xD2, 0xC3, 0xC0, 0x0B, 0xD8,
+   0x53, 0x98, 0x16, 0x56, 0x7A, 0x35, 0x50, 0xD9,
+   0xE8, 0x2C, 0x32, 0x55, 0x17, 0x5D, 0x79, 0xEB,
+   0xC8, 0x75, 0x67, 0xE2, 0x4B, 0xBA, 0xFE, 0x57,
+   0x10, 0xF4, 0x70, 0x2A, 0xBB, 0xA6, 0x72, 0x36,
+   0xAF, 0x8D, 0xAB, 0x90, 0xE3, 0x2B, 0xB2, 0x26,
+   0x93, 0x01, 0xBD, 0x71, 0xF9, 0x05, 0xC7, 0x80,
+   0x29, 0xCC, 0x3B, 0x22, 0xF2, 0x12, 0x81, 0x34,
+   0xF6, 0x1A, 0x8B, 0xDF, 0x28, 0x46, 0x9E, 0x6A,
+   0x23, 0x85, 0x74, 0xE7, 0xE6, 0x52, 0xA0, 0x49,
+   0xF0, 0x19, 0x25, 0xAC, 0x78, 0x42, 0xD6, 0xA2,
+   0x37, 0x65, 0x4D, 0x94, 0x02, 0x6F, 0xB4, 0xC6,
+   0x99, 0xD3, 0x9A, 0x33, 0xB8, 0x00, 0xCA, 0xE4,
+   0x45, 0xAD, 0x1B, 0x6C, 0x03, 0xA8, 0x07, 0x8A,
+   0x60, 0x69, 0xFF, 0xF7, 0xA7, 0x27, 0x95, 0xF5,
+   0x82, 0xCB, 0xEC, 0xED, 0x4E, 0xFB, 0xA4, 0x59,
+   0xDA, 0xCF, 0x2E, 0x20, 0xFA, 0x31, 0xD1, 0xEA,
+   0x4A, 0xE9, 0x5E, 0xA9, 0xA1, 0x08, 0x1C, 0x96,
+   0x38, 0xB9, 0xEE, 0x7F, 0xAA, 0xF1, 0x7D, 0x3A,
+   0xA5, 0x43, 0xC5, 0xE0, 0x24, 0x39, 0x0D, 0xDE,
+   0xB0, 0xF8, 0xBE, 0x58, 0x7E, 0x51, 0xD4, 0x89,
+   0x15, 0x40, 0x3E, 0xB1, 0x1F, 0x5F, 0x68, 0x63,
+   0x84, 0x3D, 0x88, 0xBC, 0x41, 0xEF, 0xB5, 0xBF,
+   0x06, 0x6E, 0x9D, 0x3F, 0x0E, 0x76, 0x5C, 0xDC,
+   0x13, 0xF3, 0xE5, 0x8C, 0x7C, 0x04, 0x0A, 0xD5,
+   0x18, 0xC4, 0x44, 0x09, 0xC9, 0x1D, 0x9F, 0xFD,
+   0xD0, 0x0F, 0x6D, 0xD7, 0x92, 0x7B, 0x0C, 0xA3,
+   0x73, 0xDB, 0xB6, 0x83, 0xCE, 0x1E, 0xC1, 0x3C };
+
+unsigned char table_70[256] = {
+   0x54, 0x23, 0xF1, 0x09, 0x9D, 0xEB, 0x26, 0xD9,
+   0x6C, 0xC1, 0xBC, 0x3D, 0x6E, 0xB0, 0x5F, 0xE2,
+   0x59, 0x4D, 0x95, 0xFA, 0xD8, 0x29, 0xAA, 0x8E,
+   0xF5, 0xEF, 0x43, 0x76, 0xFD, 0x0D, 0x4F, 0xAD,
+   0xB7, 0xFC, 0xA8, 0x9F, 0x62, 0xC2, 0x7B, 0x10,
+   0x0B, 0xF2, 0x73, 0xA9, 0x46, 0x4C, 0x53, 0xD7,
+   0x0A, 0x50, 0x89, 0x63, 0x48, 0xD6, 0xA2, 0x44,
+   0xE6, 0x8D, 0x69, 0x2C, 0xF9, 0xC0, 0x35, 0x06,
+   0x66, 0x21, 0x9E, 0xD2, 0x98, 0xF7, 0x9B, 0xE7,
+   0x12, 0xB8, 0xA5, 0xBA, 0xE0, 0x79, 0x71, 0x7E,
+   0x8C, 0x24, 0xED, 0x7C, 0x60, 0x81, 0xC3, 0x5C,
+   0x2B, 0xE5, 0xEE, 0xB5, 0xA4, 0x05, 0x03, 0x34,
+   0x16, 0x2A, 0xA3, 0x2D, 0x3F, 0xDF, 0x07, 0x5B,
+   0xAE, 0x47, 0x61, 0x08, 0x18, 0xDB, 0x6D, 0x3C,
+   0x96, 0xD5, 0xAB, 0x78, 0x94, 0x45, 0x20, 0x9A,
+   0xE4, 0x13, 0x68, 0xDD, 0xDE, 0x31, 0x14, 0x57,
+   0x02, 0x52, 0x56, 0x1C, 0x1B, 0xE9, 0xD0, 0xA1,
+   0x22, 0x64, 0xB2, 0x7A, 0xCF, 0x5D, 0x00, 0x0F,
+   0xF8, 0x5E, 0x36, 0x58, 0x40, 0xAF, 0x19, 0x32,
+   0x2E, 0xB3, 0x72, 0xBE, 0xB9, 0xD3, 0xCD, 0x7D,
+   0x4A, 0x1D, 0x33, 0x2F, 0xAC, 0x27, 0x41, 0xE8,
+   0x55, 0xCB, 0x0E, 0x5A, 0x77, 0xFB, 0x8B, 0x86,
+   0x75, 0x8A, 0x51, 0xEC, 0xDA, 0xC6, 0xA6, 0xCC,
+   0x91, 0x4B, 0x11, 0xF6, 0xEA, 0xD1, 0xB6, 0x4E,
+   0x82, 0x04, 0x92, 0x30, 0xF4, 0x25, 0x88, 0x1E,
+   0x9C, 0xA0, 0xC8, 0x6A, 0x93, 0x87, 0x1F, 0xB4,
+   0xB1, 0x8F, 0x65, 0xCA, 0xFE, 0xFF, 0x97, 0x15,
+   0x99, 0x28, 0x80, 0x42, 0x70, 0x85, 0x0C, 0x3B,
+   0xBD, 0xE1, 0xA7, 0x17, 0xC9, 0x3A, 0xBB, 0x6B,
+   0x37, 0xF0, 0xC5, 0x39, 0x6F, 0x01, 0x83, 0x67,
+   0x74, 0xCE, 0xDC, 0x90, 0x3E, 0xF3, 0x7F, 0xC4,
+   0x49, 0x84, 0x38, 0xC7, 0xE3, 0xD4, 0x1A, 0xBF };
+
+unsigned char table_71[32] = {
+   0x17, 0x13, 0x0E, 0x1A, 0x0D, 0x18, 0x19, 0x10,
+   0x14, 0x11, 0x16, 0x05, 0x04, 0x00, 0x12, 0x0A,
+   0x02, 0x07, 0x03, 0x0B, 0x09, 0x1F, 0x1C, 0x0F,
+   0x0C, 0x06, 0x1B, 0x08, 0x1D, 0x01, 0x15, 0x1E };
+
+unsigned char table_72[256] = {
+   0xC9, 0xA7, 0x1B, 0xEC, 0x2B, 0x8B, 0xB0, 0xEB,
+   0x7F, 0x39, 0x25, 0xD9, 0x1D, 0xD5, 0x67, 0xA0,
+   0xB3, 0xAC, 0x3B, 0xC8, 0x82, 0xC0, 0xE3, 0x9E,
+   0x4C, 0x9B, 0xAF, 0xFD, 0x91, 0x86, 0x5F, 0x92,
+   0xB4, 0x42, 0x3C, 0x45, 0x12, 0xC4, 0xE2, 0xE1,
+   0x6C, 0x1F, 0xC6, 0x40, 0x93, 0x2A, 0xC2, 0x72,
+   0x2E, 0x14, 0x51, 0xA5, 0x70, 0xBD, 0xA2, 0xC7,
+   0x7D, 0xF1, 0x9F, 0x64, 0xC1, 0xF7, 0x80, 0xFF,
+   0x50, 0x49, 0x8C, 0x66, 0x13, 0x48, 0x6A, 0x0A,
+   0x26, 0x94, 0x83, 0x1E, 0x84, 0xBB, 0x57, 0x27,
+   0x44, 0x5B, 0x62, 0xF6, 0x09, 0x4F, 0x77, 0x76,
+   0x2D, 0x7E, 0xCD, 0x0B, 0x24, 0xFE, 0x81, 0xB8,
+   0x21, 0x85, 0xCF, 0xA8, 0x75, 0x56, 0x37, 0x17,
+   0xAA, 0x23, 0xE5, 0xE8, 0x9A, 0x9D, 0x2F, 0x04,
+   0x31, 0x4A, 0x7C, 0xFC, 0xD6, 0xE4, 0x29, 0xC3,
+   0xFB, 0x36, 0x1C, 0x0C, 0xCE, 0xEE, 0x0D, 0xF3,
+   0x46, 0xF8, 0x41, 0x0E, 0x68, 0xAB, 0x2C, 0x69,
+   0x96, 0x90, 0x28, 0xED, 0x02, 0x63, 0x07, 0xAD,
+   0xB2, 0xDC, 0x05, 0xE6, 0x78, 0x03, 0xA4, 0x7A,
+   0x5C, 0x52, 0x95, 0x5D, 0x88, 0x01, 0xDF, 0x35,
+   0x5E, 0xB6, 0x06, 0x4D, 0x15, 0x89, 0x59, 0x3F,
+   0xF0, 0xA1, 0xA3, 0x99, 0x19, 0xEA, 0xDB, 0xE0,
+   0x6B, 0x71, 0x6E, 0xB7, 0x65, 0x54, 0x9C, 0xBC,
+   0x98, 0xDD, 0x4B, 0x60, 0x3D, 0xBF, 0xF5, 0xD1,
+   0xD7, 0xF9, 0x55, 0x61, 0xA9, 0xB1, 0x6D, 0xDE,
+   0x79, 0xAE, 0x1A, 0x34, 0x3A, 0x4E, 0xCB, 0x38,
+   0xBA, 0x97, 0x00, 0x74, 0xEF, 0xD8, 0x18, 0x33,
+   0x7B, 0xFA, 0x22, 0x32, 0x20, 0xCA, 0x8A, 0xBE,
+   0xA6, 0x43, 0x11, 0x10, 0xD0, 0xD3, 0x87, 0x73,
+   0x6F, 0xF4, 0x8D, 0xCC, 0x30, 0x0F, 0x16, 0xDA,
+   0xB5, 0xC5, 0xD4, 0x47, 0x8E, 0xE7, 0x58, 0x8F,
+   0x08, 0x53, 0xF2, 0xB9, 0x5A, 0x3E, 0xE9, 0xD2 };
+
+unsigned char table_73[256] = {
+   0x36, 0x37, 0xED, 0xD8, 0xBF, 0xD7, 0x12, 0xB7,
+   0x40, 0x32, 0x19, 0x4A, 0x44, 0x2A, 0xCE, 0xA5,
+   0x29, 0x13, 0x43, 0x51, 0x5C, 0xD0, 0x76, 0x6E,
+   0x41, 0xD6, 0xE2, 0x4F, 0xB8, 0x27, 0x2E, 0xCF,
+   0xD9, 0xE0, 0x69, 0xC0, 0x59, 0x77, 0x62, 0x6F,
+   0x53, 0xE7, 0x93, 0xD4, 0xAD, 0xC8, 0x4C, 0xC2,
+   0x2C, 0xBE, 0xAA, 0xA0, 0x22, 0x78, 0x14, 0xB3,
+   0xB0, 0xEA, 0xBA, 0x9A, 0x33, 0x1B, 0x31, 0x6C,
+   0xFC, 0x0A, 0x0B, 0xA1, 0xE4, 0x75, 0x7C, 0xE3,
+   0x65, 0x21, 0xA9, 0xA4, 0x4E, 0x3C, 0x5F, 0x39,
+   0x74, 0xA2, 0x9E, 0x03, 0x70, 0xD2, 0xFD, 0x1D,
+   0x25, 0x72, 0x73, 0x8E, 0x7B, 0xB2, 0x6A, 0x92,
+   0x81, 0xF3, 0xF0, 0x46, 0x08, 0x85, 0xE6, 0x30,
+   0x05, 0x7E, 0xEC, 0x0D, 0xDD, 0x42, 0x2F, 0x5B,
+   0xB9, 0xCB, 0x84, 0x0C, 0x16, 0xC7, 0x24, 0xFA,
+   0xF9, 0x8F, 0x20, 0xAC, 0x10, 0x55, 0xC3, 0x1A,
+   0x8B, 0x94, 0x3D, 0xDB, 0xC9, 0x04, 0xB5, 0xCC,
+   0xC6, 0x98, 0xB6, 0x8D, 0x0F, 0x3A, 0x06, 0x4B,
+   0xEF, 0x35, 0x68, 0x3F, 0xEE, 0xE5, 0x63, 0xC5,
+   0x60, 0x88, 0x52, 0x2D, 0x6D, 0xAB, 0xCD, 0xC4,
+   0x1F, 0xF4, 0xCA, 0x67, 0x7D, 0x1C, 0xDA, 0x34,
+   0xDE, 0x86, 0xAE, 0xF1, 0x61, 0x09, 0xF5, 0xF6,
+   0x49, 0xE9, 0xF2, 0x48, 0x1E, 0xD3, 0x56, 0x18,
+   0x9B, 0xB1, 0x57, 0x9D, 0xBB, 0x5E, 0xAF, 0x87,
+   0x9F, 0x8A, 0xC1, 0x79, 0xA7, 0xA8, 0xFB, 0xDC,
+   0x47, 0x3E, 0x97, 0x80, 0x91, 0xA6, 0x7A, 0xA3,
+   0x9C, 0x11, 0x02, 0x2B, 0x58, 0xD1, 0xF7, 0x00,
+   0x83, 0x01, 0xE8, 0xFE, 0x50, 0x23, 0x66, 0x4D,
+   0xD5, 0x82, 0x89, 0x3B, 0xEB, 0xE1, 0xF8, 0x5A,
+   0x15, 0x7F, 0x8C, 0x17, 0x96, 0x28, 0x5D, 0x64,
+   0x26, 0x38, 0x71, 0x0E, 0x45, 0xDF, 0xB4, 0x99,
+   0xFF, 0x90, 0x6B, 0xBC, 0x54, 0x95, 0xBD, 0x07 };
+
+unsigned char table_74[256] = {
+   0xA7, 0xCF, 0x99, 0x1A, 0x13, 0xC7, 0xE9, 0xC4,
+   0xB6, 0x0E, 0x15, 0x09, 0xFF, 0xDF, 0xBE, 0x03,
+   0xAD, 0xF1, 0xB0, 0x3C, 0x4A, 0x9B, 0xF5, 0x12,
+   0xA1, 0x2C, 0xDB, 0x51, 0x5E, 0x6F, 0xE6, 0x49,
+   0x27, 0xBB, 0xAE, 0x56, 0xC0, 0x0C, 0x77, 0x60,
+   0x5B, 0x69, 0xA2, 0xF0, 0x24, 0x8E, 0xE1, 0xA4,
+   0xBC, 0x9F, 0x50, 0xD4, 0x61, 0x19, 0x67, 0x00,
+   0x7B, 0xAB, 0xDD, 0x26, 0xCD, 0x6C, 0xE8, 0xA8,
+   0x7A, 0x93, 0xEF, 0x20, 0x52, 0x1F, 0x1B, 0x46,
+   0x25, 0x3B, 0x1E, 0x65, 0xC2, 0xF9, 0x10, 0xB2,
+   0xB3, 0xD9, 0x21, 0xD2, 0x11, 0x94, 0xE2, 0xFC,
+   0x38, 0x9E, 0x36, 0x87, 0xAA, 0x53, 0x45, 0x68,
+   0x2B, 0xE7, 0x07, 0xFA, 0xD3, 0x8D, 0x3F, 0x17,
+   0xC1, 0x06, 0x72, 0x62, 0x8C, 0x55, 0x73, 0x8A,
+   0xC9, 0x2E, 0x5A, 0x7D, 0x02, 0x6D, 0xF8, 0x4B,
+   0xE4, 0xBF, 0xEC, 0xB7, 0x31, 0xDC, 0xF4, 0xB8,
+   0x47, 0x64, 0x0A, 0x33, 0x48, 0xAC, 0xFB, 0x05,
+   0x3E, 0x34, 0x1C, 0x97, 0x1D, 0x63, 0x37, 0x2D,
+   0xB1, 0x92, 0xED, 0x9D, 0x4C, 0xD5, 0x4E, 0x9A,
+   0x0D, 0x79, 0x0F, 0xBD, 0x95, 0xBA, 0x08, 0x2A,
+   0xC6, 0x7E, 0x88, 0xCB, 0xA6, 0x29, 0x70, 0x35,
+   0x66, 0xCA, 0x89, 0x75, 0x6A, 0x4F, 0xB5, 0x6B,
+   0x74, 0xDE, 0x01, 0x04, 0x81, 0x91, 0x90, 0x18,
+   0x32, 0x0B, 0x7F, 0x44, 0xB4, 0xAF, 0xF2, 0xEB,
+   0x22, 0xFD, 0x14, 0xA0, 0xFE, 0x8B, 0xB9, 0x16,
+   0x86, 0xE3, 0xD7, 0xDA, 0xC5, 0x3A, 0x41, 0x83,
+   0xD1, 0x28, 0x54, 0x30, 0xE0, 0x40, 0xA5, 0x57,
+   0x8F, 0x84, 0xD6, 0x96, 0x39, 0xE5, 0x42, 0x80,
+   0xA9, 0x58, 0xCE, 0x5D, 0xEE, 0x5F, 0xA3, 0xD0,
+   0xC8, 0x59, 0x43, 0x4D, 0x5C, 0xF7, 0xCC, 0x76,
+   0x6E, 0xF3, 0x23, 0x3D, 0x85, 0x82, 0x78, 0xF6,
+   0x2F, 0xD8, 0xC3, 0x7C, 0x9C, 0x98, 0xEA, 0x71 };
+
+unsigned char table_75[256] = {
+   0xE7, 0xA5, 0x30, 0xE1, 0x9D, 0x81, 0xBE, 0x83,
+   0xB2, 0x1E, 0xE4, 0x69, 0x2F, 0x2B, 0x0D, 0xEB,
+   0x7C, 0x59, 0x2D, 0xAA, 0x01, 0x0C, 0xDB, 0xED,
+   0xC4, 0xEE, 0x5D, 0x38, 0x72, 0xD8, 0x70, 0xCE,
+   0x0B, 0xF6, 0x7F, 0x48, 0x26, 0x9E, 0xA3, 0x44,
+   0xD6, 0xCF, 0x0F, 0x6B, 0xFD, 0x23, 0x98, 0xAB,
+   0x11, 0xD4, 0x92, 0x91, 0x5E, 0x08, 0x4D, 0xC6,
+   0xF0, 0xA8, 0x7E, 0x8A, 0x1D, 0xA1, 0x97, 0x76,
+   0x3E, 0x64, 0x07, 0x24, 0xDE, 0x75, 0xA4, 0xCC,
+   0x1A, 0x04, 0x4B, 0x6C, 0xFA, 0xB0, 0xC7, 0x35,
+   0xE2, 0x56, 0x61, 0xA0, 0xE9, 0x27, 0xDF, 0xC3,
+   0xE5, 0xF4, 0x8D, 0xB4, 0xD3, 0x52, 0xD7, 0x49,
+   0xCD, 0x31, 0x6E, 0x3F, 0x4E, 0x6A, 0x5B, 0x65,
+   0xCA, 0x14, 0x71, 0x53, 0xD9, 0x47, 0x28, 0x7D,
+   0x17, 0x06, 0x5C, 0xFE, 0xBA, 0xB8, 0xAC, 0x15,
+   0xE8, 0xE0, 0x9A, 0xDD, 0x1F, 0xBC, 0x95, 0x42,
+   0xCB, 0x58, 0x00, 0x85, 0xD5, 0x62, 0xC9, 0xB6,
+   0x05, 0x80, 0x4C, 0x3C, 0x1C, 0xF5, 0x03, 0xF8,
+   0x96, 0x77, 0x02, 0x19, 0xF2, 0xFB, 0x5F, 0xC2,
+   0xAE, 0x60, 0x1B, 0xAD, 0x8F, 0xC1, 0x33, 0xA6,
+   0x20, 0xBF, 0xA7, 0xC8, 0x74, 0x18, 0x90, 0xE3,
+   0x68, 0x09, 0x7A, 0x79, 0xB5, 0xDA, 0xF3, 0x0E,
+   0x66, 0x84, 0xB3, 0xBB, 0xE6, 0xF7, 0xB7, 0x7B,
+   0x39, 0x4A, 0x12, 0x4F, 0xC5, 0x41, 0x54, 0xD0,
+   0xFF, 0x87, 0x63, 0x40, 0x99, 0x21, 0x29, 0xD2,
+   0x3D, 0x37, 0x3A, 0x93, 0xFC, 0x25, 0xF1, 0xD1,
+   0x2C, 0x6D, 0x8C, 0x5A, 0x8E, 0x9B, 0xBD, 0xAF,
+   0x10, 0x55, 0xF9, 0x9F, 0x43, 0x0A, 0x50, 0x16,
+   0x57, 0xB1, 0xC0, 0x73, 0x82, 0xEF, 0x88, 0x6F,
+   0xEA, 0x2A, 0xEC, 0x2E, 0x86, 0x45, 0x51, 0x22,
+   0xA9, 0x34, 0x94, 0x3B, 0xB9, 0x9C, 0xA2, 0x13,
+   0x89, 0x46, 0x78, 0xDC, 0x32, 0x8B, 0x67, 0x36 };
+
+unsigned char table_76[256] = {
+   0x3D, 0x66, 0x40, 0xC5, 0x1D, 0xF5, 0xE7, 0xB7,
+   0x2C, 0x23, 0x09, 0xC2, 0x68, 0xE6, 0xD3, 0x8D,
+   0x35, 0x94, 0x93, 0xF0, 0x43, 0x97, 0x2B, 0x4B,
+   0x1A, 0xEB, 0x00, 0x4C, 0x6F, 0xE4, 0x92, 0xEA,
+   0xB8, 0xA3, 0xA6, 0xEC, 0x11, 0x5E, 0x61, 0x81,
+   0xE1, 0x48, 0xC9, 0xCB, 0xDB, 0x2E, 0x3B, 0xED,
+   0x36, 0x52, 0x3A, 0xD2, 0x4F, 0x4E, 0x22, 0x96,
+   0x57, 0x2D, 0x62, 0x53, 0xCF, 0xD9, 0x5B, 0x9F,
+   0x8E, 0x78, 0xC6, 0x07, 0x7D, 0xA1, 0x02, 0xB4,
+   0xF4, 0xB6, 0x34, 0x98, 0xDA, 0xA9, 0xD4, 0x54,
+   0x99, 0x82, 0x0A, 0xD8, 0x88, 0x5D, 0x3C, 0xD0,
+   0xAB, 0x31, 0xFB, 0x03, 0x17, 0x46, 0xE8, 0xE2,
+   0xA4, 0xFF, 0xB0, 0xAA, 0xAD, 0x7C, 0x55, 0x49,
+   0x75, 0x6B, 0x10, 0x24, 0xC0, 0x04, 0xB1, 0xBF,
+   0x6A, 0xF6, 0x15, 0xEF, 0x5C, 0x60, 0x27, 0x3E,
+   0x38, 0x63, 0xC1, 0x76, 0xFD, 0x84, 0xE0, 0xCD,
+   0xFE, 0x30, 0xCE, 0xBB, 0xDC, 0x1E, 0x1B, 0xBC,
+   0xB5, 0xE9, 0x9E, 0x8F, 0x0D, 0x3F, 0x91, 0x19,
+   0x28, 0x37, 0x26, 0x42, 0x08, 0x9A, 0x0C, 0x83,
+   0x90, 0x6D, 0x74, 0x65, 0xF2, 0x4A, 0xDE, 0x8B,
+   0x67, 0x0E, 0x8C, 0x5F, 0xF9, 0x7F, 0x5A, 0x86,
+   0x69, 0x45, 0x44, 0xD5, 0xF7, 0xE5, 0x8A, 0xA8,
+   0xC8, 0x7E, 0x05, 0x64, 0xEE, 0x79, 0xBE, 0x7A,
+   0x14, 0xD6, 0x50, 0x18, 0x25, 0xBD, 0x85, 0xE3,
+   0xA2, 0x70, 0xCC, 0x59, 0x71, 0x77, 0xFA, 0x47,
+   0x9B, 0x1F, 0x9D, 0xBA, 0x29, 0x4D, 0xF8, 0xDF,
+   0xC4, 0x72, 0x2F, 0xAE, 0x06, 0x51, 0x41, 0xAF,
+   0xF3, 0xDD, 0x87, 0xB2, 0x9C, 0xC7, 0x12, 0x16,
+   0x20, 0xA7, 0x21, 0x73, 0xF1, 0x58, 0xD7, 0x7B,
+   0xB9, 0xB3, 0x32, 0x01, 0x80, 0x1C, 0x39, 0x0B,
+   0x13, 0x56, 0x6C, 0x89, 0x33, 0x6E, 0x2A, 0xA5,
+   0xD1, 0x95, 0xC3, 0xA0, 0x0F, 0xCA, 0xAC, 0xFC };
+
+unsigned char table_77[32] = {
+   0x1C, 0x0D, 0x1E, 0x01, 0x06, 0x16, 0x18, 0x17,
+   0x0B, 0x1F, 0x04, 0x0F, 0x00, 0x19, 0x08, 0x0A,
+   0x11, 0x03, 0x05, 0x07, 0x09, 0x0C, 0x15, 0x14,
+   0x1A, 0x12, 0x13, 0x0E, 0x1D, 0x10, 0x02, 0x1B };
+
+unsigned char table_78[32] = {
+   0x0E, 0x02, 0x17, 0x12, 0x1E, 0x09, 0x15, 0x03,
+   0x01, 0x0B, 0x0F, 0x11, 0x10, 0x0A, 0x16, 0x06,
+   0x07, 0x00, 0x1C, 0x1D, 0x1F, 0x0C, 0x18, 0x04,
+   0x13, 0x0D, 0x1B, 0x08, 0x19, 0x14, 0x05, 0x1A };
+
+unsigned char table_79[32] = {
+   0x12, 0x0B, 0x11, 0x01, 0x07, 0x0E, 0x1A, 0x0D,
+   0x1E, 0x18, 0x14, 0x1F, 0x0A, 0x17, 0x19, 0x1B,
+   0x00, 0x10, 0x0C, 0x08, 0x13, 0x02, 0x0F, 0x1D,
+   0x09, 0x06, 0x04, 0x16, 0x15, 0x1C, 0x05, 0x03 };
+
+unsigned char table_80[256] = {
+   0x14, 0xE7, 0x31, 0x0F, 0xD1, 0x5F, 0xED, 0x1E,
+   0xA6, 0x77, 0x20, 0x57, 0x34, 0x64, 0x33, 0x0B,
+   0x5A, 0xB4, 0x83, 0x62, 0xFD, 0x8E, 0xE4, 0xF3,
+   0xBD, 0xA5, 0xC8, 0x6D, 0x3E, 0x4F, 0x01, 0x7A,
+   0xD3, 0x45, 0x3C, 0xF2, 0x68, 0xFF, 0xE6, 0x84,
+   0xC2, 0xC1, 0x53, 0x72, 0x8C, 0xA1, 0xC7, 0x00,
+   0x89, 0x97, 0x69, 0xA4, 0xF8, 0xAA, 0xAD, 0x8F,
+   0x24, 0xC6, 0x9A, 0xAC, 0xE5, 0xAB, 0x6B, 0x79,
+   0x99, 0x60, 0x28, 0x2B, 0x3B, 0xAF, 0x1C, 0x80,
+   0xA3, 0x8A, 0x1A, 0xB5, 0xE1, 0x9F, 0xDA, 0x78,
+   0xD7, 0xC4, 0x87, 0x5D, 0xE9, 0x27, 0xFB, 0x18,
+   0x94, 0x3A, 0xCE, 0x3F, 0xF6, 0x12, 0x75, 0x37,
+   0x6E, 0x9E, 0x29, 0x6C, 0xF7, 0x7D, 0x92, 0x08,
+   0x42, 0xB2, 0xBF, 0x0C, 0xB6, 0x25, 0xE0, 0x49,
+   0x43, 0x91, 0x98, 0xBB, 0xDC, 0x63, 0xEA, 0xA8,
+   0x74, 0x38, 0x35, 0xCD, 0x07, 0x70, 0x81, 0x41,
+   0xC9, 0x51, 0xBC, 0xA9, 0x59, 0xD4, 0xB8, 0x2C,
+   0x7C, 0x2D, 0xB3, 0x6F, 0x11, 0x86, 0x9D, 0x46,
+   0xF0, 0x65, 0x76, 0x04, 0x0E, 0xCA, 0xBE, 0x5C,
+   0xF9, 0x71, 0x9C, 0x21, 0x4C, 0x02, 0xFE, 0x8D,
+   0xD5, 0x26, 0x40, 0xC3, 0x32, 0x9B, 0xB0, 0x5E,
+   0x48, 0xC5, 0x85, 0x4B, 0x0A, 0xCC, 0x58, 0x52,
+   0x61, 0x13, 0xEF, 0x4A, 0xEE, 0x03, 0xD9, 0xDE,
+   0xA7, 0x19, 0x09, 0x7F, 0x5B, 0x96, 0xBA, 0x0D,
+   0xCF, 0xD2, 0x06, 0x1F, 0xD8, 0xDB, 0xEC, 0xA0,
+   0xDD, 0x66, 0x10, 0xA2, 0xDF, 0x30, 0xF4, 0x88,
+   0xCB, 0x36, 0x82, 0xE3, 0x73, 0x17, 0x55, 0x15,
+   0xF5, 0xB7, 0x23, 0xB1, 0xD6, 0xE2, 0x47, 0x7E,
+   0x67, 0xE8, 0x1D, 0x16, 0x8B, 0xEB, 0xD0, 0x3D,
+   0x6A, 0x54, 0x2A, 0x4E, 0x93, 0xFA, 0x44, 0x05,
+   0x2F, 0x50, 0x2E, 0x95, 0xAE, 0x1B, 0x56, 0x7B,
+   0x39, 0xB9, 0xC0, 0x22, 0xF1, 0x4D, 0x90, 0xFC };
+
+unsigned char table_81[32] = {
+   0x03, 0x02, 0x1D, 0x0E, 0x09, 0x1A, 0x0C, 0x11,
+   0x1C, 0x0D, 0x08, 0x12, 0x19, 0x10, 0x04, 0x17,
+   0x15, 0x05, 0x0A, 0x00, 0x13, 0x16, 0x1B, 0x18,
+   0x1E, 0x0B, 0x0F, 0x01, 0x07, 0x14, 0x1F, 0x06 };
+
+unsigned char table_82[256] = {
+   0x53, 0xD3, 0x64, 0x89, 0x7D, 0xA5, 0x66, 0xA4,
+   0x09, 0x46, 0x17, 0x2C, 0xAF, 0x8C, 0x21, 0x5F,
+   0x3B, 0x22, 0xE3, 0x05, 0x07, 0x28, 0x2F, 0xAB,
+   0xF4, 0x8E, 0x51, 0x31, 0x02, 0xC7, 0x48, 0x13,
+   0x24, 0x12, 0xB8, 0xE5, 0xBD, 0xAE, 0x7E, 0xCC,
+   0xC9, 0x98, 0x08, 0xEE, 0xDB, 0x1B, 0xE8, 0x3D,
+   0x8F, 0xF2, 0xFB, 0x36, 0x4D, 0x94, 0x9C, 0x16,
+   0xF7, 0x42, 0x9B, 0x2B, 0xFD, 0x7B, 0x77, 0x3F,
+   0xC3, 0xFC, 0x23, 0x93, 0x50, 0x0C, 0x79, 0x18,
+   0x47, 0xE1, 0xCB, 0xA7, 0xB6, 0x85, 0xE6, 0x61,
+   0x2D, 0xD8, 0x9F, 0x80, 0xE9, 0x14, 0x0B, 0x1C,
+   0x40, 0x76, 0x2A, 0x25, 0x0E, 0x99, 0xAC, 0xC4,
+   0xEB, 0x29, 0x41, 0x8A, 0x73, 0x06, 0x57, 0xC6,
+   0x8D, 0xFA, 0x5A, 0xCD, 0x67, 0xB2, 0xD9, 0x0A,
+   0x1E, 0xEF, 0x3E, 0xA0, 0x45, 0x03, 0x27, 0xF1,
+   0x38, 0x54, 0xC1, 0x7A, 0xFE, 0x52, 0x75, 0xD4,
+   0x74, 0x7C, 0xD2, 0x68, 0xEA, 0x4C, 0x97, 0xF9,
+   0xF5, 0x8B, 0x0F, 0x84, 0xA8, 0x6E, 0x9E, 0x11,
+   0x6B, 0xBC, 0x4B, 0x6C, 0x9A, 0xF0, 0xA3, 0x1F,
+   0x92, 0x19, 0xA2, 0x3A, 0x15, 0x04, 0xC5, 0x62,
+   0xD5, 0x96, 0x90, 0x32, 0xAA, 0xD6, 0xCF, 0x35,
+   0xB4, 0x81, 0x2E, 0x01, 0x10, 0x49, 0x70, 0xDE,
+   0xDD, 0x88, 0xB9, 0x6D, 0x60, 0xBB, 0x44, 0xF8,
+   0x3C, 0xEC, 0x34, 0x82, 0x95, 0x72, 0x58, 0x4E,
+   0xE4, 0x0D, 0xBE, 0xDA, 0x83, 0x4A, 0x00, 0xBF,
+   0xD0, 0xC8, 0x26, 0xB3, 0x65, 0x1A, 0x69, 0xCA,
+   0xF3, 0xD7, 0x6F, 0x55, 0xE2, 0xFF, 0x5D, 0xDC,
+   0x20, 0xF6, 0x63, 0xED, 0xE0, 0x59, 0x9D, 0xB1,
+   0x1D, 0xAD, 0x91, 0xA1, 0xB7, 0xA9, 0xDF, 0xC0,
+   0x39, 0xD1, 0x43, 0xCE, 0x4F, 0x5C, 0xE7, 0x37,
+   0x5E, 0x33, 0x5B, 0xA6, 0xC2, 0xB0, 0xBA, 0x30,
+   0x6A, 0x78, 0xB5, 0x71, 0x56, 0x87, 0x7F, 0x86 };
+
+unsigned char table_83[32] = {
+   0x1B, 0x0A, 0x1F, 0x01, 0x10, 0x08, 0x0E, 0x18,
+   0x06, 0x04, 0x00, 0x1C, 0x0C, 0x19, 0x0D, 0x16,
+   0x02, 0x03, 0x09, 0x07, 0x13, 0x0F, 0x05, 0x12,
+   0x17, 0x1E, 0x1A, 0x1D, 0x0B, 0x11, 0x14, 0x15 };
+
+unsigned char table_84[32] = {
+   0x02, 0x1A, 0x0D, 0x15, 0x01, 0x16, 0x1E, 0x00,
+   0x08, 0x1B, 0x04, 0x10, 0x1C, 0x18, 0x19, 0x14,
+   0x0C, 0x11, 0x0B, 0x0E, 0x03, 0x0A, 0x07, 0x12,
+   0x1D, 0x17, 0x13, 0x06, 0x0F, 0x05, 0x09, 0x1F };
+
+unsigned char table_85[256] = {
+   0xC6, 0x7C, 0xCE, 0xBD, 0x84, 0x3E, 0x0B, 0xD8,
+   0xFE, 0xCC, 0x46, 0x50, 0xD1, 0xFB, 0xA0, 0x6D,
+   0xEA, 0xE2, 0x40, 0x51, 0x13, 0xB0, 0xD6, 0xB1,
+   0xA8, 0xDF, 0x61, 0xA4, 0x80, 0x21, 0xB3, 0x33,
+   0x06, 0x6B, 0xE3, 0x8C, 0xA1, 0x18, 0xBA, 0x03,
+   0xD7, 0x8D, 0x54, 0x12, 0x4C, 0xEE, 0x9E, 0xCF,
+   0x04, 0x2A, 0x08, 0xBB, 0xC2, 0xD4, 0xC3, 0x4A,
+   0xD5, 0xFA, 0x36, 0x2F, 0x14, 0x3F, 0xED, 0x05,
+   0x17, 0x28, 0x75, 0xFC, 0xA2, 0x1F, 0x4B, 0x6F,
+   0x91, 0x7E, 0x4E, 0x96, 0x3B, 0xF3, 0x1D, 0x78,
+   0xEB, 0x68, 0xF1, 0xA7, 0x9F, 0xC7, 0x59, 0x6C,
+   0x92, 0xE6, 0x66, 0x07, 0x8A, 0x25, 0x26, 0x72,
+   0x30, 0x5A, 0x81, 0x2C, 0x58, 0x32, 0xCB, 0xE0,
+   0xF9, 0x48, 0x83, 0x9B, 0xA5, 0xE1, 0xA6, 0x64,
+   0xFF, 0xC9, 0x8F, 0x53, 0x3D, 0x24, 0xC8, 0xDE,
+   0x02, 0x7D, 0x09, 0xB4, 0x0A, 0x95, 0x0F, 0xE4,
+   0xDB, 0xB7, 0x71, 0x4D, 0x1C, 0xAC, 0x35, 0xCD,
+   0x29, 0xDD, 0xC1, 0xF2, 0xF4, 0xC0, 0x5C, 0x74,
+   0xDC, 0x87, 0xFD, 0x4F, 0x11, 0x0E, 0x5D, 0x3C,
+   0x01, 0x73, 0xE9, 0xD9, 0x10, 0x9A, 0x5B, 0xC5,
+   0x98, 0x34, 0x15, 0xAE, 0xF7, 0xAA, 0x67, 0x23,
+   0xBC, 0x8B, 0x7B, 0x65, 0xA9, 0xB6, 0x77, 0x00,
+   0x19, 0x0C, 0x5E, 0x99, 0xF0, 0x55, 0x86, 0x97,
+   0x69, 0xDA, 0x38, 0x9C, 0x16, 0xE8, 0x27, 0xAF,
+   0x2E, 0x47, 0x6A, 0xD0, 0x79, 0x44, 0x45, 0x2B,
+   0x5F, 0x85, 0xF5, 0x62, 0x70, 0x22, 0x7F, 0xF6,
+   0x88, 0x93, 0x60, 0x42, 0x3A, 0x39, 0x49, 0x6E,
+   0x89, 0x52, 0x20, 0xF8, 0xCA, 0xD2, 0x76, 0xB9,
+   0xAB, 0x7A, 0x9D, 0xD3, 0xBE, 0x1A, 0xAD, 0x41,
+   0x56, 0x31, 0x90, 0xB5, 0xB2, 0xEC, 0xA3, 0xE5,
+   0x8E, 0x1B, 0xEF, 0xBF, 0x94, 0xC4, 0x0D, 0xB8,
+   0x2D, 0x57, 0xE7, 0x82, 0x1E, 0x37, 0x63, 0x43 };
+
+unsigned char table_86[32] = {
+   0x11, 0x07, 0x0F, 0x0A, 0x19, 0x1D, 0x0B, 0x09,
+   0x1C, 0x1E, 0x14, 0x06, 0x0C, 0x16, 0x13, 0x04,
+   0x15, 0x18, 0x00, 0x0D, 0x12, 0x05, 0x08, 0x02,
+   0x10, 0x1A, 0x1F, 0x01, 0x17, 0x0E, 0x03, 0x1B };
+
+unsigned char table_87[32] = {
+   0x17, 0x0E, 0x1D, 0x13, 0x0B, 0x19, 0x03, 0x06,
+   0x09, 0x01, 0x0D, 0x15, 0x1C, 0x16, 0x18, 0x1B,
+   0x11, 0x10, 0x00, 0x1E, 0x1F, 0x08, 0x12, 0x0F,
+   0x02, 0x04, 0x07, 0x1A, 0x14, 0x0A, 0x0C, 0x05 };
+
+unsigned char table_88[32] = {
+   0x09, 0x08, 0x17, 0x10, 0x0A, 0x07, 0x1C, 0x1F,
+   0x04, 0x0E, 0x01, 0x0C, 0x0D, 0x1B, 0x03, 0x15,
+   0x02, 0x1E, 0x18, 0x19, 0x0F, 0x06, 0x1A, 0x0B,
+   0x05, 0x11, 0x14, 0x00, 0x16, 0x1D, 0x12, 0x13 };
+
+unsigned char table_89[32] = {
+   0x15, 0x1C, 0x1D, 0x14, 0x0F, 0x1A, 0x05, 0x02,
+   0x07, 0x09, 0x06, 0x08, 0x1F, 0x00, 0x10, 0x13,
+   0x0D, 0x03, 0x0C, 0x18, 0x0E, 0x16, 0x1B, 0x1E,
+   0x12, 0x04, 0x11, 0x0A, 0x01, 0x0B, 0x17, 0x19 };
+
+unsigned char table_90[256] = {
+   0x62, 0x36, 0x64, 0x0E, 0x4C, 0x6C, 0xBE, 0xCF,
+   0x25, 0x5A, 0x3D, 0x12, 0x54, 0x9F, 0xE7, 0xA5,
+   0xDE, 0xD7, 0xB2, 0x60, 0x18, 0x8D, 0x89, 0x70,
+   0x48, 0x66, 0x1C, 0xA6, 0x17, 0x9B, 0xDF, 0x9A,
+   0x82, 0xB9, 0x2E, 0xFA, 0x83, 0x5B, 0x7A, 0x61,
+   0xFC, 0x6B, 0x8B, 0x4E, 0x0F, 0xAD, 0x78, 0xE1,
+   0xE8, 0x15, 0x1A, 0xF7, 0xA3, 0x3A, 0x04, 0xE3,
+   0x30, 0x8C, 0x06, 0xC4, 0x05, 0x32, 0x1F, 0x6A,
+   0xB8, 0x37, 0x58, 0xF5, 0x74, 0x63, 0xD4, 0xAC,
+   0xA4, 0xF3, 0xEC, 0xBB, 0x8E, 0x65, 0xA0, 0xEE,
+   0x6D, 0x11, 0xDD, 0xEA, 0x68, 0x2B, 0xDA, 0x0B,
+   0xEF, 0xC3, 0x8F, 0x03, 0x77, 0x1B, 0xFB, 0x1E,
+   0x5C, 0xD9, 0xCB, 0x33, 0x55, 0xF1, 0xA1, 0xF9,
+   0x7C, 0x38, 0x95, 0x00, 0x6E, 0x85, 0xC2, 0x7F,
+   0xBF, 0x84, 0x2A, 0x13, 0x72, 0x81, 0xE9, 0x59,
+   0x41, 0x69, 0x3B, 0x0C, 0x90, 0xB4, 0x51, 0x2F,
+   0xA2, 0xFE, 0xF8, 0x49, 0x57, 0xE5, 0x96, 0xFF,
+   0xCD, 0xD5, 0xCE, 0xAA, 0x40, 0xB0, 0x4D, 0xBA,
+   0xDB, 0xC7, 0x46, 0x86, 0xD1, 0xCA, 0xC0, 0x67,
+   0x9C, 0x21, 0xAE, 0xB3, 0x7B, 0x87, 0xE2, 0x71,
+   0xE6, 0x39, 0xA8, 0x22, 0x07, 0x2C, 0x44, 0x52,
+   0xA7, 0xF0, 0x4A, 0x92, 0x56, 0x28, 0x43, 0x8A,
+   0x5E, 0x53, 0x93, 0x47, 0x97, 0x88, 0x76, 0x79,
+   0x91, 0x26, 0xC1, 0x3F, 0xB7, 0xF6, 0x3E, 0x80,
+   0xA9, 0xC6, 0x01, 0xD2, 0xEB, 0x9E, 0x4B, 0xBC,
+   0xC8, 0xB5, 0x02, 0x5F, 0x98, 0x9D, 0x5D, 0x35,
+   0xD0, 0x16, 0xB1, 0x23, 0x7D, 0xAF, 0x10, 0x3C,
+   0xAB, 0x14, 0x09, 0x2D, 0x0D, 0xC5, 0x1D, 0xD6,
+   0x42, 0xF2, 0x34, 0x73, 0xF4, 0xFD, 0xE0, 0x24,
+   0x6F, 0xD3, 0x75, 0xD8, 0xCC, 0xB6, 0x99, 0x4F,
+   0x29, 0x0A, 0x08, 0xE4, 0x27, 0x19, 0x31, 0xC9,
+   0x20, 0x94, 0x45, 0xED, 0xDC, 0xBD, 0x7E, 0x50 };
+
+unsigned char table_91[32] = {
+   0x03, 0x04, 0x0C, 0x18, 0x10, 0x0D, 0x13, 0x1B,
+   0x1F, 0x07, 0x11, 0x17, 0x1C, 0x1D, 0x05, 0x06,
+   0x0A, 0x12, 0x02, 0x1A, 0x0B, 0x01, 0x0E, 0x08,
+   0x14, 0x16, 0x00, 0x15, 0x19, 0x09, 0x0F, 0x1E };
+
+unsigned char table_92[32] = {
+   0x1E, 0x10, 0x01, 0x07, 0x11, 0x16, 0x15, 0x17,
+   0x1F, 0x14, 0x0C, 0x1C, 0x06, 0x03, 0x00, 0x18,
+   0x08, 0x0E, 0x02, 0x1B, 0x09, 0x0D, 0x19, 0x05,
+   0x0F, 0x12, 0x0B, 0x13, 0x0A, 0x04, 0x1D, 0x1A };
+
+unsigned char table_93[256] = {
+   0x76, 0x78, 0xA2, 0x94, 0x0E, 0x7F, 0xDF, 0xC1,
+   0xB9, 0xE1, 0x3D, 0x59, 0x6F, 0x1E, 0x53, 0x99,
+   0x80, 0xE3, 0x21, 0xF8, 0x65, 0xB8, 0x08, 0xBC,
+   0x29, 0x17, 0xFD, 0x33, 0x35, 0xF2, 0x70, 0xC7,
+   0x25, 0xD0, 0xCD, 0x7A, 0xB7, 0x9B, 0xA5, 0xC3,
+   0x00, 0x90, 0xDC, 0xB1, 0x0C, 0x20, 0x67, 0x8D,
+   0x43, 0x49, 0xF3, 0x96, 0x14, 0x1A, 0xC8, 0x19,
+   0x72, 0xD7, 0x8A, 0x38, 0x66, 0xDA, 0xDD, 0x2E,
+   0xBE, 0xD5, 0x91, 0x7C, 0x3A, 0x92, 0x8E, 0xE7,
+   0x51, 0xB5, 0xA8, 0xD9, 0x0B, 0x2A, 0xBA, 0x81,
+   0x41, 0x0F, 0xBD, 0x4E, 0x31, 0x23, 0x9C, 0x8B,
+   0x2B, 0x1D, 0x04, 0x3E, 0x8C, 0xF0, 0x45, 0xA0,
+   0x1C, 0x44, 0x55, 0x5E, 0xF1, 0x98, 0x54, 0x5D,
+   0x9D, 0x84, 0xAE, 0x09, 0xA9, 0xC5, 0x83, 0x60,
+   0x86, 0x95, 0xB4, 0xFA, 0x6B, 0xA7, 0x9A, 0xCA,
+   0x8F, 0x4F, 0x0A, 0x7B, 0xB0, 0x02, 0xEA, 0xA4,
+   0x18, 0xDB, 0xD3, 0x64, 0xEB, 0xFC, 0xC4, 0xC9,
+   0xF5, 0xD6, 0xCC, 0x75, 0x0D, 0x5C, 0x93, 0x4A,
+   0x6D, 0xC0, 0x1F, 0x50, 0xE6, 0x16, 0xEE, 0x07,
+   0xFB, 0x74, 0x56, 0x58, 0x52, 0x89, 0x79, 0x68,
+   0xB6, 0xFE, 0x01, 0xD4, 0x7E, 0x06, 0xBF, 0xCB,
+   0x5B, 0xC2, 0xC6, 0x32, 0xAC, 0x26, 0x22, 0xD2,
+   0x82, 0x46, 0x69, 0x15, 0x2C, 0xF7, 0xAD, 0x13,
+   0x4D, 0xA3, 0xF6, 0x2D, 0x48, 0x71, 0x57, 0x11,
+   0x63, 0x05, 0x5F, 0x9E, 0x4B, 0xAB, 0xA6, 0x61,
+   0xBB, 0xA1, 0x3C, 0x97, 0xF9, 0x03, 0x40, 0x12,
+   0xCF, 0x37, 0xE4, 0x10, 0x6A, 0xED, 0xFF, 0x62,
+   0x42, 0x4C, 0xAF, 0x9F, 0xE5, 0xE8, 0xD8, 0xD1,
+   0x28, 0x3F, 0x1B, 0xE9, 0xCE, 0x6C, 0x27, 0x88,
+   0xEF, 0x2F, 0xE0, 0x30, 0x87, 0x5A, 0x73, 0xB3,
+   0x6E, 0x3B, 0x7D, 0x77, 0x36, 0xAA, 0x39, 0xDE,
+   0x24, 0x34, 0xE2, 0xEC, 0x85, 0x47, 0xF4, 0xB2 };
+
+unsigned char table_94[32] = {
+   0x1C, 0x07, 0x05, 0x1A, 0x10, 0x1D, 0x14, 0x12,
+   0x08, 0x0F, 0x0C, 0x01, 0x04, 0x1B, 0x16, 0x0A,
+   0x11, 0x02, 0x1F, 0x13, 0x0D, 0x1E, 0x17, 0x06,
+   0x0E, 0x09, 0x15, 0x19, 0x03, 0x18, 0x00, 0x0B };
+
+unsigned char table_95[32] = {
+   0x12, 0x10, 0x11, 0x15, 0x03, 0x0A, 0x14, 0x05,
+   0x1D, 0x07, 0x17, 0x0D, 0x09, 0x08, 0x1B, 0x1F,
+   0x0B, 0x06, 0x19, 0x0E, 0x18, 0x04, 0x00, 0x02,
+   0x1E, 0x1C, 0x01, 0x0C, 0x1A, 0x0F, 0x13, 0x16 };
+
+unsigned char table_96[256] = {
+   0x1C, 0x6E, 0xCD, 0xB4, 0xB3, 0x93, 0xA8, 0x2E,
+   0x4F, 0x09, 0xE3, 0x72, 0x64, 0x13, 0x21, 0xF5,
+   0x89, 0xB2, 0xD2, 0x22, 0x5D, 0x63, 0x90, 0xC4,
+   0x42, 0x9B, 0x07, 0xCA, 0x16, 0x19, 0x5C, 0x2B,
+   0x3D, 0xA0, 0x69, 0x5F, 0x52, 0x41, 0x66, 0xC0,
+   0x55, 0xDA, 0x82, 0x40, 0x25, 0x02, 0x3C, 0xDD,
+   0xAE, 0xD7, 0xD6, 0xDB, 0x04, 0x78, 0x05, 0x4A,
+   0x4C, 0x81, 0x00, 0xBE, 0x45, 0xC5, 0x30, 0xB0,
+   0x65, 0x5A, 0xA9, 0x38, 0x75, 0x26, 0x85, 0x4E,
+   0xF0, 0xA2, 0x91, 0x8A, 0x54, 0xD0, 0x3E, 0x0D,
+   0xFE, 0xF2, 0x0A, 0x23, 0x24, 0x37, 0x32, 0x0B,
+   0xCB, 0xB5, 0x28, 0x6A, 0x95, 0x49, 0x53, 0x9A,
+   0xEE, 0x2C, 0x9D, 0xD4, 0x1D, 0x46, 0xC9, 0x79,
+   0xCC, 0xDF, 0x17, 0xE8, 0x6D, 0x29, 0x0E, 0x80,
+   0xE0, 0x62, 0xA1, 0xFA, 0x10, 0xF6, 0x03, 0xC1,
+   0x15, 0x14, 0x1F, 0x99, 0x97, 0xD5, 0x9E, 0x3F,
+   0x7B, 0x2F, 0xEF, 0x2A, 0x68, 0x83, 0xE2, 0x1B,
+   0xC8, 0x87, 0x12, 0x70, 0xC7, 0x36, 0xD3, 0x73,
+   0x8B, 0x7D, 0x47, 0x9F, 0xD9, 0xFB, 0x6C, 0x5B,
+   0xFC, 0xAA, 0xB9, 0xB1, 0x0C, 0x31, 0x8E, 0xF3,
+   0x92, 0xA3, 0x4B, 0xF1, 0xC2, 0x3A, 0x67, 0xEA,
+   0x77, 0x11, 0xB6, 0xE4, 0x1A, 0x33, 0xD1, 0xBA,
+   0xF9, 0xAC, 0x43, 0xE5, 0xC3, 0xC6, 0xFD, 0xF4,
+   0x44, 0x6F, 0xB7, 0x88, 0xA7, 0xF8, 0x34, 0x94,
+   0x6B, 0x27, 0xDE, 0x1E, 0xDC, 0x01, 0x61, 0x50,
+   0xAD, 0x74, 0x4D, 0x86, 0xF7, 0x8D, 0x9C, 0x0F,
+   0x5E, 0xBD, 0x08, 0x84, 0x18, 0xED, 0xA5, 0x39,
+   0xAB, 0x98, 0x48, 0xE6, 0x2D, 0x96, 0xCF, 0x7F,
+   0xFF, 0xBB, 0x8F, 0xEC, 0xBF, 0xE7, 0x56, 0xA4,
+   0x35, 0x76, 0xA6, 0xAF, 0xBC, 0x71, 0xE9, 0xB8,
+   0x7E, 0x7C, 0x06, 0x3B, 0xEB, 0x60, 0x7A, 0x8C,
+   0x59, 0xCE, 0xE1, 0x57, 0x20, 0x58, 0x51, 0xD8 };
+
+unsigned char table_97[256] = {
+   0x15, 0x2D, 0xAF, 0x36, 0xCF, 0xD3, 0xD0, 0xED,
+   0xB2, 0x1B, 0xFE, 0x92, 0xBD, 0xAD, 0x58, 0x0F,
+   0x76, 0x3C, 0x47, 0x03, 0x2E, 0x4C, 0x40, 0xF7,
+   0x39, 0xA7, 0x72, 0x22, 0x95, 0xF3, 0x8C, 0xE0,
+   0x79, 0xB6, 0x75, 0x82, 0x94, 0x8F, 0x44, 0xFC,
+   0xB0, 0x05, 0xE9, 0x10, 0x68, 0xE7, 0xF1, 0xA5,
+   0xA8, 0xE2, 0x6F, 0xBE, 0xE5, 0x54, 0xA2, 0xC6,
+   0xDB, 0x1C, 0x9E, 0x6D, 0x14, 0xA1, 0x26, 0x34,
+   0x1E, 0x1A, 0x06, 0x53, 0xEE, 0x67, 0xA9, 0x73,
+   0xD5, 0x59, 0x2F, 0x61, 0xE6, 0x74, 0xD6, 0x97,
+   0xC0, 0x0C, 0xB1, 0x6E, 0x6C, 0x33, 0xC8, 0x77,
+   0x8B, 0x49, 0x43, 0xE3, 0xB5, 0xDE, 0x6A, 0xA0,
+   0x78, 0x2A, 0xC9, 0xF9, 0x9A, 0xDC, 0x90, 0x55,
+   0xF4, 0x16, 0x5E, 0x3F, 0xC5, 0x7C, 0xFA, 0x09,
+   0x8E, 0x87, 0xF2, 0x9D, 0x70, 0x27, 0x9B, 0xC4,
+   0xCD, 0x91, 0x4B, 0xB4, 0x18, 0xE1, 0x3D, 0x5D,
+   0x7A, 0xEA, 0xF0, 0x65, 0xB9, 0xF6, 0xC3, 0x66,
+   0x21, 0x96, 0xD1, 0xB8, 0x56, 0x62, 0x48, 0x28,
+   0x3A, 0x86, 0x63, 0xD4, 0xD7, 0x41, 0x8D, 0x20,
+   0xC2, 0x98, 0x37, 0xD8, 0x85, 0x42, 0x0D, 0x31,
+   0x84, 0x4E, 0x11, 0x46, 0x2B, 0x19, 0xCC, 0xB7,
+   0x69, 0x13, 0x6B, 0x29, 0x38, 0x7E, 0x0E, 0xD2,
+   0x3B, 0x60, 0x89, 0x7F, 0xEF, 0x07, 0x08, 0xCA,
+   0xBF, 0x3E, 0xA3, 0xAA, 0x52, 0x4A, 0x45, 0x00,
+   0xC7, 0xF8, 0x57, 0xEB, 0x93, 0x9C, 0x4D, 0x7B,
+   0x2C, 0xBB, 0xFB, 0xFF, 0x35, 0x4F, 0x32, 0xA6,
+   0x23, 0x8A, 0xDD, 0x12, 0xA4, 0x81, 0x17, 0x1D,
+   0x1F, 0xCB, 0x0A, 0x71, 0x02, 0xAC, 0xDF, 0x24,
+   0xAB, 0x7D, 0x30, 0x5C, 0x01, 0x5A, 0xBA, 0xEC,
+   0x51, 0xF5, 0x0B, 0x64, 0xCE, 0xAE, 0x5B, 0x50,
+   0x80, 0x88, 0xE8, 0x5F, 0x04, 0xDA, 0xE4, 0xBC,
+   0x83, 0x25, 0x9F, 0xD9, 0x99, 0xC1, 0xFD, 0xB3 };
+
+unsigned char table_98[256] = {
+   0xC8, 0xE6, 0x38, 0x93, 0xE5, 0x03, 0x18, 0x1F,
+   0xE9, 0x5A, 0xB6, 0xAF, 0xC3, 0x95, 0x00, 0x51,
+   0xC0, 0xFD, 0x32, 0xE8, 0x96, 0x57, 0xF0, 0xAA,
+   0xDC, 0x71, 0xF8, 0x01, 0x40, 0x0A, 0x4F, 0xB0,
+   0x1B, 0x9D, 0x16, 0x92, 0xF3, 0x5E, 0xA9, 0x3C,
+   0xBE, 0x6A, 0xA7, 0xE3, 0x35, 0x0D, 0xAD, 0xDB,
+   0x48, 0xE0, 0x7E, 0xC6, 0xB4, 0x6D, 0x17, 0x41,
+   0x3E, 0xE2, 0x87, 0x12, 0xE1, 0x53, 0xD9, 0x8A,
+   0xAC, 0xA6, 0xD8, 0xFA, 0x36, 0x0B, 0x06, 0xDF,
+   0x6C, 0x4E, 0xA4, 0xBC, 0xC9, 0xEE, 0x44, 0x26,
+   0xF2, 0xE4, 0x9E, 0x34, 0xEF, 0x05, 0x0F, 0x7F,
+   0xD1, 0xCD, 0x67, 0x28, 0xC1, 0x8E, 0x7D, 0x90,
+   0x8F, 0x60, 0x1E, 0x19, 0xBD, 0x77, 0xB8, 0xD5,
+   0x3D, 0x8C, 0x31, 0x99, 0x08, 0xDD, 0x04, 0x30,
+   0x61, 0xFB, 0xEB, 0x98, 0x15, 0xFC, 0x10, 0xDE,
+   0x20, 0xBA, 0xA1, 0xB3, 0xD4, 0x91, 0x6F, 0x9F,
+   0x94, 0x5B, 0x42, 0xCB, 0x75, 0x1C, 0xBB, 0x5C,
+   0x5D, 0xD6, 0x66, 0x50, 0xB9, 0xF1, 0x82, 0x7B,
+   0x33, 0x23, 0x4A, 0xA5, 0x55, 0x97, 0xEA, 0x37,
+   0xF4, 0x64, 0x6E, 0xBF, 0x8B, 0xB1, 0x07, 0x9A,
+   0x43, 0x11, 0x65, 0xC2, 0x02, 0xDA, 0x9B, 0x25,
+   0xCA, 0x3B, 0x7A, 0xCE, 0xA8, 0xCF, 0xF7, 0x56,
+   0x6B, 0xF9, 0x47, 0x2A, 0x2E, 0x1D, 0x2D, 0xE7,
+   0x46, 0xD0, 0x62, 0x4C, 0x80, 0x4B, 0x2B, 0xF5,
+   0x69, 0x9C, 0x45, 0xED, 0x83, 0xAB, 0x74, 0x39,
+   0xA3, 0x85, 0xD7, 0x5F, 0xB2, 0x86, 0x22, 0x29,
+   0x89, 0x49, 0x1A, 0xC4, 0x52, 0xEC, 0x8D, 0x73,
+   0xD3, 0x7C, 0x79, 0xD2, 0x14, 0x4D, 0x84, 0xA2,
+   0x0E, 0x70, 0x78, 0x72, 0xB7, 0xA0, 0xC5, 0x81,
+   0x58, 0x0C, 0x68, 0x27, 0xFF, 0xF6, 0xAE, 0xCC,
+   0x88, 0xFE, 0x24, 0x2F, 0x76, 0x3F, 0x59, 0x21,
+   0x54, 0x3A, 0x13, 0x09, 0x2C, 0xB5, 0xC7, 0x63 };
+
+unsigned char table_99[32] = {
+   0x19, 0x00, 0x10, 0x18, 0x09, 0x11, 0x13, 0x1D,
+   0x08, 0x1A, 0x02, 0x05, 0x03, 0x17, 0x12, 0x01,
+   0x1F, 0x14, 0x06, 0x07, 0x15, 0x0D, 0x0F, 0x0B,
+   0x0E, 0x16, 0x1E, 0x04, 0x1B, 0x0A, 0x0C, 0x1C };
+
+unsigned char table_100[256] = {
+   0x9B, 0x3A, 0xAE, 0x60, 0x27, 0x67, 0x1E, 0x4E,
+   0x91, 0xDA, 0x85, 0x43, 0x5C, 0xCC, 0x89, 0x55,
+   0x75, 0x56, 0xF2, 0x86, 0xEB, 0xC4, 0x0D, 0xE6,
+   0x63, 0x88, 0x38, 0x59, 0x68, 0xD0, 0x18, 0xF0,
+   0xBA, 0x28, 0xF5, 0x80, 0x02, 0x5B, 0xE1, 0xA4,
+   0x7A, 0x4B, 0x8E, 0xF7, 0x9E, 0x99, 0x70, 0xEF,
+   0x66, 0x50, 0xB1, 0xCD, 0x9A, 0xAF, 0x5F, 0x21,
+   0xE5, 0x5D, 0x14, 0xD4, 0x34, 0x22, 0xC3, 0x0F,
+   0x44, 0xB6, 0x92, 0xCE, 0xB4, 0x6E, 0xB0, 0x00,
+   0xF9, 0xB5, 0x10, 0xEA, 0x45, 0x2F, 0x2B, 0xF4,
+   0xF6, 0xFE, 0xCB, 0x0A, 0x42, 0xF8, 0xE7, 0xFD,
+   0xC8, 0xC2, 0x6C, 0x9C, 0x57, 0xA1, 0x46, 0x04,
+   0xE9, 0x97, 0x40, 0x32, 0x19, 0xFA, 0x51, 0xD1,
+   0x6D, 0x4C, 0x2A, 0xD9, 0x95, 0x26, 0x72, 0x1B,
+   0x83, 0x93, 0x5A, 0x15, 0x33, 0xC5, 0x77, 0x13,
+   0xE0, 0x36, 0x37, 0xDB, 0xA7, 0xC7, 0x81, 0x62,
+   0xC1, 0x47, 0x64, 0x74, 0x1D, 0x84, 0x29, 0x39,
+   0x41, 0x35, 0x09, 0x90, 0x20, 0x9F, 0x8C, 0x7D,
+   0x3E, 0x07, 0xB9, 0x76, 0x06, 0xA3, 0x31, 0x7F,
+   0x49, 0x6F, 0x3D, 0xD5, 0x25, 0xAC, 0xDF, 0x0B,
+   0x3C, 0x79, 0x01, 0x8F, 0x82, 0x2E, 0xFC, 0x98,
+   0xA5, 0x58, 0xA0, 0x4A, 0x7C, 0x24, 0xDD, 0x05,
+   0x4D, 0x12, 0xBC, 0xAA, 0xE2, 0xAB, 0xD3, 0xBF,
+   0x94, 0x2D, 0x54, 0xBB, 0xAD, 0xB7, 0x6A, 0xE3,
+   0xBD, 0x5E, 0x8D, 0x08, 0x3B, 0xB8, 0x73, 0x8A,
+   0x16, 0xD2, 0x69, 0xE8, 0xEE, 0x53, 0xD8, 0xDC,
+   0x48, 0xCF, 0xC6, 0xA9, 0x1A, 0xCA, 0x17, 0x11,
+   0xED, 0xC0, 0xA6, 0x1F, 0x96, 0x8B, 0xFF, 0x78,
+   0x03, 0x61, 0x1C, 0xA8, 0x3F, 0x9D, 0x0E, 0xC9,
+   0xE4, 0xA2, 0x52, 0xEC, 0x4F, 0xD6, 0xF3, 0x6B,
+   0x87, 0xB3, 0x7E, 0xDE, 0xD7, 0x71, 0x65, 0xF1,
+   0x30, 0x0C, 0xB2, 0x7B, 0xBE, 0xFB, 0x23, 0x2C };
+
+unsigned char table_101[32] = {
+   0x18, 0x08, 0x14, 0x17, 0x03, 0x10, 0x19, 0x04,
+   0x0D, 0x1C, 0x06, 0x1D, 0x1E, 0x12, 0x11, 0x0B,
+   0x0F, 0x02, 0x0E, 0x1B, 0x13, 0x05, 0x07, 0x16,
+   0x15, 0x0A, 0x0C, 0x1A, 0x00, 0x01, 0x1F, 0x09 };
+
+unsigned char table_102[32] = {
+   0x17, 0x1F, 0x0E, 0x05, 0x13, 0x0C, 0x14, 0x1A,
+   0x0F, 0x01, 0x12, 0x1C, 0x00, 0x07, 0x0D, 0x02,
+   0x10, 0x16, 0x04, 0x11, 0x1D, 0x03, 0x1E, 0x18,
+   0x06, 0x15, 0x0A, 0x19, 0x09, 0x08, 0x1B, 0x0B };
+
+unsigned char table_103[32] = {
+   0x0F, 0x09, 0x1E, 0x11, 0x0D, 0x08, 0x10, 0x00,
+   0x01, 0x1F, 0x1D, 0x1C, 0x12, 0x04, 0x07, 0x05,
+   0x19, 0x14, 0x1B, 0x02, 0x1A, 0x15, 0x17, 0x16,
+   0x18, 0x0B, 0x0A, 0x13, 0x0C, 0x0E, 0x03, 0x06 };
+
+unsigned char table_104[256] = {
+   0xA4, 0x9F, 0x78, 0x39, 0x3D, 0x81, 0x51, 0x24,
+   0x46, 0x2A, 0x56, 0xE8, 0xDF, 0x73, 0xA8, 0xA2,
+   0x0D, 0xDC, 0xA5, 0x4F, 0xF0, 0x93, 0xC0, 0x76,
+   0x38, 0x70, 0xB0, 0x30, 0x98, 0x13, 0x8B, 0x14,
+   0x26, 0x45, 0x0F, 0x7D, 0x34, 0x72, 0x6B, 0x89,
+   0x43, 0xE2, 0x96, 0x5B, 0xEF, 0x2B, 0xF9, 0xDE,
+   0x82, 0xB5, 0x61, 0x4A, 0x17, 0xC2, 0x5A, 0xCB,
+   0xB2, 0x8D, 0xE4, 0xEC, 0xD9, 0x80, 0xBC, 0x62,
+   0x67, 0x11, 0xA9, 0x3A, 0xE1, 0xC4, 0xEA, 0xD2,
+   0x71, 0xD0, 0xDB, 0xE5, 0x7B, 0x08, 0x77, 0xD6,
+   0x10, 0x19, 0x48, 0xEB, 0xAA, 0x2C, 0x0C, 0x59,
+   0xBE, 0xF6, 0x28, 0x50, 0x90, 0x87, 0xCD, 0x04,
+   0x1F, 0x79, 0x99, 0x5C, 0x49, 0x06, 0x8A, 0x3E,
+   0x5F, 0x5E, 0x15, 0x23, 0x2D, 0xB6, 0xA6, 0x7A,
+   0x03, 0x20, 0xDA, 0xFB, 0x35, 0x75, 0xC7, 0x47,
+   0xB9, 0x7C, 0xA1, 0xCE, 0xC5, 0xDD, 0xFD, 0x6C,
+   0x05, 0xAC, 0x09, 0xB4, 0x95, 0xD1, 0xB1, 0x63,
+   0xFF, 0xAE, 0xD5, 0x25, 0x1E, 0x6E, 0x57, 0x18,
+   0x74, 0xE6, 0x2F, 0x9A, 0xE7, 0x42, 0x65, 0xF5,
+   0x58, 0x27, 0x33, 0x9C, 0xCF, 0xB7, 0xC3, 0xF1,
+   0x12, 0x1D, 0xB8, 0xF4, 0x64, 0x4D, 0xD4, 0xBD,
+   0xE3, 0xAB, 0x44, 0x60, 0xAF, 0xCC, 0x0A, 0xFC,
+   0xD3, 0x21, 0x0B, 0x1A, 0x6D, 0x83, 0xA7, 0x8E,
+   0x3C, 0xC1, 0xED, 0xF3, 0x2E, 0x86, 0xC9, 0x41,
+   0x02, 0xF7, 0xC8, 0x40, 0x1B, 0xF8, 0xF2, 0x07,
+   0x5D, 0x4E, 0xC6, 0x29, 0xD7, 0x4B, 0x7E, 0x31,
+   0x94, 0x32, 0x01, 0x92, 0xE9, 0x36, 0x0E, 0x7F,
+   0x85, 0x16, 0xFA, 0x00, 0x88, 0x3F, 0x68, 0x4C,
+   0x22, 0x55, 0xBF, 0x9D, 0xE0, 0x6A, 0xAD, 0xBA,
+   0x91, 0xCA, 0xA3, 0x1C, 0xEE, 0xD8, 0x3B, 0x66,
+   0x69, 0x9B, 0x84, 0xA0, 0xB3, 0x6F, 0xFE, 0x52,
+   0x97, 0xBB, 0x37, 0x8C, 0x54, 0x53, 0x9E, 0x8F };
+
+unsigned char table_105[256] = {
+   0x7B, 0x35, 0x11, 0x79, 0x07, 0x2F, 0xF6, 0x82,
+   0x8E, 0xB4, 0x6E, 0xD2, 0x6D, 0xC5, 0x8C, 0x1C,
+   0xE0, 0xD6, 0x34, 0xF0, 0x4F, 0x25, 0x59, 0xE8,
+   0xDF, 0x1D, 0xEB, 0x32, 0x86, 0x51, 0xA4, 0xF2,
+   0x5C, 0xD1, 0xC8, 0x41, 0xEC, 0x9D, 0x62, 0xAC,
+   0xDD, 0x3E, 0xB8, 0x65, 0x75, 0x89, 0x12, 0x6C,
+   0x40, 0x4E, 0xC7, 0x27, 0xE1, 0x37, 0xCF, 0x09,
+   0x16, 0x78, 0xAA, 0x58, 0x0D, 0xE6, 0x54, 0xFE,
+   0x8F, 0xFD, 0xF9, 0x61, 0x26, 0x3F, 0x2E, 0xCD,
+   0x2C, 0x04, 0xB2, 0x80, 0x0F, 0x14, 0x6F, 0xC6,
+   0xAB, 0xFB, 0x13, 0xDB, 0x9A, 0x21, 0xB3, 0xC0,
+   0xA9, 0x19, 0x70, 0xF3, 0x2B, 0xAE, 0x9B, 0x49,
+   0xB7, 0xA8, 0x24, 0x1B, 0x48, 0xEA, 0xED, 0xD9,
+   0x47, 0x9E, 0x9C, 0x69, 0x3C, 0x66, 0xBB, 0x06,
+   0x46, 0x38, 0x17, 0xB5, 0xCB, 0x05, 0x4A, 0x5E,
+   0x15, 0x20, 0xB9, 0xB6, 0x33, 0x4C, 0x7D, 0xA3,
+   0xD7, 0xB1, 0x23, 0x72, 0xC3, 0x4B, 0x63, 0xBE,
+   0xF7, 0x5B, 0x74, 0x64, 0x77, 0xCC, 0xD3, 0x85,
+   0xDE, 0x1A, 0x31, 0x97, 0xA2, 0x8B, 0xFC, 0x10,
+   0x5F, 0xDC, 0xD5, 0xB0, 0xBD, 0x55, 0xC1, 0xE7,
+   0x0C, 0x50, 0x43, 0x39, 0x71, 0x52, 0xE5, 0xAF,
+   0x8A, 0x60, 0x92, 0x2D, 0xD8, 0x03, 0xF5, 0x28,
+   0xCA, 0xEF, 0xD0, 0xC2, 0x53, 0x91, 0xA6, 0x73,
+   0x56, 0xA5, 0xF1, 0x57, 0x42, 0xF4, 0xD4, 0x36,
+   0x8D, 0xBC, 0xE9, 0x7E, 0x02, 0x76, 0x18, 0x0B,
+   0x84, 0x5A, 0xE2, 0xBF, 0x68, 0x95, 0x29, 0x98,
+   0xAD, 0x88, 0x1F, 0x81, 0x67, 0xA1, 0x3A, 0xA7,
+   0x22, 0xF8, 0x01, 0xA0, 0xCE, 0x7A, 0xDA, 0x30,
+   0xC4, 0xE4, 0xEE, 0x7C, 0x3B, 0x4D, 0x3D, 0xE3,
+   0xFA, 0x6A, 0x7F, 0x99, 0x00, 0x93, 0x0E, 0xFF,
+   0x90, 0x0A, 0x2A, 0x5D, 0x96, 0x08, 0x6B, 0x83,
+   0xBA, 0x1E, 0x44, 0x87, 0x45, 0x9F, 0xC9, 0x94 };
+
+unsigned char table_106[32] = {
+   0x03, 0x11, 0x07, 0x1B, 0x0F, 0x14, 0x0C, 0x01,
+   0x04, 0x02, 0x09, 0x0A, 0x05, 0x12, 0x06, 0x1F,
+   0x1C, 0x0E, 0x0D, 0x15, 0x18, 0x08, 0x00, 0x10,
+   0x1E, 0x1D, 0x17, 0x19, 0x13, 0x16, 0x0B, 0x1A };
+
+unsigned char table_107[32] = {
+   0x13, 0x1B, 0x06, 0x11, 0x1C, 0x07, 0x08, 0x0E,
+   0x10, 0x05, 0x09, 0x18, 0x04, 0x15, 0x1E, 0x0F,
+   0x1F, 0x12, 0x02, 0x00, 0x17, 0x19, 0x1A, 0x0D,
+   0x03, 0x0C, 0x0A, 0x1D, 0x14, 0x01, 0x16, 0x0B };
+
+unsigned char table_108[256] = {
+   0x99, 0xA3, 0x48, 0xE8, 0x5A, 0x7D, 0x97, 0xCA,
+   0x7F, 0x06, 0x9B, 0x04, 0xE0, 0xF3, 0x18, 0xAE,
+   0x59, 0xA0, 0x2B, 0x15, 0x85, 0x3E, 0x12, 0x93,
+   0x3D, 0x28, 0x32, 0xF5, 0x20, 0x5D, 0x86, 0x00,
+   0x1B, 0x2E, 0x36, 0x10, 0x5E, 0x6C, 0xD8, 0x29,
+   0xB6, 0x3F, 0x05, 0x1C, 0xCE, 0xC2, 0x34, 0x5F,
+   0x5C, 0x79, 0xD1, 0x1F, 0xA2, 0xEE, 0x8A, 0x69,
+   0xB5, 0x87, 0x96, 0x6D, 0x4D, 0xC1, 0x61, 0x2C,
+   0x11, 0xE7, 0x8E, 0xBF, 0x1E, 0x53, 0xD0, 0x58,
+   0x76, 0xA4, 0x60, 0xA9, 0xB0, 0xF9, 0xEA, 0x3C,
+   0x52, 0x9A, 0x24, 0xF1, 0x9F, 0xD3, 0x40, 0x0A,
+   0x63, 0x78, 0x6A, 0x8B, 0x08, 0x22, 0x16, 0x83,
+   0x6B, 0xD2, 0x49, 0x19, 0xBD, 0xFD, 0x62, 0x72,
+   0xA8, 0x55, 0xAB, 0x0C, 0xB9, 0x13, 0xD5, 0xF0,
+   0xF2, 0x84, 0xAF, 0x2F, 0x7B, 0x2A, 0x21, 0x0F,
+   0xDA, 0x30, 0x71, 0xD6, 0x81, 0xE6, 0xEC, 0x41,
+   0x90, 0x50, 0x66, 0x0E, 0xA7, 0xB8, 0xF7, 0x3A,
+   0xB2, 0xCF, 0x3B, 0xFC, 0x56, 0x6F, 0xC3, 0xA6,
+   0xC9, 0xA1, 0x8D, 0xBB, 0x9D, 0x75, 0xF6, 0xAA,
+   0x7E, 0xF8, 0x33, 0xEF, 0xBC, 0x7C, 0x23, 0x1A,
+   0x92, 0x6E, 0x2D, 0x8F, 0xED, 0xB7, 0xB1, 0x1D,
+   0x67, 0x39, 0xAC, 0x0D, 0x74, 0xDB, 0x7A, 0x94,
+   0x07, 0x09, 0xC0, 0xD7, 0xAD, 0xFE, 0x54, 0x91,
+   0xDE, 0x45, 0xA5, 0x77, 0xCB, 0x37, 0xC6, 0x38,
+   0x89, 0x88, 0x17, 0xD9, 0x4F, 0xDF, 0x25, 0xFB,
+   0xFA, 0x4C, 0x80, 0x35, 0x82, 0xF4, 0x95, 0xC8,
+   0xFF, 0xE9, 0x31, 0x01, 0x14, 0xB3, 0x02, 0x9E,
+   0x4E, 0x43, 0x46, 0xC7, 0xEB, 0x51, 0xE5, 0x47,
+   0xB4, 0xE3, 0xDC, 0x57, 0xC4, 0x98, 0x03, 0xE1,
+   0xBA, 0x68, 0xCD, 0x27, 0xC5, 0x0B, 0xD4, 0x64,
+   0x4B, 0x9C, 0x70, 0x65, 0x4A, 0xE4, 0x42, 0xDD,
+   0xCC, 0xE2, 0x44, 0x73, 0xBE, 0x26, 0x8C, 0x5B };
+
+unsigned char table_109[256] = {
+   0xE3, 0x95, 0xDB, 0x09, 0x82, 0x0A, 0x8F, 0x9E,
+   0xC9, 0xDC, 0x28, 0x35, 0x0F, 0x8B, 0xA8, 0xA5,
+   0x7F, 0x3D, 0x8C, 0xD1, 0x93, 0x57, 0x04, 0xAA,
+   0x6A, 0x98, 0x81, 0xDD, 0x16, 0x67, 0x2E, 0xDF,
+   0xED, 0xF7, 0xB2, 0xBD, 0x14, 0xB6, 0x76, 0xC8,
+   0x75, 0x9F, 0x48, 0xAE, 0xBB, 0xB0, 0xF3, 0xE2,
+   0xD4, 0x59, 0xD8, 0x9C, 0x64, 0xC1, 0x73, 0x21,
+   0x6D, 0x96, 0x7B, 0x62, 0x56, 0x55, 0xCC, 0xFD,
+   0xCE, 0x41, 0xA3, 0x43, 0x33, 0xAF, 0x23, 0x9D,
+   0x6F, 0x65, 0x19, 0x52, 0xAD, 0xC6, 0xD3, 0x3F,
+   0x66, 0xFF, 0xD0, 0x30, 0x6C, 0xC0, 0xEB, 0xCF,
+   0x51, 0x88, 0x38, 0x72, 0x69, 0x77, 0x3B, 0xFA,
+   0xBA, 0xB7, 0xA1, 0x91, 0xE0, 0x89, 0xAB, 0x44,
+   0x1B, 0x05, 0x5B, 0xB9, 0x71, 0x47, 0x7E, 0xFB,
+   0x02, 0xC7, 0x99, 0x6E, 0x42, 0x20, 0x90, 0x1F,
+   0x4A, 0x85, 0x1A, 0xEA, 0x0C, 0x0D, 0xB3, 0xDA,
+   0xE7, 0x13, 0xE6, 0xD7, 0x6B, 0x12, 0x46, 0x53,
+   0xB5, 0xF8, 0x1D, 0x83, 0x54, 0x49, 0x8A, 0x26,
+   0x4D, 0xDE, 0xF6, 0x03, 0xA2, 0x7D, 0x0E, 0xA0,
+   0x68, 0x79, 0xCA, 0x0B, 0x5D, 0x40, 0x4F, 0x80,
+   0xC2, 0xD6, 0x87, 0x70, 0xF0, 0xD2, 0x92, 0xEE,
+   0xBE, 0x74, 0x5F, 0xBC, 0xA4, 0x4B, 0xFE, 0x37,
+   0x60, 0xA9, 0x06, 0xA7, 0xE1, 0xF5, 0x2B, 0x10,
+   0xEF, 0x2C, 0x07, 0x86, 0x7A, 0x27, 0xE9, 0xC5,
+   0xAC, 0x32, 0x22, 0xF2, 0xE5, 0x8D, 0x31, 0x01,
+   0x34, 0xA6, 0xB8, 0xC3, 0x3C, 0xE4, 0x08, 0x94,
+   0x15, 0x4E, 0xB4, 0x39, 0x58, 0x00, 0x3E, 0x29,
+   0x45, 0x3A, 0x84, 0x36, 0xF1, 0x2A, 0x50, 0x11,
+   0xC4, 0x5A, 0xFC, 0xBF, 0xD9, 0xF9, 0x17, 0x9B,
+   0x8E, 0x18, 0x63, 0x4C, 0x2F, 0x78, 0x2D, 0x5E,
+   0x9A, 0xCD, 0x24, 0xEC, 0x7C, 0x97, 0x61, 0xCB,
+   0x1E, 0xF4, 0xD5, 0xB1, 0x5C, 0x25, 0xE8, 0x1C };
+
+unsigned char table_110[256] = {
+   0xC3, 0x06, 0x3C, 0xCB, 0xD2, 0x44, 0x9D, 0x48,
+   0x28, 0xAA, 0xA9, 0xD0, 0x64, 0x25, 0x56, 0xCA,
+   0xC2, 0xF8, 0x5C, 0xAE, 0x4E, 0x63, 0xB2, 0xE9,
+   0x35, 0x11, 0xA8, 0x1A, 0x76, 0x15, 0xE0, 0x26,
+   0x97, 0x99, 0xD4, 0x43, 0x80, 0xEE, 0xC1, 0x69,
+   0xA6, 0x1E, 0x7A, 0x42, 0x55, 0x38, 0xBF, 0x75,
+   0x0E, 0x29, 0xF5, 0xF3, 0x36, 0x7D, 0x51, 0xE8,
+   0xE5, 0xEB, 0x68, 0x60, 0x0C, 0x70, 0xFD, 0xCC,
+   0xE3, 0x23, 0x09, 0x6D, 0x2D, 0x6C, 0x5E, 0xB6,
+   0x98, 0x8B, 0x1F, 0x50, 0x34, 0x8D, 0x10, 0x92,
+   0x82, 0x85, 0xD5, 0x79, 0x02, 0xA4, 0x0A, 0xBC,
+   0x40, 0xC6, 0xA3, 0x72, 0x8F, 0xC4, 0xA5, 0xE4,
+   0x49, 0xD6, 0xCE, 0xA1, 0x12, 0x4F, 0x30, 0x31,
+   0xDE, 0x2A, 0xF7, 0x95, 0xB5, 0x96, 0x14, 0x08,
+   0xE6, 0x3D, 0x86, 0xF2, 0x47, 0x74, 0xB8, 0x5D,
+   0x1D, 0x2B, 0x3A, 0x93, 0x7C, 0x6A, 0x01, 0xA0,
+   0x9A, 0x4D, 0xB7, 0x71, 0xA7, 0x41, 0xC5, 0x65,
+   0xC8, 0x89, 0xD1, 0x3E, 0x0D, 0xD8, 0xFF, 0x6F,
+   0x7F, 0xA2, 0xFE, 0xD9, 0xF0, 0x4A, 0x07, 0x1C,
+   0x0F, 0x6E, 0x03, 0x81, 0x1B, 0x05, 0xDF, 0x52,
+   0xF1, 0x8A, 0xF9, 0xDD, 0x91, 0x3B, 0xD7, 0xE1,
+   0x54, 0xAD, 0x90, 0x5A, 0x7B, 0xC7, 0x32, 0x62,
+   0x16, 0x27, 0xB9, 0x66, 0x21, 0x88, 0xBD, 0x18,
+   0x77, 0x8E, 0x94, 0x8C, 0x9B, 0x46, 0x9C, 0xB1,
+   0xD3, 0x53, 0xB0, 0xBE, 0xAC, 0xAF, 0x73, 0x24,
+   0xDA, 0x58, 0xE2, 0xFC, 0x78, 0xEA, 0xCD, 0xFA,
+   0x37, 0xED, 0x13, 0x19, 0xC0, 0x59, 0x83, 0xBA,
+   0x3F, 0x57, 0x00, 0x7E, 0xC9, 0x2E, 0x17, 0x5B,
+   0x84, 0xF6, 0xE7, 0x22, 0xFB, 0x5F, 0x4C, 0x2C,
+   0x61, 0x9F, 0x45, 0x39, 0xB3, 0xEC, 0x04, 0x87,
+   0x67, 0xDC, 0x0B, 0xF4, 0x20, 0xAB, 0x6B, 0x9E,
+   0x4B, 0xCF, 0xB4, 0x2F, 0xBB, 0xEF, 0xDB, 0x33 };
+
+unsigned char table_111[32] = {
+   0x09, 0x0F, 0x00, 0x15, 0x12, 0x17, 0x1A, 0x0D,
+   0x1C, 0x0B, 0x01, 0x0A, 0x05, 0x1E, 0x1D, 0x0C,
+   0x1B, 0x08, 0x19, 0x18, 0x14, 0x07, 0x0E, 0x03,
+   0x10, 0x16, 0x11, 0x1F, 0x04, 0x06, 0x02, 0x13 };
+
+unsigned char table_112[256] = {
+   0xF9, 0x7D, 0xBE, 0xD5, 0x9F, 0xB8, 0x95, 0x43,
+   0xDB, 0xAE, 0x7E, 0xEC, 0x5B, 0x58, 0x18, 0x49,
+   0x4B, 0x9D, 0x1C, 0x3E, 0x61, 0xD1, 0xF6, 0x2F,
+   0x41, 0x82, 0x51, 0x37, 0x72, 0x79, 0x05, 0x2A,
+   0xC2, 0xB0, 0xE2, 0xE7, 0xB2, 0xF3, 0x1B, 0x92,
+   0x86, 0xBB, 0xDC, 0x90, 0x1A, 0x19, 0xD7, 0xBA,
+   0x2C, 0x7B, 0xEF, 0xC7, 0x8A, 0x81, 0xEB, 0xDE,
+   0x73, 0x4E, 0xB7, 0x97, 0xCA, 0x29, 0x85, 0xC1,
+   0xA5, 0x7F, 0xFE, 0x56, 0xE9, 0x9E, 0x21, 0x76,
+   0x3A, 0x88, 0x70, 0xC6, 0xD3, 0x8C, 0x47, 0xC8,
+   0x83, 0x48, 0xC3, 0x6A, 0x9C, 0x80, 0x53, 0xBD,
+   0xFD, 0x54, 0x09, 0x91, 0x94, 0xAA, 0x7A, 0x59,
+   0x71, 0xDD, 0xA8, 0x07, 0xCB, 0x0F, 0xE0, 0x9A,
+   0x36, 0x4C, 0x4D, 0x0D, 0xA4, 0x96, 0x6F, 0x14,
+   0x22, 0x38, 0xAD, 0x02, 0xF4, 0x0B, 0xEA, 0x93,
+   0x20, 0x04, 0xBC, 0xE8, 0x6C, 0xFB, 0x10, 0x6B,
+   0x40, 0xB6, 0x24, 0x17, 0x06, 0x31, 0xD9, 0x33,
+   0xF5, 0x99, 0x57, 0xCD, 0xAB, 0x67, 0x5C, 0x30,
+   0x1E, 0x34, 0xB4, 0x3F, 0x16, 0x42, 0xA2, 0x68,
+   0x27, 0xB3, 0x1D, 0xED, 0x5F, 0x52, 0xF7, 0x3C,
+   0x65, 0x5D, 0xE5, 0x23, 0x0C, 0x6D, 0x84, 0x6E,
+   0xDA, 0x77, 0xF8, 0x15, 0xFA, 0x69, 0xD0, 0xA7,
+   0x11, 0xAC, 0xA6, 0xA3, 0x1F, 0x2E, 0xBF, 0x4A,
+   0x8F, 0xFC, 0xEE, 0xC9, 0x26, 0x12, 0xC0, 0xB1,
+   0x45, 0x0E, 0x3D, 0x7C, 0xCE, 0x13, 0x8E, 0x98,
+   0x46, 0x2B, 0xC5, 0x66, 0x28, 0x32, 0xD2, 0x03,
+   0xE3, 0xC4, 0x9B, 0x89, 0x5E, 0xF0, 0xCF, 0x3B,
+   0x2D, 0x50, 0xB5, 0x00, 0x0A, 0xD6, 0x55, 0xE1,
+   0x62, 0x63, 0x64, 0x87, 0xAF, 0x78, 0xB9, 0xF2,
+   0x25, 0x44, 0xFF, 0x39, 0xF1, 0x08, 0x4F, 0x74,
+   0xA9, 0x8B, 0x75, 0x01, 0xA0, 0xE4, 0x35, 0x8D,
+   0xA1, 0xCC, 0xDF, 0x60, 0xD8, 0x5A, 0xE6, 0xD4 };
+
+unsigned char table_113[256] = {
+   0x46, 0x9D, 0x39, 0xB2, 0x8D, 0x3B, 0x59, 0x5A,
+   0xD0, 0x9C, 0xE4, 0x04, 0x01, 0xE2, 0xB3, 0xD2,
+   0xD7, 0x18, 0x40, 0xD8, 0xF1, 0xEF, 0x3A, 0x1D,
+   0x8E, 0xE5, 0xD9, 0xD3, 0xCB, 0x49, 0x4C, 0xCF,
+   0xC0, 0xD6, 0xB5, 0x73, 0x77, 0x82, 0x54, 0xA2,
+   0xB1, 0xB0, 0x84, 0x5D, 0xC7, 0xDE, 0x31, 0x2F,
+   0x50, 0x78, 0xBE, 0x94, 0x64, 0x44, 0x60, 0x7A,
+   0x1A, 0x6E, 0x09, 0x6F, 0xBF, 0x76, 0x81, 0x38,
+   0x22, 0xC3, 0xEE, 0x8F, 0xFB, 0x32, 0xED, 0x92,
+   0xAE, 0xE6, 0x5F, 0xAA, 0xAC, 0x0D, 0xA3, 0x47,
+   0x1F, 0x11, 0xC1, 0x29, 0xAF, 0xFD, 0x1C, 0xDB,
+   0x00, 0x23, 0xB9, 0xB8, 0x91, 0x41, 0x27, 0x37,
+   0x43, 0x02, 0x26, 0xF6, 0x7D, 0x0A, 0x85, 0x93,
+   0x97, 0x2E, 0x20, 0x55, 0x13, 0x4B, 0x6C, 0xE7,
+   0xFC, 0x25, 0xFA, 0x9E, 0x5B, 0xA1, 0xDF, 0x2C,
+   0x3E, 0xBC, 0xEA, 0x42, 0x7C, 0x36, 0x30, 0xEB,
+   0xBD, 0x8B, 0x87, 0x16, 0x3D, 0x5C, 0x07, 0xBA,
+   0xB4, 0x1B, 0xC2, 0xE3, 0x71, 0x9A, 0x5E, 0x4D,
+   0xF2, 0xCC, 0x0E, 0xE1, 0x34, 0x75, 0x58, 0x89,
+   0x17, 0xD4, 0x68, 0x80, 0x2B, 0x74, 0x70, 0x8A,
+   0x63, 0xE8, 0x56, 0x24, 0xD1, 0x57, 0x35, 0x6D,
+   0x3C, 0xA6, 0xC8, 0x7E, 0xA8, 0x4E, 0xC4, 0x33,
+   0xA9, 0x62, 0x61, 0x7F, 0x21, 0x98, 0x2A, 0xAD,
+   0xB6, 0xA7, 0xF5, 0x3F, 0x15, 0x45, 0xF8, 0xA4,
+   0x95, 0x88, 0xDC, 0x96, 0x90, 0x08, 0x9B, 0xF9,
+   0x06, 0x14, 0x05, 0xF0, 0xF7, 0xA0, 0xE0, 0x65,
+   0xCA, 0xA5, 0x9F, 0x79, 0xCD, 0x4F, 0x72, 0xB7,
+   0x4A, 0x0F, 0x66, 0xC5, 0x0C, 0x52, 0xF3, 0x69,
+   0x83, 0x03, 0x99, 0x1E, 0x2D, 0xDA, 0x8C, 0x53,
+   0x28, 0xDD, 0xE9, 0x0B, 0xC9, 0xF4, 0x48, 0x12,
+   0x6A, 0x19, 0xCE, 0xAB, 0x51, 0xD5, 0x6B, 0xBB,
+   0xFE, 0x7B, 0x67, 0xFF, 0x10, 0xEC, 0xC6, 0x86 };
+
+unsigned char table_114[32] = {
+   0x11, 0x10, 0x04, 0x1D, 0x08, 0x15, 0x1A, 0x1B,
+   0x14, 0x18, 0x0F, 0x17, 0x16, 0x07, 0x1E, 0x0E,
+   0x12, 0x0A, 0x13, 0x0B, 0x0C, 0x00, 0x06, 0x02,
+   0x1F, 0x19, 0x09, 0x1C, 0x01, 0x0D, 0x03, 0x05 };
+
+unsigned char table_115[256] = {
+   0xB7, 0xBB, 0x63, 0x0D, 0xF0, 0x33, 0x5A, 0x05,
+   0xF2, 0x7F, 0x64, 0xDB, 0x51, 0xC9, 0x2C, 0x85,
+   0x4F, 0x41, 0xA4, 0x42, 0xCF, 0xA6, 0x52, 0x2F,
+   0x26, 0xEF, 0xFB, 0x29, 0x40, 0x16, 0xF7, 0xED,
+   0x23, 0x69, 0x8A, 0xDF, 0x77, 0x28, 0x93, 0x14,
+   0x82, 0x0C, 0xBE, 0x3D, 0x20, 0xB4, 0x79, 0x94,
+   0x54, 0xF8, 0x07, 0xB1, 0xE1, 0x66, 0x73, 0xD3,
+   0x19, 0x15, 0xFF, 0x03, 0x6A, 0x9A, 0xDC, 0x1C,
+   0xB3, 0x5D, 0x76, 0x68, 0x47, 0x6C, 0xF9, 0xFD,
+   0xE9, 0xDD, 0x01, 0x65, 0xBD, 0x80, 0x0E, 0x7A,
+   0x8D, 0x99, 0x13, 0x7C, 0xA5, 0xA7, 0x1A, 0xCC,
+   0xB8, 0xE6, 0x2B, 0xB2, 0xB6, 0xD0, 0x62, 0x2D,
+   0x4D, 0xD2, 0xB9, 0x04, 0x46, 0xAE, 0xAA, 0x44,
+   0xDA, 0x92, 0x4B, 0x4E, 0xC4, 0xE2, 0xFE, 0xA2,
+   0x75, 0x7B, 0xC3, 0xFA, 0x9F, 0x37, 0x9D, 0x1E,
+   0x72, 0xD4, 0x1F, 0x4A, 0x9B, 0xE5, 0x6D, 0xEC,
+   0x5C, 0x7D, 0x98, 0xE8, 0xEE, 0x86, 0xD1, 0xC8,
+   0xEA, 0x55, 0xBF, 0xAF, 0xDE, 0x32, 0x09, 0x3A,
+   0x8F, 0x57, 0x83, 0x43, 0x61, 0xC6, 0x8E, 0x96,
+   0x22, 0xA3, 0x97, 0x91, 0x5F, 0x11, 0x3B, 0x5B,
+   0x1B, 0x34, 0x49, 0x95, 0xF1, 0x6F, 0x89, 0xA8,
+   0xC0, 0x36, 0x0A, 0x3F, 0x60, 0x50, 0xE7, 0x08,
+   0xCE, 0x25, 0xC1, 0x71, 0xF6, 0x59, 0x58, 0x56,
+   0x4C, 0xAB, 0x27, 0xAC, 0x06, 0xCB, 0x00, 0x30,
+   0x84, 0x3E, 0xC2, 0x1D, 0x02, 0xE0, 0xC5, 0xD6,
+   0x18, 0x70, 0xA9, 0x88, 0xD9, 0x39, 0x8B, 0x6E,
+   0xF4, 0x24, 0xA0, 0x48, 0x45, 0x21, 0x87, 0x78,
+   0x38, 0x90, 0xE3, 0xCA, 0xF5, 0xD7, 0x2A, 0x53,
+   0x9C, 0xCD, 0x31, 0x35, 0xAD, 0x74, 0xD8, 0x12,
+   0xBC, 0x9E, 0x6B, 0x67, 0xB0, 0xBA, 0xE4, 0x10,
+   0x5E, 0xFC, 0xC7, 0x0F, 0x2E, 0x81, 0x7E, 0xA1,
+   0x8C, 0x17, 0xB5, 0xEB, 0xD5, 0xF3, 0x0B, 0x3C };
+
+unsigned char table_116[32] = {
+   0x00, 0x05, 0x10, 0x1C, 0x0C, 0x1A, 0x04, 0x1B,
+   0x0A, 0x0D, 0x14, 0x0B, 0x07, 0x03, 0x12, 0x1E,
+   0x06, 0x11, 0x01, 0x08, 0x15, 0x09, 0x1F, 0x0F,
+   0x19, 0x18, 0x16, 0x02, 0x13, 0x0E, 0x17, 0x1D };
+
+unsigned char table_117[256] = {
+   0xD0, 0x9A, 0xAB, 0xA8, 0xA7, 0xDF, 0x28, 0xCE,
+   0x3E, 0x51, 0xBF, 0x76, 0x03, 0xA0, 0x53, 0x3F,
+   0x90, 0x93, 0x87, 0x67, 0x98, 0x3D, 0xEA, 0x8B,
+   0x55, 0xCF, 0x10, 0xF3, 0x25, 0xFC, 0x9F, 0x41,
+   0x6B, 0x54, 0x6E, 0x0B, 0x83, 0x35, 0x69, 0x7D,
+   0xE0, 0x88, 0x4B, 0xE9, 0x1E, 0x96, 0x91, 0x57,
+   0xBD, 0x72, 0x21, 0x3C, 0xA6, 0x99, 0x6C, 0xF6,
+   0x13, 0xFA, 0x29, 0xED, 0xDB, 0x16, 0x4D, 0x07,
+   0x45, 0xA5, 0xE3, 0x0E, 0x31, 0xBC, 0x56, 0x5C,
+   0xB2, 0x23, 0xDA, 0x74, 0xFF, 0x02, 0x8F, 0xF4,
+   0x2A, 0xC9, 0x89, 0xAA, 0x05, 0xB1, 0xD1, 0x1F,
+   0x4F, 0xB0, 0x7A, 0x2C, 0x14, 0xD9, 0xE7, 0x66,
+   0x62, 0x1A, 0x4C, 0xC0, 0xC6, 0x63, 0x7F, 0xB4,
+   0xF1, 0x43, 0xFE, 0x61, 0xA3, 0xCC, 0xE8, 0x6D,
+   0xBA, 0x65, 0x42, 0x2B, 0xCA, 0xD5, 0x52, 0x3A,
+   0xCD, 0x1D, 0x24, 0xD7, 0x47, 0xDE, 0x9E, 0x95,
+   0x85, 0x48, 0x86, 0xE1, 0xC5, 0xD2, 0x34, 0xAF,
+   0x40, 0xFB, 0xE6, 0x4E, 0xC8, 0xF5, 0x7B, 0x5A,
+   0xCB, 0xD4, 0x97, 0x6F, 0x0C, 0x79, 0x9C, 0x20,
+   0x59, 0x19, 0x68, 0x2E, 0x09, 0x64, 0x73, 0x50,
+   0xC2, 0x2F, 0x0D, 0xEF, 0x9D, 0x94, 0x00, 0x81,
+   0xE2, 0x46, 0x5F, 0xB8, 0x0A, 0x12, 0x75, 0x1C,
+   0x8C, 0xB6, 0x71, 0xAC, 0x04, 0x60, 0xA9, 0x5B,
+   0xF8, 0x30, 0x49, 0x44, 0x4A, 0xBE, 0x6A, 0xEB,
+   0xD3, 0xD8, 0x36, 0xB3, 0x3B, 0x17, 0x80, 0xA4,
+   0xEC, 0x26, 0x82, 0xB5, 0x37, 0x5D, 0x1B, 0x2D,
+   0xE5, 0xA2, 0x0F, 0xB7, 0xC4, 0xF2, 0x70, 0x39,
+   0xF9, 0xC7, 0xBB, 0x8A, 0x32, 0x78, 0xC3, 0x5E,
+   0xD6, 0xE4, 0x22, 0x9B, 0x18, 0x8E, 0xEE, 0x27,
+   0x8D, 0x33, 0x11, 0x77, 0x01, 0x06, 0x38, 0xF0,
+   0x7E, 0x08, 0x15, 0xB9, 0x7C, 0xAD, 0x84, 0xDD,
+   0xC1, 0xFD, 0x92, 0xA1, 0xF7, 0xAE, 0xDC, 0x58 };
+
+unsigned char table_118[256] = {
+   0x38, 0xA0, 0xA6, 0xFC, 0x7C, 0x5A, 0x97, 0x1D,
+   0xFD, 0x00, 0x20, 0xA2, 0x72, 0x10, 0x1F, 0x48,
+   0x98, 0x7E, 0xDF, 0x2D, 0x80, 0x0A, 0x27, 0xDC,
+   0xCF, 0xBF, 0x92, 0x94, 0x53, 0xCC, 0x0E, 0x74,
+   0xA7, 0x60, 0x08, 0x15, 0x87, 0x6F, 0xB3, 0xA3,
+   0xED, 0x59, 0x09, 0x4F, 0x9E, 0x9A, 0xEE, 0x83,
+   0x56, 0x32, 0x34, 0xC7, 0x24, 0xE7, 0x96, 0x4D,
+   0xAE, 0xE3, 0xBD, 0xE2, 0x36, 0x4A, 0xB6, 0x8B,
+   0xF2, 0xC1, 0xD7, 0x40, 0x31, 0x4B, 0xDA, 0xF1,
+   0xB1, 0x70, 0xA8, 0xC3, 0xC6, 0x8A, 0xE6, 0x77,
+   0x21, 0x7D, 0xD5, 0x0C, 0x43, 0xC4, 0xF0, 0x1B,
+   0x18, 0xA1, 0x85, 0xE1, 0xFF, 0x8D, 0xE5, 0x6E,
+   0x9B, 0x51, 0x1C, 0xA4, 0x5C, 0x8E, 0x69, 0x49,
+   0x23, 0xCD, 0x52, 0xF8, 0x3E, 0x91, 0x5E, 0x1E,
+   0x25, 0xB4, 0x93, 0xCB, 0xE0, 0x47, 0xBC, 0x4E,
+   0x33, 0xB7, 0x75, 0x1A, 0x11, 0x9C, 0x3F, 0xEC,
+   0xD1, 0x46, 0xDD, 0xAA, 0xB8, 0x99, 0x86, 0x67,
+   0x58, 0xF9, 0x16, 0x17, 0x6D, 0x5F, 0x2B, 0xA5,
+   0xD3, 0x8F, 0x55, 0x71, 0xD2, 0xBA, 0x5B, 0x3C,
+   0x82, 0xB5, 0x41, 0xE4, 0x90, 0x45, 0x6C, 0xF6,
+   0xDE, 0xA9, 0x84, 0x62, 0x19, 0x3B, 0xB9, 0xC8,
+   0x2C, 0xB0, 0x76, 0x57, 0xD8, 0x26, 0x9D, 0x89,
+   0xC9, 0x54, 0xFB, 0x07, 0xCE, 0x22, 0x5D, 0x64,
+   0x65, 0xAD, 0x01, 0xDB, 0x14, 0x4C, 0x37, 0x03,
+   0x6B, 0xAF, 0xD0, 0x7F, 0x9F, 0xBB, 0xEB, 0xC0,
+   0x50, 0x66, 0x68, 0x0B, 0x42, 0x2A, 0xD4, 0xF5,
+   0x61, 0x63, 0xF3, 0x39, 0xBE, 0xC5, 0xEF, 0x28,
+   0x3A, 0xAB, 0x79, 0x05, 0xE9, 0x12, 0x73, 0x3D,
+   0xB2, 0x8C, 0xCA, 0x29, 0x0F, 0xF4, 0x7B, 0x13,
+   0x88, 0x44, 0xC2, 0x2E, 0xFA, 0xFE, 0x04, 0x35,
+   0xE8, 0x06, 0x7A, 0x78, 0x0D, 0x81, 0xF7, 0xEA,
+   0xD9, 0x2F, 0x02, 0xAC, 0x30, 0x6A, 0xD6, 0x95 };
+
+unsigned char table_119[32] = {
+   0x14, 0x0A, 0x1C, 0x00, 0x0C, 0x1F, 0x1E, 0x0B,
+   0x12, 0x1D, 0x17, 0x08, 0x07, 0x04, 0x09, 0x10,
+   0x03, 0x1B, 0x0E, 0x1A, 0x05, 0x0D, 0x11, 0x15,
+   0x18, 0x02, 0x06, 0x01, 0x19, 0x16, 0x13, 0x0F };
+
+unsigned char table_120[256] = {
+   0xCE, 0x89, 0xB2, 0x72, 0x04, 0x77, 0x64, 0xAE,
+   0x80, 0x99, 0xB5, 0x00, 0x7B, 0x50, 0x9D, 0xE3,
+   0x87, 0x37, 0x6D, 0x3D, 0x32, 0xBA, 0x20, 0xF0,
+   0xDC, 0xBD, 0x61, 0x26, 0xD4, 0xA6, 0x70, 0x54,
+   0xC1, 0x7D, 0x82, 0xFF, 0x81, 0x83, 0x2F, 0xF5,
+   0x3B, 0x42, 0x08, 0x5C, 0x30, 0x59, 0xBB, 0xC2,
+   0x33, 0x5D, 0xEE, 0xB7, 0xF7, 0x2B, 0x76, 0xD0,
+   0x43, 0x1C, 0x48, 0xFC, 0x01, 0xCD, 0x27, 0x1D,
+   0x5A, 0x96, 0x95, 0x03, 0xC6, 0x1F, 0x09, 0xCB,
+   0xF6, 0x47, 0xA9, 0x93, 0xA7, 0xD2, 0xDB, 0x51,
+   0xB0, 0x7A, 0xE6, 0x62, 0x0F, 0x12, 0x57, 0xF4,
+   0x35, 0xFE, 0xA4, 0xDF, 0x5B, 0xF3, 0x67, 0x85,
+   0x98, 0xE4, 0xAB, 0x75, 0x4C, 0xE2, 0x25, 0x74,
+   0x3A, 0x45, 0xDE, 0xEF, 0x4A, 0x97, 0x86, 0x24,
+   0xE9, 0x8F, 0xD8, 0xD7, 0x60, 0xAD, 0x36, 0x8E,
+   0x1E, 0xB9, 0x4F, 0x6B, 0x8C, 0x06, 0x23, 0x94,
+   0x0E, 0xD3, 0x49, 0x14, 0x90, 0xAF, 0x65, 0xEC,
+   0xF9, 0x0D, 0xED, 0x6C, 0xBE, 0x7F, 0xA5, 0xC5,
+   0xEA, 0x78, 0x2E, 0xBC, 0xD5, 0xDA, 0x18, 0xE1,
+   0x10, 0x2D, 0xB4, 0x16, 0x4B, 0xE8, 0xC4, 0x8D,
+   0x19, 0x1B, 0x02, 0x66, 0xB6, 0xE7, 0x9C, 0x7C,
+   0xC9, 0xA0, 0x2A, 0x53, 0x13, 0xDD, 0xF8, 0xA8,
+   0x0A, 0x6E, 0xCF, 0x6F, 0x7E, 0xE0, 0x3E, 0xE5,
+   0x07, 0xCC, 0x38, 0xD1, 0xF2, 0x2C, 0x9A, 0xAC,
+   0x88, 0x79, 0xB8, 0xC8, 0xBF, 0x63, 0x71, 0x69,
+   0x52, 0x39, 0x9F, 0x22, 0x3F, 0x9E, 0x44, 0xFA,
+   0x73, 0x6A, 0x8B, 0xA2, 0xD6, 0x1A, 0x9B, 0xB1,
+   0x8A, 0x4D, 0x58, 0xA1, 0x46, 0x5F, 0x55, 0x56,
+   0x21, 0x05, 0x15, 0x92, 0xAA, 0xEB, 0x31, 0x68,
+   0xFB, 0x41, 0xC3, 0x4E, 0xB3, 0x40, 0x34, 0x17,
+   0xD9, 0x29, 0x3C, 0x0C, 0xF1, 0x0B, 0x28, 0x84,
+   0x5E, 0xCA, 0xFD, 0x11, 0xA3, 0xC7, 0xC0, 0x91 };
+
+unsigned char table_121[32] = {
+   0x1E, 0x12, 0x06, 0x1D, 0x15, 0x1F, 0x13, 0x0B,
+   0x10, 0x0D, 0x1C, 0x01, 0x0A, 0x0E, 0x02, 0x19,
+   0x04, 0x1A, 0x03, 0x11, 0x00, 0x16, 0x0C, 0x17,
+   0x14, 0x08, 0x18, 0x05, 0x09, 0x0F, 0x1B, 0x07 };
+
+unsigned char table_122[256] = {
+   0x85, 0xDF, 0x7F, 0x7C, 0x56, 0xF0, 0x0C, 0x7D,
+   0x76, 0xA8, 0x58, 0x31, 0x25, 0x8A, 0x0D, 0x23,
+   0x05, 0x0F, 0x12, 0x64, 0x8E, 0x5D, 0xF4, 0x2C,
+   0x18, 0xFA, 0x4B, 0xFE, 0x91, 0xBF, 0x95, 0x0B,
+   0xF1, 0x88, 0x10, 0xD8, 0x3E, 0x53, 0x96, 0xB5,
+   0x75, 0x24, 0x8F, 0xD6, 0x68, 0x5C, 0x93, 0x1F,
+   0x6B, 0xC2, 0xAB, 0xED, 0x1E, 0xC0, 0xBC, 0x47,
+   0xE9, 0xD1, 0xDE, 0xCA, 0xF6, 0x62, 0x43, 0xEB,
+   0xA2, 0xB4, 0x08, 0xE6, 0x74, 0x0E, 0xA1, 0x72,
+   0x66, 0x61, 0x21, 0x2E, 0x32, 0x63, 0x29, 0xD7,
+   0x1C, 0x22, 0xAC, 0xE7, 0x54, 0xF3, 0x65, 0x17,
+   0x9F, 0x78, 0x79, 0x4C, 0xDD, 0x27, 0x90, 0x36,
+   0x19, 0x44, 0x03, 0xD9, 0x4A, 0x5A, 0x34, 0xF9,
+   0x97, 0xA6, 0x70, 0x39, 0x28, 0x77, 0x6E, 0xB7,
+   0x8C, 0x02, 0x5E, 0x9B, 0x8D, 0x59, 0x6F, 0xA5,
+   0x07, 0xE2, 0x41, 0x51, 0xC9, 0x3C, 0xE8, 0xE1,
+   0xB3, 0x16, 0x50, 0x04, 0xE3, 0x1D, 0x3B, 0xD2,
+   0x4D, 0x35, 0x71, 0xDA, 0x9E, 0xA7, 0xE4, 0xE0,
+   0xB6, 0x2B, 0xEA, 0x84, 0x55, 0xF8, 0x57, 0x3D,
+   0x73, 0x42, 0xC6, 0x0A, 0x92, 0x6A, 0xAE, 0xF5,
+   0xFC, 0xD5, 0x15, 0x52, 0x7E, 0x14, 0x81, 0x13,
+   0xE5, 0x49, 0x38, 0x2A, 0x94, 0x5B, 0xA3, 0x11,
+   0x8B, 0x80, 0xBB, 0x01, 0x9C, 0xA4, 0xDB, 0xF7,
+   0xA9, 0x20, 0xF2, 0x1A, 0xDC, 0x33, 0x3A, 0xEF,
+   0xD3, 0xFD, 0x30, 0xB0, 0x1B, 0xC4, 0x06, 0xD4,
+   0x6D, 0x87, 0x2F, 0x60, 0x5F, 0xC5, 0x09, 0x37,
+   0xAF, 0x00, 0xCB, 0x9D, 0xA0, 0xB9, 0x45, 0x86,
+   0x4F, 0x6C, 0x67, 0xFB, 0x40, 0x3F, 0xCC, 0xB8,
+   0xC8, 0x82, 0x98, 0x99, 0x7B, 0xB1, 0xCD, 0xD0,
+   0xBD, 0x48, 0xAD, 0x26, 0x7A, 0x9A, 0x46, 0xFF,
+   0x89, 0xC7, 0xC1, 0xCF, 0xBE, 0xAA, 0xEC, 0xBA,
+   0xCE, 0x2D, 0x4E, 0x83, 0xC3, 0x69, 0xEE, 0xB2 };
+
+unsigned char table_123[256] = {
+   0x9D, 0xFB, 0x3C, 0x81, 0xAA, 0x05, 0xB2, 0xBE,
+   0xD1, 0x5F, 0x4C, 0xE0, 0xA3, 0xF4, 0xDE, 0x35,
+   0xFE, 0x1B, 0x37, 0x99, 0x94, 0x7A, 0x10, 0xAB,
+   0xC0, 0xA4, 0xB5, 0xFF, 0x8F, 0x3B, 0xB4, 0x51,
+   0x04, 0xE9, 0xB9, 0xC1, 0x98, 0xC5, 0x82, 0x38,
+   0x4D, 0x71, 0xFC, 0x33, 0xC4, 0x50, 0x5D, 0x88,
+   0xB8, 0x5C, 0x32, 0xE2, 0xBB, 0xCD, 0x60, 0x2C,
+   0xD4, 0x7E, 0x27, 0x59, 0x2B, 0x1F, 0x53, 0xF6,
+   0x25, 0x86, 0xAE, 0x21, 0xFA, 0x31, 0xD7, 0x0F,
+   0x17, 0xDA, 0x7F, 0xC9, 0x46, 0x19, 0x08, 0xA8,
+   0xCF, 0x13, 0xCC, 0x03, 0x3F, 0x22, 0x6E, 0xEB,
+   0x4A, 0x63, 0x73, 0xBD, 0x36, 0xED, 0x30, 0x57,
+   0x65, 0xF8, 0x41, 0x61, 0x1E, 0xA0, 0xC6, 0x45,
+   0x3E, 0x75, 0x28, 0x87, 0xCB, 0xD6, 0x16, 0xD8,
+   0xDF, 0xEF, 0xEA, 0xA7, 0x58, 0xB0, 0x1D, 0xE6,
+   0x47, 0x76, 0xD9, 0x96, 0xE7, 0xDC, 0x00, 0x80,
+   0xDD, 0xB7, 0x9A, 0xE1, 0xF5, 0x9C, 0x4B, 0xE3,
+   0xBC, 0x8D, 0xF2, 0x2F, 0x9F, 0x6C, 0x93, 0xAF,
+   0xA9, 0xC2, 0x5E, 0x24, 0x15, 0xD2, 0x09, 0x0D,
+   0xDB, 0x4F, 0x91, 0x0E, 0x64, 0x34, 0x4E, 0xAD,
+   0x62, 0x44, 0x23, 0x85, 0xB6, 0xAC, 0xC7, 0xCA,
+   0x84, 0xF9, 0x8C, 0xBF, 0x14, 0x7C, 0x8E, 0x92,
+   0xF0, 0x0B, 0xCE, 0x90, 0x7D, 0x70, 0x9E, 0x54,
+   0x39, 0x5B, 0x6D, 0x52, 0xEE, 0xA2, 0x6F, 0x78,
+   0x2D, 0x95, 0x8B, 0x02, 0x3D, 0x7B, 0x69, 0xC3,
+   0x49, 0xA5, 0x1A, 0x26, 0xD5, 0x6B, 0xE8, 0xFD,
+   0xB3, 0xD3, 0x20, 0x55, 0x18, 0x06, 0xF3, 0xB1,
+   0x0C, 0xC8, 0x07, 0x12, 0xF7, 0x01, 0x2E, 0x72,
+   0x97, 0xA6, 0x11, 0x89, 0x56, 0x5A, 0x29, 0xBA,
+   0x67, 0x42, 0x83, 0x6A, 0x2A, 0xF1, 0xA1, 0x9B,
+   0xE5, 0xE4, 0x74, 0x66, 0x1C, 0x68, 0xEC, 0x40,
+   0x48, 0x77, 0xD0, 0x0A, 0x8A, 0x3A, 0x43, 0x79 };
+
+unsigned char table_124[256] = {
+   0x6C, 0xC3, 0x28, 0x2F, 0x42, 0x4B, 0x7C, 0x3C,
+   0xCE, 0x24, 0xC8, 0x51, 0x25, 0x3F, 0x49, 0x8D,
+   0x1E, 0x5C, 0x89, 0x3A, 0x98, 0x47, 0x0B, 0x12,
+   0xA9, 0xB1, 0xD7, 0xB6, 0x5D, 0xF9, 0x5A, 0xBC,
+   0xFA, 0x06, 0x7D, 0x08, 0xFC, 0x37, 0x54, 0x4F,
+   0xD4, 0xCD, 0xA7, 0x5E, 0xE0, 0x92, 0x82, 0x56,
+   0xF1, 0x2B, 0xC4, 0xE2, 0x29, 0xEA, 0x35, 0x57,
+   0x33, 0x4E, 0x1A, 0x17, 0x8B, 0x85, 0xBF, 0xD5,
+   0x18, 0xB3, 0x0D, 0x71, 0x45, 0x81, 0xB4, 0x27,
+   0xD1, 0xE1, 0xFF, 0x44, 0x9E, 0xA4, 0x15, 0x9A,
+   0x90, 0xC7, 0x79, 0xE3, 0x4C, 0xE9, 0x3D, 0x6B,
+   0xF5, 0xF4, 0xEE, 0xAA, 0xDB, 0x07, 0x09, 0xCF,
+   0x7B, 0x95, 0xA0, 0x53, 0x8F, 0xA1, 0x9D, 0xBE,
+   0x6F, 0xAE, 0x96, 0x46, 0x59, 0x01, 0x84, 0xCC,
+   0x3B, 0x8E, 0xF7, 0x4D, 0x6E, 0xDC, 0xE8, 0x36,
+   0x7A, 0xE5, 0xBD, 0xE7, 0x9F, 0x2C, 0x52, 0xAB,
+   0x55, 0x13, 0x1D, 0xFB, 0x58, 0x9C, 0xDF, 0xC0,
+   0x30, 0x73, 0x67, 0x39, 0x74, 0xD3, 0x11, 0xD2,
+   0x0E, 0x20, 0xB7, 0x02, 0xB9, 0x1C, 0x86, 0x76,
+   0x10, 0x68, 0x9B, 0x63, 0x48, 0x8A, 0xB2, 0xB8,
+   0xAF, 0x26, 0x99, 0x04, 0xB0, 0xE4, 0xEF, 0xEB,
+   0xEC, 0x6D, 0x61, 0xC1, 0xD0, 0x38, 0xC9, 0x19,
+   0x60, 0xA8, 0xA6, 0xF8, 0x80, 0xC5, 0x03, 0x0F,
+   0x22, 0x2D, 0x88, 0x32, 0x77, 0x70, 0xFE, 0x0C,
+   0x31, 0x40, 0x5F, 0xED, 0xA5, 0x93, 0x43, 0xF0,
+   0x8C, 0xE6, 0x34, 0x21, 0xD9, 0xC2, 0xD8, 0xC6,
+   0x6A, 0xD6, 0xCB, 0xAC, 0x75, 0xB5, 0x78, 0x0A,
+   0xA3, 0x69, 0x16, 0xBA, 0x50, 0x2A, 0x41, 0x83,
+   0xF6, 0x64, 0x00, 0x65, 0x7E, 0xDD, 0x5B, 0xDA,
+   0x14, 0xFD, 0x3E, 0x7F, 0xCA, 0x66, 0x4A, 0x1F,
+   0xA2, 0xAD, 0xF2, 0x23, 0xBB, 0x72, 0xF3, 0x94,
+   0x62, 0x1B, 0xDE, 0x91, 0x87, 0x97, 0x05, 0x2E };
+
+unsigned char table_125[32] = {
+   0x1A, 0x18, 0x12, 0x15, 0x00, 0x1C, 0x01, 0x0B,
+   0x19, 0x1B, 0x1F, 0x11, 0x07, 0x10, 0x1E, 0x06,
+   0x17, 0x04, 0x0A, 0x0E, 0x0D, 0x0C, 0x16, 0x08,
+   0x02, 0x03, 0x13, 0x14, 0x09, 0x1D, 0x05, 0x0F };
+
+unsigned char table_126[32] = {
+   0x1C, 0x1D, 0x07, 0x12, 0x18, 0x1A, 0x19, 0x09,
+   0x0F, 0x14, 0x1F, 0x0B, 0x13, 0x04, 0x0E, 0x1E,
+   0x0C, 0x0D, 0x01, 0x17, 0x1B, 0x16, 0x0A, 0x05,
+   0x15, 0x10, 0x11, 0x08, 0x00, 0x03, 0x06, 0x02 };
+
+unsigned char table_127[256] = {
+   0xA0, 0x66, 0xD8, 0x08, 0xEA, 0x39, 0x78, 0xAB,
+   0x61, 0x4E, 0xC7, 0xD1, 0xA3, 0x1C, 0x9F, 0xCB,
+   0x19, 0x51, 0x15, 0x92, 0x23, 0xFD, 0x7D, 0x1D,
+   0x95, 0xAE, 0x0E, 0x8B, 0xE6, 0x7F, 0x86, 0x6D,
+   0x06, 0xBD, 0x20, 0x1F, 0x3A, 0xE4, 0x54, 0x91,
+   0x69, 0xD3, 0xE3, 0x3D, 0x4D, 0x31, 0x49, 0xA4,
+   0x41, 0xF3, 0xE0, 0x11, 0x14, 0x9B, 0x96, 0x5A,
+   0xC4, 0x8E, 0x34, 0xDB, 0xBA, 0x83, 0xD9, 0x81,
+   0xAF, 0x58, 0x8A, 0x79, 0x13, 0xBC, 0x85, 0x37,
+   0x9E, 0x6C, 0x57, 0x71, 0x8D, 0x97, 0x5F, 0x6F,
+   0x1E, 0x74, 0x27, 0xFC, 0x5C, 0x7A, 0x64, 0x87,
+   0xF5, 0xC6, 0xF2, 0x4F, 0xDE, 0x80, 0xAA, 0x84,
+   0x2E, 0xDC, 0xE7, 0x40, 0x75, 0xC5, 0xB3, 0xC8,
+   0xCE, 0x21, 0x02, 0x67, 0xB7, 0x10, 0x47, 0x6A,
+   0xEE, 0x53, 0x2C, 0x16, 0x05, 0xC0, 0x63, 0x4C,
+   0x0D, 0xBB, 0xC3, 0x38, 0x46, 0x68, 0x7E, 0xF9,
+   0xB8, 0xB4, 0x3E, 0x36, 0xD5, 0xEC, 0x0B, 0xF6,
+   0x33, 0x0A, 0x0F, 0x5B, 0xFB, 0x45, 0xEB, 0xA9,
+   0x6E, 0x6B, 0xCF, 0x55, 0x99, 0xAC, 0x22, 0xBE,
+   0xB1, 0xA2, 0x3F, 0x25, 0x77, 0x8F, 0x7C, 0xF1,
+   0xD4, 0x59, 0xA8, 0xE5, 0xD7, 0xCA, 0xA1, 0x93,
+   0xE9, 0xAD, 0xF7, 0x94, 0xEF, 0xED, 0x3C, 0x2A,
+   0x88, 0xB5, 0x35, 0x9D, 0x9C, 0x32, 0x5E, 0xB6,
+   0x48, 0x9A, 0x7B, 0x26, 0x50, 0x90, 0x04, 0xA7,
+   0xDD, 0x09, 0xB9, 0x98, 0xB2, 0xFE, 0xDF, 0x44,
+   0x89, 0x29, 0x5D, 0xE2, 0x72, 0xC9, 0x28, 0x03,
+   0x43, 0x8C, 0x52, 0x18, 0xC1, 0x56, 0x1B, 0x1A,
+   0x01, 0x65, 0xDA, 0xBF, 0x07, 0xFF, 0x76, 0xE8,
+   0x30, 0xA5, 0x4A, 0xA6, 0x12, 0x62, 0x24, 0x60,
+   0x4B, 0x73, 0x0C, 0xF0, 0xFA, 0x42, 0xF4, 0x00,
+   0xD2, 0xD0, 0xD6, 0x3B, 0xC2, 0x2F, 0xE1, 0x2B,
+   0x70, 0xF8, 0x17, 0xCD, 0xB0, 0xCC, 0x82, 0x2D };
+
+unsigned char table_128[32] = {
+   0x1A, 0x1C, 0x09, 0x17, 0x1B, 0x0B, 0x16, 0x1E,
+   0x14, 0x0C, 0x12, 0x0E, 0x05, 0x03, 0x1F, 0x15,
+   0x19, 0x0D, 0x10, 0x13, 0x0A, 0x01, 0x00, 0x11,
+   0x02, 0x08, 0x0F, 0x18, 0x07, 0x04, 0x1D, 0x06 };
+
+unsigned char table_129[256] = {
+   0x9D, 0x5F, 0xE8, 0x99, 0x57, 0x07, 0x16, 0xA6,
+   0x9F, 0xB6, 0xDE, 0xED, 0x2D, 0xB3, 0xC0, 0x8E,
+   0xCC, 0x49, 0xCE, 0xB0, 0x1B, 0xB1, 0x7A, 0xE0,
+   0xEB, 0x28, 0xDB, 0x7D, 0x88, 0xC8, 0x06, 0x6C,
+   0x02, 0xD0, 0x85, 0x7E, 0xDF, 0xF5, 0x78, 0xE5,
+   0xA9, 0x71, 0xD9, 0xDD, 0xDC, 0xEE, 0x8C, 0x54,
+   0xA0, 0x86, 0xFE, 0x0E, 0x55, 0xF7, 0x41, 0x47,
+   0x1D, 0x15, 0xD6, 0xA4, 0xFF, 0x1F, 0x25, 0xF8,
+   0x12, 0xE9, 0x74, 0x7B, 0x04, 0xE6, 0x4C, 0x31,
+   0xA2, 0xBE, 0x0C, 0xB9, 0x17, 0xBD, 0x3D, 0xF0,
+   0x9E, 0x4D, 0x4E, 0xB2, 0xE7, 0x40, 0xC9, 0x8A,
+   0x67, 0x5E, 0x19, 0x0F, 0xB7, 0x22, 0x8D, 0xBA,
+   0xFC, 0x93, 0x14, 0xEA, 0xFD, 0x0D, 0xD5, 0x38,
+   0xA1, 0x84, 0x1C, 0x35, 0x60, 0x37, 0x43, 0x9C,
+   0xCF, 0xEF, 0x3A, 0x72, 0xF2, 0x61, 0x75, 0x6A,
+   0x42, 0xAC, 0xD3, 0x48, 0x77, 0xC5, 0x29, 0xF6,
+   0x58, 0x79, 0xFA, 0x5D, 0xC7, 0x70, 0x53, 0x9A,
+   0x6F, 0xC1, 0x0A, 0x90, 0x8F, 0x3E, 0x3B, 0x8B,
+   0xEC, 0xBC, 0x20, 0x27, 0xC3, 0x66, 0x3F, 0x33,
+   0xA5, 0x44, 0x2E, 0x32, 0x65, 0x18, 0xFB, 0x59,
+   0x52, 0x50, 0xE2, 0x63, 0x2B, 0xCD, 0x64, 0xCB,
+   0xD2, 0x68, 0x10, 0xA7, 0xAE, 0x11, 0xA8, 0x96,
+   0x69, 0xAF, 0xC2, 0x34, 0x5C, 0x56, 0xE3, 0xF9,
+   0xDA, 0x51, 0x81, 0x4A, 0x05, 0x00, 0xB8, 0x7C,
+   0x30, 0x2F, 0x46, 0xB4, 0xC6, 0x87, 0x4B, 0x94,
+   0x80, 0xF4, 0x7F, 0x3C, 0x26, 0xF1, 0x5B, 0xAB,
+   0x91, 0x6E, 0x08, 0x76, 0x98, 0xD1, 0xE1, 0x36,
+   0x21, 0xCA, 0xD8, 0x24, 0x9B, 0x39, 0xBB, 0xAD,
+   0x13, 0x62, 0x97, 0x1A, 0x6D, 0x2C, 0x5A, 0xC4,
+   0xD4, 0xA3, 0x03, 0xBF, 0x1E, 0xE4, 0xF3, 0x95,
+   0x23, 0x73, 0x92, 0xB5, 0x01, 0x83, 0x82, 0xAA,
+   0x09, 0x45, 0x6B, 0xD7, 0x0B, 0x89, 0x4F, 0x2A };
+
+unsigned char table_130[32] = {
+   0x07, 0x03, 0x15, 0x0B, 0x02, 0x11, 0x17, 0x14,
+   0x05, 0x10, 0x0A, 0x0F, 0x01, 0x1C, 0x1D, 0x0E,
+   0x12, 0x06, 0x18, 0x16, 0x1A, 0x09, 0x13, 0x19,
+   0x1B, 0x00, 0x08, 0x0D, 0x0C, 0x1E, 0x04, 0x1F };
+
+unsigned char table_131[32] = {
+   0x1D, 0x13, 0x1B, 0x10, 0x07, 0x03, 0x0A, 0x02,
+   0x00, 0x0C, 0x0E, 0x0B, 0x0D, 0x18, 0x12, 0x1F,
+   0x1A, 0x04, 0x15, 0x11, 0x1E, 0x08, 0x1C, 0x14,
+   0x19, 0x05, 0x0F, 0x17, 0x06, 0x01, 0x09, 0x16 };
+
+unsigned char table_132[256] = {
+   0x33, 0x8D, 0x45, 0x6F, 0xFF, 0xF5, 0xB6, 0x53,
+   0x3B, 0xF3, 0x07, 0xA4, 0x97, 0xEB, 0x6B, 0xA5,
+   0xD3, 0xDC, 0x7B, 0x79, 0x93, 0xE7, 0xF7, 0x67,
+   0x9C, 0x4F, 0x88, 0xF9, 0x3A, 0x2B, 0x27, 0x48,
+   0x47, 0x18, 0xF4, 0xAD, 0xB4, 0x8F, 0x2A, 0x76,
+   0x17, 0xE9, 0x1F, 0x40, 0x0C, 0x59, 0xD1, 0x4C,
+   0x20, 0x31, 0x73, 0x54, 0xCD, 0x68, 0x08, 0x52,
+   0x10, 0x62, 0x3D, 0xD2, 0x77, 0xF2, 0xD7, 0x30,
+   0xCA, 0x16, 0x01, 0x50, 0x9F, 0x3F, 0x75, 0xED,
+   0x90, 0x6A, 0x34, 0xCE, 0x05, 0x78, 0x5E, 0xD6,
+   0x85, 0xCC, 0x29, 0xB8, 0xC1, 0x0D, 0xCB, 0x80,
+   0x2E, 0x04, 0x00, 0x44, 0x32, 0x95, 0xBF, 0xFE,
+   0x6E, 0x7C, 0xFD, 0xA7, 0x3C, 0x5C, 0xF0, 0xEC,
+   0xAC, 0xF8, 0xB9, 0xC0, 0x1B, 0x3E, 0xE8, 0x66,
+   0x5D, 0xDE, 0x49, 0x71, 0xAA, 0xAF, 0x21, 0x64,
+   0x28, 0x8A, 0x4E, 0x98, 0x58, 0xA2, 0x23, 0xCF,
+   0x9E, 0x63, 0x61, 0x91, 0x12, 0xC6, 0x8C, 0x19,
+   0xA8, 0xD4, 0xC7, 0xDD, 0xFC, 0xBD, 0x38, 0xDF,
+   0xEA, 0x2D, 0x7E, 0x7D, 0xE3, 0xE0, 0xC3, 0xD9,
+   0x8B, 0x11, 0xF1, 0x4D, 0xC8, 0xB5, 0x55, 0xAE,
+   0xE1, 0x89, 0xE5, 0xB3, 0xBC, 0x69, 0x9D, 0xA6,
+   0x09, 0x9A, 0x74, 0x35, 0x1A, 0xFB, 0x24, 0xB7,
+   0x13, 0x14, 0x94, 0x0A, 0x86, 0x0F, 0x60, 0x51,
+   0xB0, 0x84, 0x22, 0x5B, 0x87, 0x43, 0x57, 0x0B,
+   0x2F, 0x5F, 0x02, 0xD0, 0xBB, 0xA3, 0xC9, 0x7A,
+   0xBE, 0xC2, 0x26, 0x46, 0xDB, 0x1E, 0x1D, 0x92,
+   0xE2, 0xB2, 0x37, 0x6D, 0xD5, 0x4A, 0x0E, 0x4B,
+   0x8E, 0xC5, 0x42, 0x99, 0xEE, 0xE4, 0xB1, 0x06,
+   0xAB, 0x5A, 0x56, 0x41, 0x65, 0xBA, 0xFA, 0x83,
+   0x15, 0xDA, 0x72, 0xA1, 0x81, 0x1C, 0xA9, 0x36,
+   0x25, 0x96, 0x6C, 0x39, 0x82, 0xE6, 0x2C, 0x9B,
+   0xC4, 0x7F, 0xA0, 0xD8, 0xEF, 0x03, 0x70, 0xF6 };
+
+unsigned char table_133[256] = {
+   0x02, 0xF0, 0xED, 0xC4, 0xE4, 0x67, 0x60, 0x8B,
+   0xF3, 0x77, 0x92, 0xE0, 0x85, 0x93, 0x1E, 0x8E,
+   0x9A, 0x38, 0x61, 0x20, 0xB7, 0x68, 0xE1, 0x5E,
+   0xD5, 0x63, 0xA9, 0xA5, 0xBE, 0x36, 0x12, 0x4D,
+   0x86, 0x16, 0xD6, 0xB1, 0x23, 0x64, 0x4F, 0x62,
+   0xFC, 0xA3, 0xD3, 0x04, 0x7D, 0x8C, 0xE2, 0xFF,
+   0x5D, 0x30, 0xF5, 0x95, 0x1B, 0x5F, 0x73, 0xAA,
+   0xE8, 0x07, 0x87, 0xDC, 0x54, 0x7C, 0xEE, 0x00,
+   0xB8, 0xDE, 0x55, 0xBA, 0xD0, 0x50, 0xBB, 0x89,
+   0x1C, 0xCC, 0x0E, 0xC0, 0x42, 0x11, 0xD8, 0xA2,
+   0x2E, 0x33, 0xFE, 0x26, 0xD4, 0x10, 0xDA, 0xC5,
+   0xFB, 0xAF, 0x98, 0x78, 0xB5, 0xBD, 0xC8, 0x8D,
+   0x46, 0xA0, 0xD1, 0x7B, 0xBC, 0x75, 0xAB, 0x25,
+   0xB2, 0x43, 0x57, 0xB6, 0xEC, 0xF4, 0x66, 0x05,
+   0x9C, 0x08, 0x53, 0x80, 0xEA, 0x21, 0x2C, 0x6C,
+   0x17, 0x71, 0xD2, 0x70, 0x76, 0x9E, 0x6B, 0x7A,
+   0x58, 0xA7, 0xBF, 0x29, 0x03, 0x1F, 0x06, 0xC1,
+   0xDD, 0x2F, 0x5C, 0x0B, 0x0D, 0x8A, 0x0A, 0xCB,
+   0xCA, 0x6F, 0x19, 0x6A, 0xFA, 0xF7, 0xA8, 0xA1,
+   0xEB, 0x88, 0x44, 0xAC, 0x01, 0x4E, 0x59, 0x94,
+   0x72, 0x2B, 0xE9, 0x0F, 0x22, 0x9B, 0x27, 0x37,
+   0x41, 0xF9, 0xF2, 0xE3, 0xEF, 0xB3, 0xD9, 0x2A,
+   0x31, 0xC2, 0x0C, 0x15, 0x90, 0x14, 0xF6, 0x83,
+   0xFD, 0x96, 0x9D, 0x7F, 0xA4, 0x39, 0xE7, 0x3F,
+   0xE6, 0xC7, 0xCD, 0x1A, 0xCF, 0x48, 0x3C, 0x51,
+   0x6D, 0x5B, 0x74, 0xC3, 0xC9, 0x09, 0x3D, 0x9F,
+   0xDB, 0x32, 0x40, 0x18, 0xD7, 0xCE, 0x69, 0x49,
+   0x3A, 0xF1, 0xB9, 0x56, 0x91, 0x99, 0x84, 0x24,
+   0x7E, 0x34, 0x4B, 0xA6, 0x47, 0xB4, 0x6E, 0xDF,
+   0x65, 0x3B, 0xAD, 0x45, 0x13, 0xC6, 0x81, 0xF8,
+   0x4A, 0x2D, 0x8F, 0x4C, 0x97, 0x28, 0x3E, 0xE5,
+   0x5A, 0x35, 0xB0, 0xAE, 0x82, 0x79, 0x1D, 0x52 };
+
+unsigned char table_134[32] = {
+   0x09, 0x0F, 0x10, 0x0C, 0x03, 0x15, 0x07, 0x17,
+   0x0E, 0x0B, 0x1D, 0x08, 0x19, 0x11, 0x00, 0x0A,
+   0x01, 0x06, 0x18, 0x16, 0x0D, 0x13, 0x14, 0x12,
+   0x02, 0x1B, 0x1A, 0x04, 0x05, 0x1F, 0x1C, 0x1E };
+
+unsigned char table_135[256] = {
+   0x14, 0x34, 0xEA, 0x02, 0x2B, 0x5A, 0x10, 0x51,
+   0xF3, 0x8F, 0x28, 0xB2, 0x50, 0x8B, 0x01, 0xCC,
+   0x80, 0x15, 0x29, 0x42, 0xF4, 0x1D, 0xFB, 0xBB,
+   0x1F, 0x43, 0x8C, 0x17, 0x1E, 0x81, 0x04, 0x98,
+   0x46, 0xD8, 0xD5, 0x65, 0x4C, 0x1C, 0xDB, 0x40,
+   0x5F, 0x1A, 0x31, 0x74, 0xF1, 0x64, 0x19, 0x05,
+   0xFC, 0xF0, 0x73, 0xB6, 0x23, 0x77, 0x9C, 0xCE,
+   0x70, 0xEF, 0xDA, 0xE0, 0xA2, 0x78, 0x84, 0xEB,
+   0x9E, 0xC5, 0x95, 0xA3, 0xF6, 0xCA, 0xAD, 0x52,
+   0xD0, 0x3F, 0x54, 0xA7, 0x33, 0xA9, 0x09, 0x6A,
+   0x89, 0x7E, 0x75, 0xA8, 0xD6, 0x79, 0x9F, 0xAB,
+   0x8E, 0x11, 0x0E, 0x3B, 0xAA, 0xE6, 0x85, 0x53,
+   0x0A, 0x59, 0xEC, 0x94, 0xD7, 0x41, 0x86, 0x7D,
+   0x2F, 0xC7, 0xDE, 0x06, 0xCB, 0x13, 0xBA, 0x58,
+   0xC8, 0xC9, 0x07, 0x67, 0x7F, 0xA5, 0xB4, 0x2C,
+   0x48, 0x6C, 0xB8, 0xD1, 0x30, 0xD3, 0x35, 0x4F,
+   0x88, 0x26, 0x93, 0x32, 0x71, 0x3E, 0x3D, 0xF7,
+   0x6D, 0x03, 0xED, 0x8A, 0x36, 0x55, 0x9B, 0x66,
+   0x8D, 0x27, 0x7C, 0xF9, 0xA6, 0xC3, 0x20, 0x69,
+   0x4A, 0xE3, 0x99, 0x5C, 0xBC, 0x45, 0x16, 0x6B,
+   0xB9, 0x49, 0x82, 0xFF, 0xBD, 0xDD, 0xE9, 0x0C,
+   0xD4, 0x44, 0xFD, 0x22, 0xE5, 0xAC, 0x61, 0xC4,
+   0x90, 0x47, 0x37, 0x72, 0xA4, 0x7A, 0x24, 0x4D,
+   0x5B, 0x12, 0x38, 0x92, 0x87, 0x1B, 0xE1, 0xA0,
+   0x91, 0x3C, 0xEE, 0x6F, 0xC1, 0x0F, 0x56, 0xC2,
+   0x9A, 0xF8, 0x18, 0xE8, 0xD2, 0xDC, 0x4B, 0xCF,
+   0x39, 0xF5, 0xFE, 0x2A, 0x2D, 0x9D, 0xA1, 0xFA,
+   0xE7, 0xBF, 0x6E, 0xE4, 0x2E, 0xB3, 0xCD, 0xE2,
+   0xAF, 0x7B, 0xC0, 0x68, 0x97, 0xB5, 0x5D, 0xB7,
+   0x21, 0x57, 0x83, 0x76, 0xB1, 0xAE, 0x5E, 0x0D,
+   0x96, 0x4E, 0x08, 0xC6, 0x0B, 0xDF, 0x3A, 0xB0,
+   0x00, 0x63, 0xD9, 0xBE, 0xF2, 0x60, 0x25, 0x62 };
+
+unsigned char table_136[256] = {
+   0xD3, 0x1A, 0x00, 0xED, 0x59, 0x24, 0xA3, 0xF2,
+   0xBA, 0x58, 0x4C, 0x5C, 0x75, 0x48, 0x98, 0xB0,
+   0xCF, 0xC3, 0xF7, 0x88, 0x70, 0xB3, 0x3D, 0x3E,
+   0x03, 0xF9, 0xC9, 0xFD, 0x80, 0x44, 0x7F, 0x3B,
+   0x95, 0x5F, 0x31, 0x47, 0x15, 0x07, 0xB8, 0x08,
+   0xCE, 0xDA, 0x71, 0x9F, 0x83, 0xB1, 0x55, 0x16,
+   0xE6, 0xB2, 0xC7, 0xBE, 0x54, 0xE7, 0x2E, 0x8D,
+   0x12, 0x21, 0x41, 0x69, 0xFE, 0x28, 0x11, 0x56,
+   0x5A, 0xDD, 0xB6, 0x87, 0x78, 0x82, 0x4D, 0x7B,
+   0x50, 0x9A, 0x9E, 0x62, 0xF8, 0x0A, 0x64, 0xF1,
+   0x4E, 0x33, 0xAD, 0xBB, 0x79, 0x76, 0xD8, 0xCD,
+   0x86, 0x34, 0x29, 0xD5, 0x7D, 0x72, 0xC5, 0xC1,
+   0xDF, 0x09, 0x4A, 0xB4, 0xD2, 0x7A, 0xF0, 0xCC,
+   0x0F, 0xA7, 0xD6, 0x2B, 0x20, 0x26, 0xEF, 0xAB,
+   0x74, 0x1E, 0xE3, 0x77, 0xCB, 0x7C, 0x73, 0x5E,
+   0x6B, 0x0D, 0x65, 0xA6, 0x30, 0xFB, 0xD0, 0xB7,
+   0xAA, 0x94, 0x9D, 0x85, 0x13, 0x18, 0xA8, 0xF3,
+   0xE0, 0xBC, 0x45, 0xCA, 0xC8, 0xDC, 0xE2, 0x3C,
+   0x23, 0xE5, 0xB9, 0x90, 0x49, 0xA5, 0xE4, 0x36,
+   0xFC, 0x53, 0xF6, 0xE8, 0xC6, 0x2C, 0x02, 0x25,
+   0xC0, 0x8F, 0x61, 0xA4, 0x39, 0x8C, 0x5D, 0xAE,
+   0x22, 0x1C, 0x2F, 0xD4, 0x6C, 0xD1, 0x51, 0xEA,
+   0x4F, 0x7E, 0xA0, 0xF5, 0x6A, 0x32, 0xA2, 0x01,
+   0xB5, 0x10, 0x2A, 0xAC, 0xA9, 0x06, 0xC4, 0x91,
+   0x68, 0xE1, 0xBD, 0x14, 0x38, 0xFA, 0x6E, 0x3F,
+   0x37, 0x66, 0xDB, 0x57, 0x43, 0x1B, 0x67, 0xAF,
+   0x1F, 0x0B, 0x6D, 0x2D, 0x89, 0x04, 0x4B, 0x52,
+   0xC2, 0xBF, 0xA1, 0x92, 0x99, 0x6F, 0x63, 0x81,
+   0x27, 0x05, 0x96, 0x3A, 0xEC, 0x0E, 0x97, 0xD9,
+   0xDE, 0x46, 0x35, 0x8B, 0x8E, 0x8A, 0xF4, 0xFF,
+   0x60, 0xD7, 0xE9, 0x17, 0xEB, 0x9C, 0x84, 0x0C,
+   0x93, 0x1D, 0x9B, 0x5B, 0x40, 0xEE, 0x42, 0x19 };
+
+unsigned char table_137[32] = {
+   0x0F, 0x09, 0x02, 0x06, 0x18, 0x0B, 0x1E, 0x05,
+   0x11, 0x1D, 0x16, 0x01, 0x13, 0x10, 0x0E, 0x1A,
+   0x1B, 0x00, 0x0D, 0x08, 0x15, 0x14, 0x19, 0x17,
+   0x03, 0x1F, 0x0A, 0x12, 0x0C, 0x07, 0x04, 0x1C };
+
+unsigned char table_138[32] = {
+   0x0D, 0x1C, 0x1F, 0x15, 0x0F, 0x14, 0x1B, 0x12,
+   0x09, 0x0B, 0x19, 0x07, 0x11, 0x16, 0x0C, 0x04,
+   0x13, 0x05, 0x1D, 0x03, 0x0E, 0x0A, 0x08, 0x1E,
+   0x01, 0x06, 0x18, 0x17, 0x10, 0x1A, 0x02, 0x00 };
+
+unsigned char table_139[32] = {
+   0x05, 0x15, 0x1D, 0x02, 0x0F, 0x03, 0x17, 0x1A,
+   0x0A, 0x00, 0x1F, 0x12, 0x0E, 0x11, 0x1B, 0x13,
+   0x0B, 0x0D, 0x09, 0x18, 0x1E, 0x08, 0x14, 0x07,
+   0x0C, 0x04, 0x16, 0x19, 0x1C, 0x06, 0x10, 0x01 };
+
+unsigned char table_140[32] = {
+   0x06, 0x1E, 0x0C, 0x11, 0x13, 0x08, 0x15, 0x01,
+   0x1D, 0x03, 0x0F, 0x19, 0x18, 0x04, 0x00, 0x14,
+   0x12, 0x1A, 0x0B, 0x0E, 0x02, 0x1B, 0x07, 0x05,
+   0x1F, 0x17, 0x09, 0x0A, 0x0D, 0x16, 0x10, 0x1C };
+
+unsigned char table_141[256] = {
+   0xE1, 0x0A, 0x28, 0xCD, 0x8A, 0x1E, 0x26, 0x10,
+   0xC0, 0x6F, 0x06, 0x2C, 0xF8, 0x51, 0x6C, 0x8F,
+   0xA8, 0x8C, 0x41, 0xF4, 0xED, 0x36, 0xAC, 0x89,
+   0xBD, 0x9D, 0x42, 0x50, 0x95, 0x07, 0x2A, 0x9B,
+   0x7E, 0xA3, 0x6B, 0x30, 0x72, 0x4E, 0xBE, 0xD8,
+   0x8B, 0x5B, 0x1A, 0x56, 0x05, 0xEF, 0xEE, 0x64,
+   0xFF, 0xFD, 0x93, 0xB5, 0xD6, 0x04, 0x57, 0xAE,
+   0x4D, 0x6D, 0x2F, 0xBA, 0x40, 0xE0, 0xDB, 0xF2,
+   0xCC, 0x08, 0x35, 0x02, 0xC4, 0x65, 0x66, 0x76,
+   0xA1, 0x97, 0x9F, 0x6A, 0x90, 0xA7, 0x34, 0x1B,
+   0x18, 0xB9, 0xA2, 0xDE, 0x23, 0x1F, 0xCB, 0xE6,
+   0xAB, 0xCF, 0xAD, 0x4A, 0xF7, 0x24, 0xD0, 0xE8,
+   0x8D, 0x49, 0xEA, 0x0F, 0x94, 0x22, 0xD3, 0x74,
+   0x71, 0x0D, 0x21, 0x14, 0x39, 0x4B, 0x16, 0x25,
+   0x5A, 0xB7, 0x17, 0x67, 0x59, 0x47, 0x27, 0x4F,
+   0x32, 0x3B, 0x63, 0x0C, 0xF0, 0xF3, 0x7B, 0xC7,
+   0xCA, 0x3A, 0x9A, 0xE2, 0xD5, 0xFA, 0x91, 0xFC,
+   0x86, 0x81, 0x99, 0xB4, 0xBC, 0x7C, 0xC5, 0xBF,
+   0xC1, 0xF5, 0x77, 0xA4, 0x79, 0x11, 0x8E, 0x75,
+   0x55, 0x3D, 0x78, 0x20, 0x37, 0x3E, 0x85, 0xE4,
+   0x2E, 0x82, 0xA9, 0x7A, 0x31, 0xC9, 0xB3, 0xFE,
+   0x4C, 0x7D, 0xC3, 0xA0, 0x0E, 0x96, 0x5C, 0xC6,
+   0x1C, 0x5F, 0xD7, 0xDD, 0x83, 0xC8, 0x9E, 0xEC,
+   0x3F, 0xAF, 0x38, 0x9C, 0xD9, 0xB6, 0xDA, 0xD4,
+   0x61, 0x44, 0x43, 0xAA, 0xB1, 0xCE, 0xE7, 0x84,
+   0x00, 0x0B, 0xFB, 0x68, 0xC2, 0x3C, 0x58, 0xB2,
+   0x69, 0x7F, 0x33, 0x2B, 0x80, 0x03, 0xE9, 0x88,
+   0x29, 0x12, 0x01, 0x6E, 0x62, 0xF1, 0xA6, 0xF9,
+   0x5D, 0xD2, 0xE3, 0x53, 0x09, 0x2D, 0xBB, 0x15,
+   0xEB, 0x13, 0xA5, 0xF6, 0x73, 0x19, 0x60, 0xB0,
+   0xD1, 0x48, 0x92, 0x1D, 0x52, 0x5E, 0x45, 0x70,
+   0x98, 0x54, 0xB8, 0xDC, 0x46, 0xDF, 0x87, 0xE5 };
+
+unsigned char table_142[256] = {
+   0x90, 0x94, 0xBE, 0x14, 0x99, 0xEB, 0x45, 0x0F,
+   0x34, 0x4A, 0xE3, 0x79, 0xD2, 0x64, 0x4D, 0x69,
+   0x91, 0xDE, 0xB9, 0x1C, 0x59, 0x20, 0x6C, 0x0B,
+   0x16, 0xC7, 0x1D, 0x18, 0x02, 0x7D, 0x13, 0xB2,
+   0x7B, 0x81, 0xCF, 0x61, 0xA3, 0x33, 0x00, 0x73,
+   0x5A, 0x8A, 0xA1, 0xA8, 0x31, 0xAC, 0xF0, 0x67,
+   0xAE, 0xA5, 0x2A, 0x96, 0x58, 0xF4, 0xB7, 0x0E,
+   0xE1, 0x54, 0x27, 0x83, 0x09, 0x85, 0xF8, 0x84,
+   0xEA, 0xAD, 0x06, 0xED, 0x43, 0xFF, 0xA2, 0x6E,
+   0x68, 0x46, 0x74, 0x47, 0x3C, 0xAA, 0xBC, 0x55,
+   0xA7, 0xC3, 0x82, 0xDC, 0xBF, 0x38, 0x80, 0x15,
+   0xF6, 0xB3, 0x92, 0x7C, 0x93, 0x3F, 0xE9, 0x4C,
+   0x35, 0x30, 0x32, 0xF3, 0x88, 0xC0, 0x49, 0x6D,
+   0xCE, 0x42, 0xDF, 0xFD, 0x78, 0x6A, 0x24, 0xCA,
+   0xB8, 0xFC, 0xA6, 0x5F, 0x29, 0xFE, 0x0C, 0x5C,
+   0x0D, 0x23, 0x8B, 0x9D, 0xD4, 0x03, 0x2C, 0x9C,
+   0x77, 0xD8, 0x39, 0x8C, 0x57, 0xD5, 0xE0, 0x8F,
+   0xC6, 0xB0, 0xCD, 0x48, 0xC9, 0xA0, 0xDA, 0xC8,
+   0xD1, 0x5B, 0xAB, 0x37, 0x5D, 0x63, 0xAF, 0xF9,
+   0x17, 0x1B, 0xE5, 0xF1, 0x36, 0xC1, 0x04, 0x26,
+   0x6F, 0x9E, 0xD9, 0x2F, 0x7F, 0xB5, 0x3A, 0xD6,
+   0xE6, 0x40, 0x07, 0xCB, 0x7E, 0x3E, 0xC5, 0x22,
+   0xEC, 0xE2, 0xD3, 0x4E, 0x65, 0x2D, 0x70, 0xE7,
+   0x10, 0x19, 0xD0, 0xEF, 0xBD, 0xC2, 0x44, 0xB4,
+   0xF7, 0xA4, 0x53, 0x9F, 0x86, 0xFA, 0xE8, 0x4B,
+   0x28, 0x3D, 0x9B, 0x56, 0x89, 0x6B, 0x25, 0x71,
+   0x60, 0x11, 0x9A, 0x5E, 0x1A, 0x52, 0x08, 0x4F,
+   0xB1, 0xDD, 0xBB, 0x98, 0xFB, 0x12, 0x3B, 0x0A,
+   0x2E, 0xDB, 0x62, 0x8D, 0xC4, 0x75, 0xA9, 0x2B,
+   0xE4, 0x97, 0x72, 0xF5, 0xEE, 0xF2, 0xB6, 0x21,
+   0xBA, 0x7A, 0x76, 0x41, 0x50, 0x66, 0x05, 0x8E,
+   0xCC, 0x1E, 0x87, 0xD7, 0x01, 0x1F, 0x51, 0x95 };
+
+unsigned char table_143[32] = {
+   0x0E, 0x16, 0x18, 0x11, 0x0C, 0x01, 0x12, 0x1F,
+   0x08, 0x15, 0x0A, 0x06, 0x1C, 0x1E, 0x02, 0x1A,
+   0x17, 0x03, 0x07, 0x13, 0x05, 0x19, 0x10, 0x0F,
+   0x0D, 0x14, 0x09, 0x0B, 0x1B, 0x00, 0x1D, 0x04 };
+
+unsigned char table_144[32] = {
+   0x00, 0x1B, 0x17, 0x19, 0x1D, 0x11, 0x0D, 0x1A,
+   0x13, 0x03, 0x1E, 0x09, 0x10, 0x0E, 0x15, 0x05,
+   0x0B, 0x1C, 0x1F, 0x08, 0x0A, 0x06, 0x01, 0x0F,
+   0x16, 0x14, 0x02, 0x04, 0x07, 0x18, 0x12, 0x0C };
+
+unsigned char table_145[256] = {
+   0xF9, 0x2C, 0x38, 0x74, 0xDA, 0x65, 0x85, 0x0E,
+   0xBA, 0x64, 0xDB, 0xE3, 0xB6, 0x8B, 0x0B, 0x5E,
+   0x01, 0x0F, 0x12, 0x8C, 0xD4, 0xCC, 0xB1, 0x7B,
+   0xE7, 0xBC, 0x2E, 0x87, 0x84, 0x3B, 0xF8, 0x4C,
+   0x8E, 0x59, 0x2D, 0xAA, 0xCE, 0x28, 0x1B, 0xEE,
+   0x7F, 0x5C, 0xFB, 0x62, 0x05, 0xD9, 0xDD, 0x9D,
+   0x49, 0x66, 0x82, 0x71, 0xD2, 0xC7, 0xEB, 0xCF,
+   0x5B, 0x41, 0x25, 0xC8, 0x6C, 0xFF, 0x78, 0x97,
+   0x0C, 0xA2, 0x50, 0x7A, 0xAF, 0x2F, 0xB0, 0x7E,
+   0xBB, 0x73, 0xA0, 0x9B, 0x09, 0xDE, 0x35, 0xE9,
+   0x5A, 0x70, 0x56, 0xC5, 0x81, 0x19, 0x55, 0xAB,
+   0xC1, 0xB4, 0x2A, 0x30, 0x54, 0x6F, 0x3E, 0x46,
+   0x5D, 0x37, 0xF5, 0x57, 0x6B, 0x7C, 0x43, 0xE1,
+   0x4A, 0x3F, 0xB2, 0x4B, 0x77, 0xB5, 0x44, 0xD6,
+   0x91, 0x11, 0x72, 0xE8, 0xBE, 0xA5, 0xA8, 0xD3,
+   0x9A, 0x17, 0x86, 0x88, 0x16, 0x3C, 0x36, 0xD8,
+   0x6E, 0x07, 0x8D, 0x5F, 0xFA, 0xF1, 0x24, 0x7D,
+   0x20, 0x60, 0x0D, 0x89, 0xC9, 0x29, 0xA7, 0x2B,
+   0x4E, 0x10, 0x9F, 0xE5, 0x61, 0x32, 0x3A, 0xBF,
+   0x93, 0xE6, 0xF3, 0x52, 0x80, 0xC4, 0x02, 0x22,
+   0xA4, 0xBD, 0xF0, 0x48, 0x51, 0xF2, 0xD7, 0x33,
+   0x00, 0x53, 0x98, 0xEC, 0x47, 0x39, 0xB9, 0x90,
+   0x76, 0x4F, 0x68, 0x3D, 0x9C, 0x92, 0xD5, 0xB8,
+   0xAE, 0xD0, 0xF4, 0x67, 0x58, 0xC0, 0x06, 0x08,
+   0x14, 0x31, 0xDC, 0xA1, 0x15, 0xDF, 0xCA, 0xE2,
+   0x23, 0xFE, 0xE4, 0x8F, 0x0A, 0xFC, 0x8A, 0xA3,
+   0xC6, 0xCD, 0x6A, 0x75, 0xFD, 0x42, 0xB7, 0x79,
+   0x96, 0x1D, 0x63, 0x18, 0xA9, 0x1C, 0x83, 0x6D,
+   0xE0, 0x34, 0x04, 0xA6, 0x13, 0xAC, 0xD1, 0xF7,
+   0x26, 0xC3, 0x1F, 0x27, 0x45, 0x95, 0xCB, 0x21,
+   0xED, 0x1A, 0x9E, 0x99, 0xEA, 0x40, 0x94, 0x4D,
+   0x69, 0xF6, 0xEF, 0xC2, 0xAD, 0x03, 0xB3, 0x1E };
+
+unsigned char table_146[256] = {
+   0x1C, 0xF5, 0x16, 0xD2, 0xCC, 0xDC, 0x1E, 0x29,
+   0xE3, 0x17, 0x3B, 0x66, 0x6A, 0xF7, 0x03, 0xB2,
+   0x92, 0x45, 0x4D, 0xD6, 0x0C, 0x5E, 0xE6, 0x01,
+   0xDE, 0xCE, 0x83, 0xFA, 0x35, 0x02, 0x85, 0xC4,
+   0x2E, 0x89, 0x8D, 0xE7, 0x30, 0x93, 0xDD, 0x70,
+   0x80, 0xD9, 0x6D, 0x81, 0x07, 0x8E, 0xA9, 0xA6,
+   0x5F, 0xC9, 0xF3, 0x9D, 0x65, 0xE8, 0x88, 0x0B,
+   0x49, 0xAA, 0xB7, 0x6C, 0x11, 0xFC, 0x6F, 0xA3,
+   0xF8, 0x52, 0x0E, 0xD4, 0x08, 0x25, 0x27, 0x33,
+   0x2F, 0xF0, 0x2B, 0x47, 0xDA, 0x4C, 0x39, 0x54,
+   0xB9, 0xC1, 0xEA, 0x7C, 0x44, 0xEB, 0x06, 0xE1,
+   0x8C, 0x9B, 0x74, 0x42, 0x4F, 0x0A, 0x69, 0x2A,
+   0x2D, 0xA1, 0x19, 0xD5, 0xC3, 0x87, 0x68, 0xFF,
+   0xEC, 0xE4, 0x86, 0xCF, 0xF6, 0x79, 0x34, 0xA8,
+   0x72, 0xF4, 0x8B, 0xAF, 0xA5, 0x00, 0xBA, 0x5C,
+   0x23, 0xB8, 0xC8, 0x59, 0xBF, 0x6E, 0xCB, 0x20,
+   0x1F, 0x53, 0x97, 0x4B, 0xD0, 0x55, 0x5B, 0xDF,
+   0x8A, 0xED, 0x9A, 0x62, 0xC5, 0xD7, 0x18, 0x82,
+   0xC7, 0x12, 0x15, 0x1B, 0xC0, 0x38, 0xCA, 0x26,
+   0xDB, 0xAE, 0xF9, 0x90, 0x1A, 0xF2, 0x56, 0x32,
+   0x21, 0x3C, 0x43, 0xEE, 0xA4, 0x13, 0x94, 0xA2,
+   0x46, 0x77, 0xBC, 0xB6, 0x9C, 0x0D, 0xCD, 0x37,
+   0x63, 0x60, 0x6B, 0x3A, 0x3E, 0xA7, 0xD8, 0xFE,
+   0xFB, 0xEF, 0x67, 0xFD, 0xAD, 0xF1, 0x09, 0x1D,
+   0xE9, 0x51, 0xB4, 0x95, 0x75, 0x0F, 0xB3, 0xD3,
+   0xAB, 0x22, 0xBB, 0x61, 0x7F, 0x5A, 0x58, 0x7B,
+   0x73, 0xC2, 0x05, 0xE0, 0x14, 0xE2, 0xAC, 0x91,
+   0xBE, 0x4E, 0xC6, 0x7A, 0x84, 0x50, 0x28, 0x3F,
+   0xB0, 0x04, 0x7E, 0xD1, 0x40, 0xBD, 0xE5, 0x71,
+   0xB1, 0x78, 0x41, 0x9E, 0x57, 0x64, 0x8F, 0x24,
+   0x4A, 0x9F, 0x3D, 0x31, 0x36, 0x5D, 0xA0, 0x2C,
+   0x7D, 0x96, 0x76, 0x99, 0xB5, 0x48, 0x98, 0x10 };
+
+unsigned char table_147[32] = {
+   0x17, 0x07, 0x0D, 0x16, 0x00, 0x1B, 0x1F, 0x09,
+   0x10, 0x11, 0x14, 0x0A, 0x02, 0x06, 0x13, 0x0C,
+   0x08, 0x1E, 0x0F, 0x12, 0x05, 0x15, 0x19, 0x01,
+   0x1C, 0x1A, 0x03, 0x18, 0x04, 0x0B, 0x1D, 0x0E };
+
+unsigned char table_148[256] = {
+   0xFB, 0x23, 0xBC, 0x5A, 0x8C, 0x02, 0x42, 0x3B,
+   0x95, 0x0C, 0x21, 0x0E, 0x14, 0xDF, 0x11, 0xC0,
+   0xDB, 0x5E, 0xD3, 0xEA, 0xCE, 0xB4, 0x32, 0x12,
+   0x70, 0x68, 0xA3, 0x25, 0x5B, 0x4B, 0x47, 0xA5,
+   0x84, 0x9B, 0xFA, 0xD1, 0xE1, 0x3C, 0x20, 0x93,
+   0x41, 0x26, 0x81, 0x39, 0x17, 0xA4, 0xCF, 0xB9,
+   0xC5, 0x5F, 0x1C, 0xB3, 0x88, 0xC2, 0x92, 0x30,
+   0x0A, 0xB8, 0xA0, 0xE2, 0x50, 0x2B, 0x48, 0x1E,
+   0xD5, 0x13, 0xC7, 0x46, 0x9E, 0x2A, 0xF7, 0x7E,
+   0xE8, 0x82, 0x60, 0x7A, 0x36, 0x97, 0x0F, 0x8F,
+   0x8B, 0x80, 0xE0, 0xEB, 0xB1, 0xC6, 0x6E, 0xAE,
+   0x90, 0x76, 0xA7, 0x31, 0xBE, 0x9C, 0x18, 0x6D,
+   0xAB, 0x6C, 0x7B, 0xFE, 0x62, 0x05, 0xE9, 0x66,
+   0x2E, 0x38, 0xB5, 0xB2, 0xFD, 0xFC, 0x7F, 0xE3,
+   0xA1, 0xF1, 0x99, 0x4D, 0x79, 0x22, 0xD2, 0x37,
+   0x29, 0x01, 0x54, 0x00, 0xBD, 0x51, 0x1B, 0x07,
+   0x0B, 0x4A, 0xEE, 0x57, 0xDA, 0x1A, 0x06, 0xCA,
+   0xCB, 0x9A, 0xC9, 0x7D, 0xE4, 0xDC, 0xE5, 0x8D,
+   0x75, 0x4F, 0xF6, 0xA2, 0x65, 0x7C, 0xD9, 0x9D,
+   0x03, 0x27, 0x2D, 0x4C, 0x49, 0xD4, 0x5D, 0x3E,
+   0xBA, 0x1D, 0xD8, 0x91, 0x74, 0x10, 0xF8, 0xDE,
+   0xEF, 0xF0, 0x6A, 0x04, 0x72, 0x08, 0x78, 0x3A,
+   0x53, 0xC4, 0x34, 0xF2, 0x64, 0xAF, 0x86, 0xC3,
+   0xF3, 0x73, 0x67, 0xCC, 0x58, 0xF4, 0x96, 0xAC,
+   0x3D, 0xE7, 0x15, 0x8E, 0x19, 0x61, 0xF9, 0xB6,
+   0xCD, 0x87, 0xAA, 0xB0, 0x1F, 0x6F, 0xAD, 0x28,
+   0xC8, 0x69, 0x56, 0xC1, 0x71, 0xED, 0xE6, 0x98,
+   0x6B, 0x59, 0xB7, 0xF5, 0x2C, 0xEC, 0xA8, 0x94,
+   0x89, 0xBB, 0xA9, 0xD7, 0x2F, 0x8A, 0x4E, 0xD6,
+   0x33, 0x16, 0x0D, 0x83, 0x5C, 0x52, 0x85, 0xA6,
+   0x40, 0x45, 0x9F, 0x44, 0x63, 0x35, 0x77, 0xFF,
+   0x09, 0x43, 0xBF, 0xD0, 0x55, 0xDD, 0x3F, 0x24 };
+
+unsigned char table_149[32] = {
+   0x1B, 0x0B, 0x0C, 0x06, 0x1F, 0x17, 0x04, 0x1A,
+   0x1E, 0x02, 0x0F, 0x16, 0x0E, 0x09, 0x10, 0x01,
+   0x13, 0x19, 0x11, 0x00, 0x0A, 0x05, 0x03, 0x1C,
+   0x18, 0x1D, 0x14, 0x0D, 0x07, 0x08, 0x15, 0x12 };
+
+unsigned char table_150[256] = {
+   0x57, 0xBC, 0x9D, 0x46, 0x14, 0xD0, 0x94, 0x95,
+   0x1B, 0x12, 0xB8, 0xD4, 0x53, 0x73, 0x83, 0xE6,
+   0x75, 0xE1, 0xD1, 0x0D, 0xDF, 0x23, 0x13, 0x40,
+   0xF1, 0x0C, 0xA0, 0xC1, 0x22, 0xDA, 0xE8, 0xFB,
+   0xE5, 0xC4, 0x16, 0x9C, 0x3F, 0xC3, 0x78, 0x3A,
+   0x06, 0xC7, 0xA8, 0x79, 0xA4, 0xB3, 0x55, 0x88,
+   0xA9, 0x82, 0xE3, 0x68, 0xFC, 0x3B, 0x26, 0x81,
+   0xB4, 0x0A, 0x7D, 0x96, 0xDB, 0x2C, 0xE2, 0xCD,
+   0x92, 0x5C, 0xED, 0x0E, 0x42, 0x98, 0xBE, 0xB7,
+   0x63, 0x25, 0x7B, 0xD9, 0xEF, 0x11, 0xB9, 0xA3,
+   0xFA, 0x00, 0x2A, 0x91, 0x71, 0xBF, 0xB2, 0x3D,
+   0x20, 0x4C, 0xB0, 0x8C, 0x3C, 0x27, 0xAF, 0x09,
+   0x10, 0x5D, 0x2B, 0x1D, 0xBD, 0x4B, 0x54, 0xD3,
+   0xAB, 0x1A, 0xE7, 0xF8, 0x56, 0x65, 0xA5, 0xAD,
+   0xEC, 0x17, 0x45, 0x28, 0xCA, 0xEA, 0x01, 0xF5,
+   0x34, 0x84, 0x43, 0x8B, 0x03, 0x02, 0x90, 0x6B,
+   0x60, 0xCE, 0x19, 0x86, 0x4F, 0x08, 0x35, 0x9A,
+   0xAE, 0x07, 0xE0, 0xB6, 0xD6, 0x2D, 0xD2, 0x89,
+   0x5F, 0xA6, 0x72, 0x05, 0x36, 0xB5, 0xC0, 0x5A,
+   0x4D, 0xD7, 0x30, 0x37, 0x87, 0x50, 0xA2, 0x48,
+   0x29, 0xAC, 0xDE, 0x93, 0x24, 0x6E, 0x1E, 0xF7,
+   0x52, 0x5E, 0x41, 0xC8, 0xEB, 0x31, 0x7E, 0xE9,
+   0x67, 0x7A, 0x47, 0x85, 0x8D, 0x74, 0x9E, 0x64,
+   0x38, 0x9B, 0xBA, 0xCC, 0x9F, 0x8E, 0xEE, 0x0F,
+   0xB1, 0x7C, 0x6A, 0xBB, 0x2E, 0x58, 0x70, 0x7F,
+   0x4E, 0x4A, 0x1C, 0x5B, 0xF0, 0xA1, 0x61, 0xF6,
+   0x15, 0x33, 0xE4, 0xF9, 0x2F, 0x62, 0x1F, 0x76,
+   0x32, 0xCB, 0x49, 0xFE, 0x8F, 0xD5, 0xDC, 0x66,
+   0x0B, 0x3E, 0xC5, 0x21, 0xC6, 0x6C, 0x18, 0xC2,
+   0x6D, 0xFF, 0x51, 0x99, 0xCF, 0xFD, 0x59, 0xA7,
+   0xAA, 0x8A, 0xF2, 0x69, 0x39, 0x6F, 0x77, 0xDD,
+   0x97, 0xC9, 0xF3, 0x04, 0xD8, 0xF4, 0x80, 0x44 };
+
+unsigned char table_151[256] = {
+   0x78, 0x6C, 0xC5, 0x0C, 0x2D, 0xA7, 0x97, 0x9C,
+   0x22, 0x76, 0x3E, 0x81, 0x51, 0x47, 0x59, 0x71,
+   0xB1, 0xA2, 0x4A, 0x3C, 0xB5, 0x16, 0x06, 0x95,
+   0xB9, 0x01, 0xE6, 0x91, 0x96, 0x1C, 0x1B, 0xAD,
+   0x61, 0x64, 0xB2, 0xE7, 0x29, 0x19, 0x52, 0x3B,
+   0xFA, 0xAF, 0x30, 0xDB, 0xD4, 0x0B, 0xFE, 0x75,
+   0x1F, 0xBE, 0xCB, 0xF6, 0xEA, 0x31, 0xF8, 0xD8,
+   0xA3, 0x82, 0x73, 0x1D, 0x99, 0xF0, 0xCC, 0xB6,
+   0x46, 0x26, 0xAA, 0x8C, 0x87, 0x90, 0x24, 0x8F,
+   0x7A, 0x13, 0xEE, 0xD1, 0xA9, 0x05, 0xB3, 0xF7,
+   0x02, 0x7C, 0x4C, 0x1E, 0xFF, 0xE5, 0x77, 0xAB,
+   0xD6, 0x98, 0x20, 0x4D, 0xC4, 0x23, 0xF4, 0xA4,
+   0x85, 0x9A, 0x8E, 0x1A, 0x0E, 0xF5, 0x15, 0x60,
+   0x38, 0x72, 0xE9, 0xF1, 0xC3, 0x68, 0xF2, 0x93,
+   0xD3, 0x2A, 0x48, 0x74, 0xC2, 0x57, 0xA1, 0x7D,
+   0x94, 0x37, 0x92, 0x5C, 0xE1, 0x41, 0x83, 0xD5,
+   0x65, 0x14, 0xA6, 0xDC, 0x44, 0x27, 0xEF, 0xD7,
+   0x25, 0x10, 0x2C, 0x7F, 0x40, 0xA5, 0x55, 0xBD,
+   0x2B, 0x0D, 0xD0, 0xFC, 0xDF, 0xA0, 0x04, 0x00,
+   0x62, 0xB4, 0x5A, 0xEB, 0x6B, 0x84, 0x7E, 0x6A,
+   0xDE, 0xED, 0x66, 0x03, 0xFB, 0x2E, 0x4F, 0x4E,
+   0xBB, 0x36, 0x5B, 0x18, 0xE3, 0x69, 0x3F, 0xEC,
+   0xE4, 0xD2, 0x0A, 0x34, 0x63, 0xCF, 0xA8, 0xF9,
+   0x9B, 0x7B, 0x6F, 0xE8, 0x49, 0xC1, 0x09, 0x54,
+   0xF3, 0x50, 0x67, 0x79, 0xC0, 0x9F, 0x8D, 0x5F,
+   0x17, 0x70, 0x11, 0xC8, 0xBC, 0xC6, 0xE0, 0x35,
+   0x39, 0xC7, 0x6E, 0x21, 0xBF, 0xDA, 0x6D, 0x28,
+   0x0F, 0xDD, 0x33, 0xAC, 0x8A, 0x12, 0xC9, 0xCD,
+   0xB8, 0x45, 0xAE, 0x32, 0xCE, 0xE2, 0x56, 0xFD,
+   0x42, 0x89, 0x86, 0xCA, 0x4B, 0x3D, 0x5E, 0xBA,
+   0x8B, 0x5D, 0xB0, 0xB7, 0xD9, 0x58, 0x2F, 0x08,
+   0x43, 0x3A, 0x53, 0x9E, 0x80, 0x88, 0x07, 0x9D };
+
+unsigned char table_152[32] = {
+   0x02, 0x1A, 0x17, 0x1D, 0x01, 0x03, 0x13, 0x1E,
+   0x05, 0x18, 0x06, 0x0A, 0x0C, 0x04, 0x1B, 0x00,
+   0x1C, 0x09, 0x1F, 0x16, 0x07, 0x0F, 0x0B, 0x0E,
+   0x14, 0x12, 0x0D, 0x10, 0x19, 0x11, 0x08, 0x15 };
+
+unsigned char table_153[32] = {
+   0x0E, 0x14, 0x12, 0x1E, 0x1C, 0x02, 0x06, 0x16,
+   0x18, 0x0D, 0x17, 0x0C, 0x1D, 0x11, 0x08, 0x19,
+   0x07, 0x0F, 0x13, 0x04, 0x03, 0x1B, 0x0B, 0x1F,
+   0x1A, 0x0A, 0x05, 0x10, 0x00, 0x01, 0x15, 0x09 };
+
+unsigned char table_154[256] = {
+   0x27, 0x5A, 0x08, 0x5B, 0xF4, 0x39, 0x13, 0x6F,
+   0x67, 0xEA, 0x22, 0xCA, 0x5C, 0xCF, 0x18, 0x7C,
+   0x05, 0x87, 0x60, 0xCC, 0x40, 0xC6, 0xE8, 0x6D,
+   0xF5, 0x2A, 0x2D, 0xA2, 0x8C, 0x82, 0xE9, 0xDC,
+   0xD6, 0x65, 0x74, 0x8E, 0x42, 0x4F, 0x3E, 0x55,
+   0xFF, 0xC7, 0x9D, 0x0F, 0x81, 0xE2, 0x4C, 0xE6,
+   0xEB, 0x4D, 0x70, 0xD1, 0x49, 0x43, 0x3D, 0x69,
+   0x0C, 0x45, 0x28, 0x00, 0x99, 0xAE, 0xEC, 0xB8,
+   0xC3, 0x17, 0x93, 0x8D, 0x36, 0x3C, 0x46, 0x2B,
+   0x29, 0xC5, 0xB4, 0xB1, 0xD0, 0x0D, 0xAD, 0xFE,
+   0xE5, 0xA8, 0x3B, 0x1A, 0x2C, 0xDF, 0x07, 0x86,
+   0xB0, 0xD3, 0x7A, 0x59, 0x79, 0x8B, 0xC1, 0x9A,
+   0x30, 0xDB, 0x24, 0xF3, 0xD8, 0x04, 0x25, 0xC2,
+   0xA3, 0x98, 0x96, 0x7B, 0x71, 0x4E, 0x5E, 0x58,
+   0xA5, 0x51, 0x88, 0xDA, 0xF8, 0xC0, 0x7D, 0xF6,
+   0x31, 0x5F, 0x09, 0x16, 0x21, 0x62, 0x01, 0x64,
+   0x9B, 0x3A, 0x2F, 0x61, 0x19, 0xA1, 0xB7, 0xE0,
+   0xB9, 0x12, 0xA0, 0xBA, 0x6E, 0x8A, 0xFB, 0xD9,
+   0x38, 0x1B, 0xD5, 0xB3, 0x10, 0xED, 0xE4, 0x6A,
+   0x32, 0xBD, 0x75, 0xD4, 0x1C, 0xFD, 0x73, 0x77,
+   0x54, 0xC8, 0x97, 0x47, 0x35, 0x94, 0xE3, 0xCD,
+   0x6B, 0xBB, 0xF9, 0xAC, 0x11, 0x14, 0xAF, 0x78,
+   0x3F, 0xCE, 0x26, 0x44, 0xEE, 0xFC, 0x15, 0x66,
+   0x4B, 0xA6, 0x20, 0x23, 0xBE, 0x84, 0x1D, 0x7E,
+   0x0B, 0x56, 0x92, 0x0A, 0xFA, 0xF7, 0x48, 0x33,
+   0x9E, 0x8F, 0xAB, 0x5D, 0x41, 0x50, 0xA4, 0x7F,
+   0x80, 0x4A, 0x68, 0x06, 0x2E, 0x6C, 0xC4, 0x02,
+   0x0E, 0x63, 0xF0, 0xC9, 0x91, 0xB2, 0xD2, 0x03,
+   0x37, 0xEF, 0x9C, 0x90, 0x83, 0x76, 0x1E, 0xA9,
+   0x85, 0xB6, 0x57, 0xD7, 0xF2, 0xF1, 0xE7, 0xDE,
+   0xCB, 0xAA, 0xBF, 0x89, 0x1F, 0xA7, 0xBC, 0x9F,
+   0x53, 0xE1, 0xDD, 0x72, 0x95, 0x52, 0x34, 0xB5 };
+
+unsigned char table_155[256] = {
+   0x75, 0x58, 0xC5, 0xA5, 0x83, 0x16, 0xF3, 0x7F,
+   0x94, 0xDE, 0xA0, 0xF6, 0xFD, 0x89, 0xA8, 0x06,
+   0x98, 0x01, 0xD9, 0x69, 0xB7, 0x0F, 0xEA, 0x73,
+   0x32, 0xF0, 0x49, 0xBF, 0x02, 0xE7, 0x22, 0x3F,
+   0xDB, 0x30, 0x5F, 0x20, 0x6A, 0x93, 0x07, 0xBC,
+   0x09, 0x0D, 0x37, 0x24, 0x90, 0x15, 0x80, 0xAF,
+   0x8F, 0x59, 0x28, 0xFF, 0x6D, 0x1E, 0x52, 0x62,
+   0xE2, 0xDD, 0x85, 0x48, 0xB5, 0xAB, 0x68, 0xAC,
+   0x7E, 0x26, 0x2C, 0xF9, 0x2A, 0xBE, 0x5B, 0xCE,
+   0x87, 0x1D, 0x96, 0xBD, 0xEF, 0x29, 0xA9, 0xC3,
+   0x9D, 0x57, 0x79, 0x6B, 0x7A, 0x82, 0x78, 0x0A,
+   0x91, 0xF2, 0x7C, 0xC2, 0x25, 0x88, 0xE3, 0x47,
+   0x64, 0x46, 0x8D, 0x19, 0xF4, 0xE6, 0xF1, 0x53,
+   0x9C, 0x54, 0x23, 0xAD, 0xA3, 0x86, 0x3A, 0x04,
+   0x67, 0x1C, 0xF5, 0x43, 0x05, 0x42, 0xD6, 0x4B,
+   0xFB, 0xD4, 0x2B, 0x08, 0x45, 0xD8, 0xCD, 0xEB,
+   0x31, 0x4A, 0x5A, 0x34, 0x9B, 0xEC, 0x4D, 0xB4,
+   0xC6, 0xFE, 0xD5, 0x5E, 0xC1, 0x39, 0x81, 0xCF,
+   0x03, 0x6E, 0x95, 0x50, 0xA1, 0x3B, 0xB3, 0xE5,
+   0x3D, 0xB1, 0xB2, 0x41, 0x17, 0x2F, 0x2E, 0xE4,
+   0x1F, 0xDC, 0xB0, 0xB6, 0x18, 0x6F, 0x44, 0x12,
+   0x0B, 0xCC, 0x4E, 0xC0, 0x51, 0x14, 0x76, 0x3C,
+   0xB9, 0x9F, 0xA4, 0xD3, 0xA7, 0xE8, 0x13, 0x55,
+   0xC8, 0x8C, 0xD2, 0xEE, 0x65, 0xB8, 0xAA, 0x6C,
+   0x2D, 0x4F, 0x56, 0xFA, 0x61, 0x4C, 0xE0, 0x5C,
+   0xA6, 0x1A, 0xD1, 0x38, 0xD7, 0x72, 0x60, 0x74,
+   0xE1, 0xBA, 0x84, 0x3E, 0x40, 0xF8, 0xC7, 0x36,
+   0x27, 0x0C, 0x70, 0x97, 0x9A, 0x7D, 0x35, 0x71,
+   0xCA, 0x1B, 0x99, 0x8E, 0xAE, 0x66, 0x63, 0xE9,
+   0xC9, 0x11, 0x8A, 0x21, 0x92, 0x5D, 0x77, 0x10,
+   0xD0, 0xC4, 0xF7, 0x7B, 0x9E, 0xCB, 0xED, 0x0E,
+   0x8B, 0x33, 0xFC, 0xBB, 0x00, 0xA2, 0xDF, 0xDA };
+
+unsigned char table_156[256] = {
+   0x31, 0x25, 0xB1, 0xD3, 0xAF, 0xAE, 0x84, 0x2C,
+   0x71, 0x5E, 0xD8, 0x80, 0x6F, 0x3E, 0x48, 0x86,
+   0xED, 0x54, 0x6A, 0xC3, 0xBC, 0xBF, 0x0E, 0xEA,
+   0x10, 0xA2, 0x9D, 0x91, 0x32, 0xE2, 0x7E, 0x1B,
+   0x49, 0x27, 0xFF, 0xDD, 0x8A, 0x2F, 0x8D, 0x38,
+   0xFA, 0x3C, 0x03, 0x14, 0x0F, 0x89, 0xCC, 0x07,
+   0x1A, 0xA0, 0x97, 0x37, 0xA6, 0xD6, 0x63, 0x87,
+   0xA1, 0xC2, 0x4B, 0x39, 0xCB, 0xCF, 0x69, 0x4E,
+   0xC9, 0x28, 0x1C, 0xBB, 0x42, 0x2B, 0xA9, 0x78,
+   0x5B, 0xF6, 0xE0, 0xD0, 0x5F, 0x46, 0x98, 0xCE,
+   0x1F, 0x7A, 0x34, 0x8B, 0xFD, 0x9B, 0xEF, 0x74,
+   0x05, 0xF2, 0x02, 0xC6, 0xDF, 0x73, 0x5C, 0x8E,
+   0xDE, 0x88, 0x57, 0x3B, 0x85, 0xBD, 0xC0, 0x3A,
+   0x45, 0x4D, 0x2D, 0x72, 0x0C, 0x60, 0xCA, 0x5D,
+   0x06, 0x04, 0x3D, 0x51, 0x15, 0xAD, 0xE8, 0x67,
+   0xBA, 0x43, 0x7D, 0xF8, 0xB2, 0xE6, 0xAB, 0xF4,
+   0x23, 0x6E, 0xF0, 0x6B, 0x0B, 0x2E, 0xC8, 0xC4,
+   0x4F, 0xA8, 0x6D, 0x26, 0xE9, 0x9C, 0x22, 0xB7,
+   0x00, 0xB3, 0x0A, 0x7C, 0x44, 0x55, 0x75, 0xD5,
+   0xAA, 0x66, 0x56, 0x24, 0x83, 0x90, 0xA4, 0xF5,
+   0xCD, 0xEC, 0x18, 0xDC, 0xFE, 0x96, 0xA3, 0xF7,
+   0xD2, 0xFB, 0xD1, 0x65, 0xC5, 0x08, 0x7B, 0x70,
+   0x16, 0x9A, 0x20, 0x09, 0x29, 0xDA, 0x52, 0x5A,
+   0x59, 0xB4, 0x77, 0x62, 0x9E, 0x19, 0x7F, 0x82,
+   0x4C, 0xB6, 0x0D, 0x58, 0xEE, 0x1D, 0xB9, 0x93,
+   0x50, 0xD9, 0x30, 0xE4, 0x13, 0x01, 0x36, 0x8F,
+   0x53, 0x3F, 0x64, 0xA5, 0xB5, 0xD7, 0x81, 0x41,
+   0x17, 0xE5, 0x94, 0xE3, 0xF9, 0x61, 0x76, 0xE1,
+   0x9F, 0xFC, 0x1E, 0x12, 0xDB, 0x21, 0x79, 0x2A,
+   0xAC, 0xF3, 0x6C, 0xC1, 0x95, 0x92, 0xEB, 0xA7,
+   0x11, 0xC7, 0xB8, 0x4A, 0x33, 0xB0, 0x99, 0xE7,
+   0xF1, 0x68, 0xBE, 0x35, 0x40, 0x8C, 0xD4, 0x47 };
+
+unsigned char table_157[32] = {
+   0x00, 0x0D, 0x03, 0x02, 0x11, 0x04, 0x18, 0x0B,
+   0x14, 0x1D, 0x1C, 0x13, 0x1B, 0x17, 0x10, 0x15,
+   0x01, 0x19, 0x07, 0x09, 0x1A, 0x16, 0x12, 0x1E,
+   0x08, 0x06, 0x0C, 0x0E, 0x1F, 0x0F, 0x0A, 0x05 };
+
+unsigned char table_158[256] = {
+   0x68, 0x26, 0x80, 0x0B, 0xB8, 0xD5, 0x8C, 0xB7,
+   0x65, 0xEF, 0xBC, 0x94, 0x28, 0xB9, 0xB2, 0xD2,
+   0x92, 0xA4, 0x55, 0x27, 0xE0, 0x40, 0x6C, 0x41,
+   0x25, 0xBD, 0xAF, 0xEA, 0xB1, 0x19, 0xA5, 0xC9,
+   0x0E, 0xED, 0xB4, 0xF9, 0x8B, 0x6A, 0xAE, 0xD8,
+   0x64, 0x83, 0xC1, 0xD3, 0x04, 0xF4, 0xFA, 0xC3,
+   0x46, 0x2C, 0xA8, 0xBB, 0x3A, 0x47, 0x33, 0x8F,
+   0x52, 0x86, 0x08, 0x9D, 0x1D, 0x59, 0x8E, 0x91,
+   0x32, 0xCF, 0x6B, 0x75, 0xB0, 0x7F, 0xC7, 0x24,
+   0x05, 0x6F, 0x00, 0x1C, 0x2D, 0xAC, 0xDA, 0x45,
+   0x73, 0xB3, 0x3E, 0xD6, 0x54, 0x61, 0x03, 0x77,
+   0xF8, 0xD9, 0xE2, 0x4B, 0xFF, 0xF2, 0x0C, 0x4F,
+   0x93, 0x71, 0xA7, 0x3D, 0x66, 0x88, 0x98, 0xF1,
+   0xB6, 0x7A, 0x2B, 0xCD, 0x44, 0x3C, 0x37, 0x5A,
+   0x96, 0x23, 0x9F, 0xBF, 0x7D, 0x5E, 0x2A, 0x35,
+   0x72, 0x79, 0xE1, 0xA3, 0x84, 0x99, 0x38, 0x49,
+   0xC8, 0xDB, 0x30, 0xDC, 0xAD, 0x3F, 0xF6, 0x09,
+   0x69, 0x95, 0xE5, 0x67, 0xA1, 0xFD, 0xF7, 0x1B,
+   0xEC, 0x17, 0xD4, 0xEB, 0x29, 0x36, 0x3B, 0x15,
+   0xDE, 0x2E, 0xC5, 0x70, 0x6D, 0x53, 0x56, 0xAB,
+   0xC0, 0x43, 0xC2, 0xE7, 0x31, 0xE6, 0xA6, 0x78,
+   0x5C, 0x7C, 0x48, 0x10, 0x87, 0xCC, 0x9E, 0x7E,
+   0x5F, 0xE9, 0x07, 0x5B, 0xF5, 0xEE, 0xB5, 0xCA,
+   0x62, 0x18, 0xBE, 0x20, 0x16, 0xDF, 0x13, 0x4E,
+   0x7B, 0x02, 0x11, 0x4C, 0x51, 0x85, 0x0D, 0x22,
+   0xF3, 0x14, 0x63, 0x76, 0xD0, 0x0F, 0xE4, 0xCB,
+   0xCE, 0xA0, 0x82, 0xE3, 0x01, 0xAA, 0x5D, 0x4A,
+   0x4D, 0xFB, 0x39, 0x8A, 0x2F, 0xDD, 0xE8, 0x06,
+   0x1A, 0x90, 0x81, 0x50, 0x8D, 0x89, 0x97, 0x1E,
+   0xFC, 0x60, 0x12, 0x42, 0x9C, 0xF0, 0x34, 0xD7,
+   0xD1, 0x1F, 0x0A, 0x21, 0xA9, 0x6E, 0xC4, 0xBA,
+   0x9A, 0x57, 0xA2, 0x74, 0xC6, 0xFE, 0x9B, 0x58 };
+
+unsigned char table_159[256] = {
+   0xE5, 0xBF, 0x84, 0x56, 0xD6, 0x43, 0x3E, 0xA5,
+   0x64, 0x87, 0x44, 0x63, 0x4A, 0x4C, 0x8D, 0x24,
+   0x1C, 0xDA, 0x89, 0x52, 0x80, 0x4F, 0xE4, 0xBC,
+   0xC5, 0xF4, 0x27, 0x75, 0x9C, 0xF0, 0xE1, 0x06,
+   0x99, 0x48, 0xF2, 0x57, 0x34, 0x9A, 0xA8, 0x62,
+   0xC9, 0xD5, 0x16, 0x6D, 0x55, 0xFA, 0x37, 0x5A,
+   0x2A, 0xC6, 0x45, 0xDD, 0x1B, 0x76, 0x50, 0xE2,
+   0x69, 0x41, 0x6C, 0xC4, 0x3C, 0x47, 0xA9, 0x92,
+   0x00, 0x3D, 0x6F, 0xE7, 0x7A, 0x3A, 0x33, 0x53,
+   0xF7, 0x03, 0xA7, 0xB1, 0x15, 0x78, 0x0B, 0x67,
+   0x2E, 0x21, 0xF1, 0xD4, 0xB3, 0x98, 0x60, 0x58,
+   0xBB, 0x82, 0x1E, 0x70, 0x0A, 0xA2, 0x02, 0x17,
+   0xFF, 0x9F, 0xD2, 0xAF, 0xC7, 0xDC, 0x68, 0x83,
+   0x42, 0xCA, 0x08, 0x39, 0x20, 0xEC, 0x77, 0x96,
+   0x5B, 0xAD, 0x09, 0x6B, 0x40, 0xC2, 0x91, 0x51,
+   0x10, 0xD9, 0xF9, 0xC1, 0xB5, 0xDF, 0xDB, 0xC0,
+   0x7D, 0xAB, 0xAE, 0x54, 0x35, 0xF3, 0xA1, 0xE6,
+   0xEA, 0x14, 0xBA, 0xFC, 0xE8, 0xEB, 0xF6, 0xBD,
+   0x8C, 0x72, 0x1F, 0xE9, 0xFB, 0x7C, 0xCF, 0x49,
+   0xE3, 0xA3, 0x22, 0x9D, 0x46, 0x71, 0x94, 0x31,
+   0x2D, 0x65, 0x2B, 0x32, 0x18, 0xB6, 0x90, 0xF8,
+   0x11, 0x5F, 0xA0, 0xEF, 0xED, 0x1A, 0x25, 0x2C,
+   0x3B, 0xFD, 0x2F, 0x73, 0xB9, 0x7E, 0xDE, 0xB4,
+   0x97, 0x0F, 0x7F, 0x86, 0x93, 0x07, 0x19, 0xCE,
+   0xE0, 0xB7, 0xEE, 0x26, 0xD1, 0x01, 0x59, 0x5C,
+   0xC3, 0x79, 0x8B, 0xD3, 0x4B, 0x04, 0xD0, 0x29,
+   0x0D, 0x3F, 0xB2, 0x30, 0xCC, 0x36, 0xFE, 0xB0,
+   0xF5, 0x8E, 0xA6, 0x8A, 0xC8, 0xD8, 0x05, 0xB8,
+   0x12, 0xBE, 0x81, 0x4D, 0x38, 0xAC, 0x1D, 0x9E,
+   0x66, 0x5E, 0x7B, 0x6E, 0x0C, 0xCD, 0x6A, 0x88,
+   0xAA, 0x0E, 0x61, 0x5D, 0x95, 0x4E, 0xD7, 0x74,
+   0xCB, 0x9B, 0x13, 0x8F, 0xA4, 0x28, 0x23, 0x85 };
+
+unsigned char table_160[256] = {
+   0x35, 0x44, 0x0E, 0x92, 0x75, 0x83, 0x9D, 0x53,
+   0xA5, 0x90, 0xF8, 0xF7, 0x54, 0x74, 0xDF, 0x3D,
+   0x5A, 0xAA, 0xC6, 0x26, 0x7A, 0xFC, 0x79, 0x6C,
+   0x56, 0xB3, 0x32, 0xE3, 0x1C, 0xF9, 0xDC, 0xE6,
+   0xA2, 0x93, 0x71, 0xFF, 0x1D, 0xEB, 0xB2, 0x04,
+   0x96, 0x46, 0x0C, 0x2B, 0x17, 0xEE, 0x28, 0x25,
+   0xD9, 0xAE, 0x11, 0xA7, 0x40, 0x45, 0xFB, 0x80,
+   0x18, 0xF1, 0xCB, 0x2E, 0x24, 0xF3, 0xEC, 0x4F,
+   0xAB, 0xD7, 0xD4, 0xC4, 0xFD, 0x4B, 0xAD, 0xC9,
+   0x4C, 0x08, 0xAC, 0xF4, 0xCD, 0xB7, 0xF2, 0x15,
+   0x02, 0x2F, 0x16, 0x34, 0x65, 0x8A, 0x87, 0xCC,
+   0x50, 0x0F, 0x9B, 0xC2, 0xC8, 0x7B, 0xEA, 0x8E,
+   0xE4, 0xD6, 0x97, 0x30, 0xA8, 0xA0, 0x94, 0xC5,
+   0xE8, 0x12, 0x27, 0xCE, 0x84, 0xDD, 0xB1, 0x47,
+   0x7E, 0xE7, 0xE1, 0x3A, 0x37, 0x21, 0x2D, 0x3B,
+   0x20, 0x60, 0x1E, 0x1B, 0x82, 0xBE, 0xA3, 0x70,
+   0x98, 0xBF, 0xA6, 0x4D, 0x76, 0x86, 0x42, 0x9F,
+   0xCF, 0xE0, 0x14, 0x4A, 0x0B, 0xB4, 0x36, 0xF5,
+   0x85, 0xB8, 0xC0, 0x6A, 0xE9, 0x7D, 0xBD, 0x4E,
+   0x8F, 0x51, 0x0D, 0x5B, 0x6B, 0x58, 0x5F, 0x03,
+   0x6F, 0xBC, 0x5D, 0x1F, 0x7F, 0xDB, 0x00, 0xC1,
+   0x13, 0xF0, 0xD1, 0xFA, 0xDA, 0x05, 0x39, 0xD3,
+   0x38, 0xD2, 0x89, 0xE2, 0x88, 0x5E, 0x5C, 0x6D,
+   0xCA, 0xB0, 0x01, 0x63, 0x8B, 0x59, 0xA4, 0xD0,
+   0x78, 0x19, 0xB5, 0x62, 0x1A, 0x69, 0x8D, 0x9C,
+   0x22, 0x3F, 0x9E, 0x33, 0x72, 0x2A, 0x41, 0x29,
+   0xFE, 0xF6, 0x64, 0x7C, 0x66, 0xB6, 0xAF, 0x23,
+   0x8C, 0x68, 0x6E, 0x49, 0x07, 0x99, 0x77, 0x3E,
+   0x9A, 0x73, 0xD8, 0x55, 0x0A, 0x3C, 0xBA, 0xA9,
+   0x52, 0xED, 0x91, 0x09, 0x95, 0xC7, 0x43, 0xD5,
+   0x57, 0x61, 0x81, 0xEF, 0x06, 0xDE, 0x48, 0x31,
+   0xBB, 0x2C, 0xE5, 0xC3, 0x67, 0xA1, 0x10, 0xB9 };
+
+unsigned char table_161[256] = {
+   0x8F, 0x1A, 0x81, 0xA2, 0x2C, 0x56, 0x6D, 0xCD,
+   0x4A, 0x33, 0x50, 0xE9, 0xE0, 0x12, 0x5A, 0x43,
+   0x2D, 0x4F, 0xEA, 0x95, 0xFD, 0x49, 0xAB, 0xA3,
+   0x79, 0x42, 0x0B, 0xB8, 0x89, 0x40, 0x71, 0x14,
+   0x80, 0x55, 0xAF, 0xCF, 0x3E, 0x64, 0x8B, 0x74,
+   0xBF, 0x9C, 0x24, 0x97, 0xD1, 0xBA, 0x48, 0xD2,
+   0x08, 0x1F, 0xDD, 0xA7, 0xDC, 0x92, 0x30, 0x75,
+   0x31, 0x37, 0x67, 0x06, 0x68, 0x72, 0x6F, 0x05,
+   0x8A, 0x7C, 0x4C, 0x3C, 0x19, 0x28, 0x86, 0x3D,
+   0x93, 0xDA, 0xF4, 0xC7, 0x17, 0x85, 0xAC, 0x02,
+   0x78, 0x04, 0xAD, 0x03, 0x8D, 0x11, 0xC5, 0x9D,
+   0x3A, 0x73, 0x82, 0x59, 0x51, 0x9F, 0x27, 0x47,
+   0xE7, 0xED, 0x1E, 0xFF, 0x34, 0x01, 0x5B, 0x4B,
+   0xCA, 0x6C, 0x69, 0xBB, 0x3B, 0xC4, 0x5F, 0xDF,
+   0x09, 0x6B, 0x7D, 0xC9, 0x88, 0x45, 0x57, 0xD3,
+   0x2A, 0x4E, 0xF1, 0xC2, 0xA9, 0xB6, 0x18, 0xD4,
+   0xA0, 0x1C, 0x4D, 0x0E, 0xE5, 0xE1, 0xD7, 0xB2,
+   0x0C, 0x3F, 0x00, 0x61, 0x16, 0x0D, 0x32, 0x62,
+   0x58, 0x63, 0xEE, 0xEF, 0x2F, 0x5D, 0xB0, 0x20,
+   0x7A, 0x10, 0xE6, 0xA1, 0xF9, 0xD8, 0x6E, 0xCB,
+   0xF0, 0x9B, 0x84, 0x8E, 0xF2, 0xFE, 0xC8, 0x7F,
+   0xBD, 0xF8, 0x07, 0xC6, 0x39, 0xBC, 0xCC, 0x22,
+   0x54, 0x15, 0x9A, 0xA4, 0xC1, 0x2B, 0x1B, 0x25,
+   0xDE, 0x6A, 0xDB, 0x90, 0xEB, 0xB7, 0xD0, 0x44,
+   0xA6, 0xB9, 0xB1, 0x23, 0x9E, 0x65, 0x83, 0xFA,
+   0x96, 0xB5, 0x0F, 0xF6, 0xD6, 0xE8, 0x53, 0x13,
+   0x76, 0xD5, 0x35, 0x87, 0xE3, 0x38, 0xF5, 0xAE,
+   0xB3, 0xCE, 0xE2, 0x70, 0xD9, 0x66, 0x5C, 0x26,
+   0xC3, 0xFC, 0xF7, 0x94, 0xF3, 0xEC, 0xFB, 0x99,
+   0x91, 0x77, 0xB4, 0x46, 0xA5, 0x98, 0x7B, 0x1D,
+   0x52, 0x2E, 0xA8, 0x60, 0x5E, 0x29, 0x21, 0x7E,
+   0xBE, 0x0A, 0x36, 0x41, 0xC0, 0x8C, 0xE4, 0xAA };
+
+unsigned char table_162[256] = {
+   0xF7, 0x1B, 0xC0, 0x31, 0x5A, 0x23, 0xEA, 0xE9,
+   0xFB, 0x14, 0x6A, 0xE8, 0x04, 0x65, 0x5B, 0x2C,
+   0x41, 0xD9, 0xEB, 0xE4, 0x8D, 0x1D, 0xCA, 0x8F,
+   0x5E, 0x43, 0xAF, 0x46, 0x0A, 0x01, 0x0C, 0xB4,
+   0x95, 0x52, 0x92, 0xE0, 0x10, 0x57, 0x0F, 0x71,
+   0xB1, 0x26, 0xD8, 0x05, 0x69, 0x3C, 0x54, 0xDF,
+   0xFF, 0x9D, 0x51, 0xA0, 0xA1, 0x0B, 0xC1, 0x20,
+   0x6D, 0xFA, 0x47, 0x15, 0x09, 0xD3, 0xE1, 0xA9,
+   0x66, 0x12, 0x5C, 0x49, 0x1E, 0x3B, 0xD0, 0x8B,
+   0x62, 0xBD, 0x06, 0xE5, 0x00, 0x98, 0x4E, 0x32,
+   0xB0, 0x2D, 0x2A, 0x7F, 0x03, 0xD5, 0x99, 0x7E,
+   0xAB, 0x22, 0xC6, 0xC3, 0x2F, 0x4C, 0x33, 0x45,
+   0xE3, 0x3F, 0xF9, 0xB2, 0xFE, 0x36, 0xE7, 0xF8,
+   0x55, 0x0D, 0x56, 0x1F, 0x4B, 0xE6, 0x50, 0x81,
+   0xCE, 0x80, 0xCD, 0x67, 0x6B, 0xCF, 0x2E, 0x9B,
+   0xBC, 0xBE, 0x11, 0x75, 0x4D, 0xAC, 0x59, 0x40,
+   0x85, 0x0E, 0xC9, 0x17, 0xA3, 0x60, 0xED, 0x16,
+   0xA4, 0xDD, 0xEE, 0x96, 0x77, 0x83, 0x34, 0xD2,
+   0xCB, 0xFC, 0x6C, 0x08, 0xEC, 0x35, 0xF2, 0x6F,
+   0x3A, 0x7B, 0x21, 0x4A, 0x70, 0xEF, 0xAD, 0xDE,
+   0x90, 0x9E, 0x7D, 0x64, 0x2B, 0x79, 0xF5, 0xF3,
+   0x13, 0x1C, 0x7A, 0x07, 0x4F, 0x78, 0x89, 0xB6,
+   0x97, 0xF1, 0xD7, 0x7C, 0x48, 0xAE, 0x39, 0xA8,
+   0xA6, 0x86, 0x3E, 0x27, 0x87, 0x73, 0x82, 0x24,
+   0x30, 0x74, 0x5F, 0xD1, 0x9F, 0x9C, 0x1A, 0x8C,
+   0x42, 0x6E, 0x28, 0xB9, 0xF0, 0xC4, 0x68, 0x25,
+   0xC5, 0xDC, 0xB8, 0x29, 0xD6, 0x84, 0x3D, 0xBB,
+   0x88, 0x76, 0xFD, 0x61, 0x94, 0x91, 0xDA, 0xB7,
+   0x72, 0xBA, 0xC2, 0xDB, 0xB5, 0xA5, 0xE2, 0x18,
+   0xF6, 0xAA, 0x8A, 0x19, 0x63, 0x9A, 0xA7, 0xC8,
+   0xD4, 0x02, 0x8E, 0x37, 0xF4, 0xB3, 0xA2, 0x53,
+   0x38, 0xCC, 0x58, 0x44, 0xBF, 0x93, 0x5D, 0xC7 };
+
+unsigned char table_163[32] = {
+   0x1B, 0x14, 0x12, 0x15, 0x11, 0x1D, 0x17, 0x19,
+   0x10, 0x09, 0x08, 0x06, 0x1A, 0x16, 0x07, 0x13,
+   0x1F, 0x0B, 0x1C, 0x05, 0x0E, 0x00, 0x18, 0x0A,
+   0x04, 0x01, 0x03, 0x0C, 0x0D, 0x1E, 0x02, 0x0F };
+
+unsigned char table_164[32] = {
+   0x15, 0x00, 0x10, 0x0B, 0x1D, 0x0A, 0x06, 0x1C,
+   0x0D, 0x1F, 0x17, 0x0F, 0x03, 0x14, 0x13, 0x12,
+   0x1B, 0x18, 0x08, 0x1E, 0x16, 0x09, 0x1A, 0x04,
+   0x02, 0x0C, 0x0E, 0x01, 0x07, 0x19, 0x11, 0x05 };
+
+unsigned char table_165[256] = {
+   0x98, 0xF5, 0x1D, 0xFB, 0x13, 0x20, 0x41, 0xA3,
+   0xE3, 0x76, 0x49, 0x7E, 0x60, 0xD8, 0x68, 0x30,
+   0x88, 0x45, 0xD5, 0x77, 0x00, 0xC3, 0x09, 0x31,
+   0x44, 0x18, 0xD4, 0x14, 0xC8, 0x1B, 0x8B, 0x38,
+   0x08, 0x52, 0xD1, 0xF3, 0x69, 0x9F, 0xDA, 0x61,
+   0x16, 0x1C, 0xE4, 0x7D, 0xEE, 0xD9, 0x5E, 0x4C,
+   0xA7, 0xAA, 0xA6, 0xF6, 0xCF, 0xA0, 0xBA, 0x10,
+   0xE2, 0xDE, 0x0F, 0xEA, 0xBC, 0x32, 0x63, 0xC0,
+   0x54, 0xC5, 0xBE, 0x71, 0x80, 0x56, 0x5C, 0xA4,
+   0xAD, 0x15, 0x9D, 0x11, 0x43, 0x67, 0x95, 0xAE,
+   0xC6, 0xC4, 0x91, 0x9C, 0xE5, 0x37, 0xE1, 0x7A,
+   0xDB, 0xEF, 0x03, 0x65, 0x86, 0x66, 0x2A, 0xB5,
+   0xBF, 0xB4, 0x0D, 0xB3, 0xD7, 0x2D, 0x01, 0xEB,
+   0x8C, 0xF2, 0x5A, 0x2E, 0x64, 0x25, 0x02, 0xCB,
+   0x4A, 0xB0, 0xCE, 0x35, 0xA8, 0x47, 0x85, 0x33,
+   0x34, 0x24, 0x23, 0x7B, 0xB6, 0x48, 0x83, 0x40,
+   0x87, 0x57, 0x3C, 0xD6, 0xCD, 0x2C, 0x6D, 0xE7,
+   0xBB, 0xED, 0x81, 0x5D, 0x55, 0x46, 0xDD, 0xD3,
+   0x70, 0xBD, 0xB8, 0x75, 0x53, 0x6E, 0xD0, 0x99,
+   0xCA, 0x58, 0xC7, 0x4B, 0x3D, 0xA5, 0x50, 0x7C,
+   0x93, 0x51, 0xB7, 0xFD, 0x05, 0x3A, 0xE8, 0x8F,
+   0x28, 0x74, 0x39, 0xF0, 0x7F, 0x4F, 0x06, 0x36,
+   0xB2, 0x19, 0x2F, 0x1F, 0x8D, 0x0C, 0xB9, 0xFC,
+   0x89, 0x21, 0x12, 0xF7, 0x3F, 0x94, 0x6F, 0xDC,
+   0x3E, 0x4E, 0x3B, 0xC9, 0x07, 0x9B, 0x17, 0x9A,
+   0x73, 0x6A, 0x5B, 0xA1, 0x1E, 0x8A, 0x04, 0x72,
+   0x6C, 0xA2, 0xEC, 0x96, 0xFE, 0xF8, 0x84, 0xC1,
+   0x79, 0x0E, 0x62, 0x90, 0x8E, 0xF4, 0x42, 0x29,
+   0x92, 0x9E, 0xAC, 0x82, 0x4D, 0xAF, 0x2B, 0x6B,
+   0xA9, 0xFF, 0x0A, 0xAB, 0x22, 0x5F, 0xDF, 0xD2,
+   0x0B, 0x78, 0xF1, 0xE6, 0x59, 0x27, 0xC2, 0xE0,
+   0x1A, 0x26, 0xCC, 0xB1, 0xF9, 0xFA, 0x97, 0xE9 };
+
+unsigned char table_166[256] = {
+   0xCB, 0xEA, 0x2A, 0x36, 0x6D, 0x93, 0x4E, 0xD5,
+   0xBC, 0x6A, 0xD4, 0x68, 0xF7, 0x18, 0xAB, 0x8B,
+   0x66, 0x95, 0x94, 0x64, 0xB7, 0x00, 0x4D, 0x97,
+   0x38, 0xB3, 0xFC, 0xE1, 0xBB, 0x63, 0xF3, 0x1F,
+   0x6B, 0x2C, 0x2F, 0x5E, 0xA4, 0x7E, 0xFB, 0xF4,
+   0xA8, 0x8A, 0x65, 0x53, 0x90, 0x58, 0x40, 0x60,
+   0x28, 0x8E, 0x35, 0x49, 0xED, 0xBD, 0x1B, 0x0B,
+   0xBA, 0xB8, 0x61, 0x50, 0xE9, 0x39, 0xEF, 0xC3,
+   0x74, 0xB6, 0x46, 0x8D, 0xD9, 0x32, 0x92, 0x9A,
+   0x30, 0x01, 0xF2, 0x41, 0xB9, 0xE7, 0x3A, 0xB0,
+   0x80, 0x15, 0xDE, 0x7D, 0x7F, 0x09, 0xC2, 0x76,
+   0xF8, 0x12, 0x59, 0xDD, 0x1D, 0xE6, 0x75, 0xBE,
+   0xA3, 0x04, 0xCA, 0x78, 0x7B, 0xAC, 0xD8, 0x70,
+   0xD3, 0xC1, 0x25, 0x6F, 0x03, 0x6C, 0x14, 0x45,
+   0xE5, 0x2B, 0x87, 0x83, 0xAA, 0x77, 0x5F, 0x4A,
+   0x9C, 0x27, 0x0C, 0x10, 0xAE, 0x56, 0x85, 0x0D,
+   0xE3, 0xFA, 0x71, 0xEE, 0x9F, 0x21, 0xC0, 0xCD,
+   0xFD, 0xDC, 0x5B, 0x11, 0x02, 0x0F, 0x96, 0x3D,
+   0x3C, 0x26, 0xEB, 0x08, 0x7A, 0x82, 0xA7, 0x19,
+   0xD7, 0xC5, 0xF6, 0x52, 0x57, 0x88, 0xFF, 0x47,
+   0x8F, 0xC6, 0x33, 0xB5, 0x2E, 0x8C, 0x81, 0x91,
+   0x44, 0xA6, 0x17, 0xF0, 0x4B, 0x9D, 0x34, 0x73,
+   0x72, 0x67, 0xD2, 0x0E, 0xA0, 0x99, 0xA5, 0xAF,
+   0xFE, 0x9E, 0x6E, 0xDA, 0x3B, 0xE2, 0x23, 0xD6,
+   0xD0, 0x13, 0x89, 0x5A, 0x42, 0x98, 0x5C, 0xD1,
+   0x86, 0x24, 0xDF, 0x37, 0xF9, 0xCC, 0xF5, 0xA9,
+   0x2D, 0xBF, 0x5D, 0xF1, 0x69, 0xE8, 0xA2, 0x06,
+   0x48, 0xC7, 0xDB, 0x29, 0xE4, 0xAD, 0x3E, 0xA1,
+   0xC9, 0x4C, 0x1A, 0xCE, 0x62, 0x4F, 0x7C, 0xC8,
+   0x05, 0xC4, 0xB1, 0x1E, 0x79, 0x55, 0x84, 0xB2,
+   0x20, 0x31, 0x9B, 0xEC, 0xB4, 0xCF, 0x54, 0x22,
+   0x1C, 0xE0, 0x51, 0x16, 0x43, 0x07, 0x0A, 0x3F };
+
+unsigned char table_167[256] = {
+   0x91, 0xEA, 0x4F, 0x6A, 0x6E, 0x2D, 0x27, 0x22,
+   0x44, 0xA5, 0x6D, 0xE3, 0x45, 0x06, 0xE2, 0x87,
+   0x9A, 0xC9, 0x2C, 0x4A, 0x93, 0x6F, 0x00, 0xEB,
+   0x7C, 0x7F, 0xA2, 0xFE, 0x40, 0x3C, 0x3F, 0xC0,
+   0xC7, 0xFB, 0x8B, 0xDF, 0xA3, 0x28, 0x78, 0x48,
+   0x46, 0xD5, 0x70, 0x5C, 0x35, 0x4E, 0xD7, 0x3A,
+   0x42, 0x47, 0x5B, 0x26, 0x8E, 0xE0, 0x21, 0xB1,
+   0x77, 0x1E, 0x53, 0x4B, 0xCC, 0xE5, 0x65, 0xF6,
+   0x66, 0x2A, 0xA0, 0x5E, 0x3E, 0xAD, 0xA8, 0x95,
+   0x1B, 0x0D, 0x8A, 0x05, 0x68, 0x59, 0x0C, 0x38,
+   0x18, 0xC3, 0x81, 0xA4, 0xFD, 0x13, 0x50, 0xCA,
+   0xE8, 0xDD, 0xD9, 0x76, 0x8C, 0xC5, 0xF4, 0x17,
+   0xB4, 0x3D, 0xEC, 0x0B, 0x67, 0xC6, 0x8D, 0xE1,
+   0xBB, 0x7E, 0xCB, 0x10, 0x99, 0xE9, 0x39, 0xF3,
+   0x75, 0xFA, 0xAC, 0x16, 0x54, 0x51, 0xBC, 0x24,
+   0x58, 0x08, 0xA7, 0x0F, 0x5D, 0xBF, 0xBA, 0xE7,
+   0x9D, 0x2B, 0xB5, 0x29, 0xE4, 0xCD, 0x37, 0x30,
+   0x55, 0xAE, 0x1D, 0x4D, 0x94, 0x34, 0x92, 0x1C,
+   0x6B, 0xBE, 0x52, 0x7B, 0x33, 0xB0, 0x0A, 0x5A,
+   0x03, 0x23, 0x41, 0x49, 0x61, 0x64, 0x73, 0x97,
+   0xC2, 0x9F, 0x5F, 0x07, 0x04, 0xF8, 0xC1, 0xFC,
+   0x74, 0x02, 0x0E, 0x60, 0x9E, 0xD4, 0x85, 0x88,
+   0xC4, 0xF5, 0x90, 0x31, 0xF7, 0xEE, 0x9B, 0xB9,
+   0x20, 0xE6, 0xA6, 0x63, 0x79, 0x56, 0x62, 0xF0,
+   0x2F, 0xD8, 0x4C, 0x83, 0xF9, 0x36, 0x3B, 0x84,
+   0xDE, 0x57, 0xB8, 0xB7, 0x11, 0xF2, 0xC8, 0xD3,
+   0xD1, 0x96, 0x19, 0x2E, 0x72, 0x9C, 0xDB, 0xB3,
+   0xA1, 0xAA, 0xCE, 0x09, 0x98, 0xED, 0xA9, 0xDA,
+   0xAF, 0x86, 0xD0, 0x12, 0xFF, 0xDC, 0x1F, 0xD6,
+   0x01, 0xF1, 0xD2, 0x80, 0x43, 0x7A, 0x71, 0x82,
+   0xB6, 0xAB, 0x89, 0xBD, 0x8F, 0xEF, 0x7D, 0xB2,
+   0x14, 0x15, 0x25, 0x32, 0x6C, 0x69, 0x1A, 0xCF };
+
+unsigned char table_168[256] = {
+   0x28, 0xEE, 0xB1, 0xFD, 0xB3, 0xEF, 0x36, 0x8E,
+   0x85, 0x5D, 0x1C, 0x53, 0x1E, 0xDA, 0xBA, 0x3C,
+   0xA8, 0x90, 0x99, 0x49, 0x45, 0xE0, 0x27, 0x8D,
+   0x22, 0xE4, 0x51, 0x3E, 0xAB, 0xE8, 0x70, 0xF5,
+   0x81, 0xE6, 0x34, 0x29, 0xF3, 0x11, 0x46, 0x5F,
+   0x5C, 0xA0, 0xD1, 0xE3, 0x15, 0x68, 0x3A, 0x01,
+   0xE9, 0xD7, 0x24, 0x5A, 0x18, 0x16, 0x88, 0x3B,
+   0x64, 0xA1, 0xDB, 0xBF, 0xAA, 0x43, 0xEA, 0x19,
+   0xA2, 0xD5, 0x7B, 0xBD, 0x2A, 0x0E, 0x4F, 0xB5,
+   0x4B, 0xB7, 0x5B, 0x73, 0xC9, 0xAC, 0x1B, 0x67,
+   0xC7, 0xB4, 0x69, 0x00, 0xBC, 0x6D, 0xC1, 0x04,
+   0xF4, 0x74, 0xD6, 0xD0, 0x60, 0xAE, 0x17, 0xFE,
+   0x63, 0xB6, 0x89, 0x41, 0x7C, 0x44, 0x8B, 0xDC,
+   0x50, 0xE5, 0x79, 0x77, 0x47, 0x9F, 0xA6, 0x3D,
+   0x09, 0x8A, 0x2F, 0xC0, 0x0F, 0xCD, 0x2B, 0x4D,
+   0x0D, 0xC2, 0x5E, 0xB0, 0x57, 0x62, 0xAF, 0x1A,
+   0x21, 0x82, 0x48, 0x9E, 0x38, 0xB9, 0xB8, 0xF2,
+   0x37, 0x07, 0xCA, 0xC5, 0x84, 0xDF, 0xF9, 0xEC,
+   0x42, 0x6B, 0x8F, 0x6C, 0x3F, 0xC4, 0x94, 0xED,
+   0x7A, 0x2D, 0xA3, 0x83, 0xD9, 0x55, 0x02, 0x9A,
+   0xA9, 0x75, 0x10, 0x2C, 0xCB, 0x95, 0xBB, 0x6E,
+   0x23, 0x65, 0x35, 0x97, 0x56, 0xAD, 0xCE, 0xF8,
+   0xF0, 0x0C, 0xE2, 0x52, 0x05, 0x91, 0xCC, 0xC8,
+   0x78, 0x06, 0x96, 0x4E, 0x03, 0xD3, 0x98, 0xA7,
+   0x13, 0x58, 0x93, 0xD4, 0xDD, 0xC6, 0xFC, 0x25,
+   0x9C, 0x86, 0x1F, 0xCF, 0x76, 0xA4, 0x6A, 0xFA,
+   0x0B, 0x4A, 0x54, 0x40, 0x59, 0xD8, 0x61, 0xFF,
+   0x7F, 0x80, 0x6F, 0x7D, 0xF1, 0x8C, 0x92, 0xDE,
+   0x9D, 0xC3, 0xB2, 0xE7, 0xFB, 0x20, 0x31, 0x72,
+   0x12, 0xBE, 0x1D, 0xF6, 0x9B, 0x14, 0x26, 0x0A,
+   0xEB, 0xF7, 0x71, 0x39, 0x30, 0xA5, 0x87, 0xD2,
+   0x66, 0x2E, 0x08, 0x32, 0x4C, 0x33, 0x7E, 0xE1 };
+
+unsigned char table_169[256] = {
+   0xA4, 0x31, 0xA9, 0x3F, 0x13, 0x4D, 0x1B, 0x29,
+   0x73, 0x43, 0xF1, 0xE7, 0x9C, 0xC2, 0xF6, 0xCD,
+   0xA1, 0x94, 0x0D, 0x27, 0xFE, 0x7B, 0x9B, 0x0B,
+   0x89, 0xBA, 0x23, 0xEC, 0x76, 0xC3, 0x6C, 0xD8,
+   0x8D, 0xF8, 0xF9, 0x7D, 0x68, 0x5B, 0x61, 0x87,
+   0x28, 0x14, 0x55, 0x0C, 0xFC, 0xD9, 0x07, 0xE8,
+   0x36, 0x88, 0x67, 0x4C, 0xEA, 0xBD, 0xF5, 0x9D,
+   0xB6, 0xC6, 0x24, 0x32, 0x93, 0x03, 0x79, 0x8C,
+   0x12, 0x84, 0xFF, 0x7E, 0x42, 0xE4, 0x3C, 0xF2,
+   0x50, 0xEB, 0x1F, 0x47, 0xB0, 0xA5, 0xB1, 0x71,
+   0x30, 0x5F, 0x5C, 0x53, 0xF7, 0x10, 0xC5, 0x6E,
+   0xE0, 0xDE, 0xC8, 0x58, 0xB7, 0x90, 0xA6, 0x95,
+   0x70, 0x8F, 0xFD, 0xC1, 0x48, 0xB5, 0x19, 0x92,
+   0xBC, 0x15, 0x4E, 0xE6, 0x11, 0xDD, 0x81, 0x0E,
+   0xBB, 0x75, 0x5D, 0x4A, 0xAB, 0x2D, 0x02, 0x54,
+   0x4B, 0x66, 0xD6, 0x2B, 0x2A, 0xE5, 0x26, 0xE1,
+   0xEE, 0xE9, 0x8B, 0x6A, 0x7A, 0xF4, 0x51, 0x39,
+   0x1C, 0xC9, 0xCF, 0x77, 0x00, 0xF3, 0x25, 0xCC,
+   0x08, 0xFB, 0x0F, 0x3E, 0xCE, 0xED, 0x3D, 0x56,
+   0xEF, 0x1D, 0x85, 0x96, 0x52, 0xA8, 0xD3, 0xCB,
+   0xE3, 0x33, 0x06, 0x7C, 0xAE, 0x72, 0x09, 0x04,
+   0x91, 0xC4, 0x5A, 0x69, 0x98, 0xB4, 0x40, 0xDF,
+   0x7F, 0x9F, 0xAA, 0x83, 0xE2, 0x78, 0x74, 0x20,
+   0xAD, 0x6D, 0xDC, 0xD4, 0xCA, 0x60, 0xF0, 0x35,
+   0x37, 0xD0, 0x18, 0x1A, 0x64, 0x3A, 0x99, 0xDB,
+   0x62, 0x44, 0x2C, 0x82, 0x8E, 0xD7, 0xD1, 0xFA,
+   0x16, 0xD5, 0x46, 0xBF, 0xA7, 0xC0, 0x2E, 0x3B,
+   0x01, 0x63, 0xB2, 0x1E, 0x05, 0x21, 0xB8, 0x17,
+   0x22, 0x97, 0xAF, 0x4F, 0x86, 0x34, 0xDA, 0xC7,
+   0xA3, 0xA0, 0xB3, 0x2F, 0xAC, 0x49, 0xD2, 0x57,
+   0x6F, 0x9A, 0x65, 0xB9, 0x41, 0xBE, 0x8A, 0xA2,
+   0x6B, 0x0A, 0x59, 0x9E, 0x5E, 0x38, 0x45, 0x80 };
+
+unsigned char table_170[256] = {
+   0xE3, 0x00, 0x99, 0x03, 0xF6, 0xDD, 0xD1, 0x41,
+   0x58, 0x7E, 0xD9, 0x46, 0x04, 0xAF, 0x5C, 0x43,
+   0xDE, 0x5E, 0xFC, 0x97, 0x3D, 0x68, 0xC8, 0x37,
+   0x3C, 0xFB, 0x0F, 0x5A, 0xBE, 0xFA, 0x4C, 0x82,
+   0x0C, 0xA0, 0x0A, 0xD4, 0x9D, 0xCE, 0x78, 0xA8,
+   0x55, 0x56, 0x60, 0xAA, 0xC9, 0x96, 0x62, 0xEA,
+   0x0D, 0xB8, 0xE2, 0x84, 0x17, 0xAE, 0x2B, 0x2C,
+   0x91, 0x57, 0x38, 0x01, 0xA9, 0xCD, 0x34, 0xBA,
+   0x8D, 0xC0, 0xD6, 0xFF, 0xF2, 0xD3, 0x5F, 0x26,
+   0xCA, 0x9B, 0x21, 0x75, 0x4E, 0x49, 0x20, 0x59,
+   0x39, 0xBF, 0x90, 0x6C, 0xFE, 0x8F, 0x2F, 0x18,
+   0x36, 0xD7, 0xB4, 0xAC, 0xBD, 0xF3, 0x1D, 0x4F,
+   0xA3, 0x74, 0x5B, 0x44, 0x05, 0x9C, 0x6D, 0x6B,
+   0x1E, 0xE8, 0x25, 0x16, 0x80, 0xCC, 0x29, 0xC7,
+   0x94, 0x4A, 0xF5, 0xF4, 0x27, 0x85, 0xBB, 0x24,
+   0xDA, 0xB5, 0x76, 0x69, 0xA5, 0x54, 0x23, 0x31,
+   0x11, 0xA4, 0x09, 0xE4, 0x64, 0x10, 0xC5, 0xC1,
+   0x7D, 0xE7, 0x92, 0xF8, 0x9E, 0x6A, 0x15, 0x8B,
+   0x98, 0x42, 0x52, 0x66, 0x0B, 0xA1, 0x35, 0x1A,
+   0x14, 0x7C, 0xE1, 0x9F, 0x28, 0xF1, 0x1B, 0xA6,
+   0x71, 0x73, 0x81, 0xAB, 0xE6, 0x95, 0x06, 0x1F,
+   0xC6, 0xB0, 0x51, 0x0E, 0xEE, 0x77, 0xF0, 0xD8,
+   0xC2, 0x89, 0x7B, 0x07, 0xA2, 0xB7, 0x19, 0x67,
+   0x2E, 0x8E, 0x47, 0xA7, 0xEF, 0x32, 0xD2, 0x93,
+   0xDC, 0x9A, 0xB2, 0xED, 0x45, 0xC4, 0x50, 0x3F,
+   0xE5, 0xCF, 0x88, 0x1C, 0x7A, 0x79, 0xEB, 0x70,
+   0x2A, 0x7F, 0xBC, 0xDB, 0xD0, 0xB1, 0xCB, 0x08,
+   0x86, 0x5D, 0x53, 0x72, 0xB6, 0x4B, 0xB3, 0x22,
+   0xC3, 0x6F, 0xB9, 0xD5, 0x3B, 0x13, 0x2D, 0xAD,
+   0x33, 0xFD, 0x02, 0x40, 0x8A, 0x3A, 0xF7, 0xE0,
+   0x8C, 0x3E, 0x61, 0x6E, 0xE9, 0x63, 0xF9, 0xEC,
+   0x48, 0x30, 0x87, 0x83, 0x12, 0x4D, 0x65, 0xDF };
+
+unsigned char table_171[32] = {
+   0x07, 0x06, 0x11, 0x08, 0x0C, 0x1F, 0x19, 0x02,
+   0x14, 0x04, 0x0D, 0x18, 0x1A, 0x05, 0x17, 0x13,
+   0x1C, 0x1B, 0x15, 0x03, 0x01, 0x0F, 0x16, 0x1E,
+   0x1D, 0x10, 0x00, 0x12, 0x0B, 0x0E, 0x09, 0x0A };
+
+unsigned char table_172[32] = {
+   0x11, 0x01, 0x1F, 0x06, 0x1A, 0x04, 0x02, 0x09,
+   0x05, 0x0D, 0x0B, 0x18, 0x0E, 0x12, 0x1B, 0x17,
+   0x07, 0x08, 0x1D, 0x1E, 0x14, 0x19, 0x16, 0x15,
+   0x03, 0x0C, 0x00, 0x10, 0x0A, 0x1C, 0x0F, 0x13 };
+
+unsigned char table_173[32] = {
+   0x1F, 0x0B, 0x13, 0x00, 0x16, 0x15, 0x14, 0x0A,
+   0x1D, 0x05, 0x1E, 0x1A, 0x0F, 0x04, 0x0E, 0x01,
+   0x19, 0x07, 0x02, 0x12, 0x0C, 0x17, 0x08, 0x09,
+   0x03, 0x11, 0x18, 0x10, 0x1C, 0x1B, 0x06, 0x0D };
+
+unsigned char table_174[32] = {
+   0x02, 0x1B, 0x0C, 0x17, 0x1F, 0x05, 0x15, 0x1E,
+   0x16, 0x09, 0x1A, 0x12, 0x0F, 0x1C, 0x18, 0x0A,
+   0x19, 0x10, 0x0D, 0x13, 0x04, 0x11, 0x08, 0x14,
+   0x1D, 0x0E, 0x06, 0x00, 0x01, 0x07, 0x0B, 0x03 };
+
+unsigned char table_175[32] = {
+   0x00, 0x06, 0x0B, 0x08, 0x0C, 0x04, 0x1A, 0x1C,
+   0x05, 0x1E, 0x14, 0x03, 0x0A, 0x18, 0x12, 0x1D,
+   0x16, 0x1F, 0x07, 0x09, 0x0F, 0x0E, 0x17, 0x13,
+   0x11, 0x19, 0x10, 0x0D, 0x1B, 0x02, 0x01, 0x15 };
+
+unsigned char table_176[32] = {
+   0x12, 0x03, 0x1A, 0x15, 0x04, 0x19, 0x0B, 0x1B,
+   0x17, 0x1E, 0x0D, 0x05, 0x11, 0x14, 0x1C, 0x00,
+   0x18, 0x10, 0x0A, 0x06, 0x0E, 0x08, 0x02, 0x07,
+   0x13, 0x09, 0x16, 0x1D, 0x0F, 0x0C, 0x01, 0x1F };
+
+unsigned char table_177[256] = {
+   0x5E, 0x4D, 0x76, 0xFE, 0xB5, 0x50, 0x83, 0x23,
+   0x72, 0xDD, 0x93, 0x08, 0x69, 0xAD, 0xEC, 0x3B,
+   0x0B, 0x9A, 0x36, 0xC9, 0xCA, 0xBE, 0xF7, 0x30,
+   0x19, 0x39, 0x2C, 0xAB, 0xE3, 0x7B, 0xBC, 0x32,
+   0xA0, 0xE4, 0xA6, 0xB6, 0xCB, 0xC8, 0x37, 0x07,
+   0xD2, 0xA1, 0xD9, 0xF6, 0xBF, 0xF5, 0x88, 0x01,
+   0x95, 0x0F, 0x03, 0xFD, 0xE6, 0x68, 0x90, 0x61,
+   0x21, 0x6D, 0x3C, 0x62, 0x34, 0x2B, 0x71, 0x4B,
+   0x44, 0x64, 0x75, 0xA2, 0x6A, 0xFF, 0x29, 0xBD,
+   0x35, 0x15, 0xF9, 0xC1, 0x09, 0x45, 0xB2, 0xF2,
+   0x3F, 0xCE, 0xB0, 0xC0, 0xB8, 0x00, 0x05, 0xD7,
+   0x11, 0xC6, 0x78, 0x53, 0x9E, 0xB3, 0xED, 0x56,
+   0x22, 0x5C, 0x9D, 0x6C, 0x99, 0x43, 0x2F, 0xAE,
+   0xEB, 0x40, 0x8C, 0x1F, 0xC2, 0xDF, 0x92, 0x65,
+   0x6F, 0x79, 0x5D, 0x5B, 0xAA, 0xDB, 0xF1, 0x96,
+   0xD4, 0xF4, 0x8B, 0x51, 0xD5, 0xE2, 0xBB, 0x80,
+   0x17, 0x7C, 0x2A, 0x6E, 0xDE, 0xEA, 0x94, 0x31,
+   0xA4, 0x2D, 0xC3, 0x8D, 0x55, 0x14, 0x9B, 0x0E,
+   0x7D, 0xC4, 0x06, 0x33, 0x73, 0xE9, 0x7A, 0x38,
+   0x5F, 0x89, 0x84, 0xD6, 0xA8, 0x13, 0xE8, 0xCF,
+   0x46, 0xD0, 0x7F, 0x24, 0x8F, 0xF8, 0x87, 0x1B,
+   0x47, 0x02, 0x0C, 0x97, 0x52, 0xFB, 0x8E, 0x20,
+   0x70, 0x3E, 0x7E, 0xD1, 0xE5, 0xEE, 0xCC, 0x91,
+   0x74, 0xCD, 0x42, 0x04, 0x8A, 0xEF, 0xE1, 0x10,
+   0x4F, 0x1C, 0x28, 0x9F, 0xD8, 0x0A, 0x18, 0x49,
+   0x9C, 0x16, 0xF3, 0x82, 0x57, 0x1D, 0x26, 0x66,
+   0x27, 0x86, 0xE7, 0x59, 0xFA, 0x25, 0x54, 0x0D,
+   0x98, 0xDC, 0xF0, 0x3D, 0x63, 0x1E, 0x77, 0x3A,
+   0xDA, 0xB7, 0x6B, 0x2E, 0x48, 0x4C, 0xBA, 0xC7,
+   0x60, 0xAC, 0x1A, 0xB9, 0xFC, 0xA3, 0xA7, 0xA5,
+   0xB4, 0x67, 0xA9, 0x81, 0xB1, 0x12, 0xD3, 0x85,
+   0x5A, 0xC5, 0xE0, 0x58, 0x41, 0x4E, 0x4A, 0xAF };
+
+unsigned char table_178[256] = {
+   0x33, 0xBA, 0x98, 0xDA, 0x07, 0x2C, 0x22, 0x9B,
+   0xE0, 0xED, 0xB7, 0xA1, 0x93, 0xEB, 0xDC, 0x49,
+   0xDF, 0xE1, 0x6C, 0xC2, 0x64, 0x52, 0xD0, 0x8F,
+   0xA2, 0x48, 0x26, 0x21, 0x6E, 0x5E, 0x0B, 0x7C,
+   0x0D, 0x90, 0xA4, 0xCE, 0xF5, 0x5F, 0xF9, 0x1D,
+   0x55, 0x83, 0x8D, 0xFB, 0x38, 0xB3, 0xF2, 0x67,
+   0xDE, 0x0A, 0xBE, 0xEC, 0x5B, 0x35, 0x08, 0x50,
+   0xE7, 0x56, 0x4A, 0x02, 0xBC, 0x5A, 0xBD, 0x43,
+   0x6F, 0x79, 0xB2, 0xF7, 0x60, 0xE9, 0xA0, 0x1B,
+   0xC8, 0xDD, 0x9D, 0xA3, 0x5C, 0x61, 0x77, 0x72,
+   0x9C, 0x31, 0x0E, 0x05, 0x1E, 0x12, 0xF1, 0xC9,
+   0x78, 0x4E, 0x15, 0x7D, 0x54, 0xCB, 0x73, 0xEA,
+   0xC5, 0x2B, 0x0F, 0x7E, 0x42, 0x96, 0xC6, 0x74,
+   0x09, 0x65, 0x34, 0xE6, 0x63, 0xA6, 0x70, 0xD3,
+   0x27, 0x87, 0x3A, 0x16, 0x7B, 0x13, 0x06, 0x40,
+   0x46, 0x69, 0xAD, 0x88, 0x81, 0xC0, 0x37, 0x58,
+   0xD1, 0x8A, 0x8E, 0x9A, 0x5D, 0x6D, 0xC7, 0xC3,
+   0xD2, 0xF4, 0x3F, 0x57, 0x3C, 0x4F, 0xA9, 0x6A,
+   0x92, 0xA5, 0x97, 0x0C, 0x2A, 0x36, 0x47, 0xDB,
+   0x8C, 0xEE, 0x03, 0x89, 0x7F, 0x91, 0x24, 0x80,
+   0x2F, 0x62, 0xE4, 0xAF, 0x17, 0x99, 0xD6, 0xCD,
+   0xFE, 0x76, 0x1C, 0xD4, 0x3E, 0xFF, 0xD8, 0xC4,
+   0x39, 0x32, 0xCF, 0xE2, 0xE3, 0x53, 0xD7, 0xCC,
+   0xD9, 0x11, 0xAA, 0x1F, 0x01, 0x3B, 0x51, 0xB5,
+   0x94, 0x4B, 0x28, 0xF0, 0xAC, 0x44, 0x14, 0x4C,
+   0xB9, 0xA7, 0xB8, 0x1A, 0xD5, 0xCA, 0xE8, 0x82,
+   0x9F, 0x2D, 0xAB, 0x2E, 0x29, 0xFD, 0x68, 0xB1,
+   0x66, 0xC1, 0x7A, 0xFA, 0x71, 0x04, 0xA8, 0xB0,
+   0x59, 0x18, 0xAE, 0x25, 0x3D, 0xE5, 0xF6, 0x41,
+   0x86, 0x75, 0x6B, 0xBB, 0xFC, 0x84, 0x8B, 0x85,
+   0x10, 0x23, 0xB6, 0xF3, 0x19, 0x30, 0x20, 0x4D,
+   0x95, 0x9E, 0xBF, 0xEF, 0xF8, 0x45, 0x00, 0xB4 };
+
+unsigned char table_179[256] = {
+   0x50, 0x3D, 0x41, 0x42, 0x06, 0x5B, 0xD6, 0x34,
+   0x9D, 0x3C, 0x7B, 0x14, 0xE2, 0x9B, 0x80, 0x15,
+   0x51, 0x01, 0x6A, 0x30, 0xD7, 0xFC, 0x61, 0x4B,
+   0x8A, 0xEC, 0x38, 0x71, 0x70, 0x2E, 0x1C, 0x72,
+   0x79, 0x26, 0x4C, 0x48, 0xED, 0xAD, 0x25, 0x53,
+   0x03, 0xD9, 0xB5, 0x0D, 0x8E, 0x19, 0xCC, 0xBE,
+   0xE1, 0x91, 0x64, 0xA6, 0x21, 0xCE, 0x76, 0xAB,
+   0x9F, 0xD1, 0xB6, 0x23, 0x6D, 0xB0, 0x90, 0xBD,
+   0x09, 0x3A, 0x5E, 0xD0, 0x73, 0x10, 0x44, 0x08,
+   0xFF, 0xB8, 0x24, 0x58, 0xDB, 0x65, 0x95, 0xAA,
+   0xE9, 0xC4, 0x32, 0x2B, 0x84, 0xC9, 0xC7, 0xB1,
+   0x4F, 0x0C, 0xCB, 0x11, 0x4E, 0x22, 0x4A, 0x16,
+   0xDE, 0xBC, 0xEE, 0x68, 0x13, 0xFA, 0xC3, 0x98,
+   0xEB, 0x29, 0x43, 0x9A, 0xA1, 0xE0, 0xF0, 0x3F,
+   0x2F, 0x1B, 0xC2, 0x66, 0x35, 0xF5, 0xC8, 0xD8,
+   0x5A, 0xE5, 0x87, 0x47, 0xD3, 0x7A, 0xE6, 0x39,
+   0x77, 0x81, 0xF2, 0x0E, 0x83, 0x7E, 0x17, 0x6C,
+   0xB3, 0x5C, 0xE8, 0xD2, 0xC0, 0xA4, 0xF9, 0x86,
+   0xCD, 0xFB, 0x54, 0x7C, 0xBF, 0x2D, 0x82, 0xDA,
+   0x96, 0x74, 0x97, 0xC5, 0x7D, 0x27, 0x57, 0x56,
+   0xDC, 0xBA, 0x69, 0x8C, 0x9C, 0x88, 0xB4, 0x8D,
+   0x37, 0xEA, 0x3B, 0x33, 0x2C, 0xB2, 0x45, 0xF7,
+   0xC1, 0x1E, 0x46, 0x02, 0x6B, 0x3E, 0xA7, 0xD5,
+   0x05, 0x0A, 0xA9, 0x1D, 0xA3, 0x4D, 0xAE, 0x6F,
+   0x49, 0xDD, 0x8F, 0xEF, 0xBB, 0x67, 0x0B, 0x40,
+   0x9E, 0xF1, 0x78, 0x28, 0xDF, 0x52, 0xF4, 0x92,
+   0x94, 0x0F, 0xB9, 0x93, 0xF6, 0x1F, 0xAF, 0xA8,
+   0xCA, 0xE4, 0x59, 0x7F, 0x85, 0x75, 0xC6, 0xFD,
+   0x00, 0xB7, 0x55, 0xFE, 0x8B, 0x62, 0x5F, 0x12,
+   0xF8, 0xD4, 0x89, 0xA0, 0x20, 0xE7, 0xCF, 0x60,
+   0x5D, 0xAC, 0x1A, 0x36, 0x63, 0x99, 0x31, 0xF3,
+   0x2A, 0x04, 0x18, 0xA5, 0xA2, 0x6E, 0x07, 0xE3 };
+
+unsigned char table_180[256] = {
+   0xDA, 0xCC, 0x72, 0xA6, 0xE7, 0x07, 0xFD, 0x25,
+   0x92, 0x39, 0x49, 0x02, 0xD6, 0x09, 0xA8, 0x65,
+   0x2E, 0x6C, 0xA1, 0x19, 0xBF, 0x21, 0x11, 0xC7,
+   0x3F, 0x9F, 0xF4, 0x51, 0xAF, 0x8C, 0xFE, 0xCD,
+   0x7A, 0xEB, 0x5A, 0xF7, 0x18, 0x69, 0xB9, 0xED,
+   0x37, 0x45, 0x13, 0xB4, 0xAA, 0x75, 0x47, 0x42,
+   0xA3, 0x81, 0x88, 0x70, 0xC1, 0x36, 0x73, 0x1D,
+   0x3B, 0x22, 0xB6, 0x35, 0xE9, 0x31, 0x56, 0x23,
+   0xE1, 0xF5, 0xAD, 0x46, 0x99, 0x32, 0xE4, 0x40,
+   0x00, 0x0F, 0x05, 0xC6, 0x33, 0x84, 0x7B, 0x4D,
+   0x4B, 0x7D, 0x91, 0x3D, 0xCE, 0x64, 0x77, 0x55,
+   0xD7, 0x2B, 0x2F, 0x2C, 0xB8, 0xD3, 0x85, 0xD1,
+   0xB5, 0x6A, 0xF9, 0x41, 0x08, 0xBB, 0x87, 0xEC,
+   0x78, 0xE0, 0xEE, 0x8D, 0x01, 0x58, 0x15, 0x8F,
+   0x06, 0xF0, 0x8B, 0x27, 0x0D, 0x0B, 0x6D, 0xBD,
+   0xCA, 0x2A, 0xA2, 0xE6, 0xDD, 0xBC, 0x4E, 0x5D,
+   0x74, 0x04, 0x3A, 0x96, 0x66, 0x12, 0x1E, 0xF2,
+   0xF6, 0xC4, 0xAE, 0x3C, 0x0C, 0x90, 0x68, 0xD8,
+   0x24, 0x5E, 0x79, 0x10, 0xAC, 0xDF, 0x9B, 0xC5,
+   0x44, 0xC3, 0x50, 0x5C, 0xA5, 0x89, 0x60, 0x5F,
+   0x48, 0x17, 0x34, 0xA7, 0xE2, 0xF3, 0xD9, 0x3E,
+   0x9C, 0xB7, 0x7C, 0x1F, 0xA9, 0xD4, 0xA4, 0x0E,
+   0x8E, 0x4C, 0xDC, 0xF8, 0xF1, 0x98, 0xDE, 0x2D,
+   0x61, 0xCB, 0xD5, 0x43, 0x86, 0x26, 0xB0, 0x7F,
+   0x7E, 0xFF, 0xAB, 0x83, 0x14, 0x9A, 0x80, 0x16,
+   0x30, 0xA0, 0x53, 0x97, 0x52, 0x9E, 0xB1, 0x1B,
+   0xD0, 0x1A, 0xC8, 0x57, 0xBA, 0x6E, 0xFA, 0x94,
+   0xE8, 0x63, 0x5B, 0x29, 0xEF, 0x71, 0x8A, 0x03,
+   0xB3, 0x76, 0xC9, 0xD2, 0xBE, 0xE5, 0x82, 0x1C,
+   0x95, 0x9D, 0x4A, 0x28, 0xEA, 0x0A, 0xC0, 0xE3,
+   0x6F, 0x20, 0x54, 0xFB, 0x93, 0xFC, 0x6B, 0x38,
+   0x62, 0x4F, 0xCF, 0xB2, 0xC2, 0x59, 0xDB, 0x67 };
+
+unsigned char table_181[256] = {
+   0x2B, 0xED, 0x14, 0x05, 0x80, 0xCC, 0x5A, 0xF8,
+   0x43, 0xB7, 0x86, 0xC6, 0xEE, 0xA6, 0xD7, 0xD6,
+   0xA0, 0xC4, 0x21, 0x34, 0xB1, 0x8C, 0xF9, 0xF4,
+   0x7C, 0x53, 0x06, 0xD4, 0x6B, 0x3F, 0xE1, 0x12,
+   0x6A, 0xCE, 0xCF, 0xBF, 0x74, 0x3E, 0xD5, 0xCB,
+   0x97, 0x01, 0xA2, 0x2D, 0xAE, 0xF7, 0x17, 0x29,
+   0x47, 0x03, 0x0E, 0xE9, 0x82, 0x46, 0x94, 0xAF,
+   0x2A, 0x90, 0xFE, 0x4A, 0x7E, 0x0C, 0x71, 0xB6,
+   0xA5, 0xF2, 0x67, 0x41, 0xBA, 0xC2, 0x8A, 0x9D,
+   0x36, 0xFF, 0x50, 0x2E, 0xC3, 0x91, 0x9C, 0x37,
+   0x66, 0xAD, 0xB2, 0x1F, 0xE4, 0xE3, 0x9F, 0xDD,
+   0x87, 0xC0, 0xE6, 0xEF, 0x13, 0x70, 0x5B, 0xDE,
+   0x5C, 0x75, 0x7F, 0x4F, 0x44, 0xCA, 0x55, 0x57,
+   0xF0, 0x26, 0xA7, 0xC7, 0x10, 0x51, 0x00, 0xB3,
+   0x5D, 0x99, 0x81, 0x3B, 0xB9, 0x1C, 0x64, 0x7B,
+   0xFB, 0xD9, 0x8D, 0x4E, 0xAC, 0x25, 0xBB, 0x69,
+   0xDF, 0x02, 0x9E, 0x2C, 0xAB, 0xF3, 0x65, 0x09,
+   0xA3, 0x6C, 0xC1, 0x76, 0x52, 0x30, 0xD8, 0x3A,
+   0x40, 0x18, 0x59, 0xD0, 0xE5, 0xB4, 0x5F, 0x33,
+   0x68, 0x92, 0x2F, 0xB8, 0x93, 0xD1, 0xEB, 0xA4,
+   0xFC, 0x77, 0x19, 0x62, 0xC9, 0x49, 0x84, 0x1A,
+   0x9A, 0xE7, 0x31, 0xE8, 0xE2, 0x58, 0xF1, 0x4B,
+   0x1E, 0x0B, 0x39, 0xFD, 0x42, 0x7A, 0x89, 0x38,
+   0x11, 0x98, 0x63, 0x08, 0xE0, 0xEA, 0xBE, 0xB0,
+   0x45, 0x1B, 0x4C, 0x54, 0xC8, 0x27, 0x3D, 0x73,
+   0x04, 0x8F, 0x79, 0xBC, 0x6F, 0x0D, 0x0F, 0xA1,
+   0x60, 0xDC, 0xC5, 0xFA, 0x8E, 0xDA, 0x15, 0x96,
+   0xD3, 0x07, 0xF5, 0x3C, 0x88, 0x72, 0x1D, 0x4D,
+   0x8B, 0x61, 0x0A, 0xDB, 0xAA, 0x20, 0x23, 0xEC,
+   0x6E, 0x22, 0x48, 0x28, 0xBD, 0xA9, 0x56, 0x5E,
+   0x85, 0xA8, 0x95, 0x6D, 0x16, 0x78, 0xB5, 0xF6,
+   0x32, 0x24, 0x7D, 0x9B, 0xD2, 0x83, 0x35, 0xCD };
+
+unsigned char table_182[256] = {
+   0x06, 0x7F, 0x66, 0xB5, 0xBA, 0x1E, 0xFD, 0x51,
+   0x81, 0x8D, 0x28, 0xA3, 0x15, 0x37, 0xDC, 0x58,
+   0xE6, 0x3D, 0xB4, 0xB9, 0x2E, 0xA0, 0x2F, 0xC4,
+   0xCB, 0xB1, 0x25, 0xBF, 0xC1, 0x4E, 0x5A, 0xE4,
+   0x0F, 0x10, 0x7C, 0x52, 0xA7, 0x29, 0x76, 0x55,
+   0xAA, 0x70, 0x62, 0x54, 0x43, 0x93, 0x3A, 0x7D,
+   0x5B, 0x56, 0x33, 0x64, 0x74, 0x2A, 0xD9, 0x9B,
+   0x88, 0xC0, 0x3C, 0x63, 0xDE, 0xF4, 0x73, 0xDF,
+   0x9E, 0xB2, 0xA8, 0x4F, 0x04, 0x57, 0x47, 0x87,
+   0x14, 0xFC, 0x27, 0x53, 0x83, 0xDB, 0xD7, 0x20,
+   0x96, 0x31, 0xD0, 0xCF, 0x30, 0x19, 0x69, 0x1A,
+   0xAE, 0x3B, 0x11, 0x0C, 0xA6, 0x95, 0x8A, 0xF2,
+   0x1B, 0xCC, 0x78, 0xEF, 0xB3, 0x71, 0x84, 0xA2,
+   0xF1, 0x7A, 0x92, 0x61, 0xCA, 0x90, 0x94, 0x89,
+   0x68, 0xEE, 0x97, 0x38, 0x0D, 0xF9, 0x1F, 0x8E,
+   0xE9, 0x26, 0xBD, 0xC9, 0xFF, 0x4C, 0x44, 0x1D,
+   0x98, 0xE5, 0x86, 0xF3, 0x18, 0xB6, 0x09, 0xD2,
+   0x7E, 0xC5, 0xE7, 0x2B, 0x8C, 0x8B, 0x60, 0x3F,
+   0x2C, 0x6A, 0x08, 0x0E, 0x50, 0x32, 0x9F, 0xF0,
+   0x9A, 0xC2, 0x39, 0xBE, 0xEA, 0x12, 0x16, 0xBB,
+   0x5E, 0x67, 0xE3, 0xB8, 0x79, 0x46, 0xDA, 0x00,
+   0xD3, 0xBC, 0xCE, 0x1C, 0x80, 0xFA, 0xAB, 0x65,
+   0x4A, 0xF8, 0xAC, 0x72, 0x01, 0xC6, 0x35, 0x85,
+   0x3E, 0x5C, 0xA1, 0x05, 0xA5, 0xA9, 0xE1, 0x40,
+   0xEB, 0xE8, 0x5F, 0xF5, 0xC3, 0xD1, 0x34, 0xFB,
+   0xEC, 0xF7, 0x9C, 0xC7, 0xDD, 0x6C, 0x36, 0x9D,
+   0x42, 0x59, 0x99, 0x5D, 0xD8, 0x82, 0x07, 0x24,
+   0x6D, 0xAD, 0x13, 0x48, 0x6B, 0x6E, 0x75, 0x4D,
+   0xD5, 0x02, 0xED, 0xFE, 0x91, 0xCD, 0x77, 0xB0,
+   0xF6, 0xC8, 0x6F, 0x23, 0xAF, 0xB7, 0x2D, 0xD6,
+   0xA4, 0xE2, 0x45, 0x8F, 0x21, 0xE0, 0x49, 0x22,
+   0x7B, 0x17, 0x0B, 0x0A, 0x41, 0x03, 0xD4, 0x4B };
+
+unsigned char table_183[32] = {
+   0x1E, 0x1B, 0x11, 0x07, 0x08, 0x06, 0x18, 0x17,
+   0x0D, 0x0F, 0x12, 0x03, 0x1D, 0x04, 0x0A, 0x1A,
+   0x0C, 0x13, 0x14, 0x1F, 0x0B, 0x19, 0x10, 0x01,
+   0x16, 0x05, 0x1C, 0x0E, 0x02, 0x00, 0x09, 0x15 };
+
+unsigned char table_184[32] = {
+   0x0F, 0x1D, 0x17, 0x16, 0x0D, 0x05, 0x13, 0x1F,
+   0x1B, 0x09, 0x1C, 0x1E, 0x15, 0x01, 0x06, 0x08,
+   0x0C, 0x10, 0x0B, 0x02, 0x04, 0x0A, 0x07, 0x1A,
+   0x18, 0x0E, 0x03, 0x11, 0x12, 0x14, 0x19, 0x00 };
+
+unsigned char table_185[256] = {
+   0xA5, 0xEE, 0x2E, 0x28, 0xA7, 0xAC, 0xD9, 0xB2,
+   0x6E, 0x04, 0xB4, 0x03, 0xE8, 0x92, 0x5F, 0x4D,
+   0x73, 0x20, 0x71, 0xE0, 0x43, 0x53, 0x3F, 0xF8,
+   0x96, 0xA1, 0x24, 0x97, 0xAD, 0x7B, 0xE5, 0xE6,
+   0xF2, 0xCE, 0xE3, 0x76, 0x2F, 0xA2, 0x48, 0x0E,
+   0x4B, 0x4A, 0x8B, 0x5A, 0x81, 0x2C, 0xBF, 0xD7,
+   0xFB, 0x7D, 0x4C, 0x16, 0xF4, 0x00, 0xF5, 0x40,
+   0x64, 0x74, 0xA9, 0x37, 0x86, 0xD3, 0x1B, 0xCD,
+   0xF1, 0x1A, 0x90, 0x9F, 0x54, 0x79, 0x29, 0xC3,
+   0x77, 0x85, 0x02, 0xB1, 0x70, 0xFE, 0x5B, 0xDA,
+   0x6B, 0x01, 0x0C, 0x07, 0xB8, 0x58, 0x47, 0x42,
+   0x09, 0xE4, 0x27, 0xDD, 0xF3, 0x1E, 0x10, 0x9E,
+   0x49, 0x30, 0x05, 0xBE, 0x59, 0xEB, 0xD2, 0xAA,
+   0xC8, 0x9D, 0x8C, 0x5E, 0x14, 0x56, 0x8E, 0xF7,
+   0x38, 0x55, 0x87, 0xA3, 0x5D, 0x41, 0x4F, 0x1F,
+   0xF6, 0x0F, 0x57, 0x91, 0xAE, 0xBA, 0xB3, 0x95,
+   0x9B, 0x69, 0xC1, 0x11, 0xD0, 0x25, 0x7F, 0x3B,
+   0x62, 0xCF, 0xC0, 0xA0, 0xFC, 0xB6, 0x12, 0x6C,
+   0xF0, 0x13, 0x93, 0xAB, 0xC6, 0x78, 0x6D, 0x88,
+   0x22, 0x08, 0x2A, 0xE2, 0xB7, 0x65, 0x31, 0x3A,
+   0xA6, 0x7C, 0xF9, 0xDC, 0xE7, 0xA4, 0xC9, 0x63,
+   0xA8, 0x0B, 0xED, 0x50, 0x36, 0xD8, 0x3E, 0xB0,
+   0x6A, 0x5C, 0x45, 0x4E, 0x23, 0x84, 0x34, 0x9A,
+   0xCC, 0x3D, 0xB5, 0xEA, 0xDE, 0x75, 0xD6, 0xFF,
+   0x6F, 0xC2, 0xDB, 0x8D, 0x7A, 0x1C, 0xE9, 0x61,
+   0x0A, 0x1D, 0x32, 0x52, 0x3C, 0x19, 0xFA, 0xD1,
+   0xD4, 0x68, 0xC7, 0x0D, 0x99, 0x83, 0xEF, 0x80,
+   0x82, 0xBD, 0xD5, 0x7E, 0x39, 0x72, 0x51, 0xAF,
+   0x8A, 0x2D, 0xB9, 0x89, 0xC4, 0x67, 0x35, 0xE1,
+   0x44, 0x06, 0xEC, 0xCB, 0x8F, 0x17, 0xDF, 0x94,
+   0x60, 0xCA, 0x26, 0xFD, 0x33, 0x46, 0x21, 0xBB,
+   0x2B, 0xC5, 0x98, 0x18, 0x66, 0x15, 0x9C, 0xBC };
+
+unsigned char table_186[256] = {
+   0xB7, 0xFA, 0x03, 0x7C, 0x76, 0x43, 0xA7, 0x15,
+   0x4B, 0x4F, 0x04, 0xAA, 0x4E, 0xD2, 0x52, 0xC8,
+   0x79, 0x16, 0xF6, 0x61, 0x01, 0x5D, 0xD6, 0x47,
+   0xDE, 0xC5, 0x4D, 0x2F, 0xF5, 0x29, 0x21, 0xE6,
+   0x97, 0x35, 0xDC, 0x0E, 0x8B, 0xF4, 0x0F, 0xBE,
+   0x30, 0x07, 0x1D, 0x46, 0x75, 0xCE, 0x56, 0x42,
+   0x28, 0x93, 0x84, 0x20, 0xA5, 0xC2, 0x87, 0x45,
+   0x1C, 0x6B, 0x55, 0x06, 0xEB, 0xB0, 0xF9, 0x14,
+   0x23, 0xF1, 0xFC, 0xD7, 0x98, 0xD1, 0xA4, 0xED,
+   0x5B, 0xB1, 0x12, 0x7A, 0xD5, 0x5F, 0x53, 0x88,
+   0x95, 0x71, 0xE7, 0x5C, 0xF8, 0x83, 0xC7, 0x49,
+   0xDD, 0xDA, 0x0B, 0xC1, 0x70, 0xEC, 0x67, 0xE2,
+   0xEA, 0x72, 0x4C, 0x92, 0xA6, 0xE5, 0x59, 0xA9,
+   0x3C, 0xFE, 0x0A, 0x65, 0x6E, 0xF3, 0xA3, 0x22,
+   0x24, 0x81, 0xF2, 0xCC, 0xD3, 0xA0, 0xDF, 0xDB,
+   0xAB, 0x09, 0x13, 0x96, 0x36, 0x9C, 0xEE, 0xD4,
+   0x33, 0x5E, 0x26, 0xAE, 0x48, 0x38, 0xFF, 0x08,
+   0x1F, 0x6D, 0x02, 0xEF, 0x7E, 0x57, 0x2A, 0x8A,
+   0xBA, 0x90, 0xAF, 0xA8, 0x37, 0x8E, 0x9B, 0xC0,
+   0x69, 0x32, 0x86, 0xBD, 0x73, 0x6C, 0xB9, 0x31,
+   0x66, 0xBF, 0x1B, 0x44, 0x9E, 0xB2, 0xD0, 0xE0,
+   0xF0, 0x2C, 0x3F, 0xE1, 0x91, 0x18, 0x19, 0x50,
+   0xCA, 0x8F, 0x54, 0xB5, 0x8D, 0x0C, 0x17, 0x39,
+   0x8C, 0x00, 0x7F, 0x41, 0xE3, 0x2E, 0x1A, 0x9D,
+   0x27, 0xA1, 0x10, 0x34, 0x1E, 0x3A, 0x60, 0x77,
+   0xBB, 0xB6, 0x0D, 0x4A, 0x3E, 0x6A, 0xB4, 0xA2,
+   0xB3, 0xFD, 0xCD, 0x80, 0x51, 0xAD, 0xCF, 0xBC,
+   0x40, 0x74, 0x6F, 0x68, 0x2B, 0xC3, 0xF7, 0x63,
+   0xB8, 0x25, 0xC4, 0x62, 0xE9, 0xFB, 0x58, 0x85,
+   0x78, 0xCB, 0x9A, 0x3D, 0xE4, 0xC9, 0x89, 0x2D,
+   0x64, 0x82, 0xC6, 0x05, 0xD8, 0xAC, 0x99, 0x9F,
+   0x11, 0x3B, 0x94, 0xE8, 0x7D, 0x7B, 0xD9, 0x5A };
+
+unsigned char table_187[32] = {
+   0x0F, 0x04, 0x1D, 0x1B, 0x15, 0x10, 0x01, 0x0B,
+   0x00, 0x17, 0x13, 0x07, 0x1E, 0x1F, 0x08, 0x0A,
+   0x19, 0x09, 0x05, 0x06, 0x0C, 0x1A, 0x14, 0x16,
+   0x0E, 0x18, 0x03, 0x1C, 0x12, 0x11, 0x0D, 0x02 };
+
+struct yahoo_fn yahoo_fntable[5][96] = 
+   {{{ IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 },
+     { IDENT, 0, 0 }},
+    {{ MULADD, 0x36056CD7, 0x4387 },
+     { LOOKUP, (long)table_0, 0 },
+     { LOOKUP, (long)table_1, 0 },
+     { BITFLD, (long)table_2, 0 },
+     { LOOKUP, (long)table_3, 0 },
+     { BITFLD, (long)table_4, 0 },
+     { MULADD, 0x4ABB534D, 0x3769 },
+     { XOR, 0x1D242DA5, 0 },
+     { MULADD, 0x3C23132D, 0x339B },
+     { XOR, 0x0191265C, 0 },
+     { XOR, 0x3DB979DB, 0 },
+     { LOOKUP, (long)table_5, 0 },
+     { XOR, 0x1A550E1E, 0 },
+     { XOR, 0x2F140A2D, 0 },
+     { MULADD, 0x7C466A4B, 0x29BF },
+     { XOR, 0x2D3F30D3, 0 },
+     { MULADD, 0x7E823B21, 0x6BB3 },
+     { BITFLD, (long)table_6, 0 },
+     { LOOKUP, (long)table_7, 0 },
+     { BITFLD, (long)table_8, 0 },
+     { LOOKUP, (long)table_9, 0 },
+     { BITFLD, (long)table_10, 0 },
+     { LOOKUP, (long)table_11, 0 },
+     { BITFLD, (long)table_12, 0 },
+     { LOOKUP, (long)table_13, 0 },
+     { BITFLD, (long)table_14, 0 },
+     { MULADD, 0x5B756AB9, 0x7E9B },
+     { LOOKUP, (long)table_15, 0 },
+     { XOR, 0x1D1C4911, 0 },
+     { LOOKUP, (long)table_16, 0 },
+     { LOOKUP, (long)table_17, 0 },
+     { XOR, 0x46BD7771, 0 },
+     { XOR, 0x51AE2B42, 0 },
+     { MULADD, 0x2417591B, 0x177B },
+     { MULADD, 0x57F27C5F, 0x2433 },
+     { LOOKUP, (long)table_18, 0 },
+     { LOOKUP, (long)table_19, 0 },
+     { XOR, 0x71422261, 0 },
+     { BITFLD, (long)table_20, 0 },
+     { MULADD, 0x58E937F9, 0x1075 },
+     { LOOKUP, (long)table_21, 0 },
+     { BITFLD, (long)table_22, 0 },
+     { LOOKUP, (long)table_23, 0 },
+     { LOOKUP, (long)table_24, 0 },
+     { MULADD, 0x0B4C3D13, 0x1597 },
+     { BITFLD, (long)table_25, 0 },
+     { XOR, 0x0FE07D38, 0 },
+     { MULADD, 0x689B4017, 0x3CFB },
+     { BITFLD, (long)table_26, 0 },
+     { LOOKUP, (long)table_27, 0 },
+     { XOR, 0x35413DF3, 0 },
+     { MULADD, 0x05B611AB, 0x570B },
+     { MULADD, 0x0DA5334F, 0x3AC7 },
+     { XOR, 0x47706008, 0 },
+     { BITFLD, (long)table_28, 0 },
+     { LOOKUP, (long)table_29, 0 },
+     { BITFLD, (long)table_30, 0 },
+     { XOR, 0x57611B36, 0 },
+     { MULADD, 0x314C2CD1, 0x2B5B },
+     { XOR, 0x1EF33946, 0 },
+     { MULADD, 0x28EA041F, 0x638F },
+     { LOOKUP, (long)table_31, 0 },
+     { LOOKUP, (long)table_32, 0 },
+     { LOOKUP, (long)table_33, 0 },
+     { MULADD, 0x511537CB, 0x7135 },
+     { MULADD, 0x1CF71007, 0x5E17 },
+     { XOR, 0x583D4BCF, 0 },
+     { LOOKUP, (long)table_34, 0 },
+     { XOR, 0x373E6856, 0 },
+     { MULADD, 0x4D595519, 0x1A7D },
+     { LOOKUP, (long)table_35, 0 },
+     { LOOKUP, (long)table_36, 0 },
+     { XOR, 0x0E2A36A7, 0 },
+     { LOOKUP, (long)table_37, 0 },
+     { LOOKUP, (long)table_38, 0 },
+     { BITFLD, (long)table_39, 0 },
+     { BITFLD, (long)table_40, 0 },
+     { XOR, 0x53F3604F, 0 },
+     { BITFLD, (long)table_41, 0 },
+     { BITFLD, (long)table_42, 0 },
+     { MULADD, 0x1EDC0BA3, 0x7531 },
+     { LOOKUP, (long)table_43, 0 },
+     { XOR, 0x10DF1038, 0 },
+     { BITFLD, (long)table_44, 0 },
+     { LOOKUP, (long)table_45, 0 },
+     { XOR, 0x4EDE0CAC, 0 },
+     { MULADD, 0x2F076EEB, 0x5BCF },
+     { XOR, 0x6D86030F, 0 },
+     { XOR, 0x3F331713, 0 },
+     { LOOKUP, (long)table_46, 0 },
+     { MULADD, 0x41CD726F, 0x3F79 },
+     { BITFLD, (long)table_47, 0 },
+     { XOR, 0x0ECE0054, 0 },
+     { MULADD, 0x19B32B03, 0x4AD1 },
+     { BITFLD, (long)table_48, 0 },
+     { BITFLD, (long)table_49, 0 }},
+    {{ MULADD, 0x39731111, 0x419B },
+     { XOR, 0x54F7757A, 0 },
+     { BITFLD, (long)table_50, 0 },
+     { BITFLD, (long)table_51, 0 },
+     { LOOKUP, (long)table_52, 0 },
+     { LOOKUP, (long)table_53, 0 },
+     { MULADD, 0x3CC0256B, 0x7CE7 },
+     { XOR, 0x79991847, 0 },
+     { MULADD, 0x228F7FB5, 0x472D },
+     { MULADD, 0x32DA290B, 0x7745 },
+     { XOR, 0x7A28180D, 0 },
+     { BITFLD, (long)table_54, 0 },
+     { BITFLD, (long)table_55, 0 },
+     { MULADD, 0x5C814F8B, 0x227F },
+     { LOOKUP, (long)table_56, 0 },
+     { MULADD, 0x0B496F6D, 0x412D },
+     { XOR, 0x6F4B62DA, 0 },
+     { LOOKUP, (long)table_57, 0 },
+     { XOR, 0x64973977, 0 },
+     { LOOKUP, (long)table_58, 0 },
+     { LOOKUP, (long)table_59, 0 },
+     { BITFLD, (long)table_60, 0 },
+     { LOOKUP, (long)table_61, 0 },
+     { LOOKUP, (long)table_62, 0 },
+     { XOR, 0x6DD14C92, 0 },
+     { LOOKUP, (long)table_63, 0 },
+     { BITFLD, (long)table_64, 0 },
+     { BITFLD, (long)table_65, 0 },
+     { BITFLD, (long)table_66, 0 },
+     { LOOKUP, (long)table_67, 0 },
+     { XOR, 0x5E6324D8, 0 },
+     { LOOKUP, (long)table_68, 0 },
+     { LOOKUP, (long)table_69, 0 },
+     { LOOKUP, (long)table_70, 0 },
+     { BITFLD, (long)table_71, 0 },
+     { XOR, 0x62745ED0, 0 },
+     { MULADD, 0x102C215B, 0x0581 },
+     { LOOKUP, (long)table_72, 0 },
+     { LOOKUP, (long)table_73, 0 },
+     { LOOKUP, (long)table_74, 0 },
+     { MULADD, 0x19511111, 0x12C1 },
+     { LOOKUP, (long)table_75, 0 },
+     { MULADD, 0x2A6E2953, 0x6977 },
+     { LOOKUP, (long)table_76, 0 },
+     { XOR, 0x55CD5445, 0 },
+     { BITFLD, (long)table_77, 0 },
+     { BITFLD, (long)table_78, 0 },
+     { MULADD, 0x646C21EB, 0x43E5 },
+     { XOR, 0x71DC4898, 0 },
+     { XOR, 0x167519CB, 0 },
+     { XOR, 0x6D3158F8, 0 },
+     { XOR, 0x7EA95BEA, 0 },
+     { BITFLD, (long)table_79, 0 },
+     { XOR, 0x47377587, 0 },
+     { XOR, 0x2D8B6E8F, 0 },
+     { MULADD, 0x5E6105DB, 0x1605 },
+     { XOR, 0x65B543C8, 0 },
+     { LOOKUP, (long)table_80, 0 },
+     { BITFLD, (long)table_81, 0 },
+     { MULADD, 0x48AF73CB, 0x0A67 },
+     { XOR, 0x4FB96154, 0 },
+     { LOOKUP, (long)table_82, 0 },
+     { BITFLD, (long)table_83, 0 },
+     { XOR, 0x622C4954, 0 },
+     { BITFLD, (long)table_84, 0 },
+     { XOR, 0x20D220F3, 0 },
+     { XOR, 0x361D4F0D, 0 },
+     { XOR, 0x2B2000D1, 0 },
+     { XOR, 0x6FB8593E, 0 },
+     { LOOKUP, (long)table_85, 0 },
+     { BITFLD, (long)table_86, 0 },
+     { XOR, 0x2B7F7DFC, 0 },
+     { MULADD, 0x5FC41A57, 0x0693 },
+     { MULADD, 0x17154387, 0x2489 },
+     { BITFLD, (long)table_87, 0 },
+     { BITFLD, (long)table_88, 0 },
+     { BITFLD, (long)table_89, 0 },
+     { LOOKUP, (long)table_90, 0 },
+     { XOR, 0x7E221470, 0 },
+     { XOR, 0x7A600061, 0 },
+     { BITFLD, (long)table_91, 0 },
+     { BITFLD, (long)table_92, 0 },
+     { LOOKUP, (long)table_93, 0 },
+     { BITFLD, (long)table_94, 0 },
+     { MULADD, 0x00E813A5, 0x2CE5 },
+     { MULADD, 0x3D707E25, 0x3827 },
+     { MULADD, 0x77A53E07, 0x6A5F },
+     { BITFLD, (long)table_95, 0 },
+     { LOOKUP, (long)table_96, 0 },
+     { LOOKUP, (long)table_97, 0 },
+     { XOR, 0x43A73788, 0 },
+     { LOOKUP, (long)table_98, 0 },
+     { BITFLD, (long)table_99, 0 },
+     { LOOKUP, (long)table_100, 0 },
+     { XOR, 0x55F4606B, 0 },
+     { BITFLD, (long)table_101, 0 }},
+    {{ BITFLD, (long)table_102, 0 },
+     { MULADD, 0x32CA58E3, 0x04F9 },
+     { XOR, 0x11756B30, 0 },
+     { MULADD, 0x218B2569, 0x5DB1 },
+     { XOR, 0x77D64B90, 0 },
+     { BITFLD, (long)table_103, 0 },
+     { LOOKUP, (long)table_104, 0 },
+     { MULADD, 0x7D1428CB, 0x3D },
+     { XOR, 0x6F872C49, 0 },
+     { XOR, 0x2E484655, 0 },
+     { MULADD, 0x1E3349F7, 0x41F5 },
+     { LOOKUP, (long)table_105, 0 },
+     { BITFLD, (long)table_106, 0 },
+     { XOR, 0x61640311, 0 },
+     { BITFLD, (long)table_107, 0 },
+     { LOOKUP, (long)table_108, 0 },
+     { LOOKUP, (long)table_109, 0 },
+     { LOOKUP, (long)table_110, 0 },
+     { XOR, 0x007044D3, 0 },
+     { BITFLD, (long)table_111, 0 },
+     { MULADD, 0x5C221625, 0x576F },
+     { LOOKUP, (long)table_112, 0 },
+     { LOOKUP, (long)table_113, 0 },
+     { XOR, 0x2D406BB1, 0 },
+     { MULADD, 0x680B1F17, 0x12CD },
+     { BITFLD, (long)table_114, 0 },
+     { MULADD, 0x12564D55, 0x32B9 },
+     { MULADD, 0x21A67897, 0x6BAB },
+     { LOOKUP, (long)table_115, 0 },
+     { MULADD, 0x06405119, 0x7143 },
+     { XOR, 0x351D01ED, 0 },
+     { MULADD, 0x46356F6B, 0x0A49 },
+     { MULADD, 0x32C77969, 0x72F3 },
+     { BITFLD, (long)table_116, 0 },
+     { LOOKUP, (long)table_117, 0 },
+     { LOOKUP, (long)table_118, 0 },
+     { BITFLD, (long)table_119, 0 },
+     { LOOKUP, (long)table_120, 0 },
+     { BITFLD, (long)table_121, 0 },
+     { MULADD, 0x74D52C55, 0x5F43 },
+     { XOR, 0x26201CA8, 0 },
+     { XOR, 0x7AEB3255, 0 },
+     { LOOKUP, (long)table_122, 0 },
+     { MULADD, 0x578F1047, 0x640B },
+     { LOOKUP, (long)table_123, 0 },
+     { LOOKUP, (long)table_124, 0 },
+     { BITFLD, (long)table_125, 0 },
+     { BITFLD, (long)table_126, 0 },
+     { XOR, 0x4A1352CF, 0 },
+     { MULADD, 0x4BFB6EF3, 0x704F },
+     { MULADD, 0x1B4C7FE7, 0x5637 },
+     { MULADD, 0x04091A3B, 0x4917 },
+     { XOR, 0x270C2F52, 0 },
+     { LOOKUP, (long)table_127, 0 },
+     { BITFLD, (long)table_128, 0 },
+     { LOOKUP, (long)table_129, 0 },
+     { BITFLD, (long)table_130, 0 },
+     { MULADD, 0x127549D5, 0x579B },
+     { MULADD, 0x0AB54121, 0x7A47 },
+     { BITFLD, (long)table_131, 0 },
+     { XOR, 0x751E6E49, 0 },
+     { LOOKUP, (long)table_132, 0 },
+     { LOOKUP, (long)table_133, 0 },
+     { XOR, 0x670C3F74, 0 },
+     { MULADD, 0x6B080851, 0x7E8B },
+     { XOR, 0x71CD789E, 0 },
+     { XOR, 0x3EB20B7B, 0 },
+     { BITFLD, (long)table_134, 0 },
+     { LOOKUP, (long)table_135, 0 },
+     { MULADD, 0x58A67753, 0x272B },
+     { MULADD, 0x1AB54AD7, 0x4D33 },
+     { MULADD, 0x07D30A45, 0x0569 },
+     { MULADD, 0x737616BF, 0x70C7 },
+     { LOOKUP, (long)table_136, 0 },
+     { MULADD, 0x45C4485D, 0x2063 },
+     { BITFLD, (long)table_137, 0 },
+     { XOR, 0x2598043D, 0 },
+     { MULADD, 0x223A4FE3, 0x49A7 },
+     { XOR, 0x1EED619F, 0 },
+     { BITFLD, (long)table_138, 0 },
+     { XOR, 0x6F477561, 0 },
+     { BITFLD, (long)table_139, 0 },
+     { BITFLD, (long)table_140, 0 },
+     { LOOKUP, (long)table_141, 0 },
+     { MULADD, 0x4BC13C4F, 0x45C1 },
+     { XOR, 0x3B547BFB, 0 },
+     { LOOKUP, (long)table_142, 0 },
+     { MULADD, 0x71406AB3, 0x7A5F },
+     { XOR, 0x2F1467E9, 0 },
+     { MULADD, 0x009366D1, 0x22D1 },
+     { MULADD, 0x587D1B75, 0x2CA5 },
+     { MULADD, 0x213A4BE7, 0x4499 },
+     { MULADD, 0x62653E89, 0x2D5D },
+     { BITFLD, (long)table_143, 0 },
+     { MULADD, 0x4F5F3257, 0x444F },
+     { MULADD, 0x4C0E2B2B, 0x19D3 }},
+    {{ MULADD, 0x3F867B35, 0x7B3B },
+     { MULADD, 0x32D25CB1, 0x3D6D },
+     { BITFLD, (long)table_144, 0 },
+     { MULADD, 0x50FA1C51, 0x5F4F },
+     { LOOKUP, (long)table_145, 0 },
+     { XOR, 0x05FE7AF1, 0 },
+     { MULADD, 0x14067C29, 0x10C5 },
+     { LOOKUP, (long)table_146, 0 },
+     { MULADD, 0x4A5558C5, 0x271F },
+     { XOR, 0x3C0861B1, 0 },
+     { BITFLD, (long)table_147, 0 },
+     { LOOKUP, (long)table_148, 0 },
+     { MULADD, 0x18837C9D, 0x6335 },
+     { BITFLD, (long)table_149, 0 },
+     { XOR, 0x7DAB5033, 0 },
+     { LOOKUP, (long)table_150, 0 },
+     { MULADD, 0x03B87321, 0x7225 },
+     { XOR, 0x7F906745, 0 },
+     { LOOKUP, (long)table_151, 0 },
+     { BITFLD, (long)table_152, 0 },
+     { XOR, 0x21C46C2C, 0 },
+     { MULADD, 0x2B36757D, 0x028D },
+     { BITFLD, (long)table_153, 0 },
+     { LOOKUP, (long)table_154, 0 },
+     { XOR, 0x106B4A85, 0 },
+     { XOR, 0x17640F11, 0 },
+     { LOOKUP, (long)table_155, 0 },
+     { XOR, 0x69E60486, 0 },
+     { LOOKUP, (long)table_156, 0 },
+     { MULADD, 0x3782017D, 0x05BF },
+     { BITFLD, (long)table_157, 0 },
+     { LOOKUP, (long)table_158, 0 },
+     { XOR, 0x6BCA53B0, 0 },
+     { LOOKUP, (long)table_159, 0 },
+     { LOOKUP, (long)table_160, 0 },
+     { LOOKUP, (long)table_161, 0 },
+     { LOOKUP, (long)table_162, 0 },
+     { XOR, 0x0B8236E3, 0 },
+     { BITFLD, (long)table_163, 0 },
+     { MULADD, 0x5EE51C43, 0x4553 },
+     { BITFLD, (long)table_164, 0 },
+     { LOOKUP, (long)table_165, 0 },
+     { LOOKUP, (long)table_166, 0 },
+     { LOOKUP, (long)table_167, 0 },
+     { MULADD, 0x42B14C6F, 0x5531 },
+     { XOR, 0x4A2548E8, 0 },
+     { MULADD, 0x5C071D85, 0x2437 },
+     { LOOKUP, (long)table_168, 0 },
+     { MULADD, 0x29195861, 0x108B },
+     { XOR, 0x24012258, 0 },
+     { LOOKUP, (long)table_169, 0 },
+     { XOR, 0x63CC2377, 0 },
+     { XOR, 0x08D04B59, 0 },
+     { MULADD, 0x3FD30CF5, 0x7027 },
+     { XOR, 0x7C3E0478, 0 },
+     { MULADD, 0x457776B7, 0x24B3 },
+     { XOR, 0x086652BC, 0 },
+     { MULADD, 0x302F5B13, 0x371D },
+     { LOOKUP, (long)table_170, 0 },
+     { MULADD, 0x58692D47, 0x0671 },
+     { XOR, 0x6601178E, 0 },
+     { MULADD, 0x0F195B9B, 0x1369 },
+     { XOR, 0x07BA21D8, 0 },
+     { BITFLD, (long)table_171, 0 },
+     { BITFLD, (long)table_172, 0 },
+     { XOR, 0x13AC3D21, 0 },
+     { MULADD, 0x5BCF3275, 0x6E1B },
+     { MULADD, 0x62725C5B, 0x16B9 },
+     { MULADD, 0x5B950FDF, 0x2D35 },
+     { BITFLD, (long)table_173, 0 },
+     { BITFLD, (long)table_174, 0 },
+     { MULADD, 0x73BA5335, 0x1C13 },
+     { BITFLD, (long)table_175, 0 },
+     { BITFLD, (long)table_176, 0 },
+     { XOR, 0x3E144154, 0 },
+     { MULADD, 0x4EED7B27, 0x38AB },
+     { LOOKUP, (long)table_177, 0 },
+     { MULADD, 0x627C7E0F, 0x7F01 },
+     { MULADD, 0x5D7E1F73, 0x2C0F },
+     { LOOKUP, (long)table_178, 0 },
+     { MULADD, 0x55C9525F, 0x4659 },
+     { XOR, 0x3765334C, 0 },
+     { MULADD, 0x5DF66DDF, 0x7C25 },
+     { LOOKUP, (long)table_179, 0 },
+     { LOOKUP, (long)table_180, 0 },
+     { XOR, 0x16AE5776, 0 },
+     { LOOKUP, (long)table_181, 0 },
+     { LOOKUP, (long)table_182, 0 },
+     { BITFLD, (long)table_183, 0 },
+     { BITFLD, (long)table_184, 0 },
+     { LOOKUP, (long)table_185, 0 },
+     { MULADD, 0x4392327B, 0x7E0D },
+     { LOOKUP, (long)table_186, 0 },
+     { MULADD, 0x3D8B0CB5, 0x640D },
+     { MULADD, 0x32865601, 0x4D43 },
+     { BITFLD, (long)table_187, 0 }}};
+
+#define A( x ) (( x ) & 0xFF )
+#define B( x ) (( x ) >> 8 & 0xFF )
+#define C( x ) (( x ) >> 16 & 0xFF )
+#define D( x ) (( x ) >> 24 & 0xFF )
+
+int yahoo_xfrm( int table, int depth, int seed )
+{
+   struct yahoo_fn *xfrm;
+   int i, j, z;
+   unsigned int n = seed;
+   unsigned char *arg;
+
+   for( i = 0; i < depth; i++ )
+   {
+      xfrm = &yahoo_fntable[table][n % 96];
+      switch( xfrm->type )
+      {
+      case IDENT:
+         return seed;
+      case XOR:
+         seed ^= xfrm->arg1;
+         break;
+      case MULADD:
+         seed = seed * xfrm->arg1 + xfrm->arg2;
+         break;
+      case LOOKUP:
+         arg = (unsigned char *)xfrm->arg1;
+         seed = arg[A( seed )] | arg[B( seed )] << 8 | arg[C( seed )] << 16
+            | arg[D( seed )] << 24;
+         break;
+      case BITFLD:
+         arg = (unsigned char *)xfrm->arg1;
+         for( j = 0, z = 0; j < 32; j++ )
+            z = ((( seed >> j ) & 1 ) << arg[j] ) | ( ~( 1 << arg[j] ) & z );
+         seed = z;
+         break;
+      }
+      if( depth - i == 1 )
+         return seed;
+      z = (((((( A( seed ) * 0x9E3779B1 ) ^ B( seed )) * 0x9E3779B1 )
+         ^ C( seed )) * 0x9E3779B1 ) ^ D( seed )) * 0x9E3779B1;
+      n = (((( z ^ ( z >> 8 )) >> 16 ) ^ z ) ^ ( z >> 8 )) & 0xFF;
+      seed *= 0x00010DCD;
+   }
+   return seed;
+}
Index: protocols/yahoo/yahoo_fn.h
===================================================================
--- protocols/yahoo/yahoo_fn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_fn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,33 @@
+/*
+ * libyahoo2 - originally from gaim patches by Amatus
+ *
+ * Copyright (C) 2003-2004
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define IDENT  1 /* identify function */
+#define XOR    2 /* xor with arg1 */
+#define MULADD 3 /* multipy by arg1 then add arg2 */
+#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */
+#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */
+
+struct yahoo_fn
+{
+	int type; 
+	long arg1, arg2;
+};
+
+int yahoo_xfrm( int table, int depth, int seed );
Index: protocols/yahoo/yahoo_httplib.c
===================================================================
--- protocols/yahoo/yahoo_httplib.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_httplib.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,437 @@
+/*
+ * libyahoo2: yahoo_httplib.c
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+#  define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+
+#include <errno.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <ctype.h>
+#include "yahoo2.h"
+#include "yahoo2_callbacks.h"
+#include "yahoo_httplib.h"
+#include "yahoo_util.h"
+
+#include "yahoo_debug.h"
+#ifdef __MINGW32__
+# include <winsock2.h>
+# define write(a,b,c) send(a,b,c,0)
+# define read(a,b,c)  recv(a,b,c,0)
+# define snprintf _snprintf
+#endif
+
+#ifdef USE_STRUCT_CALLBACKS
+extern struct yahoo_callbacks *yc;
+#define YAHOO_CALLBACK(x)	yc->x
+#else
+#define YAHOO_CALLBACK(x)	x
+#endif
+
+extern enum yahoo_log_level log_level;
+
+int yahoo_tcp_readline(char *ptr, int maxlen, int fd)
+{
+	int n, rc;
+	char c;
+
+	for (n = 1; n < maxlen; n++) {
+
+		do {
+			rc = read(fd, &c, 1);
+		} while(rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */
+
+		if (rc == 1) {
+			if(c == '\r')			/* get rid of \r */
+				continue;
+			*ptr = c;
+			if (c == '\n')
+				break;
+			ptr++;
+		} else if (rc == 0) {
+			if (n == 1)
+				return (0);		/* EOF, no data */
+			else
+				break;			/* EOF, w/ data */
+		} else {
+			return -1;
+		}
+	}
+
+	*ptr = 0;
+	return (n);
+}
+
+static int url_to_host_port_path(const char *url,
+		char *host, int *port, char *path)
+{
+	char *urlcopy=NULL;
+	char *slash=NULL;
+	char *colon=NULL;
+	
+	/*
+	 * http://hostname
+	 * http://hostname/
+	 * http://hostname/path
+	 * http://hostname/path:foo
+	 * http://hostname:port
+	 * http://hostname:port/
+	 * http://hostname:port/path
+	 * http://hostname:port/path:foo
+	 */
+
+	if(strstr(url, "http://") == url) {
+		urlcopy = strdup(url+7);
+	} else {
+		WARNING(("Weird url - unknown protocol: %s", url));
+		return 0;
+	}
+
+	slash = strchr(urlcopy, '/');
+	colon = strchr(urlcopy, ':');
+
+	if(!colon || (slash && slash < colon)) {
+		*port = 80;
+	} else {
+		*colon = 0;
+		*port = atoi(colon+1);
+	}
+
+	if(!slash) {
+		strcpy(path, "/");
+	} else {
+		strcpy(path, slash);
+		*slash = 0;
+	}
+
+	strcpy(host, urlcopy);
+	
+	FREE(urlcopy);
+
+	return 1;
+}
+
+static int isurlchar(unsigned char c)
+{
+	return (isalnum(c) || '-' == c || '_' == c);
+}
+
+char *yahoo_urlencode(const char *instr)
+{
+	int ipos=0, bpos=0;
+	char *str = NULL;
+	int len = strlen(instr);
+
+	if(!(str = y_new(char, 3*len + 1) ))
+		return "";
+
+	while(instr[ipos]) {
+		while(isurlchar(instr[ipos]))
+			str[bpos++] = instr[ipos++];
+		if(!instr[ipos])
+			break;
+		
+		snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]);
+		bpos+=3;
+		ipos++;
+	}
+	str[bpos]='\0';
+
+	/* free extra alloc'ed mem. */
+	len = strlen(str);
+	str = y_renew(char, str, len+1);
+
+	return (str);
+}
+
+char *yahoo_urldecode(const char *instr)
+{
+	int ipos=0, bpos=0;
+	char *str = NULL;
+	char entity[3]={0,0,0};
+	unsigned dec;
+	int len = strlen(instr);
+
+	if(!(str = y_new(char, len+1) ))
+		return "";
+
+	while(instr[ipos]) {
+		while(instr[ipos] && instr[ipos]!='%')
+			if(instr[ipos]=='+') {
+				str[bpos++]=' ';
+				ipos++;
+			} else
+				str[bpos++] = instr[ipos++];
+		if(!instr[ipos])
+			break;
+		
+		if(instr[ipos+1] && instr[ipos+2]) {
+			ipos++;
+			entity[0]=instr[ipos++];
+			entity[1]=instr[ipos++];
+			sscanf(entity, "%2x", &dec);
+			str[bpos++] = (char)dec;
+		} else {
+			str[bpos++] = instr[ipos++];
+		}
+	}
+	str[bpos]='\0';
+
+	/* free extra alloc'ed mem. */
+	len = strlen(str);
+	str = y_renew(char, str, len+1);
+
+	return (str);
+}
+
+char *yahoo_xmldecode(const char *instr)
+{
+	int ipos=0, bpos=0, epos=0;
+	char *str = NULL;
+	char entity[4]={0,0,0,0};
+	char *entitymap[5][2]={
+		{"amp;",  "&"}, 
+		{"quot;", "\""},
+		{"lt;",   "<"}, 
+		{"gt;",   "<"}, 
+		{"nbsp;", " "}
+	};
+	unsigned dec;
+	int len = strlen(instr);
+
+	if(!(str = y_new(char, len+1) ))
+		return "";
+
+	while(instr[ipos]) {
+		while(instr[ipos] && instr[ipos]!='&')
+			if(instr[ipos]=='+') {
+				str[bpos++]=' ';
+				ipos++;
+			} else
+				str[bpos++] = instr[ipos++];
+		if(!instr[ipos] || !instr[ipos+1])
+			break;
+		ipos++;
+
+		if(instr[ipos] == '#') {
+			ipos++;
+			epos=0;
+			while(instr[ipos] != ';')
+				entity[epos++]=instr[ipos++];
+			sscanf(entity, "%u", &dec);
+			str[bpos++] = (char)dec;
+			ipos++;
+		} else {
+			int i;
+			for (i=0; i<5; i++) 
+				if(!strncmp(instr+ipos, entitymap[i][0], 
+					       strlen(entitymap[i][0]))) {
+				       	str[bpos++] = entitymap[i][1][0];
+					ipos += strlen(entitymap[i][0]);
+					break;
+				}
+		}
+	}
+	str[bpos]='\0';
+
+	/* free extra alloc'ed mem. */
+	len = strlen(str);
+	str = y_renew(char, str, len+1);
+
+	return (str);
+}
+
+typedef void (*http_connected)(int id, int fd, int error);
+
+struct callback_data {
+	int id;
+	yahoo_get_fd_callback callback;
+	char *request;
+	void *user_data;
+};
+
+static void connect_complete(int fd, int error, void *data)
+{
+	struct callback_data *ccd = data;
+	if(error == 0 && fd > 0)
+		write(fd, ccd->request, strlen(ccd->request));
+	FREE(ccd->request);
+	ccd->callback(ccd->id, fd, error, ccd->user_data);
+	FREE(ccd);
+}
+
+static void yahoo_send_http_request(int id, char *host, int port, char *request, 
+		yahoo_get_fd_callback callback, void *data)
+{
+	struct callback_data *ccd=y_new0(struct callback_data, 1);
+	ccd->callback = callback;
+	ccd->id = id;
+	ccd->request = strdup(request);
+	ccd->user_data = data;
+	
+	YAHOO_CALLBACK(ext_yahoo_connect_async)(id, host, port, connect_complete, ccd);
+}
+
+void yahoo_http_post(int id, const char *url, const char *cookies, long content_length,
+		yahoo_get_fd_callback callback, void *data)
+{
+	char host[255];
+	int port = 80;
+	char path[255];
+	char buff[1024];
+	
+	if(!url_to_host_port_path(url, host, &port, path))
+		return;
+
+	snprintf(buff, sizeof(buff), 
+			"POST %s HTTP/1.0\r\n"
+			"Content-length: %ld\r\n"
+			"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n"
+			"Host: %s:%d\r\n"
+			"Cookie: %s\r\n"
+			"\r\n",
+			path, content_length, 
+			host, port,
+			cookies);
+
+	yahoo_send_http_request(id, host, port, buff, callback, data);
+}
+
+void yahoo_http_get(int id, const char *url, const char *cookies,
+		yahoo_get_fd_callback callback, void *data)
+{
+	char host[255];
+	int port = 80;
+	char path[255];
+	char buff[1024];
+	
+	if(!url_to_host_port_path(url, host, &port, path))
+		return;
+
+	snprintf(buff, sizeof(buff), 
+			"GET %s HTTP/1.0\r\n"
+			"Host: %s:%d\r\n"
+			"User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n"
+			"Cookie: %s\r\n"
+			"\r\n",
+			path, host, port, cookies);
+
+	yahoo_send_http_request(id, host, port, buff, callback, data);
+}
+
+struct url_data {
+	yahoo_get_url_handle_callback callback;
+	void *user_data;
+};
+
+static void yahoo_got_url_fd(int id, int fd, int error, void *data)
+{
+	char *tmp=NULL;
+	char buff[1024];
+	unsigned long filesize=0;
+	char *filename=NULL;
+	int n;
+
+	struct url_data *ud = data;
+
+	if(error || fd < 0) {
+		ud->callback(id, fd, error, filename, filesize, ud->user_data);
+		FREE(ud);
+		return;
+	}
+
+	while((n=yahoo_tcp_readline(buff, sizeof(buff), fd)) > 0) {
+		LOG(("Read:%s:\n", buff));
+		if(!strcmp(buff, ""))
+			break;
+
+		if( !strncasecmp(buff, "Content-length:", 
+				strlen("Content-length:")) ) {
+			tmp = strrchr(buff, ' ');
+			if(tmp)
+				filesize = atol(tmp);
+		}
+
+		if( !strncasecmp(buff, "Content-disposition:", 
+				strlen("Content-disposition:")) ) {
+			tmp = strstr(buff, "name=");
+			if(tmp) {
+				tmp+=strlen("name=");
+				if(tmp[0] == '"') {
+					char *tmp2;
+					tmp++;
+					tmp2 = strchr(tmp, '"');
+					if(tmp2)
+						*tmp2 = '\0';
+				} else {
+					char *tmp2;
+					tmp2 = strchr(tmp, ';');
+					if(!tmp2)
+						tmp2 = strchr(tmp, '\r');
+					if(!tmp2)
+						tmp2 = strchr(tmp, '\n');
+					if(tmp2)
+						*tmp2 = '\0';
+				}
+
+				filename = strdup(tmp);
+			}
+		}
+	}
+
+	LOG(("n == %d\n", n));
+	LOG(("Calling callback, filename:%s, size: %ld\n", filename, filesize));
+	ud->callback(id, fd, error, filename, filesize, ud->user_data);
+	FREE(ud);
+	FREE(filename);
+}
+
+void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd,
+		yahoo_get_url_handle_callback callback, void *data)
+{
+	char buff[1024];
+	struct url_data *ud = y_new0(struct url_data, 1);
+	snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t);
+	ud->callback = callback;
+	ud->user_data = data;
+	yahoo_http_get(id, url, buff, yahoo_got_url_fd, ud);
+}
+
Index: protocols/yahoo/yahoo_httplib.h
===================================================================
--- protocols/yahoo/yahoo_httplib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_httplib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,48 @@
+/*
+ * libyahoo2: yahoo_httplib.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef YAHOO_HTTPLIB_H
+#define YAHOO_HTTPLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "yahoo2_types.h"
+
+char *yahoo_urlencode(const char *instr);
+char *yahoo_urldecode(const char *instr);
+char *yahoo_xmldecode(const char *instr);
+
+int yahoo_tcp_readline(char *ptr, int maxlen, int fd);
+void yahoo_http_post(int id, const char *url, const char *cookies, long size,
+		yahoo_get_fd_callback callback, void *data);
+void yahoo_http_get(int id, const char *url, const char *cookies,
+		yahoo_get_fd_callback callback, void *data);
+void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd,
+		yahoo_get_url_handle_callback callback, void *data);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
Index: protocols/yahoo/yahoo_list.c
===================================================================
--- protocols/yahoo/yahoo_list.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_list.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,236 @@
+/*
+ * yahoo_list.c: linked list routines
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ * Other code copyright Meredydd Luff <meredydd AT everybuddy.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Some of this code was borrowed from elist.c in the eb-lite sources
+ *
+ */
+
+#include <stdlib.h>
+
+#include "yahoo_list.h"
+
+YList *y_list_append(YList * list, void *data)
+{
+	YList *n;
+	YList *new_list = malloc(sizeof(YList));
+	YList *attach_to = NULL;
+
+	new_list->next = NULL;
+	new_list->data = data;
+
+	for (n = list; n != NULL; n = n->next) {
+		attach_to = n;
+	}
+
+	if (attach_to == NULL) {
+		new_list->prev = NULL;
+		return new_list;
+	} else {
+		new_list->prev = attach_to;
+		attach_to->next = new_list;
+		return list;
+	}
+}
+
+YList *y_list_prepend(YList * list, void *data)
+{
+	YList *n = malloc(sizeof(YList));
+
+	n->next = list;
+	n->prev = NULL;
+	n->data = data;
+	if (list)
+		list->prev = n;
+
+	return n;
+}
+
+YList *y_list_concat(YList * list, YList * add)
+{
+	YList *l;
+
+	if(!list)
+		return add;
+
+	if(!add)
+		return list;
+
+	for (l = list; l->next; l = l->next)
+		;
+
+	l->next = add;
+	add->prev = l;
+
+	return list;
+}
+
+YList *y_list_remove(YList * list, void *data)
+{
+	YList *n;
+
+	for (n = list; n != NULL; n = n->next) {
+		if (n->data == data) {
+			list=y_list_remove_link(list, n);
+			y_list_free_1(n);
+			break;
+		}
+	}
+
+	return list;
+}
+
+/* Warning */
+/* link MUST be part of list */
+/* caller must free link using y_list_free_1 */
+YList *y_list_remove_link(YList * list, const YList * link)
+{
+	if (!link)
+		return list;
+
+	if (link->next)
+		link->next->prev = link->prev;
+	if (link->prev)
+		link->prev->next = link->next;
+
+	if (link == list)
+		list = link->next;
+	
+	return list;
+}
+
+int y_list_length(const YList * list)
+{
+	int retval = 0;
+	const YList *n = list;
+
+	for (n = list; n != NULL; n = n->next) {
+		retval++;
+	}
+
+	return retval;
+}
+
+/* well, you could just check for list == NULL, but that would be
+ * implementation dependent
+ */
+int y_list_empty(const YList * list)
+{
+	if(!list)
+		return 1;
+	else
+		return 0;
+}
+
+int y_list_singleton(const YList * list)
+{
+	if(!list || list->next)
+		return 0;
+	return 1;
+}
+
+YList *y_list_copy(YList * list)
+{
+	YList *n;
+	YList *copy = NULL;
+
+	for (n = list; n != NULL; n = n->next) {
+		copy = y_list_append(copy, n->data);
+	}
+
+	return copy;
+}
+
+void y_list_free_1(YList * list)
+{
+	free(list);
+}
+
+void y_list_free(YList * list)
+{
+	YList *n = list;
+
+	while (n != NULL) {
+		YList *next = n->next;
+		free(n);
+		n = next;
+	}
+}
+
+YList *y_list_find(YList * list, const void *data)
+{
+	YList *l;
+	for (l = list; l && l->data != data; l = l->next)
+		;
+
+	return l;
+}
+
+void y_list_foreach(YList * list, YListFunc fn, void * user_data)
+{
+	for (; list; list = list->next)
+		fn(list->data, user_data);
+}
+
+YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp)
+{
+	YList *l;
+	for (l = list; l; l = l->next)
+		if (comp(l->data, data) == 0)
+			return l;
+
+	return NULL;
+}
+
+YList *y_list_nth(YList * list, int n)
+{
+	int i=n;
+	for ( ; list && i; list = list->next, i--)
+		;
+
+	return list;
+}
+
+YList *y_list_insert_sorted(YList * list, void *data, YListCompFunc comp)
+{
+	YList *l, *n, *prev = NULL;
+	if (!list)
+		return y_list_append(list, data);
+
+       	n = malloc(sizeof(YList));
+	n->data = data;
+	for (l = list; l && comp(l->data, n->data) <= 0; l = l->next)
+		prev = l;
+
+	if (l) {
+		n->prev = l->prev;
+		l->prev = n;
+	} else
+		n->prev = prev;
+
+	n->next = l;
+
+	if(n->prev) {
+		n->prev->next = n;
+		return list;
+	} else {
+		return n;
+	}
+		
+}
Index: protocols/yahoo/yahoo_list.h
===================================================================
--- protocols/yahoo/yahoo_list.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_list.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,74 @@
+/*
+ * yahoo_list.h: linked list routines
+ *
+ * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ * Other code copyright Meredydd Luff <meredydd AT everybuddy.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * This is a replacement for the GList.  It only provides functions that 
+ * we use in Ayttm.  Thanks to Meredyyd from everybuddy dev for doing 
+ * most of it.
+ */
+
+#ifndef __YLIST_H__
+#define __YLIST_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _YList {
+	struct _YList *next;
+	struct _YList *prev;
+	void *data;
+} YList;
+
+typedef int (*YListCompFunc) (const void *, const void *);
+typedef void (*YListFunc) (void *, void *);
+
+YList *y_list_append(YList * list, void *data);
+YList *y_list_prepend(YList * list, void *data);
+YList *y_list_remove_link(YList * list, const YList * link);
+YList *y_list_remove(YList * list, void *data);
+
+YList *y_list_insert_sorted(YList * list, void * data, YListCompFunc comp);
+
+YList *y_list_copy(YList * list);
+
+YList *y_list_concat(YList * list, YList * add);
+
+YList *y_list_find(YList * list, const void *data);
+YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp);
+
+YList *y_list_nth(YList * list, int n);
+
+void y_list_foreach(YList * list, YListFunc fn, void *user_data);
+
+void y_list_free_1(YList * list);
+void y_list_free(YList * list);
+int  y_list_length(const YList * list);
+int  y_list_empty(const YList * list);
+int  y_list_singleton(const YList * list);
+
+#define y_list_next(list)	list->next
+
+#ifdef __cplusplus
+}
+#endif
+#endif
Index: protocols/yahoo/yahoo_util.c
===================================================================
--- protocols/yahoo/yahoo_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,159 @@
+/*
+ * libyahoo2: yahoo_util.c
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if STDC_HEADERS
+# include <string.h>
+#else
+# if !HAVE_STRCHR
+#  define strchr index
+#  define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# if !HAVE_MEMCPY
+#  define memcpy(d, s, n) bcopy ((s), (d), (n))
+#  define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#include "yahoo_util.h"
+
+char * y_string_append(char * string, char * append)
+{
+	int size = strlen(string) + strlen(append) + 1;
+	char * new_string = y_renew(char, string, size);
+
+	if(new_string == NULL) {
+		new_string = y_new(char, size);
+		strcpy(new_string, string);
+		FREE(string);
+	}
+
+	strcat(new_string, append);
+
+	return new_string;
+}
+
+char * y_str_to_utf8(const char *in)
+{
+	unsigned int n, i = 0;
+	char *result = NULL;
+
+	if(in == NULL || *in == '\0')
+		return "";
+	
+	result = y_new(char, strlen(in) * 2 + 1);
+
+	/* convert a string to UTF-8 Format */
+	for (n = 0; n < strlen(in); n++) {
+		unsigned char c = (unsigned char)in[n];
+
+		if (c < 128) {
+			result[i++] = (char) c;
+		} else {
+			result[i++] = (char) ((c >> 6) | 192);
+			result[i++] = (char) ((c & 63) | 128);
+		}
+	}
+	result[i] = '\0';
+	return result;
+}
+
+char * y_utf8_to_str(const char *in)
+{
+	int i = 0;
+	unsigned int n;
+	char *result = NULL;
+
+	if(in == NULL || *in == '\0')
+		return "";
+	
+	result = y_new(char, strlen(in) + 1);
+
+	/* convert a string from UTF-8 Format */
+	for (n = 0; n < strlen(in); n++) {
+		unsigned char c = in[n];
+
+		if (c < 128) {
+			result[i++] = (char) c;
+		} else {
+			result[i++] = (c << 6) | (in[++n] & 63);
+		}
+	}
+	result[i] = '\0';
+	return result;
+}
+
+#if !HAVE_GLIB
+
+void y_strfreev(char ** vector)
+{
+	char **v;
+	for(v = vector; *v; v++) {
+		FREE(*v);
+	}
+	FREE(vector);
+}
+
+char ** y_strsplit(char * str, char * sep, int nelem)
+{
+	char ** vector;
+	char *s, *p;
+	int i=0;
+	int l = strlen(sep);
+	if(nelem < 0) {
+		char * s;
+		nelem=0;
+		for(s=strstr(str, sep); s; s=strstr(s+l, sep),nelem++)
+			;
+		if(strcmp(str+strlen(str)-l, sep))
+			nelem++;
+	}
+
+	vector = y_new(char *, nelem + 1);
+
+	for(p=str, s=strstr(p,sep); i<nelem && s; p=s+l, s=strstr(p,sep), i++) {
+		int len = s-p;
+		vector[i] = y_new(char, len+1);
+		strncpy(vector[i], p, len);
+		vector[i][len] = '\0';
+	}
+
+	if(i<nelem) /* str didn't end with sep */
+		vector[i++] = strdup(p);
+			
+	vector[i] = NULL;
+
+	return vector;
+}
+
+void * y_memdup(const void * addr, int n)
+{
+	void * new_chunk = malloc(n);
+	if(new_chunk)
+		memcpy(new_chunk, addr, n);
+	return new_chunk;
+}
+
+#endif
Index: protocols/yahoo/yahoo_util.h
===================================================================
--- protocols/yahoo/yahoo_util.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_util.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,102 @@
+/*
+ * libyahoo2: yahoo_util.h
+ *
+ * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __YAHOO_UTIL_H__
+#define __YAHOO_UTIL_H__
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if HAVE_GLIB
+# include <glib.h>
+
+# define FREE(x)	if(x) {g_free(x); x=NULL;}
+
+# define y_new		g_new
+# define y_new0		g_new0
+# define y_renew	g_renew
+
+# define y_memdup	g_memdup
+# define y_strsplit	g_strsplit
+# define y_strfreev	g_strfreev
+# ifndef strdup
+#  define strdup	g_strdup
+# endif
+# ifndef strncasecmp
+#  define strncasecmp	g_strncasecmp
+#  define strcasecmp	g_strcasecmp
+# endif
+
+# define snprintf	g_snprintf
+# define vsnprintf	g_vsnprintf
+
+#else
+
+# include <stdlib.h>
+# include <stdarg.h>
+
+# define FREE(x)		if(x) {free(x); x=NULL;}
+
+# define y_new(type, n)		(type *)malloc(sizeof(type) * (n))
+# define y_new0(type, n)	(type *)calloc((n), sizeof(type))
+# define y_renew(type, mem, n)	(type *)realloc(mem, n)
+
+void * y_memdup(const void * addr, int n);
+char ** y_strsplit(char * str, char * sep, int nelem);
+void y_strfreev(char ** vector);
+
+int strncasecmp(const char * s1, const char * s2, size_t n);
+int strcasecmp(const char * s1, const char * s2);
+
+char * strdup(const char *s);
+
+int snprintf(char *str, size_t size, const char *format, ...);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef MIN
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+/* 
+ * The following three functions return newly allocated memory.
+ * You must free it yourself
+ */
+char * y_string_append(char * str, char * append);
+char * y_str_to_utf8(const char * in);
+char * y_utf8_to_str(const char * in);
+
+#endif
+
