Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,528 +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"
-
-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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(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);
+}
