changeset 417:c3ae9251c197

Sync libjabber with upstream Sync with jabberd-1.4.4.
author Mikael Berthe <mikael@lilotux.net>
date Thu, 01 Sep 2005 23:29:21 +0200
parents 48e7808c4191
children 05bcc91b8699
files mcabber/libjabber/expat.c mcabber/libjabber/genhash.c mcabber/libjabber/jabber.h mcabber/libjabber/jid.c mcabber/libjabber/jpacket.c mcabber/libjabber/jutil.c mcabber/libjabber/libxode.h mcabber/libjabber/pool.c mcabber/libjabber/pproxy.c mcabber/libjabber/rate.c mcabber/libjabber/snprintf.c mcabber/libjabber/socket.c mcabber/libjabber/str.c mcabber/libjabber/xmlnode.c mcabber/libjabber/xstream.c
diffstat 15 files changed, 2007 insertions(+), 668 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/libjabber/expat.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/expat.c	Thu Sep 01 23:29:21 2005 +0200
@@ -19,6 +19,17 @@
 
 #include <libxode.h>
 
+/**
+ * callback function used for start elements
+ *
+ * This function is used internally by expat.c as a callback function
+ * given to expat. It will create a new xmlnode and add it to the
+ * already created xmlnode tree.
+ *
+ * @param userdata pointer to the parent xmlnode instance (NULL if this function is called for the root note)
+ * @param name name of the starting element
+ * @param atts attributes that are contained in the start element
+ */
 void expat_startElement(void* userdata, const char* name, const char** atts)
 {
     /* get the xmlnode pointed to by the userdata */
@@ -39,6 +50,16 @@
     }
 }
 
+/**
+ * callback function used for end elements
+ *
+ * This function is used internally by expat.c as a callback function
+ * given to expat. It will complete an xmlnode and update the userdata pointer
+ * to point to the node that is parent of the next starting element.
+ *
+ * @param userdata pointer to the current xmlnode
+ * @param name name of the ending element (ignored by this function)
+ */
 void expat_endElement(void* userdata, const char* name)
 {
     xmlnode *x = userdata;
@@ -52,6 +73,15 @@
         *x = current;
 }
 
+/**
+ * callback function for CDATA nodes
+ *
+ * This function will insert CDATA in an xmlnode
+ *
+ * @param userdata pointer to the current xmlnode
+ * @param s pointer to the CDATA string (not zero terminated!)
+ * @param len length of the CDATA string
+ */
 void expat_charData(void* userdata, const char* s, int len)
 {
     xmlnode *x = userdata;
@@ -60,7 +90,15 @@
     xmlnode_insert_cdata(current, s, len);
 }
 
-
+/**
+ * create an xmlnode instance (possibly including other xmlnode instances) by parsing a string
+ *
+ * This function will parse a string containing an XML document and create an xmlnode graph
+ *
+ * @param str the string containing the XML document (not necessarily zero terminated)
+ * @param len the length of the string (without the zero byte, if present)
+ * @return the graph of xmlnodes that represent the parsed document, NULL on failure
+ */
 xmlnode xmlnode_str(char *str, int len)
 {
     XML_Parser p;
@@ -88,6 +126,14 @@
     return node; /* return the xmlnode x points to */
 }
 
+/**
+ * create an xmlnode instance (possibly including other xmlnode instances) by parsing a file
+ *
+ * This function will parse a file containing an XML document and create an xmlnode graph
+ *
+ * @param file the filename
+ * @return the graph of xmlnodes that represent the parsed document, NULL on failure
+ */
 xmlnode xmlnode_file(char *file)
 {
     XML_Parser p;
@@ -128,27 +174,69 @@
     return node; /* return the xmlnode x points to */
 }
 
+/**
+ * write an xmlnode to a file (without a size limit)
+ *
+ * @param file the target file
+ * @param node the xmlnode that should be written
+ * @return 1 on success, -1 on failure
+ */
 int xmlnode2file(char *file, xmlnode node)
 {
-    char *doc;
+    return xmlnode2file_limited(file, node, 0);
+}
+
+/**
+ * write an xmlnode to a file, limited by size
+ *
+ * @param file the target file
+ * @param node the xmlnode that should be written
+ * @param sizelimit the maximum length of the file to be written
+ * @return 1 on success, 0 if failed due to size limit, -1 on failure
+ */
+int xmlnode2file_limited(char *file, xmlnode node, size_t sizelimit)
+{
+    char *doc, *ftmp;
     int fd, i;
+    size_t doclen;
 
     if(file == NULL || node == NULL)
         return -1;
 
-    fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0600);
+    ftmp = spools(xmlnode_pool(node),file,".t.m.p",xmlnode_pool(node));
+    fd = open(ftmp, O_CREAT | O_WRONLY | O_TRUNC, 0600);
     if(fd < 0)
         return -1;
 
     doc = xmlnode2str(node);
-    i = write(fd,doc,strlen(doc));
+    doclen = strlen(doc);
+
+    if (sizelimit > 0 && doclen > sizelimit)
+    {
+	close(fd);
+	return 0;
+    }
+
+    i = write(fd,doc,doclen);
     if(i < 0)
         return -1;
 
     close(fd);
+
+    if(rename(ftmp,file) < 0)
+    {
+        unlink(ftmp);
+        return -1;
+    }
     return 1;
 }
 
+/**
+ * append attributes in the expat format to an existing xmlnode
+ *
+ * @param owner where to add the attributes
+ * @param atts the attributes in expat format (even indexes are the attribute names, odd indexes the values)
+ */
 void xmlnode_put_expat_attribs(xmlnode owner, const char** atts)
 {
     int i = 0;
--- a/mcabber/libjabber/genhash.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/genhash.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,8 +13,19 @@
  *  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/
+ * 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.
+ *
  */
 #include <libxode.h>
 
--- a/mcabber/libjabber/jabber.h	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/jabber.h	Thu Sep 01 23:29:21 2005 +0200
@@ -57,7 +57,7 @@
 #define JID_SERVER   4
 
 typedef struct jid_struct
-{ 
+{
     pool               p;
     char*              resource;
     char*              user;
@@ -65,7 +65,7 @@
     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 */
@@ -75,6 +75,7 @@
 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 */
 
 
 /* --------------------------------------------------------- */
@@ -104,21 +105,22 @@
 #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;
+    unsigned char type;             /**< stanza type (JPACKET_*) */
+    int           subtype;          /**< subtype of a stanza */
+    int           flag;             /**< used by the session manager to flag messages, that are read from offline storage */
+    void*         aux1;             /**< pointer to data passed around with a jpacket, multiple use inside jsm */
+    xmlnode       x;                /**< xmlnode containing the stanza inside the jpacket */
+    jid           to;               /**< destination of the stanza */
+    jid           from;             /**< source address for the stanza */
+    char*         iqns;             /**< pointer to the namespace inside an IQ stanza */
+    xmlnode       iq;               /**< "content" of an iq stanza, pointer to the element in its own namespace */
+    pool          p;                /**< memory pool used for this stanza */
 } *jpacket, _jpacket;
- 
+
 jpacket jpacket_new(xmlnode x);     /* Creates a jabber packet from the xmlnode */
 jpacket jpacket_reset(jpacket p);   /* Resets the jpacket values based on the xmlnode */
 int     jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */
@@ -130,7 +132,7 @@
 /*                                                           */
 /* --------------------------------------------------------- */
 typedef struct ppdb_struct
-{                             
+{
     jid     id;                /* entry data */
     int     pri;
     xmlnode x;
@@ -159,7 +161,7 @@
     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);
@@ -194,6 +196,49 @@
 #define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"}
 #define TERROR_DISCONNECTED (terror){510,"Disconnected"}
 
+/* we define this to signal that we support xterror */
+#define HAS_XTERROR
+
+typedef struct xterror_struct
+{
+    int  code;
+    char msg[256];
+    char type[9];
+    char condition[64];
+} xterror;
+
+#define XTERROR_BAD		(xterror){400,"Bad Request","modify","bad-request"}
+#define XTERROR_CONFLICT	(xterror){409,"Conflict","cancel","conflict"}
+#define XTERROR_NOTIMPL		(xterror){501,"Not Implemented","cancel","feature-not-implemented"}
+#define XTERROR_FORBIDDEN	(xterror){403,"Forbidden","auth","forbidden"}
+#define XTERROR_GONE		(xterror){302,"Gone","modify","gone"}
+#define XTERROR_INTERNAL	(xterror){500,"Internal Server Error","wait","internal-server-error"}
+#define XTERROR_NOTFOUND	(xterror){404,"Not Found","cancel","item-not-found"}
+#define XTERROR_JIDMALFORMED	(xterror){400,"Bad Request","modify","jid-malformed"}
+#define XTERROR_NOTACCEPTABLE	(xterror){406,"Not Acceptable","modify","not-acceptable"}
+#define XTERROR_NOTALLOWED	(xterror){405,"Not Allowed","cancel","not-allowed"}
+#define XTERROR_AUTH		(xterror){401,"Unauthorized","auth","not-authorized"}
+#define XTERROR_PAY		(xterror){402,"Payment Required","auth","payment-required"}
+#define XTERROR_RECIPIENTUNAVAIL (xterror){404,"Receipient Is Unavailable","wait","recipient-unavailable"}
+#define XTERROR_REDIRECT	(xterror){302,"Redirect","modify","redirect"}
+#define XTERROR_REGISTER	(xterror){407,"Registration Required","auth","registration-required"}
+#define XTERROR_REMOTENOTFOUND	(xterror){404,"Remote Server Not Found","cancel","remote-server-not-found"}
+#define XTERROR_REMOTETIMEOUT	(xterror){504,"Remote Server Timeout","wait","remote-server-timeout"}
+#define XTERROR_RESCONSTRAINT	(xterror){500,"Resource Constraint","wait","resource-constraint"}
+#define XTERROR_UNAVAIL		(xterror){503,"Service Unavailable","cancel","service-unavailable"}
+#define XTERROR_SUBSCRIPTIONREQ	(xterror){407,"Subscription Required","auth","subscription-required"}
+#define XTERROR_UNDEF_CANCEL	(xterror){500,NULL,"cancel","undefined-condition"}
+#define XTERROR_UNDEF_CONTINUE	(xterror){500,NULL,"continue","undefined-condition"}
+#define XTERROR_UNDEF_MODIFY	(xterror){500,NULL,"modify","undefined-condition"}
+#define XTERROR_UNDEF_AUTH	(xterror){500,NULL,"auth","undefined-condition"}
+#define XTERROR_UNDEF_WAIT	(xterror){500,NULL,"wait","undefined-condition"}
+#define XTERROR_UNEXPECTED	(xterror){400,"Unexpected Request","wait","unexpected-request"}
+
+#define XTERROR_REQTIMEOUT	(xterror){408,"Request Timeout","wait","remote-server-timeout"}
+#define XTERROR_EXTERNAL	(xterror){502,"Remote Server Error","wait","service-unavailable"}
+#define XTERROR_EXTTIMEOUT	(xterror){504,"Remote Server Timeout","wait","remote-server-timeout"}
+#define XTERROR_DISCONNECTED	(xterror){510,"Disconnected","cancel","service-unavailable"}
+
 /* --------------------------------------------------------- */
 /*                                                           */
 /* Namespace constants                                       */
@@ -203,7 +248,9 @@
 
 #define NS_CLIENT    "jabber:client"
 #define NS_SERVER    "jabber:server"
+#define NS_DIALBACK  "jabber:server:dialback"
 #define NS_AUTH      "jabber:iq:auth"
+#define NS_AUTH_CRYPT "jabber:iq:auth:crypt"
 #define NS_REGISTER  "jabber:iq:register"
 #define NS_ROSTER    "jabber:iq:roster"
 #define NS_OFFLINE   "jabber:x:offline"
@@ -220,7 +267,30 @@
 #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_DISCO_INFO "http://jabber.org/protocol/disco#info"
+#define NS_DISCO_ITEMS "http://jabber.org/protocol/disco#items"
+#define NS_IQ_AUTH    "http://jabber.org/features/iq-auth"
+#define NS_REGISTER_FEATURE "http://jabber.org/features/iq-register"
 
+#define NS_XDBGINSERT "jabber:xdb:ginsert"
+#define NS_XDBNSLIST  "jabber:xdb:nslist"
+
+#define NS_XMPP_STANZAS "urn:ietf:params:xml:ns:xmpp-stanzas"
+#define NS_XMPP_TLS  "urn:ietf:params:xml:ns:xmpp-tls"
+#define NS_XMPP_STREAMS "urn:ietf:params:xml:ns:xmpp-streams"
+
+#define NS_JABBERD_STOREDPRESENCE "http://jabberd.org/ns/storedpresence"
+#define NS_JABBERD_HISTORY "http://jabberd.org/ns/history"
 
 /* --------------------------------------------------------- */
 /*                                                           */
--- a/mcabber/libjabber/jid.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/jid.c	Thu Sep 01 23:29:21 2005 +0200
@@ -17,30 +17,474 @@
  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
  */
 
+/**
+ * @file jid.c
+ * @brief representation and normalization of JabberIDs
+ */
+
 #include "jabber.h"
 
-jid jid_safe(jid id)
-{
+#ifdef LIBIDN
+
+#  include <stringprep.h>
+
+
+/**
+ * @brief datastructure to build the stringprep caches
+ */
+typedef struct _jid_prep_entry_st {
+    char *preped;	/**< the result of the preparation, NULL if unchanged */
+    time_t last_used;	/**< when this result has last been successfully used */
+    unsigned int used_count; /**< how often this result has been successfully used */
+    int size;		/**< the min buffer size needed to hold the result (strlen+1) */
+} *_jid_prep_entry_t;
+
+/**
+ * @brief string preparation cache
+ */
+typedef struct _jid_prep_cache_st {
+    xht hashtable;	/**< the hash table containing the preped strings */
+    pth_mutex_t mutex;	/**< mutex controling the access to the hashtable */
+    const Stringprep_profile *profile;
+    			/**< the stringprep profile used for this cache */
+} *_jid_prep_cache_t;
+
+/**
+ * stringprep cache containging already preped nodes
+ *
+ * we are using global caches here for two reasons:
+ * - I do not see why different instances would want
+ *   to have different caches as we are always doing
+ *   the same
+ * - For per instance caches I would have to modify the
+ *   interface of the jid_*() functions which would break
+ *   compatibility with transports
+ */
+_jid_prep_cache_t _jid_prep_cache_node = NULL;
+
+/**
+ * stringprep cache containing already preped domains
+ */
+_jid_prep_cache_t _jid_prep_cache_domain = NULL;
+
+/**
+ * stringprep cache containing already preped resources
+ */
+_jid_prep_cache_t _jid_prep_cache_resource = NULL;
+
+/**
+ * walker for cleaning up stringprep caches
+ *
+ * @param h the hash we are walking through
+ * @param key the key of this item
+ * @param val the value of this item
+ * @param arg delete entries older as this unix timestamp
+ */
+void _jid_clean_walker(xht h, const char *key, void *val, void *arg) {
+    time_t *keep_newer_as = (time_t*)arg;
+    _jid_prep_entry_t entry = (_jid_prep_entry_t)val;
+
+    if (entry == NULL)
+	return;
+
+    if (entry->last_used <= *keep_newer_as) {
+	xhash_zap(h, key);
+	if (entry->preped != NULL)
+	    free(entry->preped);
+	free(entry);
+
+	/* sorry, I have to cast the const away */
+	/* any idea how I could delete the key else? */
+	if (key != NULL)
+	    free((void*)key);
+    }
+}
+
+/**
+ * walk through a single stringprep cache and check which entries have expired
+ */
+void _jid_clean_single_cache(_jid_prep_cache_t cache, time_t keep_newer_as) {
+    /* acquire the lock on the cache */
+    pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
+
+    /* walk over all entries */
+    xhash_walk(cache->hashtable, _jid_clean_walker, (void*)&keep_newer_as);
+
+    /* we're done, release the lock on the cache */
+    pth_mutex_release(&(cache->mutex));
+}
+
+/**
+ * walk through the stringprep caches and check which entries have expired
+ */
+void jid_clean_cache() {
+    /* XXX make this configurable? */
+    time_t keep_newer_as = time(NULL) - 900;
+
+    /* cleanup the nodeprep cache */
+    _jid_clean_single_cache(_jid_prep_cache_node, keep_newer_as);
+    
+    /* cleanup the domain preparation cache */
+    _jid_clean_single_cache(_jid_prep_cache_domain, keep_newer_as);
+    
+    /* cleanup the resourceprep cache */
+    _jid_clean_single_cache(_jid_prep_cache_resource, keep_newer_as);
+}
+
+/**
+ * caching wrapper around a stringprep function
+ *
+ * @param in_out_buffer buffer containing what has to be stringpreped and that gets the result
+ * @param max_len size of the buffer
+ * @param cache the used cache, defining also the used stringprep profile
+ * @return the return code of the stringprep call
+ */
+int _jid_cached_stringprep(char *in_out_buffer, int max_len, _jid_prep_cache_t cache) {
+    _jid_prep_entry_t preped;
+    int result = STRINGPREP_OK;
+
+    /* check that the cache already exists
+     * we can not do anything as we don't know which profile has to be used */
+    if (cache == NULL) {
+	return STRINGPREP_UNKNOWN_PROFILE;
+    }
+
+    /* is there something that has to be stringpreped? */
+    if (in_out_buffer == NULL) {
+	return STRINGPREP_OK;
+    }
+
+    /* acquire the lock on the cache */
+    pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
+
+    /* check if the requested preparation has already been done */
+    preped = (_jid_prep_entry_t)xhash_get(cache->hashtable, in_out_buffer);
+    if (preped != NULL) {
+	/* we already prepared this argument */
+	if (preped->size <= max_len) {
+	    /* we can use the result */
+
+	    /* update the statistic */
+	    preped->used_count++;
+	    preped->last_used = time(NULL);
+
+	    /* do we need to copy the result? */
+	    if (preped->preped != NULL) {
+		/* copy the result */
+		strcpy(in_out_buffer, preped->preped);
+	    }
+
+	    result = STRINGPREP_OK;
+	} else {
+	    /* we need a bigger buffer */
+	    result = STRINGPREP_TOO_SMALL_BUFFER;
+	}
+	
+	/* we're done, release the lock on the cache */
+	pth_mutex_release(&(cache->mutex));
+    } else {
+	char *original;
+
+	/* stringprep needs time, release the lock on the cache for the meantime */
+	pth_mutex_release(&(cache->mutex));
+
+	/* we have to keep the key */
+	original = strdup(in_out_buffer);
+	
+	/* try to prepare the string */
+	result = stringprep(in_out_buffer, max_len, STRINGPREP_NO_UNASSIGNED, cache->profile);
+
+	/* did we manage to prepare the string? */
+	if (result == STRINGPREP_OK && original != NULL) {
+	    /* generate an entry for the cache */
+	    preped = (_jid_prep_entry_t)malloc(sizeof(struct _jid_prep_entry_st));
+	    if (preped != NULL) {
+		/* has there been modified something? */
+		if (j_strcmp(in_out_buffer, original) == 0) {
+		    /* no, we don't need to store a copy of the original string */
+		    preped->preped = NULL;
+		} else {
+		    /* yes, store the stringpreped string */
+		    preped->preped = strdup(in_out_buffer);
+		}
+		preped->last_used = time(NULL);
+		preped->used_count = 1;
+		preped->size = strlen(in_out_buffer)+1;
+
+		/* acquire the lock on the cache again */
+		pth_mutex_acquire(&(cache->mutex), FALSE, NULL);
+
+		/* store the entry in the cache */
+		xhash_put(cache->hashtable, original, preped);
+
+		/* we're done, release the lock on the cache */
+		pth_mutex_release(&(cache->mutex));
+	    } else {
+		/* we don't need the copy of the key, if there is no memory to store it */
+		free(original);
+	    }
+	} else {
+	    /* we don't need the copy of the original value */
+	    if (original != NULL)
+		free(original);
+	}
+    }
+
+    return result;
+}
+
+/**
+ * free a single stringprep cache
+ *
+ * @param cache the cache to free
+ */
+void _jid_stop_single_cache(_jid_prep_cache_t *cache) {
+    if (*cache == NULL)
+	return;
+
+    _jid_clean_single_cache(*cache, time(NULL));
+    
+    pth_mutex_acquire(&((*cache)->mutex), FALSE, NULL);
+    xhash_free((*cache)->hashtable);
+
+    free(*cache);
+
+    *cache = NULL;
+}
+
+/**
+ * init a single stringprep cache
+ *
+ * @param cache the cache to init
+ * @param prime the prime used to init the hashtable
+ * @param profile profile used to prepare the strings
+ */
+void _jid_init_single_cache(_jid_prep_cache_t *cache, int prime, const Stringprep_profile *profile) {
+    /* do not init a cache twice */
+    if (*cache == NULL) {
+	*cache = (_jid_prep_cache_t)malloc(sizeof(struct _jid_prep_cache_st));
+	pth_mutex_init(&((*cache)->mutex));
+	(*cache)->hashtable = xhash_new(prime);
+	(*cache)->profile = profile;
+    }
+}
+
+/**
+ * free the stringprep caches
+ */
+void jid_stop_caching() {
+    _jid_stop_single_cache(&_jid_prep_cache_node);
+    _jid_stop_single_cache(&_jid_prep_cache_domain);
+    _jid_stop_single_cache(&_jid_prep_cache_resource);
+}
+
+/**
+ * init the stringprep caches
+ * (do not call this twice at the same time, we do not have the mutexes yet)
+ */
+void jid_init_cache() {
+    /* init the nodeprep cache */
+    _jid_init_single_cache(&_jid_prep_cache_node, 2003, stringprep_xmpp_nodeprep);
+
+    /* init the nameprep cache (domains) */
+    _jid_init_single_cache(&_jid_prep_cache_domain, 2003, stringprep_nameprep);
+
+    /* init the resourceprep cache */
+    _jid_init_single_cache(&_jid_prep_cache_resource, 2003, stringprep_xmpp_resourceprep);
+}
+
+/**
+ * nameprep the domain identifier in a JID and check if it is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if JID is valid, non zero otherwise
+ */
+int _jid_safe_domain(jid id) {
+    int result=0;
+
+    /* there must be a domain identifier */
+    if (j_strlen(id->server) == 0)
+	return 1;
+
+    /* nameprep the domain identifier */
+    result = _jid_cached_stringprep(id->server, strlen(id->server)+1, _jid_prep_cache_domain);
+    if (result == STRINGPREP_TOO_SMALL_BUFFER) {
+	/* nameprep wants to expand the string, e.g. conversion from &szlig; to ss */
+	size_t biggerbuffersize = 1024;
+	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
+	if (biggerbuffer == NULL)
+	    return 1;
+	strcpy(biggerbuffer, id->server);
+	result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_domain);
+	id->server = biggerbuffer;
+    }
+    if (result != STRINGPREP_OK)
+	return 1;
+
+    /* the namepreped domain must not be longer than 1023 bytes */
+    if (j_strlen(id->server) > 1023)
+	return 1;
+
+    /* if nothing failed, the domain is valid */
+    return 0;
+}
+
+/**
+ * nodeprep the node identifier in a JID and check if it is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if JID is valid, non zero otherwise
+ */
+int _jid_safe_node(jid id) {
+    int result=0;
+
+    /* it is valid to have no node identifier in the JID */
+    if (id->user == NULL)
+	return 0;
+
+    /* nodeprep */
+    result = _jid_cached_stringprep(id->user, strlen(id->user)+1, _jid_prep_cache_node);
+    if (result == STRINGPREP_TOO_SMALL_BUFFER) {
+	/* nodeprep wants to expand the string, e.g. conversion from &szlig; to ss */
+	size_t biggerbuffersize = 1024;
+	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
+	if (biggerbuffer == NULL)
+	    return 1;
+	strcpy(biggerbuffer, id->user);
+	result = _jid_cached_stringprep(biggerbuffer, biggerbuffersize, _jid_prep_cache_node);
+	id->user = biggerbuffer;
+    }
+    if (result != STRINGPREP_OK)
+	return 1;
+
+    /* the nodepreped node must not be longer than 1023 bytes */
+    if (j_strlen(id->user) > 1023)
+	return 1;
+
+    /* if nothing failed, the node is valid */
+    return 0;
+}
+
+/**
+ * resourceprep the resource identifier in a JID and check if it is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if JID is valid, non zero otherwise
+ */
+int _jid_safe_resource(jid id) {
+    int result=0;
+
+    /* it is valid to have no resource identifier in the JID */
+    if (id->resource == NULL)
+	return 0;
+
+    /* resource prep the resource identifier */
+    result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource);
+    if (result == STRINGPREP_TOO_SMALL_BUFFER) {
+	/* resourceprep wants to expand the string, e.g. conversion from &szlig; to ss */
+	size_t biggerbuffersize = 1024;
+	char *biggerbuffer = pmalloc(id->p, biggerbuffersize);
+	if (biggerbuffer == NULL)
+	    return 1;
+	strcpy(biggerbuffer, id->resource);
+	result = _jid_cached_stringprep(id->resource, strlen(id->resource)+1, _jid_prep_cache_resource);
+	id->resource = biggerbuffer;
+    }
+    if (result != STRINGPREP_OK)
+	return 1;
+
+    /* the resourcepreped node must not be longer than 1023 bytes */
+    if (j_strlen(id->resource) > 1023)
+	return 1;
+
+    /* if nothing failed, the resource is valid */
+    return 0;
+
+}
+
+#else /* no LIBIDN */
+
+/**
+ * check if the domain identifier in a JID is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if domain is valid, non zero otherwise
+ */
+int _jid_safe_domain(jid id) {
     char *str;
 
-    if(strlen(id->server) == 0 || strlen(id->server) > 255)
-	return NULL;
+    /* there must be a domain identifier */
+    if (j_strlen(id->server) == 0)
+	return 1;
+
+    /* and it must not be longer than 1023 bytes */
+    if (strlen(id->server) > 1023)
+	return 1;
 
     /* 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;
+        *str = tolower(*str);
+        if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return 1;
     }
 
-    /* cut off the user */
-    if(id->user != NULL && strlen(id->user) > 64)
-	id->user[64] = '\0';
+    /* otherwise it's okay as far as we can tell without LIBIDN */
+    return 0;
+}
+
+/**
+ * check if the node identifier in a JID is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if node is valid, non zero otherwise
+ */
+int _jid_safe_node(jid id) {
+    char *str;
+
+    /* node identifiers may not be longer than 1023 bytes */
+    if (j_strlen(id->user) > 1023)
+	return 1;
 
     /* 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;
+        for(str = id->user; *str != '\0'; str++)
+            if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return 1;
+
+    /* otherwise it's okay as far as we can tell without LIBIDN */
+    return 0;
+}
+
+/**
+ * check if the resource identifier in a JID is valid
+ *
+ * @param jid data structure holding the JID
+ * @return 0 if resource is valid, non zero otherwise
+ */
+int _jid_safe_resource(jid id) {
+    /* resources may not be longer than 1023 bytes */
+    if (j_strlen(id->resource) > 1023)
+	return 1;
+
+    /* otherwise it's okay as far as we can tell without LIBIDN */
+    return 0;
+}
+
+#endif
+
+/**
+ * nodeprep/nameprep/resourceprep the JID and check if it is valid
+ *
+ * @param jid data structure holding the JID
+ * @return NULL if the JID is invalid, pointer to the jid otherwise
+ */
+jid jid_safe(jid id)
+{
+    if (_jid_safe_domain(id))
+	return NULL;
+    if (_jid_safe_node(id))
+	return NULL;
+    if (_jid_safe_resource(id))
+	return NULL;
 
     return id;
 }
@@ -51,46 +495,44 @@
     jid id;
 
     if(p == NULL || idstr == NULL || strlen(idstr) == 0)
-	return NULL;
+        return NULL;
 
     /* user@server/resource */
 
     str = pstrdup(p, idstr);
 
-    id = pmalloc(p,sizeof(struct jid_struct));
-    id->full = id->server = id->user = id->resource = NULL;
+    id = pmalloco(p,sizeof(struct jid_struct));
     id->p = p;
-    id->next = NULL;
 
     resource = strstr(str,"/");
     if(resource != NULL)
     {
-	*resource = '\0';
-	++resource;
-	if(strlen(resource) > 0)
-	    id->resource = resource;
+        *resource = '\0';
+        ++resource;
+        if(strlen(resource) > 0)
+            id->resource = resource;
     }else{
-	resource = str + strlen(str); /* point to end */
+        resource = str + strlen(str); /* point to end */
     }
 
     type = strstr(str,":");
     if(type != NULL && type < resource)
     {
-	*type = '\0';
-	++type;
-	str = type; /* ignore the type: prefix */
+        *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;
+        id->server = str;
     }else{
-	*server = '\0';
-	++server;
-	id->server = server;
-	if(strlen(str) > 0)
-	    id->user = str;
+        *server = '\0';
+        ++server;
+        id->server = server;
+        if(strlen(str) > 0)
+            id->user = str;
     }
 
     return jid_safe(id);
@@ -101,7 +543,7 @@
     char *old;
 
     if(id == NULL)
-	return;
+        return;
 
     /* invalidate the cached copy */
     id->full = NULL;
@@ -109,26 +551,29 @@
     switch(item)
     {
     case JID_RESOURCE:
-	if(str != NULL && strlen(str) != 0)
-	    id->resource = pstrdup(id->p, str);
-	else
-	    id->resource = NULL;
-	break;
+	old = id->resource;
+        if(str != NULL && strlen(str) != 0)
+            id->resource = pstrdup(id->p, str);
+        else
+            id->resource = NULL;
+        if(_jid_safe_resource(id))
+            id->resource = old; /* revert if invalid */
+        break;
     case JID_USER:
-	old = id->user;
-	if(str != NULL && strlen(str) != 0)
-	    id->user = pstrdup(id->p, str);
-	else
-	    id->user = NULL;
-	if(jid_safe(id) == NULL)
-	    id->user = old; /* revert if invalid */
-	break;
+        old = id->user;
+        if(str != NULL && strlen(str) != 0)
+            id->user = pstrdup(id->p, str);
+        else
+            id->user = NULL;
+        if(_jid_safe_node(id))
+            id->user = old; /* revert if invalid */
+        break;
     case JID_SERVER:
-	old = id->server;
-	id->server = pstrdup(id->p, str);
-	if(jid_safe(id) == NULL)
-	    id->server = old; /* revert if invalid */
-	break;
+        old = id->server;
+        id->server = pstrdup(id->p, str);
+        if(_jid_safe_domain(id))
+            id->server = old; /* revert if invalid */
+        break;
     }
 
 }
@@ -138,21 +583,21 @@
     spool s;
 
     if(id == NULL)
-	return NULL;
+        return NULL;
 
     /* use cached copy */
     if(id->full != NULL)
-	return id->full;
+        return id->full;
 
     s = spool_new(id->p);
 
     if(id->user != NULL)
-	spooler(s, id->user,"@",s);
+        spooler(s, id->user,"@",s);
 
     spool_add(s, id->server);
 
     if(id->resource != NULL)
-	spooler(s, "/",id->resource,s);
+        spooler(s, "/",id->resource,s);
 
     id->full = spool_print(s);
     return id->full;
@@ -177,24 +622,24 @@
     cur = qmark;
     while(cur != '\0')
     {
-	eq = strstr(cur, "=");
-	if(eq == NULL) break;
-	*eq = '\0';
-	eq++;
+        eq = strstr(cur, "=");
+        if(eq == NULL) break;
+        *eq = '\0';
+        eq++;
 
-	amp = strstr(eq, "&");
-	if(amp != NULL)
-	{
-	    *amp = '\0';
-	    amp++;
-	}
+        amp = strstr(eq, "&");
+        if(amp != NULL)
+        {
+            *amp = '\0';
+            amp++;
+        }
 
-	xmlnode_put_attrib(x,cur,eq);
+        xmlnode_put_attrib(x,cur,eq);
 
-	if(amp != NULL)
-	    cur = amp;
-	else
-	    break;
+        if(amp != NULL)
+            cur = amp;
+        else
+            break;
     }
 
     return x;
@@ -217,7 +662,7 @@
 int jid_cmp(jid a, jid b)
 {
     if(a == NULL || b == NULL)
-	return -1;
+        return -1;
 
     if(_jid_nullstrcmp(a->resource, b->resource) != 0) return -1;
     if(_jid_nullstrcasecmp(a->user, b->user) != 0) return -1;
@@ -230,7 +675,7 @@
 int jid_cmpx(jid a, jid b, int parts)
 {
     if(a == NULL || b == NULL)
-	return -1;
+        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;
@@ -245,20 +690,20 @@
     jid next;
 
     if(a == NULL)
-	return NULL;
+        return NULL;
 
     if(b == NULL)
-	return a;
+        return a;
 
     next = a;
     while(next != NULL)
     {
-	/* check for dups */
-	if(jid_cmp(next,b) == 0)
-	    break;
-	if(next->next == NULL)
-	    next->next = jid_new(a->p,jid_full(b));
-	next = next->next;
+        /* check for dups */
+        if(jid_cmp(next,b) == 0)
+            break;
+        if(next->next == NULL)
+            next->next = jid_new(a->p,jid_full(b));
+        next = next->next;
     }
     return a;
 }
@@ -274,14 +719,28 @@
     p = pool_new();
     for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
     {
-	if(xmlnode_get_type(cur) != NTYPE_TAG) continue;
+        if(xmlnode_get_type(cur) != NTYPE_TAG) continue;
 
-	tmp = jid_new(p,xmlnode_get_attrib(cur,"jid"));
-	if(tmp == NULL) continue;
+        tmp = jid_new(p,xmlnode_get_attrib(cur,"jid"));
+        if(tmp == NULL) continue;
 
-	if(jid_cmp(tmp,id) == 0) break;
+        if(jid_cmp(tmp,id) == 0) break;
     }
     pool_free(p);
 
     return cur;
 }
+
+jid jid_user(jid a)
+{
+    jid ret;
+
+    if(a == NULL || a->resource == NULL) return a;
+
+    ret = pmalloco(a->p,sizeof(struct jid_struct));
+    ret->p = a->p;
+    ret->user = a->user;
+    ret->server = a->server;
+
+    return ret;
+}
--- a/mcabber/libjabber/jpacket.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/jpacket.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,18 +13,46 @@
  *  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/
+ * 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.
+ *
+ */
+
+/**
+ * @file jpacket.c
+ * @brief a jpacket is a wrapper around an xmlnode that contains an XMPP stanza
+ *
+ * A jpacket adds some variables to an xmlnode that contains a stanza, so that
+ * jabberd is able to cache information on the stanza type (message, presence, iq)
+ * that is contained in this jpacket and to further classify the presence stanzas.
+ * It also adds some pointers to important data inside the xmlnode, that has
+ * to be accessed often (e.g. sender and receiver of a stanza).
  */
 
 #include "jabber.h"
 
+/**
+ * create a new jpacket by wrapping an xmlnode
+ *
+ * @param x the xmlnode that should be wrapped
+ * @return the newly created jpacket (NULL on failure)
+ */
 jpacket jpacket_new(xmlnode x)
 {
     jpacket p;
 
     if(x == NULL)
-	return NULL;
+        return NULL;
 
     p = pmalloc(xmlnode_pool(x),sizeof(_jpacket));
     p->x = x;
@@ -32,6 +60,12 @@
     return jpacket_reset(p);
 }
 
+/**
+ * recalculate the information the jpacket holds about the stanza
+ *
+ * @param p the packet that should get its information recalculated
+ * @return the jpacket (as given as the p parameter)
+ */
 jpacket jpacket_reset(jpacket p)
 {
     char *val;
@@ -42,90 +76,97 @@
     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(*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");
+    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 */
+	    /* XXX better reject this presence? */
+            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;
+        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;
+        if((p->from = jid_new(p->p, val)) == NULL)
+            p->type = JPACKET_UNKNOWN;
 
     return p;
 }
 
-
+/**
+ * get the subtype of a jpacket
+ *
+ * @param p the jpacket for which the caller wants to know the subtype
+ * @return the subtype of the jpacket (one of the JPACKET__* constants)
+ */
 int jpacket_subtype(jpacket p)
 {
     char *type;
     int ret = p->subtype;
 
     if(ret != JPACKET__UNKNOWN)
-	return ret;
+        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;
+        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;
-	}
+        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;
--- a/mcabber/libjabber/jutil.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/jutil.c	Thu Sep 01 23:29:21 2005 +0200
@@ -1,6 +1,48 @@
+/*
+ *  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.
+ *
+ * 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.
+ *
+ */
+
+/**
+ * @file jutil.c
+ * @brief various utilities mainly for handling xmlnodes containing stanzas
+ */
+
 #include "jabber.h"
 
-/* util for making presence packets */
+/**
+ * utility for making presence stanzas
+ *
+ * @param type the type of the presence (one of the JPACKET__* contants)
+ * @param to to whom the presence should be sent, NULL for a broadcast presence
+ * @param status optional status (CDATA for the <status/> element, NULL for now <status/> element)
+ * @return the xmlnode containing the created presence stanza
+ */
 xmlnode jutil_presnew(int type, char *to, char *status)
 {
     xmlnode pres;
@@ -9,33 +51,46 @@
     switch(type)
     {
     case JPACKET__SUBSCRIBE:
-	xmlnode_put_attrib(pres,"type","subscribe");
-	break;
+        xmlnode_put_attrib(pres,"type","subscribe");
+        break;
     case JPACKET__UNSUBSCRIBE:
-	xmlnode_put_attrib(pres,"type","unsubscribe");
-	break;
+        xmlnode_put_attrib(pres,"type","unsubscribe");
+        break;
     case JPACKET__SUBSCRIBED:
-	xmlnode_put_attrib(pres,"type","subscribed");
-	break;
+        xmlnode_put_attrib(pres,"type","subscribed");
+        break;
     case JPACKET__UNSUBSCRIBED:
-	xmlnode_put_attrib(pres,"type","unsubscribed");
-	break;
+        xmlnode_put_attrib(pres,"type","unsubscribed");
+        break;
     case JPACKET__PROBE:
-	xmlnode_put_attrib(pres,"type","probe");
-	break;
+        xmlnode_put_attrib(pres,"type","probe");
+        break;
     case JPACKET__UNAVAILABLE:
-	xmlnode_put_attrib(pres,"type","unavailable");
-	break;
+        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);
+        xmlnode_put_attrib(pres,"to",to);
     if(status != NULL)
-	xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
+        xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
 
     return pres;
 }
 
-/* util for making IQ packets */
+/**
+ * utility for making IQ stanzas, that contain a <query/> element in a different namespace
+ *
+ * @note In traditional Jabber protocols the element inside an iq element has the name "query".
+ * This util is not able to create IQ stanzas that contain a query which a element that does
+ * not have the name "query"
+ *
+ * @param type the type of the iq stanza (one of JPACKET__GET, JPACKET__SET, JPACKET__RESULT, JPACKET__ERROR)
+ * @param ns the namespace of the <query/> element
+ * @return the created xmlnode
+ */
 xmlnode jutil_iqnew(int type, char *ns)
 {
     xmlnode iq;
@@ -44,43 +99,64 @@
     switch(type)
     {
     case JPACKET__GET:
-	xmlnode_put_attrib(iq,"type","get");
-	break;
+        xmlnode_put_attrib(iq,"type","get");
+        break;
     case JPACKET__SET:
-	xmlnode_put_attrib(iq,"type","set");
-	break;
+        xmlnode_put_attrib(iq,"type","set");
+        break;
     case JPACKET__RESULT:
-	xmlnode_put_attrib(iq,"type","result");
-	break;
+        xmlnode_put_attrib(iq,"type","result");
+        break;
     case JPACKET__ERROR:
-	xmlnode_put_attrib(iq,"type","error");
-	break;
+        xmlnode_put_attrib(iq,"type","error");
+        break;
     }
     xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns);
 
     return iq;
 }
 
-/* util for making message packets */
+/**
+ * utility for making message stanzas
+ *
+ * @param type the type of the message (as a string!)
+ * @param to the recipient of the message
+ * @param subj the subject of the message (NULL for no subject element)
+ * @param body the body of the message
+ * @return the xmlnode containing the new message stanza
+ */
 xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body)
 {
     xmlnode msg;
 
     msg = xmlnode_new_tag("message");
-    xmlnode_put_attrib (msg, "type", type);
-    xmlnode_put_attrib (msg, "to", to);
 
-    if (subj)
-    {
-	xmlnode_insert_cdata (xmlnode_insert_tag (msg, "subject"), subj, strlen (subj));
+    if (type != NULL) {
+	xmlnode_put_attrib (msg, "type", type);
     }
 
-    xmlnode_insert_cdata (xmlnode_insert_tag (msg, "body"), body, strlen (body));
+    if (to != NULL) {
+	xmlnode_put_attrib (msg, "to", to);
+    }
+
+    if (subj != NULL) {
+	xmlnode_insert_cdata(xmlnode_insert_tag(msg, "subject"), subj, strlen(subj));
+    }
+
+    if (body != NULL) {
+	xmlnode_insert_cdata(xmlnode_insert_tag(msg, "body"), body, strlen(body));
+    }
 
     return msg;
 }
 
-/* util for making stream packets */
+/**
+ * utility for making stream packets (containing the stream header element)
+ *
+ * @param xmlns the default namespace of the stream (e.g. jabber:client or jabber:server)
+ * @param server the domain of the server
+ * @return the xmlnode containing the root element of the stream
+ */
 xmlnode jutil_header(char* xmlns, char* server)
 {
      xmlnode result;
@@ -94,33 +170,41 @@
      return result;
 }
 
-/* returns the priority on a presence packet */
+/**
+ * returns the priority on an available presence packet
+ *
+ * @param xmlnode the xmlnode containing the presence packet
+ * @return the presence priority, -129 for unavailable presences and errors
+ */
 int jutil_priority(xmlnode x)
 {
     char *str;
     int p;
 
     if(x == NULL)
-	return -1;
+        return -129;
 
     if(xmlnode_get_attrib(x,"type") != NULL)
-	return -1;
+        return -129;
 
     x = xmlnode_get_tag(x,"priority");
     if(x == NULL)
-	return 0;
+        return 0;
 
     str = xmlnode_get_data((x));
     if(str == NULL)
-	return 0;
+        return 0;
 
     p = atoi(str);
-    if(p >= 0)
-	return p;
-    else
-	return 0;
+    /* xmpp-im section 2.2.2.3 */
+    return p<-128 ? -128 : p>127 ? 127 : p;
 }
 
+/**
+ * reverse sender and destination of a packet
+ *
+ * @param x the xmlnode where sender and receiver should be exchanged
+ */
 void jutil_tofrom(xmlnode x)
 {
     char *to, *from;
@@ -131,6 +215,12 @@
     xmlnode_put_attrib(x,"to",from);
 }
 
+/**
+ * change and xmlnode to be the result xmlnode for the original iq query
+ *
+ * @param x the xmlnode that should become the result for itself
+ * @return the result xmlnode (same as given as parameter x)
+ */
 xmlnode jutil_iqresult(xmlnode x)
 {
     xmlnode cur;
@@ -141,11 +231,18 @@
 
     /* hide all children of the iq, they go back empty */
     for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
-	xmlnode_hide(cur);
+        xmlnode_hide(cur);
 
     return x;
 }
 
+/**
+ * get the present time as a textual timestamp in the format YYYYMMDDTHH:MM:SS
+ *
+ * @note this function is not thread safe
+ *
+ * @return pointer to a static (!) buffer containing the timestamp (or NULL on failure)
+ */
 char *jutil_timestamp(void)
 {
     time_t t;
@@ -156,35 +253,170 @@
     t = time(NULL);
 
     if(t == (time_t)-1)
-	return NULL;
+        return NULL;
     new_time = gmtime(&t);
 
     ret = snprintf(timestamp, 18, "%d%02d%02dT%02d:%02d:%02d", 1900+new_time->tm_year,
-		   new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour,
-		   new_time->tm_min, new_time->tm_sec);
+                   new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour,
+                   new_time->tm_min, new_time->tm_sec);
 
     if(ret == -1)
-	return NULL;
+        return NULL;
 
     return timestamp;
 }
 
-void jutil_error(xmlnode x, terror E)
+/**
+ * map a terror structure to a xterror structure
+ *
+ * terror structures have been used in jabberd14 up to version 1.4.3 but
+ * are not able to hold XMPP compliant stanza errors. The xterror
+ * structure has been introduced to be XMPP compliant. This function
+ * is to ease writting wrappers that accept terror structures and call
+ * the real functions that require now xterror structures
+ *
+ * @param old the terror struct that should be converted
+ * @param mapped pointer to the xterror struct that should be filled with the converted error
+ */
+void jutil_error_map(terror old, xterror *mapped)
+{
+    mapped->code = old.code;
+    if (old.msg == NULL)
+	mapped->msg[0] = 0;
+    else
+	strncpy(mapped->msg, old.msg, sizeof(mapped->msg));
+
+    switch (old.code)
+    {
+	case 302:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "redirect");
+	    break;
+	case 400:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "bad-request");
+	    break;
+	case 401:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "not-authorized");
+	    break;
+	case 402:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "payment-required");
+	    break;
+	case 403:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "forbidden");
+	    break;
+	case 404:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "item-not-found");
+	    break;
+	case 405:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "not-allowed");
+	    break;
+	case 406:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "not-acceptable");
+	    break;
+	case 407:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "registration-requited");
+	    break;
+	case 408:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "remote-server-timeout");
+	    break;
+	case 409:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "conflict");
+	    break;
+	case 500:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "internal-server-error");
+	    break;
+	case 501:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "feature-not-implemented");
+	    break;
+	case 502:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	case 503:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	case 504:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "remote-server-timeout");
+	    break;
+	case 510:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	default:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "undefined-condition");
+    }
+}
+
+/**
+ * update an xmlnode to be the error stanza for itself
+ *
+ * @param x the xmlnode that should become an stanza error message
+ * @param E the structure that holds the error information
+ */
+void jutil_error_xmpp(xmlnode x, xterror E)
 {
     xmlnode err;
     char code[4];
 
-    xmlnode_put_attrib(x,"type","error");
-    err = xmlnode_insert_tag(x,"error");
+    xmlnode_put_attrib(x, "type", "error");
+    err = xmlnode_insert_tag(x, "error");
 
-    snprintf(code,4,"%d",E.code);
-    xmlnode_put_attrib(err,"code",code);
-    if(E.msg != NULL)
-	xmlnode_insert_cdata(err,E.msg,strlen(E.msg));
+    snprintf(code, sizeof(code), "%d", E.code);
+    xmlnode_put_attrib(err, "code", code);
+    if (E.type != NULL)
+	xmlnode_put_attrib(err, "type", E.type);
+    if (E.condition != NULL)
+	xmlnode_put_attrib(xmlnode_insert_tag(err, E.condition), "xmlns", NS_XMPP_STANZAS);
+    if (E.msg != NULL)
+    {
+	xmlnode text;
+	text = xmlnode_insert_tag(err, "text");
+	xmlnode_put_attrib(text, "xmlns", NS_XMPP_STANZAS);
+	xmlnode_insert_cdata(text, E.msg, strlen(E.msg));
+    }
 
     jutil_tofrom(x);
 }
 
+/**
+ * wrapper around jutil_error_xmpp for compatibility with modules for jabberd up to version 1.4.3
+ *
+ * @deprecated use jutil_error_xmpp instead!
+ *
+ * @param x the xmlnode that should become an stanza error message
+ * @param E the strucutre that holds the error information
+ */
+void jutil_error(xmlnode x, terror E)
+{
+    xterror xE;
+    jutil_error_map(E, &xE);
+    jutil_error_xmpp(x, xE);
+}
+
+/**
+ * add a delayed delivery (JEP-0091) element to a message using the
+ * present timestamp.
+ * If a reason is given, this reason will be added as CDATA to the
+ * inserted element
+ *
+ * @param msg the message where the element should be added
+ * @param reason plain text information why the delayed delivery information has been added
+ */
 void jutil_delay(xmlnode msg, char *reason)
 {
     xmlnode delay;
@@ -194,11 +426,33 @@
     xmlnode_put_attrib(delay,"from",xmlnode_get_attrib(msg,"to"));
     xmlnode_put_attrib(delay,"stamp",jutil_timestamp());
     if(reason != NULL)
-	xmlnode_insert_cdata(delay,reason,strlen(reason));
+        xmlnode_insert_cdata(delay,reason,strlen(reason));
 }
 
 #define KEYBUF 100
 
+/**
+ * create or validate a key value for stone-age jabber protocols
+ *
+ * Before dialback had been introduced for s2s (and therefore only in jabberd 1.0),
+ * Jabber used these keys to protect some iq requests. A client first had to
+ * request a key with a IQ get and use it inside the IQ set request. By being able
+ * to receive the key in the IQ get response, the client (more or less) proved to be
+ * who he claimed to be.
+ *
+ * The implementation of this function uses a static array with KEYBUF entries (default
+ * value of KEYBUF is 100). Therefore a key gets invalid at the 100th key that is created
+ * afterwards. It is also invalidated after it has been validated once.
+ *
+ * @deprecated This function is not really used anymore. jabberd14 does not check any
+ * keys anymore and only creates them in the jsm's mod_register.c for compatibility. This
+ * function is also used in mod_groups.c and the key is even checked there, but I do not
+ * know if mod_groups.c still works at all.
+ *
+ * @param key for validation the key the client sent, for generation of a new key NULL
+ * @param seed the seed for generating the key, must stay the same for the same user
+ * @return the new key when created, the key if the key has been validated, NULL if the key is invalid
+ */
 char *jutil_regkey(char *key, char *seed)
 {
     static char keydb[KEYBUF][41];
@@ -210,37 +464,37 @@
     /* blanket the keydb first time */
     if(last == -1)
     {
-	last = 0;
-	memset(&keydb,0,KEYBUF*41);
-	memset(&seeddb,0,KEYBUF*41);
-	srand(time(NULL));
+        last = 0;
+        memset(&keydb,0,KEYBUF*41);
+        memset(&seeddb,0,KEYBUF*41);
+        srand(time(NULL));
     }
 
     /* creation phase */
     if(key == NULL && seed != NULL)
     {
-	/* create a random key hash and store it */
-	sprintf(strint,"%d",rand());
-	strcpy(keydb[last],shahash(strint));
+        /* create a random key hash and store it */
+        sprintf(strint,"%d",rand());
+        strcpy(keydb[last],shahash(strint));
 
-	/* store a hash for the seed associated w/ this key */
-	strcpy(seeddb[last],shahash(seed));
+        /* store a hash for the seed associated w/ this key */
+        strcpy(seeddb[last],shahash(seed));
 
-	/* return it all */
-	str = keydb[last];
-	last++;
-	if(last == KEYBUF) last = 0;
-	return str;
+        /* return it all */
+        str = keydb[last];
+        last++;
+        if(last == KEYBUF) last = 0;
+        return str;
     }
 
     /* validation phase */
     str = shahash(seed);
     for(i=0;i<KEYBUF;i++)
-	if(j_strcmp(keydb[i],key) == 0 && j_strcmp(seeddb[i],str) == 0)
-	{
-	    seeddb[i][0] = '\0'; /* invalidate this key */
-	    return keydb[i];
-	}
+        if(j_strcmp(keydb[i],key) == 0 && j_strcmp(seeddb[i],str) == 0)
+        {
+            seeddb[i][0] = '\0'; /* invalidate this key */
+            return keydb[i];
+        }
 
     return NULL;
 }
--- a/mcabber/libjabber/libxode.h	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/libxode.h	Thu Sep 01 23:29:21 2005 +0200
@@ -1,3 +1,27 @@
+/*
+ * jabberd - Jabber Open Source Server
+ * Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
+ *                    Ryan Eatmon, Robert Norris
+ *
+ * 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, MA02111-1307USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#   include <config.h>
+#endif
+
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -7,26 +31,23 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <signal.h>
-#include <syslog.h>
 #include <strings.h>
 #include <unistd.h>
-#include <sys/socket.h>
+#include <sys/time.h>
+#include <syslog.h>
 #include <netinet/in.h>
 #include <netdb.h>
 #include <arpa/inet.h>
-#include <sys/time.h>
+#include <sys/socket.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
+#define MAXSHORTSTR    203        /* max short string length */
+#define QUAD_T    unsigned long long
 
 #ifdef __STDC__
 
@@ -77,7 +98,7 @@
 #undef POOL_DEBUG
 /*
  flip these, this should be a prime number for top # of pools debugging
-#define POOL_DEBUG 40009 
+#define POOL_DEBUG 40009
 */
 
 /* pheap - singular allocation of memory */
@@ -88,7 +109,7 @@
 };
 
 /* pool_cleaner - callback type which is associated
-   with a pool entry; invoked when the pool entry is 
+   with a pool entry; invoked when the pool entry is
    free'd */
 typedef void (*pool_cleaner)(void *arg);
 
@@ -113,16 +134,16 @@
     char name[8], zone[32];
     int lsize;
 } _pool, *pool;
-#define pool_new() _pool_new(ZONE) 
-#define pool_heap(i) _pool_new_heap(i,ZONE) 
+#define pool_new() _pool_new(__FILE__,__LINE__)
+#define pool_heap(i) _pool_new_heap(i,__FILE__,__LINE__)
 #else
 } _pool, *pool;
-#define pool_heap(i) _pool_new_heap(i,NULL) 
-#define pool_new() _pool_new(NULL)
+#define pool_heap(i) _pool_new_heap(i, NULL, 0)
+#define pool_new() _pool_new(NULL, 0)
 #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 */
+pool _pool_new(char *zone, int line); /* new pool :) */
+pool _pool_new_heap(int size, char *zone, int line); /* 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 */
@@ -246,7 +267,7 @@
 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 */
+void spool_add(spool s, char *str); /* add a single string to the pool */
 char *spools(pool p, ...); /* wrap all the spooler stuff in one function, the happy fun ball! */
 
 
@@ -262,8 +283,8 @@
 #define NTYPE_LAST   2
 #define NTYPE_UNDEF  -1
 
-/* -------------------------------------------------------------------------- 
-   Node structure. Do not use directly! Always use accessor macros 
+/* --------------------------------------------------------------------------
+   Node structure. Do not use directly! Always use accessor macros
    and methods!
    -------------------------------------------------------------------------- */
 typedef struct xmlnode_t
@@ -275,9 +296,9 @@
      int                 complete;
      pool               p;
      struct xmlnode_t*  parent;
-     struct xmlnode_t*  firstchild; 
+     struct xmlnode_t*  firstchild;
      struct xmlnode_t*  lastchild;
-     struct xmlnode_t*  prev; 
+     struct xmlnode_t*  prev;
      struct xmlnode_t*  next;
      struct xmlnode_t*  firstattrib;
      struct xmlnode_t*  lastattrib;
@@ -287,7 +308,7 @@
 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_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);
@@ -340,13 +361,14 @@
 /* Node-to-string translation */
 char*    xmlnode2str(xmlnode node);
 
-/* Node-to-terminated-string translation 
+/* 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 */
+int xmlnode2file_limited(char *file, xmlnode node, size_t sizelimit);
 
 /* Expat callbacks */
 void expat_startElement(void* userdata, const char* name, const char** atts);
@@ -388,32 +410,32 @@
 char *xstream_header_char(xmlnode x);
 
 /* 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 
+ * 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 
+ * "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
--- a/mcabber/libjabber/pool.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/pool.c	Thu Sep 01 23:29:21 2005 +0200
@@ -1,67 +1,140 @@
 /*
- *  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
+ *  pool.c
+ * This code comes from jabberd - Jabber Open Source Server
+ *  Copyright (c) 2002 Jeremie Miller, Thomas Muldowney,
+ *                    Ryan Eatmon, Robert Norris
  *  Copyright (C) 1998-1999 The Jabber Team http://jabber.org/
  *
- *  2/27/00:3am, random plans by jer
+ * 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.
  *
- *  ok based on gprof, we really need some innovation here... my thoughs are this:
+ * 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, MA02111-1307USA
+ *
+ * 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.
  *
- *  most things are strings, so have a string-based true-blue garbage collector
- *  one big global hash containing all the strings created by any pstrdup, returning const char *
- *  a refcount on each string block
- *  when a pool is freed, it moves down the refcount
- *  garbage collector collects pools on the free stack, and runs through the hash for unused strings
- *  j_strcmp can check for == (if they are both from a pstrdup)
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ */
+
+/**
+ * @file pool.c
+ * @brief Handling of memory pools
  *
- *  let's see... this would change:
- *  pstrdup: do a hash lookup, success=return, fail=pmalloc & hash put
- *  pool_free:
+ * Jabberd handles its memory allocations in pools. You create a pool, can
+ * allocate memory from it and all allocations will be freed if you free
+ * the pool. Therefore you don't have to care that each malloc is freed,
+ * you only have to take care that the pool is freed.
  *
+ * The normal call-flow for pools is:
  *
- *
+ * pool p = pool_new();
+ * struct mystruct *allocation1 = pmalloc(sizeof(struct mystruct));
+ * struct myotherstruct *allocation2 = pmalloc(sizeof(struct myotherstruct));
+ * ...
+ * pool_free(p);
  */
 
 #include "libxode.h"
 
+#define MAX_MALLOC_TRIES 10 /**< how many seconds we try to allocate memory */
+
 #ifdef POOL_DEBUG
-int pool__total = 0;
+int pool__total = 0;		/**< how many memory blocks are allocated */
 int pool__ltotal = 0;
-HASHTABLE pool__disturbed = NULL;
+xht pool__disturbed = NULL;
+
+/**
+ * create a new memory allocation and increment the pool__total counter
+ *
+ * only used if POOL_DEBUG is defined, else it is an alias for malloc
+ *
+ * @param size size of the memory to allocate
+ * @return pointer to the allocated memory
+ */
 void *_pool__malloc(size_t size)
 {
     pool__total++;
     return malloc(size);
 }
+
+/**
+ * free memory and decrement the pool__total counter
+ *
+ * only used if POOL_DEBUG is defined, else it is an alias for free
+ *
+ * @param block pointer to the memory allocation that should be freed
+ */
 void _pool__free(void *block)
 {
     pool__total--;
     free(block);
 }
 #else
-#define _pool__malloc malloc
-#define _pool__free free
+#define _pool__malloc malloc	/**< _pool__malloc updates pool__total counter if POOL_DEBUG is defined */
+#define _pool__free free	/**< _pool__free updates pool__total counter if POOL_DEBUG is defined */
 #endif
 
+/**
+ * try to allocate memory
+ *
+ * If allocation fails, it will be retries for MAX_MALLOC_TRIES seconds.
+ * If it still fails, we exit the process
+ *
+ * @param size how many bytes of memory we allocate
+ * @return pointer to the allocated memory
+ */
+inline void *_retried__malloc(size_t size) {
+    void *allocated_memory;
+    int malloc_tries = 0;
 
-/* make an empty pool */
-pool _pool_new(char *zone)
+    while ((allocated_memory=_pool__malloc(size)) == NULL) {
+	if (malloc_tries++ > MAX_MALLOC_TRIES) {
+	    exit(999);
+	}
+
+	sleep(1); //pth_sleep(1);
+    }
+
+    return allocated_memory;
+}
+
+/**
+ * make an empty pool
+ *
+ * Use the macro pool_new() instead of a direct call to this function. The
+ * macro will create the parameters for you.
+ *
+ * @param zone the file in which the pool_new macro is called
+ * @param line the line in the file in which the pool_new macro is called
+ * @return the new allocated memory pool
+ */
+pool _pool_new(char *zone, int line)
 {
-    pool p;
-    while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1);
+    // int malloc_tries = 0;
+#ifdef POOL_DEBUG
+    int old__pool__total;
+#endif
+
+    pool p = _retried__malloc(sizeof(_pool));
+
     p->cleanup = NULL;
     p->heap = NULL;
     p->size = 0;
@@ -70,17 +143,26 @@
     p->lsize = -1;
     p->zone[0] = '\0';
     strcat(p->zone,zone);
-    sprintf(p->name,"%X",p);
+    snprintf(p->zone, sizeof(p->zone), "%s:%i", zone, line);
+    snprintf(p->name, sizeof(p->name), "%X", p);
 
     if(pool__disturbed == NULL)
-	pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
-    ghash_put(pool__disturbed,p->name,p);
+    {
+        pool__disturbed = (xht)1; /* reentrancy flag! */
+        pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp);
+    }
+    if(pool__disturbed != (xht)1)
+        ghash_put(pool__disturbed,p->name,p);
 #endif
 
     return p;
 }
 
-/* free a heap */
+/**
+ * free a memory heap (struct pheap)
+ *
+ * @param arg which heep should be freed
+ */
 void _pool_heap_free(void *arg)
 {
     struct pheap *h = (struct pheap *)arg;
@@ -89,15 +171,25 @@
     _pool__free(h);
 }
 
-/* mem should always be freed last */
+/**
+ * append a pool_cleaner function (callback) to a pool
+ *
+ * mem should always be freed last
+ *
+ * All appended pool_cleaner functions will be called if a pool is freed.
+ * This might be used to clean logically subpools.
+ *
+ * @param p to which pool the pool_cleaner should be added
+ * @param pf structure containing the reference to the pool_cleaner and links for the list
+ */
 void _pool_cleanup_append(pool p, struct pfree *pf)
 {
     struct pfree *cur;
 
     if(p->cleanup == NULL)
     {
-	p->cleanup = pf;
-	return;
+        p->cleanup = pf;
+        return;
     }
 
     /* fast forward to end of list */
@@ -106,13 +198,22 @@
     cur->next = pf;
 }
 
-/* create a cleanup tracker */
+/**
+ * create a cleanup tracker
+ *
+ * this function is used to create a pfree structure that can be passed to _pool_cleanup_append()
+ *
+ * @param p the pool to which the pool_cleaner should be added
+ * @param f the function that should be called if the pool is freed
+ * @param arg the parameter that should be passed to the pool_cleaner function
+ * @return pointer to the new pfree structure
+ */
 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 = _retried__malloc(sizeof(struct pfree));
     ret->f = f;
     ret->arg = arg;
     ret->next = NULL;
@@ -120,15 +221,25 @@
     return ret;
 }
 
-/* create a heap and make sure it get's cleaned up */
+/**
+ * create a heap and make sure it get's cleaned up
+ *
+ * pheaps are used by memory pools internally to handle the memory allocations
+ *
+ * @note the macro pool_heap calls _pool_new_heap and NOT _pool_heap
+ *
+ * @param p for which pool the heap should be created
+ * @param size how big the pool should be
+ * @return pointer to the new pheap
+ */
 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 = _retried__malloc(sizeof(struct pheap));
+    ret->block = _retried__malloc(size);
     ret->size = size;
     p->size += size;
     ret->used = 0;
@@ -141,40 +252,57 @@
     return ret;
 }
 
-pool _pool_new_heap(int size, char *zone)
+/**
+ * create a new memory pool and set the initial heap size
+ *
+ * @note you should not call this function but use the macro pool_heap instead which fills zone and line automatically
+ *
+ * @param size the initial size of the memory pool
+ * @param zone the file where this function is called (for debugging)
+ * @param line the line in the file where this function is called
+ * @return the new memory pool
+ */
+pool _pool_new_heap(int size, char *zone, int line)
 {
     pool p;
-    p = _pool_new(zone);
+    p = _pool_new(zone, line);
     p->heap = _pool_heap(p,size);
     return p;
 }
 
+/**
+ * allocate memory from a memory pool
+ *
+ * @param p the pool to use
+ * @param size how much memory to allocate
+ * @return pointer to the allocated memory
+ */
 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();
+        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;
+	block = _retried__malloc(size);
+        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++;
+        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);
+        p->heap = _pool_heap(p, p->heap->size);
 
     /* the current heap has room */
     block = (char *)p->heap->block + p->heap->used;
@@ -182,15 +310,33 @@
     return block;
 }
 
+/**
+ * allocate memory and initialize the memory with the given char c
+ *
+ * @deprecated jabberd does use pmalloco instead, this function will be removed
+ *
+ * @param p which pool to use
+ * @param size the size of the allocation
+ * @param c the initialization character
+ * @return pointer to the allocated memory
+ */
 void *pmalloc_x(pool p, int size, char c)
 {
    void* result = pmalloc(p, size);
    if (result != NULL)
-	   memset(result, c, size);
+           memset(result, c, size);
    return result;
 }
 
-/* easy safety utility (for creating blank mem for structs, etc) */
+/**
+ * allocate memory and initialize the memory with zero bytes
+ *
+ * easy safety utility (for creating blank mem for structs, etc)
+ *
+ * @param p which pool to use
+ * @param size the size of the allocation
+ * @return pointer to the allocated memory
+ */
 void *pmalloco(pool p, int size)
 {
     void *block = pmalloc(p, size);
@@ -198,13 +344,21 @@
     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 */
+/**
+ * duplicate a string and allocate memory for it
+ *
+ * @todo efficient: move this to const char* and then loop through the existing heaps to see if src is within a block in this pool
+ *
+ * @param p the pool to use
+ * @param src the string that should be duplicated
+ * @return the duplicated string
+ */
 char *pstrdup(pool p, const char *src)
 {
     char *ret;
 
     if(src == NULL)
-	return NULL;
+        return NULL;
 
     ret = pmalloc(p,strlen(src) + 1);
     strcpy(ret,src);
@@ -212,12 +366,20 @@
     return ret;
 }
 
-/* when move above, this one would actually return a new block */
+/**
+ * when pstrdup() is moved to "const char*", this one would actually return a new block
+ */
 char *pstrdupx(pool p, const char *src)
 {
     return pstrdup(p, src);
 }
 
+/**
+ * get the size of a memory pool
+ *
+ * @param p the pool
+ * @return the size
+ */
 int pool_size(pool p)
 {
     if(p == NULL) return 0;
@@ -225,6 +387,11 @@
     return p->size;
 }
 
+/**
+ * free a pool (and all memory that is allocated in it)
+ *
+ * @param p which pool to free
+ */
 void pool_free(pool p)
 {
     struct pfree *cur, *stub;
@@ -234,10 +401,10 @@
     cur = p->cleanup;
     while(cur != NULL)
     {
-	(*cur->f)(cur->arg);
-	stub = cur->next;
-	_pool__free(cur);
-	cur = stub;
+        (*cur->f)(cur->arg);
+        stub = cur->next;
+        _pool__free(cur);
+        cur = stub;
     }
 
 #ifdef POOL_DEBUG
@@ -248,7 +415,9 @@
 
 }
 
-/* public cleanup utils, insert in a way that they are run FIFO, before mem frees */
+/**
+ * public cleanup utils, insert in a way that they are run FIFO, before mem frees
+ */
 void pool_cleanup(pool p, pool_cleaner f, void *arg)
 {
     struct pfree *clean;
@@ -260,29 +429,42 @@
 
 #ifdef POOL_DEBUG
 void debug_log(char *zone, const char *msgfmt, ...);
-int _pool_stat(void *arg, const void *key, void *data)
+void _pool_stat(xht h, const char *key, void *data, void *arg)
 {
     pool p = (pool)data;
 
     if(p->lsize == -1)
-	debug_log("leak","%s: %X is a new pool",p->zone,p->name);
+        debug_log("pool_debug","%s: %s is a new pool",p->zone, p->name);
     else if(p->size > p->lsize)
-	debug_log("leak","%s: %X grew %d",p->zone,p->name, p->size - p->lsize);
+        debug_log("pool_debug","%s: %s grew %d",p->zone, p->name, p->size - p->lsize);
     else if((int)arg)
-	debug_log("leak","%s: %X exists %d",p->zone,p->name, p->size);
+        debug_log("pool_debug","%s: %s exists %d",p->zone,p->name, p->size);
     p->lsize = p->size;
-    return 1;
 }
 
+/**
+ * print memory pool statistics (for debugging purposes)
+ *
+ * @param full make a full report? (0 = no, 1 = yes)
+ */
 void pool_stat(int full)
 {
+    if (pool__disturbed == NULL || pool__disturbed == (xht)1)
+	return;
+
     ghash_walk(pool__disturbed,_pool_stat,(void *)full);
     if(pool__total != pool__ltotal)
-	debug_log("leak","%d\ttotal missed mallocs",pool__total);
+        debug_log("pool_debug","%d\ttotal missed mallocs",pool__total);
     pool__ltotal = pool__total;
+
     return;
 }
 #else
+/**
+ * dummy implementation: print memory pool statistics (for debugging purposes, real implementation if POOL_DEBUG is defined)
+ *
+ * @param full make a full report? (0 = no, 1 = yes)
+ */
 void pool_stat(int full)
 {
     return;
--- a/mcabber/libjabber/pproxy.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/pproxy.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,8 +13,31 @@
  *  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/
+ * 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.
+ *
+ *
+ */
+
+/**
+ * @file pproxy.c
+ * @brief presence proxy database - DEPRECATED
+ *
+ * @deprecated these functions are not used by jabberd itself (but aim-t uses them), they will be removed from jabberd
+ *
+ * The presence proxy database is used to store presences for different resources of a JID.
+ *
+ * these aren't the most efficient things in the world, a hash optimized for tiny spaces would be far better
  */
 
 #include "jabber.h"
@@ -42,25 +65,24 @@
     if(db == NULL || id == NULL) return NULL;
 
     for(cur = db->next; cur != NULL; cur = cur->next)
-	if(jid_cmp(cur->id,id) == 0) return cur;
+        if(jid_cmp(cur->id,id) == 0) return cur;
 
     return NULL;
 }
 
 ppdb ppdb_insert(ppdb db, jid id, xmlnode x)
 {
-    char *res;
     ppdb cur, curu;
     pool p;
 
     if(id == NULL || id->server == NULL || x == NULL)
-	return db;
+        return db;
 
     /* new ppdb list dummy holder */
     if(db == NULL)
     {
-	p = pool_heap(1024);
-	db = _ppdb_new(p,id);
+        p = pool_heap(1024);
+        db = _ppdb_new(p,NULL);
     }
 
     cur = _ppdb_get(db,id);
@@ -68,10 +90,10 @@
     /* just update it */
     if(cur != NULL)
     {
-	xmlnode_free(cur->x);
-	cur->x = xmlnode_dup(x);
-	cur->pri = jutil_priority(x);
-	return db;
+        xmlnode_free(cur->x);
+        cur->x = xmlnode_dup(x);
+        cur->pri = jutil_priority(x);
+        return db;
     }
 
     /* make an entry for it */
@@ -81,28 +103,20 @@
     cur->next = db->next;
     db->next = cur;
 
-    /* this is a presence from a resource, make an entry for just the user */
-    if(id->user != NULL && id->resource != NULL)
+    /* if this is a user's resource presence, get the the user entry */
+    if(id->user != NULL && (curu = _ppdb_get(db,jid_user(id))) != cur)
     {
-	/* modify the id to just user@host */
-	res = id->resource;
-	jid_set(id,NULL,JID_RESOURCE);
-	curu = _ppdb_get(db,id);
+        /* no user entry, make one */
+        if(curu == NULL)
+        {
+            curu = _ppdb_new(db->p,jid_user(id));
+            curu->next = db->next;
+            db->next = curu;
+        }
 
-	/* no user entry, make one */
-	if(curu == NULL)
-	{
-	    curu = _ppdb_new(db->p,id);
-	    curu->next = db->next;
-	    db->next = curu;
-	}
-
-	/* restore the id */
-	jid_set(id,res,JID_RESOURCE);
-
-	/* insert this resource into the user list */
-	cur->user = curu->user;
-	curu->user = cur;
+        /* insert this resource into the user list */
+        cur->user = curu->user;
+        curu->user = cur;
     }
 
     return db;
@@ -123,7 +137,7 @@
 
     top = cur;
     for(cur = cur->user; cur != NULL; cur = cur->user)
-	if(cur->pri >= top->pri) top = cur;
+        if(cur->pri >= top->pri) top = cur;
 
     if(top != NULL && top->pri >= 0) return top->x;
 
@@ -141,18 +155,18 @@
     /* MODE: if this is NOT just user@host addy, return just the single entry */
     if(id->user == NULL || id->resource != NULL)
     {
-	/* we were just here, return now */
-	if(last != NULL)
-	{
-	    last = NULL;
-	    return NULL;
-	}
+        /* we were just here, return now */
+        if(last != NULL)
+        {
+            last = NULL;
+            return NULL;
+        }
 
-	last = _ppdb_get(db,id);
-	if(last != NULL)
-	    return last->x;
-	else
-	    return NULL;
+        last = _ppdb_get(db,id);
+        if(last != NULL)
+            return last->x;
+        else
+            return NULL;
     }
 
     /* handle looping for user@host */
@@ -160,15 +174,15 @@
     /* we're already in the loop */
     if(last != NULL)
     {
-	/* this is the last entry in the list */
-	if(last->user == NULL)
-	{
-	    last = NULL;
-	    return NULL;
-	}
+        /* this is the last entry in the list */
+        if(last->user == NULL)
+        {
+            last = NULL;
+            return NULL;
+        }
 
-	last = last->user;
-	return last->x;
+        last = last->user;
+        return last->x;
     }
 
     /* start a new loop */
@@ -178,9 +192,9 @@
 
     last = cur->user;
     if(last != NULL)
-	return last->x;
+        return last->x;
     else
-	return NULL;
+        return NULL;
 }
 
 
@@ -191,7 +205,7 @@
     if(db == NULL) return;
 
     for(cur = db; cur != NULL; cur = cur->next)
-	xmlnode_free(cur->x);
+        xmlnode_free(cur->x);
 
     pool_free(db->p);
 }
--- a/mcabber/libjabber/rate.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/rate.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,8 +13,29 @@
  *  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/
+ * 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.
+ *
+ */
+
+/**
+ * create a new instance of jlimit that is used to limit events
+ *
+ * limit the events to maxp points per maxt seconds
+ *
+ * @param maxt time interval (in seconds) after which the points are cleared
+ * @param maxp maximum number of points available for the time interval given in maxt
+ * @return new instance of jlimit (has to be freed with jlimit_free if not used anymore)
  */
 
 #include "jabber.h"
@@ -35,15 +56,31 @@
     return r;
 }
 
+/**
+ * free a jlimit instance
+ *
+ * @param r the jlimit instance that should be freed
+ */
 void jlimit_free(jlimit r)
 {
     if(r != NULL)
     {
-	if(r->key != NULL) free(r->key);
-	pool_free(r->p);
+        if(r->key != NULL) free(r->key);
+        pool_free(r->p);
     }
 }
 
+/**
+ * update/check a key in a jlimit instance
+ *
+ * Each jlimit instance can track many limits (that have the same setup).
+ * The limit is selected by the key, which can be an IP address.
+ *
+ * @param r the jlimit instance
+ * @param key for which key the limit should be checked
+ * @param points how many points of the limit should be consumed
+ * @return 1 if limit reached, 0 if we are still within the rate limit
+ */
 int jlimit_check(jlimit r, char *key, int points)
 {
     int now = time(NULL);
@@ -53,15 +90,15 @@
     /* make sure we didn't go over the time frame or get a null/new key */
     if((now - r->start) > r->maxt || key == NULL || j_strcmp(key,r->key) != 0)
     { /* start a new key */
-	free(r->key);
-	if(key != NULL)
-	  /* We use strdup instead of pstrdup since r->key needs to be free'd before
-	     and more often than the rest of the rlimit structure */
-	    r->key = strdup(key);
-	else
-	    r->key = NULL;
-	r->start = now;
-	r->points = 0;
+        free(r->key);
+        if(key != NULL)
+          /* We use strdup instead of pstrdup since r->key needs to be free'd before
+             and more often than the rest of the rlimit structure */
+            r->key = strdup(key);
+        else
+            r->key = NULL;
+        r->start = now;
+        r->points = 0;
     }
 
     r->points += points;
@@ -69,7 +106,7 @@
     /* if we're within the time frame and over the point limit */
     if(r->points > r->maxp && (now - r->start) < r->maxt)
     {
-	return 1; /* we don't reset the rate here, so that it remains rated until the time runs out */
+        return 1; /* we don't reset the rate here, so that it remains rated until the time runs out */
     }
 
     return 0;
--- a/mcabber/libjabber/snprintf.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/snprintf.c	Thu Sep 01 23:29:21 2005 +0200
@@ -53,6 +53,15 @@
  * <panos@alumni.cs.colorado.edu> for xinetd.
  */
 
+/**
+ * @file snprintf.c
+ * @brief implement snprintf if not present in the libc
+ *
+ * snprintf is not implemented by all libc implementations, this file implements this
+ * function, if it is not already present. You should not call any of the functions
+ * in this file directly!
+ */
+
 #include <libxode.h>
 
 #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF)
@@ -242,8 +251,12 @@
     NO = 0, YES = 1
 } boolean_e;
 
-#define FALSE           0
-#define TRUE            1
+#ifndef FALSE
+#  define FALSE           0
+#endif
+#ifndef TRUE
+#  define TRUE            1
+#endif
 #define NUL         '\0'
 #define INT_NULL        ((int *)0)
 #define WIDE_INT        long
--- a/mcabber/libjabber/socket.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/socket.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,21 +13,40 @@
  *  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/
+ * 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.
+ *
+ */
+
+/**
+ * @file socket.c
+ * @brief some misc functions to handle sockets
  */
 
 #include "libxode.h"
 #include "connwrap.h"
 
-/* socket.c
- *
+/**
  * Simple wrapper to make socket creation easy.
- * type = NETSOCKET_SERVER is local listening socket
- * type = NETSOCKET_CLIENT is connection socket
- * type = NETSOCKET_UDP
+ *
+ * @param port port number of the socket
+ * @param host hostname where to connect to or listen on
+ * @param type type of socket (NETSOCKET_SERVER, NETSOCKET_CLIENT; or NETSOCKET_UDP)
+ * @return file handle of the new socket
+ *
+ * NETSOCKET_SERVER is local listening socket
+ * NETSOCKET_CLIENT is connection socket
  */
-
 int make_netsocket(u_short port, char *host, int type, int ssl)
 {
     int s, flag = 1;
@@ -41,53 +60,53 @@
     bzero((void *)&sa,sizeof(struct sockaddr_in));
 
     if((s = socket(AF_INET,socket_type,0)) < 0)
-	return(-1);
+        return(-1);
     if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(flag)) < 0)
-	return(-1);
+        return(-1);
 
     saddr = make_addr(host);
     if(saddr == NULL)
-	return(-1);
+        return(-1);
     sa.sin_family = AF_INET;
     sa.sin_port = htons(port);
 
     if(type == NETSOCKET_SERVER)
     {
-	/* bind to specific address if specified */
-	if(host != NULL)
-	    sa.sin_addr.s_addr = saddr->s_addr;
+        /* bind to specific address if specified */
+        if(host != NULL)
+            sa.sin_addr.s_addr = saddr->s_addr;
 
-	if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
+        {
+            close(s);
+            return(-1);
+        }
     }
     if(type == NETSOCKET_CLIENT)
     {
-	sa.sin_addr.s_addr = saddr->s_addr;
-	if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        sa.sin_addr.s_addr = saddr->s_addr;
+        if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
+        {
+            close(s);
+            return(-1);
+        }
     }
     if(type == NETSOCKET_UDP)
     {
-	/* bind to all addresses for now */
-	if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        /* bind to all addresses for now */
+        if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
+        {
+            close(s);
+            return(-1);
+        }
 
-	/* specify default recipient for read/write */
-	sa.sin_addr.s_addr = saddr->s_addr;
-	if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        /* specify default recipient for read/write */
+        sa.sin_addr.s_addr = saddr->s_addr;
+        if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
+        {
+            close(s);
+            return(-1);
+        }
     }
 
 
@@ -131,56 +150,56 @@
     bzero((void *)&sa,sizeof(struct sockaddr_in));
 
     if((s = socket(AF_INET,socket_type,0)) < 0)
-	return(-1);
+        return(-1);
     if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char*)&flag, sizeof(flag)) < 0)
-	return(-1);
+        return(-1);
     change_socket_to_nonblocking(s);
 
     saddr = make_addr(host);
     if(saddr == NULL)
-	return(-1);
+        return(-1);
     sa.sin_family = AF_INET;
     sa.sin_port = htons(port);
 
     if(type == NETSOCKET_SERVER)
     {
-	/* bind to specific address if specified */
-	if(host != NULL)
-	    sa.sin_addr.s_addr = saddr->s_addr;
+        /* bind to specific address if specified */
+        if(host != NULL)
+            sa.sin_addr.s_addr = saddr->s_addr;
 
-	if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
+        {
+            close(s);
+            return(-1);
+        }
     }
     if(type == NETSOCKET_CLIENT)
     {
-	int rc;
-	sa.sin_addr.s_addr = saddr->s_addr;
-	rc = cw_nb_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl, state);
-	if (rc == -1 )
-	{
-	    close(s);
-	    return(-1);
-	}
+        int rc;
+        sa.sin_addr.s_addr = saddr->s_addr;
+        rc = cw_nb_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl, state);
+        if (rc == -1 )
+        {
+            close(s);
+            return(-1);
+        }
     }
     if(type == NETSOCKET_UDP)
     {
-	/* bind to all addresses for now */
-	if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        /* bind to all addresses for now */
+        if(bind(s,(struct sockaddr*)&sa,sizeof sa) < 0)
+        {
+            close(s);
+            return(-1);
+        }
 
-	/* specify default recipient for read/write */
-	sa.sin_addr.s_addr = saddr->s_addr;
-	if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
-	{
-	    close(s);
-	    return(-1);
-	}
+        /* specify default recipient for read/write */
+        sa.sin_addr.s_addr = saddr->s_addr;
+        if(cw_connect(s,(struct sockaddr*)&sa,sizeof sa,ssl) < 0)
+        {
+            close(s);
+            return(-1);
+        }
     }
 
 
@@ -193,32 +212,32 @@
     static struct in_addr addr;
     char myname[MAXHOSTNAMELEN + 1];
 
-    if(host == NULL || strlen(host) == 0)
-    {
-	gethostname(myname,MAXHOSTNAMELEN);
-	hp = gethostbyname(myname);
-	if(hp != NULL)
-	{
-	    return (struct in_addr *) *hp->h_addr_list;
-	}
-    }else{
-	addr.s_addr = inet_addr(host);
-	if(addr.s_addr != -1)
-	{
-	    return &addr;
-	}
-	hp = gethostbyname(host);
-	if(hp != NULL)
-	{
-	    return (struct in_addr *) *hp->h_addr_list;
-	}
+    if (host == NULL || strlen(host) == 0) {
+        gethostname(myname,MAXHOSTNAMELEN);
+        hp = gethostbyname(myname);
+        if(hp != NULL) {
+            return (struct in_addr *) *hp->h_addr_list;
+        }
+    } else {
+        addr.s_addr = inet_addr(host);
+        if(addr.s_addr != -1) {
+            return &addr;
+        }
+        hp = gethostbyname(host);
+        if(hp != NULL) {
+            return (struct in_addr *) *hp->h_addr_list;
+        }
     }
     return NULL;
 }
 
-/* Sets a file descriptor to close on exec.  "flag" is 1 to close on exec, 0 to
- * leave open across exec.
- * -- EJB 7/31/2000
+/**
+ * Sets a file descriptor to close on exec.
+ *
+ * @param fd the file descriptor
+ * @param flag 1 to close on exec, 0 to leave open across exec
+ *
+ * @deprecated this function is not used by jabberd14 and might be removed in future versions
  */
 int set_fd_close_on_exec(int fd, int flag)
 {
@@ -226,12 +245,12 @@
     int newflags;
 
     if(flag)
-	newflags = oldflags | FD_CLOEXEC;
+        newflags = oldflags | FD_CLOEXEC;
     else
-	newflags = oldflags & (~FD_CLOEXEC);
+        newflags = oldflags & (~FD_CLOEXEC);
 
     if(newflags==oldflags)
-	return 0;
+        return 0;
     return fcntl(fd,F_SETFL,(long)newflags);
 }
 
--- a/mcabber/libjabber/str.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/str.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,12 +13,42 @@
  *  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/
+ * 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.
+ *
+ */
+
+/**
+ * @file str.c
+ * @brief utilities for string handling
+ *
+ * This file contains utility functions for string handling:
+ * - NULL pointer save versions of many functions in string.c
+ * - string spools
+ * - functions to (un)escape strings for XML usage
+ *
+ * String spools allow to create a string by concatenating several smaller strings
+ * and the spool implementation is allocating the neccessary memory using memory pools.
  */
 
 #include "libxode.h"
 
+/**
+ * NULL pointer save version of strdup()
+ *
+ * @param str the string the should be duplicated
+ * @return the duplicated string
+ */
 char *j_strdup(const char *str)
 {
     if(str == NULL)
@@ -27,6 +57,17 @@
         return strdup(str);
 }
 
+/**
+ * NULL pointer save version of strcat()
+ *
+ * @note the return value of j_strcat() is not compatible with the return value of strcat()
+ *
+ * @todo check if the behaviour of the return value is intended
+ *
+ * @param dest where to append the string
+ * @param txt what to append
+ * @return dest if txt contains a NULL pointer, pointer to the terminating zero byte of the result else
+ */
 char *j_strcat(char *dest, char *txt)
 {
     if(!txt) return(dest);
@@ -38,14 +79,38 @@
     return(dest);
 }
 
+/**
+ * NULL pointer save version of strcmp
+ *
+ * If one of the parameters contains a NULL pointer, the string is considered to be unequal.
+ *
+ * @note the return value is not compatible with strcmp()
+ *
+ * @param a the one string
+ * @param b the other string
+ * @return 0 if the strings are equal, -1 if the strings are not equal
+ */
 int j_strcmp(const char *a, const char *b)
 {
     if(a == NULL || b == NULL)
         return -1;
-    else
-        return strcmp(a, b);
+
+    while(*a == *b && *a != '\0' && *b != '\0'){ a++; b++; }
+
+    if(*a == *b) return 0;
+
+    return -1;
 }
 
+/**
+ * NULL pointer save version of strcasecmp()
+ *
+ * If one of the parameters contains a NULL pointer, the string is considered to be unequal
+ *
+ * @param a the one string
+ * @param b the other string
+ * @return 0 if the strings are equal, non zero else
+ */
 int j_strcasecmp(const char *a, const char *b)
 {
     if(a == NULL || b == NULL)
@@ -54,6 +119,16 @@
         return strcasecmp(a, b);
 }
 
+/**
+ * NULL pointer save version of strncmp()
+ *
+ * If one of the parameters contains a NULL pointer, the string is considered to be unequal
+ *
+ * @param a the first string
+ * @param b the second string
+ * @param i how many characters to compare at most
+ * @return 0 if the strings are equal (within the given length limitation), non zero else
+ */
 int j_strncmp(const char *a, const char *b, int i)
 {
     if(a == NULL || b == NULL)
@@ -62,6 +137,16 @@
         return strncmp(a, b, i);
 }
 
+/**
+ * NULL pointer save version of strncasecmp()
+ *
+ * If one of the parameters contains a NULL pointer, the string is considered to be unequal
+ *
+ * @param a the first string
+ * @param b the second string
+ * @param i how many characters to compare at most
+ * @return 0 if the strings are equal (within the given length limitation), non zero else
+ */
 int j_strncasecmp(const char *a, const char *b, int i)
 {
     if(a == NULL || b == NULL)
@@ -70,6 +155,14 @@
         return strncasecmp(a, b, i);
 }
 
+/**
+ * NULL pointer save version of strlen
+ *
+ * If the parameter contains a NULL pointer, 0 is returned
+ *
+ * @param a the string for which the length should be calculated
+ * @return 0 if a==NULL, length of the string else
+ */
 int j_strlen(const char *a)
 {
     if(a == NULL)
@@ -136,7 +229,7 @@
     while(1)
     {
         arg = va_arg(ap,char *);
-        if((int)arg == (int)s)
+        if((spool)arg == s)
             break;
         else
             spool_add(s, arg);
@@ -185,7 +278,7 @@
     while(1)
     {
         arg = va_arg(ap,char *);
-        if((int)arg == (int)p)
+        if((pool)arg == p)
             break;
         else
             spool_add(s, arg);
@@ -319,61 +412,3 @@
 
     return buff;
 }
-
-void str_b64decode(char* str)
-{
-    char *cur;
-    int d, dlast, phase;
-    unsigned char c;
-    static int table[256] = {
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
-        52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
-        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
-        15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
-        -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
-        41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
-        -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1   /* F0-FF */
-    };
-
-    dlast = 0;
-    phase = 0;
-    for (cur = str; *cur != '\0'; ++cur )
-    {
-        d = table[(int)*cur];
-        if(d != -1)
-        {
-            switch(phase)
-            {
-            case 0:
-                ++phase;
-                break;
-            case 1:
-                c = ((dlast << 2) | ((d & 0x30) >> 4));
-                *str++ = c;
-                ++phase;
-                break;
-            case 2:
-                c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
-                *str++ = c;
-                ++phase;
-                break;
-            case 3:
-                c = (((dlast & 0x03 ) << 6) | d);
-                *str++ = c;
-                phase = 0;
-                break;
-            }
-            dlast = d;
-        }
-    }
-    *str = '\0';
-}
--- a/mcabber/libjabber/xmlnode.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/xmlnode.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,8 +13,19 @@
  *  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/
+ * 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.
+ *
  */
 
 #include "libxode.h"
@@ -35,8 +46,7 @@
     }
 
     /* Allocate & zero memory */
-    result = (xmlnode)pmalloc(p, sizeof(_xmlnode));
-    memset(result, '\0', sizeof(_xmlnode));
+    result = (xmlnode)pmalloco(p, sizeof(_xmlnode));
 
     /* Initialize fields */
     if (type != NTYPE_CDATA)
@@ -64,7 +74,7 @@
 {
     xmlnode result;
 
-    if(parent == NULL || name == NULL) return NULL;
+    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)
@@ -100,18 +110,36 @@
     return NULL;
 }
 
-static char* _xmlnode_merge(pool p, char* dest, unsigned int destsize, const char* src, unsigned int srcsize)
+void _xmlnode_merge(xmlnode data)
 {
-    char* result;
-    result = (char*)pmalloc(p, destsize + srcsize + 1);
-    memcpy(result, dest, destsize);
-    memcpy(result+destsize, src, srcsize);
-    result[destsize + srcsize] = '\0';
+    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;
 
-    /* WARNING: major ugly hack: since we're throwing the old data away, let's jump in the pool and subtract it from the size, this is for xmlstream's big-node checking */
-    p->size -= destsize;
+    /* 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';
 
-    return result;
+    /* 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)
@@ -155,7 +183,7 @@
     xmlnode tmp;
 
     if(!node || xmlnode_get_type(node)!=NTYPE_TAG)
-	return NULL;
+        return NULL;
 
     s = spool_new(xmlnode_pool(node));
     if(!s) return(NULL);
@@ -164,40 +192,34 @@
     {
         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
+            if(xmlnode_get_type(node) == NTYPE_TAG)
             {
-		        spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node)));
-	        }
-	    }
+                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)
+        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;
-	    }
+            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;
@@ -260,8 +282,6 @@
 
 /*
  *  xmlnode_insert_cdata -- append character data to a tag
- *  If last child of the parent is CDATA, merges CDATA nodes. Otherwise
- *  creates a CDATA node, and appends it to the parent's child list.
  *
  *  parameters
  *      parent -- parent tag
@@ -283,22 +303,13 @@
     if(size == -1)
         size = strlen(CDATA);
 
-    if ((parent->lastchild != NULL) && (parent->lastchild->type == NTYPE_CDATA))
-    {
-        result = parent->lastchild;
-        result->data = _xmlnode_merge(result->p, result->data, result->data_sz, CDATA, size);
-        result->data_sz = result->data_sz + size;
-    }
-    else
+    result = _xmlnode_insert(parent, NULL, NTYPE_CDATA);
+    if (result != NULL)
     {
-        result = _xmlnode_insert(parent, "", 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;
-        }
+        result->data = (char*)pmalloc(result->p, size + 1);
+        memcpy(result->data, CDATA, size);
+        result->data[size] = '\0';
+        result->data_sz = size;
     }
 
     return result;
@@ -314,7 +325,8 @@
  *              "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
- *              or any combination: "name/name/?attrib", etc
+ *              "=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
@@ -325,17 +337,43 @@
     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)
+    if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL)
         return _xmlnode_search(parent->firstchild, name, NTYPE_TAG);
 
-    /* jer's note: why can't I modify the name directly, why do I have to strdup it?  damn c grrr! */
     str = 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;
+        }
+
+        free(str);
+        return step;
+    }
+
+
     if(qmark != NULL && (slash == NULL || qmark < slash))
     { /* of type ?attrib */
 
@@ -528,33 +566,34 @@
 
 char* xmlnode_get_data(xmlnode node)
 {
-    xmlnode cur;
+    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;
 
-    if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA */
-    {
-        for(cur = xmlnode_get_firstchild(node); cur != NULL; cur = xmlnode_get_nextsibling(cur))
-            if(xmlnode_get_type(cur) == NTYPE_CDATA)
-                return cur->data;
-    }else{
-        return node->data;
-    }
-    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;
 }
 
 int xmlnode_get_datasz(xmlnode node)
 {
-    if (node != NULL)
-        return node->data_sz;
-    return (int)NULL;
+    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;
 }
 
 int xmlnode_get_type(xmlnode node)
 {
     if (node != NULL)
         return node->type;
-    return (int)NULL;
+    return NTYPE_UNDEF;
 }
 
 int xmlnode_has_children(xmlnode node)
--- a/mcabber/libjabber/xstream.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/xstream.c	Thu Sep 01 23:29:21 2005 +0200
@@ -13,16 +13,37 @@
  *  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/
+ * 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.
+ *
+ */
+
+/**
+ * @file xstream.c
+ * @brief handling of incoming XML stream based events
+ *
+ * xstream is a way to have a consistent method of handling incoming XML stream based events ...
+ * it doesn't handle the generation of an XML stream, but provides some facilities to help doing that
  */
 
 #include <time.h>
 #include <libxode.h>
 
-/* xstream is a way to have a consistent method of handling incoming XML Stream based events... it doesn't handle the generation of an XML Stream, but provides some facilities to help do that */
+/* ========== internal expat callbacks =========== */
 
-/******* internal expat callbacks *********/
+/**
+ * internal expat callback for read start tags of an element
+ */
 void _xstream_startElement(xstream xs, const char* name, const char** atts)
 {
     pool p;
@@ -53,7 +74,9 @@
         xs->status = XSTREAM_ERR;
 }
 
-
+/**
+ * internal expat callback for read end tags of an element
+ */
 void _xstream_endElement(xstream xs, const char* name)
 {
     xmlnode parent;
@@ -78,7 +101,9 @@
     xs->depth--;
 }
 
-
+/**
+ * internal expat callback for read CDATA
+ */
 void _xstream_charData(xstream xs, const char *str, int len)
 {
     /* if xstream is bad, get outa here */
@@ -93,7 +118,11 @@
     xmlnode_insert_cdata(xs->node, str, len);
 }
 
-
+/**
+ * internal function to be registered as pool cleaner, frees a stream if the associated memory pool is freed
+ *
+ * @param pointer to the xstream to free
+ */
 void _xstream_cleanup(void *arg)
 {
     xstream xs = (xstream)arg;
@@ -103,7 +132,14 @@
 }
 
 
-/* creates a new xstream with given pool, xstream will be cleaned up w/ pool */
+/**
+ * creates a new xstream with given pool, xstream will be cleaned up w/ pool
+ *
+ * @param p the memory pool to use for the stream
+ * @param f function pointer to the event handler function
+ * @param arg parameter to pass to the event handler function
+ * @return the created xstream
+ */
 xstream xstream_new(pool p, xstream_onNode f, void *arg)
 {
     xstream newx;
@@ -129,7 +165,14 @@
     return newx;
 }
 
-/* attempts to parse the buff onto this stream firing events to the handler, returns the last known status */
+/**
+ * attempts to parse the buff onto this stream firing events to the handler
+ *
+ * @param xs the xstream to parse the data on
+ * @param buff the new data
+ * @param len length of the data
+ * @return last known xstream status
+ */
 int xstream_eat(xstream xs, char *buff, int len)
 {
     char *err;
@@ -174,11 +217,17 @@
 
 /* STREAM CREATION UTILITIES */
 
-/* give a standard template xmlnode to work from */
+/** give a standard template xmlnode to work from 
+ *
+ * @param namespace ("jabber:client", "jabber:server", ...)
+ * @param to where the stream is sent to
+ * @param from where we are (source of the stream)
+ * @return the xmlnode that has been generated as the template
+ */
 xmlnode xstream_header(char *namespace, char *to, char *from)
 {
     xmlnode x;
-    char id[10];
+    char id[11];
 
     sprintf(id,"%X",(int)time(NULL));
 
@@ -195,14 +244,20 @@
     return x;
 }
 
-/* trim the xmlnode to only the opening header :) [NO CHILDREN ALLOWED] */
+/**
+ * trim the xmlnode to only the opening header :)
+ *
+ * @note NO CHILDREN ALLOWED
+ *
+ * @param x the xmlnode
+ * @return string representation of the start tag
+ */
 char *xstream_header_char(xmlnode x)
 {
     spool s;
     char *fixr, *head;
 
-    if(xmlnode_has_children(x))
-    {
+    if(xmlnode_has_children(x)) {
         fprintf(stderr,"Fatal Programming Error: xstream_header_char() was sent a header with children!\n");
         return NULL;
     }