diff mcabber/libjabber/jutil.c @ 417:c3ae9251c197

Sync libjabber with upstream Sync with jabberd-1.4.4.
author Mikael Berthe <mikael@lilotux.net>
date Thu, 01 Sep 2005 23:29:21 +0200
parents bf3d6e241714
children f791f5f0cfce
line wrap: on
line diff
--- a/mcabber/libjabber/jutil.c	Thu Sep 01 21:18:19 2005 +0200
+++ b/mcabber/libjabber/jutil.c	Thu Sep 01 23:29:21 2005 +0200
@@ -1,6 +1,48 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyrights
+ *
+ * Portions created by or assigned to Jabber.com, Inc. are
+ * Copyright (c) 1999-2002 Jabber.com, Inc.  All Rights Reserved.  Contact
+ * information for Jabber.com, Inc. is available at http://www.jabber.com/.
+ *
+ * Portions Copyright (c) 1998-1999 Jeremie Miller.
+ *
+ * Acknowledgements
+ *
+ * Special thanks to the Jabber Open Source Contributors for their
+ * suggestions and support of Jabber.
+ *
+ */
+
+/**
+ * @file jutil.c
+ * @brief various utilities mainly for handling xmlnodes containing stanzas
+ */
+
 #include "jabber.h"
 
-/* util for making presence packets */
+/**
+ * utility for making presence stanzas
+ *
+ * @param type the type of the presence (one of the JPACKET__* contants)
+ * @param to to whom the presence should be sent, NULL for a broadcast presence
+ * @param status optional status (CDATA for the <status/> element, NULL for now <status/> element)
+ * @return the xmlnode containing the created presence stanza
+ */
 xmlnode jutil_presnew(int type, char *to, char *status)
 {
     xmlnode pres;
@@ -9,33 +51,46 @@
     switch(type)
     {
     case JPACKET__SUBSCRIBE:
-	xmlnode_put_attrib(pres,"type","subscribe");
-	break;
+        xmlnode_put_attrib(pres,"type","subscribe");
+        break;
     case JPACKET__UNSUBSCRIBE:
-	xmlnode_put_attrib(pres,"type","unsubscribe");
-	break;
+        xmlnode_put_attrib(pres,"type","unsubscribe");
+        break;
     case JPACKET__SUBSCRIBED:
-	xmlnode_put_attrib(pres,"type","subscribed");
-	break;
+        xmlnode_put_attrib(pres,"type","subscribed");
+        break;
     case JPACKET__UNSUBSCRIBED:
-	xmlnode_put_attrib(pres,"type","unsubscribed");
-	break;
+        xmlnode_put_attrib(pres,"type","unsubscribed");
+        break;
     case JPACKET__PROBE:
-	xmlnode_put_attrib(pres,"type","probe");
-	break;
+        xmlnode_put_attrib(pres,"type","probe");
+        break;
     case JPACKET__UNAVAILABLE:
-	xmlnode_put_attrib(pres,"type","unavailable");
-	break;
+        xmlnode_put_attrib(pres,"type","unavailable");
+        break;
+    case JPACKET__INVISIBLE:
+        xmlnode_put_attrib(pres,"type","invisible");
+        break;
     }
     if(to != NULL)
-	xmlnode_put_attrib(pres,"to",to);
+        xmlnode_put_attrib(pres,"to",to);
     if(status != NULL)
-	xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
+        xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status));
 
     return pres;
 }
 
-/* util for making IQ packets */
+/**
+ * utility for making IQ stanzas, that contain a <query/> element in a different namespace
+ *
+ * @note In traditional Jabber protocols the element inside an iq element has the name "query".
+ * This util is not able to create IQ stanzas that contain a query which a element that does
+ * not have the name "query"
+ *
+ * @param type the type of the iq stanza (one of JPACKET__GET, JPACKET__SET, JPACKET__RESULT, JPACKET__ERROR)
+ * @param ns the namespace of the <query/> element
+ * @return the created xmlnode
+ */
 xmlnode jutil_iqnew(int type, char *ns)
 {
     xmlnode iq;
@@ -44,43 +99,64 @@
     switch(type)
     {
     case JPACKET__GET:
-	xmlnode_put_attrib(iq,"type","get");
-	break;
+        xmlnode_put_attrib(iq,"type","get");
+        break;
     case JPACKET__SET:
-	xmlnode_put_attrib(iq,"type","set");
-	break;
+        xmlnode_put_attrib(iq,"type","set");
+        break;
     case JPACKET__RESULT:
-	xmlnode_put_attrib(iq,"type","result");
-	break;
+        xmlnode_put_attrib(iq,"type","result");
+        break;
     case JPACKET__ERROR:
-	xmlnode_put_attrib(iq,"type","error");
-	break;
+        xmlnode_put_attrib(iq,"type","error");
+        break;
     }
     xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns);
 
     return iq;
 }
 
-/* util for making message packets */
+/**
+ * utility for making message stanzas
+ *
+ * @param type the type of the message (as a string!)
+ * @param to the recipient of the message
+ * @param subj the subject of the message (NULL for no subject element)
+ * @param body the body of the message
+ * @return the xmlnode containing the new message stanza
+ */
 xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body)
 {
     xmlnode msg;
 
     msg = xmlnode_new_tag("message");
-    xmlnode_put_attrib (msg, "type", type);
-    xmlnode_put_attrib (msg, "to", to);
 
-    if (subj)
-    {
-	xmlnode_insert_cdata (xmlnode_insert_tag (msg, "subject"), subj, strlen (subj));
+    if (type != NULL) {
+	xmlnode_put_attrib (msg, "type", type);
     }
 
-    xmlnode_insert_cdata (xmlnode_insert_tag (msg, "body"), body, strlen (body));
+    if (to != NULL) {
+	xmlnode_put_attrib (msg, "to", to);
+    }
+
+    if (subj != NULL) {
+	xmlnode_insert_cdata(xmlnode_insert_tag(msg, "subject"), subj, strlen(subj));
+    }
+
+    if (body != NULL) {
+	xmlnode_insert_cdata(xmlnode_insert_tag(msg, "body"), body, strlen(body));
+    }
 
     return msg;
 }
 
-/* util for making stream packets */
+/**
+ * utility for making stream packets (containing the stream header element)
+ *
+ * @param xmlns the default namespace of the stream (e.g. jabber:client or jabber:server)
+ * @param server the domain of the server
+ * @return the xmlnode containing the root element of the stream
+ */
 xmlnode jutil_header(char* xmlns, char* server)
 {
      xmlnode result;
@@ -94,33 +170,41 @@
      return result;
 }
 
-/* returns the priority on a presence packet */
+/**
+ * returns the priority on an available presence packet
+ *
+ * @param xmlnode the xmlnode containing the presence packet
+ * @return the presence priority, -129 for unavailable presences and errors
+ */
 int jutil_priority(xmlnode x)
 {
     char *str;
     int p;
 
     if(x == NULL)
-	return -1;
+        return -129;
 
     if(xmlnode_get_attrib(x,"type") != NULL)
-	return -1;
+        return -129;
 
     x = xmlnode_get_tag(x,"priority");
     if(x == NULL)
-	return 0;
+        return 0;
 
     str = xmlnode_get_data((x));
     if(str == NULL)
-	return 0;
+        return 0;
 
     p = atoi(str);
-    if(p >= 0)
-	return p;
-    else
-	return 0;
+    /* xmpp-im section 2.2.2.3 */
+    return p<-128 ? -128 : p>127 ? 127 : p;
 }
 
+/**
+ * reverse sender and destination of a packet
+ *
+ * @param x the xmlnode where sender and receiver should be exchanged
+ */
 void jutil_tofrom(xmlnode x)
 {
     char *to, *from;
@@ -131,6 +215,12 @@
     xmlnode_put_attrib(x,"to",from);
 }
 
+/**
+ * change and xmlnode to be the result xmlnode for the original iq query
+ *
+ * @param x the xmlnode that should become the result for itself
+ * @return the result xmlnode (same as given as parameter x)
+ */
 xmlnode jutil_iqresult(xmlnode x)
 {
     xmlnode cur;
@@ -141,11 +231,18 @@
 
     /* hide all children of the iq, they go back empty */
     for(cur = xmlnode_get_firstchild(x); cur != NULL; cur = xmlnode_get_nextsibling(cur))
-	xmlnode_hide(cur);
+        xmlnode_hide(cur);
 
     return x;
 }
 
+/**
+ * get the present time as a textual timestamp in the format YYYYMMDDTHH:MM:SS
+ *
+ * @note this function is not thread safe
+ *
+ * @return pointer to a static (!) buffer containing the timestamp (or NULL on failure)
+ */
 char *jutil_timestamp(void)
 {
     time_t t;
@@ -156,35 +253,170 @@
     t = time(NULL);
 
     if(t == (time_t)-1)
-	return NULL;
+        return NULL;
     new_time = gmtime(&t);
 
     ret = snprintf(timestamp, 18, "%d%02d%02dT%02d:%02d:%02d", 1900+new_time->tm_year,
-		   new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour,
-		   new_time->tm_min, new_time->tm_sec);
+                   new_time->tm_mon+1, new_time->tm_mday, new_time->tm_hour,
+                   new_time->tm_min, new_time->tm_sec);
 
     if(ret == -1)
-	return NULL;
+        return NULL;
 
     return timestamp;
 }
 
-void jutil_error(xmlnode x, terror E)
+/**
+ * map a terror structure to a xterror structure
+ *
+ * terror structures have been used in jabberd14 up to version 1.4.3 but
+ * are not able to hold XMPP compliant stanza errors. The xterror
+ * structure has been introduced to be XMPP compliant. This function
+ * is to ease writting wrappers that accept terror structures and call
+ * the real functions that require now xterror structures
+ *
+ * @param old the terror struct that should be converted
+ * @param mapped pointer to the xterror struct that should be filled with the converted error
+ */
+void jutil_error_map(terror old, xterror *mapped)
+{
+    mapped->code = old.code;
+    if (old.msg == NULL)
+	mapped->msg[0] = 0;
+    else
+	strncpy(mapped->msg, old.msg, sizeof(mapped->msg));
+
+    switch (old.code)
+    {
+	case 302:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "redirect");
+	    break;
+	case 400:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "bad-request");
+	    break;
+	case 401:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "not-authorized");
+	    break;
+	case 402:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "payment-required");
+	    break;
+	case 403:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "forbidden");
+	    break;
+	case 404:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "item-not-found");
+	    break;
+	case 405:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "not-allowed");
+	    break;
+	case 406:
+	    strcpy(mapped->type, "modify");
+	    strcpy(mapped->condition, "not-acceptable");
+	    break;
+	case 407:
+	    strcpy(mapped->type, "auth");
+	    strcpy(mapped->condition, "registration-requited");
+	    break;
+	case 408:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "remote-server-timeout");
+	    break;
+	case 409:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "conflict");
+	    break;
+	case 500:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "internal-server-error");
+	    break;
+	case 501:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "feature-not-implemented");
+	    break;
+	case 502:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	case 503:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	case 504:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "remote-server-timeout");
+	    break;
+	case 510:
+	    strcpy(mapped->type, "cancel");
+	    strcpy(mapped->condition, "service-unavailable");
+	    break;
+	default:
+	    strcpy(mapped->type, "wait");
+	    strcpy(mapped->condition, "undefined-condition");
+    }
+}
+
+/**
+ * update an xmlnode to be the error stanza for itself
+ *
+ * @param x the xmlnode that should become an stanza error message
+ * @param E the structure that holds the error information
+ */
+void jutil_error_xmpp(xmlnode x, xterror E)
 {
     xmlnode err;
     char code[4];
 
-    xmlnode_put_attrib(x,"type","error");
-    err = xmlnode_insert_tag(x,"error");
+    xmlnode_put_attrib(x, "type", "error");
+    err = xmlnode_insert_tag(x, "error");
 
-    snprintf(code,4,"%d",E.code);
-    xmlnode_put_attrib(err,"code",code);
-    if(E.msg != NULL)
-	xmlnode_insert_cdata(err,E.msg,strlen(E.msg));
+    snprintf(code, sizeof(code), "%d", E.code);
+    xmlnode_put_attrib(err, "code", code);
+    if (E.type != NULL)
+	xmlnode_put_attrib(err, "type", E.type);
+    if (E.condition != NULL)
+	xmlnode_put_attrib(xmlnode_insert_tag(err, E.condition), "xmlns", NS_XMPP_STANZAS);
+    if (E.msg != NULL)
+    {
+	xmlnode text;
+	text = xmlnode_insert_tag(err, "text");
+	xmlnode_put_attrib(text, "xmlns", NS_XMPP_STANZAS);
+	xmlnode_insert_cdata(text, E.msg, strlen(E.msg));
+    }
 
     jutil_tofrom(x);
 }
 
+/**
+ * wrapper around jutil_error_xmpp for compatibility with modules for jabberd up to version 1.4.3
+ *
+ * @deprecated use jutil_error_xmpp instead!
+ *
+ * @param x the xmlnode that should become an stanza error message
+ * @param E the strucutre that holds the error information
+ */
+void jutil_error(xmlnode x, terror E)
+{
+    xterror xE;
+    jutil_error_map(E, &xE);
+    jutil_error_xmpp(x, xE);
+}
+
+/**
+ * add a delayed delivery (JEP-0091) element to a message using the
+ * present timestamp.
+ * If a reason is given, this reason will be added as CDATA to the
+ * inserted element
+ *
+ * @param msg the message where the element should be added
+ * @param reason plain text information why the delayed delivery information has been added
+ */
 void jutil_delay(xmlnode msg, char *reason)
 {
     xmlnode delay;
@@ -194,11 +426,33 @@
     xmlnode_put_attrib(delay,"from",xmlnode_get_attrib(msg,"to"));
     xmlnode_put_attrib(delay,"stamp",jutil_timestamp());
     if(reason != NULL)
-	xmlnode_insert_cdata(delay,reason,strlen(reason));
+        xmlnode_insert_cdata(delay,reason,strlen(reason));
 }
 
 #define KEYBUF 100
 
+/**
+ * create or validate a key value for stone-age jabber protocols
+ *
+ * Before dialback had been introduced for s2s (and therefore only in jabberd 1.0),
+ * Jabber used these keys to protect some iq requests. A client first had to
+ * request a key with a IQ get and use it inside the IQ set request. By being able
+ * to receive the key in the IQ get response, the client (more or less) proved to be
+ * who he claimed to be.
+ *
+ * The implementation of this function uses a static array with KEYBUF entries (default
+ * value of KEYBUF is 100). Therefore a key gets invalid at the 100th key that is created
+ * afterwards. It is also invalidated after it has been validated once.
+ *
+ * @deprecated This function is not really used anymore. jabberd14 does not check any
+ * keys anymore and only creates them in the jsm's mod_register.c for compatibility. This
+ * function is also used in mod_groups.c and the key is even checked there, but I do not
+ * know if mod_groups.c still works at all.
+ *
+ * @param key for validation the key the client sent, for generation of a new key NULL
+ * @param seed the seed for generating the key, must stay the same for the same user
+ * @return the new key when created, the key if the key has been validated, NULL if the key is invalid
+ */
 char *jutil_regkey(char *key, char *seed)
 {
     static char keydb[KEYBUF][41];
@@ -210,37 +464,37 @@
     /* blanket the keydb first time */
     if(last == -1)
     {
-	last = 0;
-	memset(&keydb,0,KEYBUF*41);
-	memset(&seeddb,0,KEYBUF*41);
-	srand(time(NULL));
+        last = 0;
+        memset(&keydb,0,KEYBUF*41);
+        memset(&seeddb,0,KEYBUF*41);
+        srand(time(NULL));
     }
 
     /* creation phase */
     if(key == NULL && seed != NULL)
     {
-	/* create a random key hash and store it */
-	sprintf(strint,"%d",rand());
-	strcpy(keydb[last],shahash(strint));
+        /* create a random key hash and store it */
+        sprintf(strint,"%d",rand());
+        strcpy(keydb[last],shahash(strint));
 
-	/* store a hash for the seed associated w/ this key */
-	strcpy(seeddb[last],shahash(seed));
+        /* store a hash for the seed associated w/ this key */
+        strcpy(seeddb[last],shahash(seed));
 
-	/* return it all */
-	str = keydb[last];
-	last++;
-	if(last == KEYBUF) last = 0;
-	return str;
+        /* return it all */
+        str = keydb[last];
+        last++;
+        if(last == KEYBUF) last = 0;
+        return str;
     }
 
     /* validation phase */
     str = shahash(seed);
     for(i=0;i<KEYBUF;i++)
-	if(j_strcmp(keydb[i],key) == 0 && j_strcmp(seeddb[i],str) == 0)
-	{
-	    seeddb[i][0] = '\0'; /* invalidate this key */
-	    return keydb[i];
-	}
+        if(j_strcmp(keydb[i],key) == 0 && j_strcmp(seeddb[i],str) == 0)
+        {
+            seeddb[i][0] = '\0'; /* invalidate this key */
+            return keydb[i];
+        }
 
     return NULL;
 }