# HG changeset patch # User Mikael Berthe # Date 1125610161 -7200 # Node ID c3ae9251c1972f9e52d832e34e6bd09c4063112c # Parent 48e7808c4191d21735e6c3f72c841db060919ae8 Sync libjabber with upstream Sync with jabberd-1.4.4. diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/expat.c --- 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 +/** + * 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; diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/genhash.c --- 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 diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/jabber.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" /* --------------------------------------------------------- */ /* */ diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/jid.c --- 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 + + +/** + * @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 ß 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 ß 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 ß 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; +} diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/jpacket.c --- 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; diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/jutil.c --- 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 element, NULL for now 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 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 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 +#endif + #include #include #include @@ -7,26 +31,23 @@ #include #include #include -#include #include #include -#include +#include +#include #include #include #include -#include +#include #include "xmlparse.h" -#ifdef HAVE_CONFIG_H -#include -#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 diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/pool.c --- 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; diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/pproxy.c --- 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); } diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/rate.c --- 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; diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/snprintf.c --- 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 @@ * 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 #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 diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/socket.c --- 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); } diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/str.c --- 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'; -} diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/xmlnode.c --- 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) diff -r 48e7808c4191 -r c3ae9251c197 mcabber/libjabber/xstream.c --- 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 #include -/* 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; }