diff options
| author | ulim <a.sporto+bee@gmail.com> | 2008-04-14 15:10:53 +0200 | 
|---|---|---|
| committer | ulim <a.sporto+bee@gmail.com> | 2008-04-14 15:10:53 +0200 | 
| commit | b79308b943149d729b1daea8c56cff9fc02711a0 (patch) | |
| tree | a5f80445ed63d864703941474dc6cf8998df3136 /lib | |
| parent | 6cac643f6933e431b90fcb937dec505f989e6a53 (diff) | |
| parent | aa311173a85020bcbbbf61135a5451e171d422f5 (diff) | |
merged in upstream r379 (somewhere after 1.2-3).
Just one trivial conflict in the jabber Makefile, went smoothly.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile | 4 | ||||
| -rw-r--r-- | lib/arc.c | 27 | ||||
| -rw-r--r-- | lib/arc.h | 8 | ||||
| -rw-r--r-- | lib/base64.c | 2 | ||||
| -rw-r--r-- | lib/events_glib.c | 5 | ||||
| -rw-r--r-- | lib/misc.c | 41 | ||||
| -rw-r--r-- | lib/misc.h | 2 | ||||
| -rw-r--r-- | lib/proxy.c | 4 | ||||
| -rw-r--r-- | lib/ssl_client.h | 3 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 6 | ||||
| -rw-r--r-- | lib/ssl_nss.c | 6 | ||||
| -rw-r--r-- | lib/ssl_openssl.c | 23 | ||||
| -rw-r--r-- | lib/url.c | 24 | ||||
| -rw-r--r-- | lib/url.h | 18 | ||||
| -rw-r--r-- | lib/xmltree.c | 590 | ||||
| -rw-r--r-- | lib/xmltree.h | 97 | 
16 files changed, 825 insertions, 35 deletions
| diff --git a/lib/Makefile b/lib/Makefile index a79f7c4c..03fef1ab 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@  -include ../Makefile.settings  # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o  CFLAGS += -Wall  LFLAGS += -r @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: lib.o  check: all -lcov: +lcov: check  gcov:  	gcov *.c @@ -130,18 +130,40 @@ unsigned char arc_getbyte( struct arc_state *st )     don't need it anymore.     Both functions return the number of bytes in the result string. +    +   Note that if you use the pad_to argument, you will need zero-termi- +   nation to find back the original string length after decryption. So +   it shouldn't be used if your string contains \0s by itself!  */ -int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password ) +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )  {  	struct arc_state *st;  	unsigned char *key; -	int key_len, i; +	char *padded = NULL; +	int key_len, i, padded_len;  	key_len = strlen( password ) + ARC_IV_LEN;  	if( clear_len <= 0 )  		clear_len = strlen( clear ); +	/* Pad the string to the closest multiple of pad_to. This makes it +	   impossible to see the exact length of the password. */ +	if( pad_to > 0 && ( clear_len % pad_to ) > 0 ) +	{ +		padded_len = clear_len + pad_to - ( clear_len % pad_to ); +		padded = g_malloc( padded_len ); +		memcpy( padded, clear, clear_len ); +		 +		/* First a \0 and then random data, so we don't have to do +		   anything special when decrypting. */ +		padded[clear_len] = 0; +		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 ); +		 +		clear = padded; +		clear_len = padded_len; +	} +	  	/* Prepare buffers and the key + IV */  	*crypt = g_malloc( clear_len + ARC_IV_LEN );  	key = g_malloc( key_len ); @@ -160,6 +182,7 @@ int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *passwor  		crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st );  	g_free( st ); +	g_free( padded );  	return clear_len + ARC_IV_LEN;  } @@ -30,7 +30,11 @@ struct arc_state  	unsigned char i, j;  }; -struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +#ifndef G_GNUC_MALLOC +#define G_GNUC_MALLOC +#endif + +G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );  unsigned char arc_getbyte( struct arc_state *st ); -int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password ); +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );  int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ); diff --git a/lib/base64.c b/lib/base64.c index 64e9692a..ea0db6b9 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -117,7 +117,7 @@ int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev  {  	int i, outlen = 0; -	for( i = 0; in[i]; i += 4 ) +	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )  	{  		int sx; diff --git a/lib/events_glib.c b/lib/events_glib.c index 1198dba6..3e194e98 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -50,11 +50,12 @@ typedef struct _GaimIOClosure {  	gpointer data;  } GaimIOClosure; -static GMainLoop *loop; +static GMainLoop *loop = NULL;  void b_main_init()  { -	loop = g_main_new( FALSE ); +	if( loop == NULL ) +		loop = g_main_new( FALSE );  }  void b_main_run() @@ -32,6 +32,7 @@  #define BITLBEE_CORE  #include "nogaim.h" +#include "base64.h"  #include <stdio.h>  #include <stdlib.h>  #include <string.h> @@ -596,3 +597,43 @@ gboolean ssl_sockerr_again( void *ssl )  	else  		return sockerr_again();  } + +/* Returns values: -1 == Failure (base64-decoded to something unexpected) +                    0 == Okay +                    1 == Password doesn't match the hash. */ +int md5_verify_password( char *password, char *hash ) +{ +	md5_byte_t *pass_dec = NULL; +	md5_byte_t pass_md5[16]; +	md5_state_t md5_state; +	int ret, i; +	 +	if( base64_decode( hash, &pass_dec ) != 21 ) +	{ +		ret = -1; +	} +	else +	{ +		md5_init( &md5_state ); +		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) ); +		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ +		md5_finish( &md5_state, pass_md5 ); +		 +		for( i = 0; i < 16; i ++ ) +		{ +			if( pass_dec[i] != pass_md5[i] ) +			{ +				ret = 1; +				break; +			} +		} +		 +		/* If we reached the end of the loop, it was a match! */ +		if( i == 16 ) +			ret = 0; +	} +	 +	g_free( pass_dec ); + +	return ret; +} @@ -66,4 +66,6 @@ G_MODULE_EXPORT char *word_wrap( char *msg, int line_len );  G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); +G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); +  #endif diff --git a/lib/proxy.c b/lib/proxy.c index 0e1c8f07..53b89d64 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -529,7 +529,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat  {  	struct PHB *phb; -	if (!host || !port || (port == -1) || !func || strlen(host) > 128) { +	if (!host || port <= 0 || !func || strlen(host) > 128) {  		return -1;  	} @@ -537,7 +537,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat  	phb->func = func;  	phb->data = data; -	if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1)) +	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)  		return proxy_connect_none(host, port, phb);  	else if (proxytype == PROXY_HTTP)  		return proxy_connect_http(host, port, phb); diff --git a/lib/ssl_client.h b/lib/ssl_client.h index dcbf9a01..f91d0d70 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -59,6 +59,9 @@ G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer da  G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );  G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); +/* See ssl_openssl.c for an explanation. */ +G_MODULE_EXPORT int ssl_pending( void *conn ); +  /* Abort the SSL connection and disconnect the socket. Do not use close()     directly, both the SSL library and the peer will be unhappy! */  G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index b964ab49..f5945442 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -210,6 +210,12 @@ int ssl_write( void *conn, const char *buf, int len )  	return st;  } +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	return 0; +} +  void ssl_disconnect( void *conn_ )  {  	struct scd *conn = conn_; diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index 218b3a80..eba3c441 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -168,6 +168,12 @@ int ssl_write( void *conn, const char *buf, int len )  	return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );  } +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	return 0; +} +  void ssl_disconnect( void *conn_ )  {  	struct scd *conn = conn_; diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index b1ba1db9..fc6d433e 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -61,16 +61,16 @@ void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data  	struct scd *conn = g_new0( struct scd, 1 );  	conn->fd = proxy_connect( host, port, ssl_connected, conn ); -	conn->func = func; -	conn->data = data; -	conn->inpa = -1; -	  	if( conn->fd < 0 )  	{  		g_free( conn );  		return NULL;  	} +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	  	return conn;  } @@ -230,6 +230,21 @@ int ssl_write( void *conn, const char *buf, int len )  	return st;  } +/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info: +   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209 +   http://www.openssl.org/docs/ssl/SSL_pending.html +    +   Required because OpenSSL empties the TCP buffer completely but doesn't +   necessarily give us all the unencrypted data. +    +   Returns 0 if there's nothing left or if we don't have to care (GnuTLS), +   1 if there's more data. */ +int ssl_pending( void *conn ) +{ +	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ? +	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0; +} +  void ssl_disconnect( void *conn_ )  {  	struct scd *conn = conn_; @@ -25,13 +25,16 @@  #include "url.h" -/* Convert an URL to a url_t structure					*/ +/* Convert an URL to a url_t structure */  int url_set( url_t *url, char *set_url )  { -	char s[MAX_STRING]; +	char s[MAX_STRING+1];  	char *i; -	/* protocol://							*/ +	memset( url, 0, sizeof( url_t ) ); +	memset( s, 0, sizeof( s ) ); +	 +	/* protocol:// */  	if( ( i = strstr( set_url, "://" ) ) == NULL )  	{  		url->proto = PROTO_DEFAULT; @@ -48,13 +51,12 @@ int url_set( url_t *url, char *set_url )  		else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 )  			url->proto = PROTO_SOCKS5;  		else -		{ -			return( 0 ); -		} +			return 0; +		  		strncpy( s, i + 3, MAX_STRING );  	} -	/* Split							*/ +	/* Split */  	if( ( i = strchr( s, '/' ) ) == NULL )  	{  		strcpy( url->file, "/" ); @@ -66,7 +68,7 @@ int url_set( url_t *url, char *set_url )  	}  	strncpy( url->host, s, MAX_STRING ); -	/* Check for username in host field				*/ +	/* Check for username in host field */  	if( strrchr( url->host, '@' ) != NULL )  	{  		strncpy( url->user, url->host, MAX_STRING ); @@ -75,19 +77,19 @@ int url_set( url_t *url, char *set_url )  		strcpy( url->host, i + 1 );  		*url->pass = 0;  	} -	/* If not: Fill in defaults					*/ +	/* If not: Fill in defaults */  	else  	{  		*url->user = *url->pass = 0;  	} -	/* Password?							*/ +	/* Password? */  	if( ( i = strchr( url->user, ':' ) ) != NULL )  	{  		*i = 0;  		strcpy( url->pass, i + 1 );  	} -	/* Port number?							*/ +	/* Port number? */  	if( ( i = strchr( url->host, ':' ) ) != NULL )  	{  		*i = 0; @@ -25,20 +25,20 @@  #include "bitlbee.h" -#define PROTO_HTTP		2 -#define PROTO_HTTPS		5 -#define PROTO_SOCKS4	3 -#define PROTO_SOCKS5	4 -#define PROTO_DEFAULT	PROTO_HTTP +#define PROTO_HTTP      2 +#define PROTO_HTTPS     5 +#define PROTO_SOCKS4    3 +#define PROTO_SOCKS5    4 +#define PROTO_DEFAULT   PROTO_HTTP  typedef struct url  {  	int proto;  	int port; -	char host[MAX_STRING]; -	char file[MAX_STRING]; -	char user[MAX_STRING]; -	char pass[MAX_STRING]; +	char host[MAX_STRING+1]; +	char file[MAX_STRING+1]; +	char user[MAX_STRING+1]; +	char pass[MAX_STRING+1];  } url_t;  int url_set( url_t *url, char *set_url ); diff --git a/lib/xmltree.c b/lib/xmltree.c new file mode 100644 index 00000000..e65b4f41 --- /dev/null +++ b/lib/xmltree.c @@ -0,0 +1,590 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#include <glib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> + +#include "xmltree.h" + +static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt; +	int i; +	 +	node->parent = xt->cur; +	node->name = g_strdup( element_name ); +	 +	/* First count the number of attributes */ +	for( i = 0; attr_names[i]; i ++ ); +	 +	/* Then allocate a NULL-terminated array. */ +	node->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* And fill it, saving one variable by starting at the end. */ +	for( i --; i >= 0; i -- ) +	{ +		node->attr[i].key = g_strdup( attr_names[i] ); +		node->attr[i].value = g_strdup( attr_values[i] ); +	} +	 +	/* Add it to the linked list of children nodes, if we have a current +	   node yet. */ +	if( xt->cur ) +	{ +		if( xt->cur->children ) +		{ +			for( nt = xt->cur->children; nt->next; nt = nt->next ); +			nt->next = node; +		} +		else +		{ +			xt->cur->children = node; +		} +	} +	else if( xt->root ) +	{ +		/* ERROR situation: A second root-element??? */ +	} +	 +	/* Now this node will be the new current node. */ +	xt->cur = node; +	/* And maybe this is the root? */ +	if( xt->root == NULL ) +		xt->root = node; +} + +static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = xt->cur; +	 +	if( node == NULL ) +		return; +	 +	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */ +	node->text = g_renew( char, node->text, node->text_len + text_len + 1 ); +	memcpy( node->text + node->text_len, text, text_len ); +	node->text_len += text_len; +	/* Zero termination is always nice to have. */ +	node->text[node->text_len] = 0; +} + +static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	 +	xt->cur->flags |= XT_COMPLETE; +	xt->cur = xt->cur->parent; +} + +GMarkupParser xt_parser_funcs = +{ +	xt_start_element, +	xt_end_element, +	xt_text, +	NULL, +	NULL +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ) +{ +	struct xt_parser *xt = g_new0( struct xt_parser, 1 ); +	 +	xt->data = data; +	xt->handlers = handlers; +	xt_reset( xt ); +	 +	return xt; +} + +/* Reset the parser, flush everything we have so far. For example, we need +   this for XMPP when doing TLS/SASL to restart the stream. */ +void xt_reset( struct xt_parser *xt ) +{ +	if( xt->parser ) +		g_markup_parse_context_free( xt->parser ); +	 +	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL ); +	 +	if( xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = NULL; +		xt->cur = NULL; +	} +} + +/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on +   end-of-stream and 1 otherwise. */ +int xt_feed( struct xt_parser *xt, char *text, int text_len ) +{ +	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) +	{ +		return -1; +	} +	 +	return !( xt->root && xt->root->flags & XT_COMPLETE ); +} + +/* Find completed nodes and see if a handler has to be called. Passing +   a node isn't necessary if you want to start at the root, just pass +   NULL. This second argument is needed for recursive calls. */ +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c; +	xt_status st; +	int i; +	 +	/* Just in case someone likes infinite loops... */ +	if( xt->root == NULL ) +		return 0; +	 +	if( node == NULL ) +		return xt_handle( xt, xt->root, depth ); +	 +	if( depth != 0 ) +		for( c = node->children; c; c = c->next ) +			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) ) +				return 0; +	 +	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) +	{ +		for( i = 0; xt->handlers[i].func; i ++ ) +		{ +			/* This one is fun! \o/ */ +			 +						/* If handler.name == NULL it means it should always match. */ +			if( ( xt->handlers[i].name == NULL ||  +						/* If it's not, compare. There should always be a name. */ +			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && +						/* If handler.parent == NULL, it's a match. */ +			    ( xt->handlers[i].parent == NULL || +						/* If there's a parent node, see if the name matches. */ +			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 :  +						/* If there's no parent, the handler should mention <root> as a parent. */ +			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) +			{ +				st = xt->handlers[i].func( node, xt->data ); +				 +				if( st == XT_ABORT ) +					return 0; +				else if( st != XT_NEXT ) +					break; +			} +		} +		 +		node->flags |= XT_SEEN; +	} +	 +	return 1; +} + +/* Garbage collection: Cleans up all nodes that are handled. Useful for +   streams because there's no reason to keep a complete packet history +   in memory. */ +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c, *prev; +	 +	if( !xt || !xt->root ) +		return; +	 +	if( node == NULL ) +		return xt_cleanup( xt, xt->root, depth ); +	 +	if( node->flags & XT_SEEN && node == xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = xt->cur = NULL; +		/* xt->cur should be NULL already, BTW... */ +		 +		return; +	} +	 +	/* c contains the current node, prev the previous node (or NULL). +	   I admit, this one's pretty horrible. */ +	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children ) +	{ +		if( c->flags & XT_SEEN ) +		{ +			/* Remove the node from the linked list. */ +			if( prev ) +				prev->next = c->next; +			else +				node->children = c->next; +			 +			xt_free_node( c ); +			 +			/* Since the for loop wants to get c->next, make sure +			   c points at something that exists (and that c->next +			   will actually be the next item we should check). c +			   can be NULL now, if we just removed the first item. +			   That explains the ? thing in for(). */ +			c = prev; +		} +		else +		{ +			/* This node can't be cleaned up yet, but maybe a +			   subnode can. */ +			if( depth != 0 ) +				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth ); +		} +	} +} + +static void xt_to_string_real( struct xt_node *node, GString *str ) +{ +	char *buf; +	struct xt_node *c; +	int i; +	 +	g_string_append_printf( str, "<%s", node->name ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	if( node->text == NULL && node->children == NULL ) +	{ +		g_string_append( str, "/>" ); +		return; +	} +	 +	g_string_append( str, ">" ); +	if( node->text_len > 0 ) +	{ +		buf = g_markup_escape_text( node->text, node->text_len ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	for( c = node->children; c; c = c->next ) +		xt_to_string_real( c, str ); +	 +	g_string_append_printf( str, "</%s>", node->name ); +} + +char *xt_to_string( struct xt_node *node ) +{ +	GString *ret; +	char *real; +	 +	ret = g_string_new( "" ); +	xt_to_string_real( node, ret ); +	 +	real = ret->str; +	g_string_free( ret, FALSE ); +	 +	return real; +} + +#ifdef DEBUG +void xt_print( struct xt_node *node ) +{ +	int i; +	struct xt_node *c; +	 +	/* Indentation */ +	for( c = node; c->parent; c = c->parent ) +		printf( "\t" ); +	 +	/* Start the tag */ +	printf( "<%s", node->name ); +	 +	/* Print the attributes */ +	for( i = 0; node->attr[i].key; i ++ ) +		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) ); +	 +	/* /> in case there's really *nothing* inside this tag, otherwise +	   just >. */ +	/* If this tag doesn't have any content at all... */ +	if( node->text == NULL && node->children == NULL ) +	{ +		printf( "/>\n" ); +		return; +		/* Then we're finished! */ +	} +	 +	/* Otherwise... */ +	printf( ">" ); +	 +	/* Only print the text if it contains more than whitespace (TEST). */ +	if( node->text_len > 0 ) +	{ +		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ ); +		if( node->text[i] ) +			printf( "%s", g_markup_escape_text( node->text, -1 ) ); +	} +	 +	if( node->children ) +		printf( "\n" ); +	 +	for( c = node->children; c; c = c->next ) +		xt_print( c ); +	 +	if( node->children ) +		for( c = node; c->parent; c = c->parent ) +			printf( "\t" ); +	 +	/* Non-empty tag is now finished. */ +	printf( "</%s>\n", node->name ); +} +#endif + +struct xt_node *xt_dup( struct xt_node *node ) +{ +	struct xt_node *dup = g_new0( struct xt_node, 1 ); +	struct xt_node *c, *dc = NULL; +	int i; +	 +	/* Let's NOT copy the parent element here BTW! Only do it for children. */ +	 +	dup->name = g_strdup( node->name ); +	dup->flags = node->flags; +	if( node->text ) +	{ +		dup->text = g_memdup( node->text, node->text_len + 1 ); +		dup->text_len = node->text_len; +	} +	 +	/* Count the number of attributes and allocate the new array. */ +	for( i = 0; node->attr[i].key; i ++ ); +	dup->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* Copy them all! */ +	for( i --; i >= 0; i -- ) +	{ +		dup->attr[i].key = g_strdup( node->attr[i].key ); +		dup->attr[i].value = g_strdup( node->attr[i].value ); +	} +	 +	/* This nice mysterious loop takes care of the children. */ +	for( c = node->children; c; c = c->next ) +	{ +		if( dc == NULL ) +			dc = dup->children = xt_dup( c ); +		else +			dc = ( dc->next = xt_dup( c ) ); +		 +		dc->parent = dup; +	} +	 +	return dup; +} + +/* Frees a node. This doesn't clean up references to itself from parents! */ +void xt_free_node( struct xt_node *node ) +{ +	int i; +	 +	if( !node ) +		return; +	 +	g_free( node->name ); +	g_free( node->text ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		g_free( node->attr[i].key ); +		g_free( node->attr[i].value ); +	} +	g_free( node->attr ); +	 +	while( node->children ) +	{ +		struct xt_node *next = node->children->next; +		 +		xt_free_node( node->children ); +		node->children = next; +	} +	 +	g_free( node ); +} + +void xt_free( struct xt_parser *xt ) +{ +	if( !xt ) +		return; +	 +	if( xt->root ) +		xt_free_node( xt->root ); +	 +	g_markup_parse_context_free( xt->parser ); +	 +	g_free( xt ); +} + +/* To find a node's child with a specific name, pass the node's children +   list, not the node itself! The reason you have to do this by hand: So +   that you can also use this function as a find-next. */ +struct xt_node *xt_find_node( struct xt_node *node, const char *name ) +{ +	while( node ) +	{ +		if( g_strcasecmp( node->name, name ) == 0 ) +			break; +		 +		node = node->next; +	} +	 +	return node; +} + +char *xt_find_attr( struct xt_node *node, const char *key ) +{ +	int i; +	 +	if( !node ) +		return NULL; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( g_strcasecmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	return node->attr[i].value; +} + +struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ) +{ +	struct xt_node *node, *c; +	 +	node = g_new0( struct xt_node, 1 ); +	node->name = g_strdup( name ); +	node->children = children; +	node->attr = g_new0( struct xt_attr, 1 ); +	 +	if( text ) +	{ +		node->text_len = strlen( text ); +		node->text = g_memdup( text, node->text_len + 1 ); +	} +	 +	for( c = children; c; c = c->next ) +	{ +		if( c->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		c->parent = node; +	} +	 +	return node; +} + +void xt_add_child( struct xt_node *parent, struct xt_node *child ) +{ +	struct xt_node *node; +	 +	/* This function can actually be used to add more than one child, so +	   do handle this properly. */ +	for( node = child; node; node = node->next ) +	{ +		if( node->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		node->parent = parent; +	} +	 +	if( parent->children == NULL ) +	{ +		parent->children = child; +	} +	else +	{ +		for( node = parent->children; node->next; node = node->next ); +		node->next = child; +	} +} + +void xt_add_attr( struct xt_node *node, const char *key, const char *value ) +{ +	int i; +	 +	/* Now actually it'd be nice if we can also change existing attributes +	   (which actually means this function doesn't have the right name). +	   So let's find out if we have this attribute already... */ +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	if( node->attr[i].key == NULL ) +	{ +		/* If not, allocate space for a new attribute. */ +		node->attr = g_renew( struct xt_attr, node->attr, i + 2 ); +		node->attr[i].key = g_strdup( key ); +		node->attr[i+1].key = NULL; +	} +	else +	{ +		/* Otherwise, free the old value before setting the new one. */ +		g_free( node->attr[i].value ); +	} +	 +	node->attr[i].value = g_strdup( value ); +} + +int xt_remove_attr( struct xt_node *node, const char *key ) +{ +	int i, last; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	/* If we didn't find the attribute... */ +	if( node->attr[i].key == NULL ) +		return 0; +	 +	g_free( node->attr[i].key ); +	g_free( node->attr[i].value ); +	 +	/* If it's the last, this is easy: */ +	if( node->attr[i+1].key == NULL ) +	{ +		node->attr[i].key = node->attr[i].value = NULL; +	} +	else /* It's also pretty easy, actually. */ +	{ +		/* Find the last item. */ +		for( last = i + 1; node->attr[last+1].key; last ++ ); +		 +		node->attr[i] = node->attr[last]; +		node->attr[last].key = NULL; +		node->attr[last].value = NULL; +	} +	 +	/* Let's not bother with reallocating memory here. It takes time and +	   most packets don't stay in memory for long anyway. */ +	 +	return 1; +} diff --git a/lib/xmltree.h b/lib/xmltree.h new file mode 100644 index 00000000..10677412 --- /dev/null +++ b/lib/xmltree.h @@ -0,0 +1,97 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#ifndef _XMLTREE_H +#define _XMLTREE_H + +typedef enum +{ +	XT_COMPLETE	= 1,	/* </tag> reached */ +	XT_SEEN		= 2,	/* Handler called (or not defined) */ +} xt_flags; + +typedef enum +{ +	XT_ABORT,		/* Abort, don't handle the rest anymore */ +	XT_HANDLED,		/* Handled this tag properly, go to the next one */ +	XT_NEXT			/* Try if there's another matching handler */ +} xt_status; + +struct xt_attr +{ +	char *key, *value; +}; + +struct xt_node +{ +	struct xt_node *parent; +	struct xt_node *children; +	 +	char *name; +	struct xt_attr *attr; +	char *text; +	int text_len; +	 +	struct xt_node *next; +	xt_flags flags; +}; + +typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data ); + +struct xt_handler_entry +{ +	char *name, *parent; +	xt_handler_func func; +}; + +struct xt_parser +{ +	GMarkupParseContext *parser; +	struct xt_node *root; +	struct xt_node *cur; +	 +	const struct xt_handler_entry *handlers; +	gpointer data; +	 +	GError *gerr; +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); +void xt_reset( struct xt_parser *xt ); +int xt_feed( struct xt_parser *xt, char *text, int text_len ); +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +char *xt_to_string( struct xt_node *node ); +void xt_print( struct xt_node *node ); +struct xt_node *xt_dup( struct xt_node *node ); +void xt_free_node( struct xt_node *node ); +void xt_free( struct xt_parser *xt ); +struct xt_node *xt_find_node( struct xt_node *node, const char *name ); +char *xt_find_attr( struct xt_node *node, const char *key ); + +struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ); +void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_add_attr( struct xt_node *node, const char *key, const char *value ); +int xt_remove_attr( struct xt_node *node, const char *key ); + +#endif | 
