diff options
Diffstat (limited to 'protocols/jabber')
| -rw-r--r-- | protocols/jabber/Makefile | 2 | ||||
| -rw-r--r-- | protocols/jabber/conference.c | 246 | ||||
| -rw-r--r-- | protocols/jabber/io.c | 60 | ||||
| -rw-r--r-- | protocols/jabber/iq.c | 35 | ||||
| -rw-r--r-- | protocols/jabber/jabber.c | 76 | ||||
| -rw-r--r-- | protocols/jabber/jabber.h | 64 | ||||
| -rw-r--r-- | protocols/jabber/jabber_util.c | 178 | ||||
| -rw-r--r-- | protocols/jabber/message.c | 31 | ||||
| -rw-r--r-- | protocols/jabber/presence.c | 71 | ||||
| -rw-r--r-- | protocols/jabber/xmltree.c | 8 | ||||
| -rw-r--r-- | protocols/jabber/xmltree.h | 8 | 
11 files changed, 680 insertions, 99 deletions
| diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e81e6c5a..e042f812 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -9,7 +9,7 @@  -include ../../Makefile.settings  # [SH] Program variables -objects = io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o +objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o  CFLAGS += -Wall  LFLAGS += -r diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c new file mode 100644 index 00000000..3fc9ee70 --- /dev/null +++ b/protocols/jabber/conference.c @@ -0,0 +1,246 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Jabber module - Conference rooms                                         * +*                                                                           * +*  Copyright 2007 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This program is free software; you can redistribute it and/or modify     * +*  it under the terms of the GNU General Public License as published by     * +*  the Free Software Foundation; either version 2 of the License, or        * +*  (at your option) any later version.                                      * +*                                                                           * +*  This program is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            * +*  GNU General Public License for more details.                             * +*                                                                           * +*  You should have received a copy of the GNU General Public License along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +#include "jabber.h" + +struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ) +{ +	struct jabber_chat *jc; +	struct xt_node *node; +	struct groupchat *c; +	char *roomjid; +	 +	roomjid = g_strdup_printf( "%s/%s", room, nick ); +	node = xt_new_node( "x", NULL, NULL ); +	xt_add_attr( node, "xmlns", XMLNS_MUC ); +	node = jabber_make_packet( "presence", NULL, roomjid, node ); +	 +	if( !jabber_write_packet( ic, node ) ) +	{ +		g_free( roomjid ); +		xt_free_node( node ); +		return NULL; +	} +	xt_free_node( node ); +	 +	jc = g_new0( struct jabber_chat, 1 ); +	jc->name = jabber_normalize( room ); +	 +	if( ( jc->me = jabber_buddy_add( ic, roomjid ) ) == NULL ) +	{ +		g_free( roomjid ); +		g_free( jc->name ); +		g_free( jc ); +		return NULL; +	} +	 +	/* roomjid isn't normalized yet, and we need an original version +	   of the nick to send a proper presence update. */ +	jc->my_full_jid = roomjid; +	 +	c = imcb_chat_new( ic, room ); +	c->data = jc; +	 +	return c; +} + +void jabber_chat_free( struct groupchat *c ) +{ +	struct jabber_chat *jc = c->data; +	 +	jabber_buddy_remove_bare( c->ic, jc->name ); +	 +	g_free( jc->my_full_jid ); +	g_free( jc->name ); +	g_free( jc ); +	 +	imcb_chat_free( c ); +} + +int jabber_chat_msg( struct groupchat *c, char *message, int flags ) +{ +	struct im_connection *ic = c->ic; +	struct jabber_chat *jc = c->data; +	struct xt_node *node; +	 +	node = xt_new_node( "body", message, NULL ); +	node = jabber_make_packet( "message", "groupchat", jc->name, node ); +	 +	if( !jabber_write_packet( ic, node ) ) +	{ +		xt_free_node( node ); +		return 0; +	} +	xt_free_node( node ); +	 +	return 1; +} + +int jabber_chat_leave( struct groupchat *c, const char *reason ) +{ +	struct im_connection *ic = c->ic; +	struct jabber_chat *jc = c->data; +	struct xt_node *node; +	 +	node = xt_new_node( "x", NULL, NULL ); +	xt_add_attr( node, "xmlns", XMLNS_MUC ); +	node = jabber_make_packet( "presence", "unavailable", jc->my_full_jid, node ); +	 +	if( !jabber_write_packet( ic, node ) ) +	{ +		xt_free_node( node ); +		return 0; +	} +	xt_free_node( node ); +	 +	return 1; +} + +/* Not really the same syntax as the normal pkt_ functions, but this isn't +   called by the xmltree parser directly and this way I can add some extra +   parameters so we won't have to repeat too many things done by the caller +   already. */ +void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ) +{ +	struct groupchat *chat; +	struct xt_node *c; +	char *type = xt_find_attr( node, "type" ); +	struct jabber_chat *jc; +	char *s; +	 +	if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL ) +	{ +		/* How could this happen?? We could do kill( self, 11 ) +		   now or just wait for the OS to do it. :-) */ +		return; +	} +	 +	jc = chat->data; +	 +	if( type == NULL && !( bud->flags & JBFLAG_IS_CHATROOM ) ) +	{ +		bud->flags |= JBFLAG_IS_CHATROOM; +		/* If this one wasn't set yet, this buddy just joined the chat. +		   Slightly hackish way of finding out eh? ;-) */ +		 +		/* This is pretty messy... Here it sets ext_jid to the real +		   JID of the participant. Works for non-anonymized channels. +		   Might break if someone joins a chat twice, though. */ +		for( c = node->children; ( c = xt_find_node( c, "x" ) ); c = c->next ) +			if( ( s = xt_find_attr( c, "xmlns" ) ) && +			    ( strcmp( s, XMLNS_MUC_USER ) == 0 ) ) +			{ +				c = xt_find_node( c->children, "item" ); +				if( ( s = xt_find_attr( c, "jid" ) ) ) +				{ +					/* Yay, found what we need. :-) */ +					bud->ext_jid = jabber_normalize( s ); +					break; +				} +			} +		 +		/* Make up some other handle, if necessary. */ +		if( bud->ext_jid == NULL ) +		{ +			if( bud == jc->me ) +			{ +				bud->ext_jid = jabber_normalize( ic->acc->user ); +			} +			else +			{ +				int i; +				 +				/* Don't want the nick to be at the end, so let's +				   think of some slightly different notation to use +				   for anonymous groupchat participants in BitlBee. */ +				bud->ext_jid = g_strdup_printf( "%s=%s", bud->resource, bud->bare_jid ); +				 +				/* And strip any unwanted characters. */ +				for( i = 0; bud->resource[i]; i ++ ) +					if( bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@' ) +						bud->ext_jid[i] = '_'; +				 +				/* Some program-specific restrictions. */ +				imcb_clean_handle( ic, bud->ext_jid ); +			} +			bud->flags |= JBFLAG_IS_ANONYMOUS; +		} +		 +		if( bud != jc->me ) +		{ +			imcb_add_buddy( ic, bud->ext_jid, NULL ); +			imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource ); +		} +		 +		s = strchr( bud->ext_jid, '/' ); +		if( s ) *s = 0; /* Should NEVER be NULL, but who knows... */ +		imcb_chat_add_buddy( chat, bud->ext_jid ); +		if( s ) *s = '/'; +	} +	else if( type ) /* type can only be NULL or "unavailable" in this function */ +	{ +		s = strchr( bud->ext_jid, '/' ); +		if( s ) *s = 0; +		imcb_chat_remove_buddy( chat, bud->ext_jid, NULL ); +		if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) +			imcb_remove_buddy( ic, bud->ext_jid, NULL ); +		if( s ) *s = '/'; +		 +		if( bud == jc->me ) +			jabber_chat_free( chat ); +	} +} + +void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ) +{ +	struct xt_node *body = xt_find_node( node->children, "body" ); +	struct groupchat *chat; +	char *s; +	 +	if( bud == NULL ) +	{ +		s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */ +		if( strchr( s, '/' ) == NULL ) +			/* This is fine, the groupchat itself isn't in jd->buddies. */ +			imcb_log( ic, "System message from groupchat %s: %s", s, body? body->text : "NULL" ); +		else +			/* This, however, isn't fine! */ +			imcb_log( ic, "Groupchat message from unknown participant %s: %s", s, body ? body->text : "NULL" ); +		 +		return; +	} +	else if( ( chat = jabber_chat_by_name( ic, bud->bare_jid ) ) == NULL ) +	{ +		/* How could this happen?? We could do kill( self, 11 ) +		   now or just wait for the OS to do it. :-) */ +		return; +	} +	 +	if( body && body->text_len > 0 ) +	{ +		s = strchr( bud->ext_jid, '/' ); +		if( s ) *s = 0; +		imcb_chat_msg( chat, bud->ext_jid, body->text, 0, jabber_get_timestamp( node ) ); +		if( s ) *s = '/'; +	} +} diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 925463a4..61cd142e 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -44,6 +44,15 @@ int jabber_write( struct im_connection *ic, char *buf, int len )  	struct jabber_data *jd = ic->proto_data;  	gboolean ret; +	if( jd->flags & JFLAG_XMLCONSOLE ) +	{ +		char *msg; +		 +		msg = g_strdup_printf( "TX: %s", buf ); +		imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 ); +		g_free( msg ); +	} +	  	if( jd->tx_len == 0 )  	{  		/* If the queue is empty, allocate a new buffer. */ @@ -426,56 +435,61 @@ static xt_status jabber_pkt_proceed_tls( struct xt_node *node, gpointer data )  static xt_status jabber_pkt_stream_error( struct xt_node *node, gpointer data )  {  	struct im_connection *ic = data; -	struct xt_node *c; -	char *s, *type = NULL, *text = NULL;  	int allow_reconnect = TRUE; +	struct jabber_error *err; -	for( c = node->children; c; c = c->next ) -	{ -		if( !( s = xt_find_attr( c, "xmlns" ) ) || -		    strcmp( s, XMLNS_STREAM_ERROR ) != 0 ) -			continue; -		 -		if( strcmp( c->name, "text" ) != 0 ) -		{ -			type = c->name; -		} -		/* Only use the text if it doesn't have an xml:lang attribute, -		   if it's empty or if it's set to something English. */ -		else if( !( s = xt_find_attr( c, "xml:lang" ) ) || -		         !*s || strncmp( s, "en", 2 ) == 0 ) -		{ -			text = c->text; -		} -	} +	err = jabber_error_parse( node, XMLNS_STREAM_ERROR );  	/* Tssk... */ -	if( type == NULL ) +	if( err->code == NULL )  	{  		imcb_error( ic, "Unknown stream error reported by server" );  		imc_logout( ic, allow_reconnect ); +		jabber_error_free( err );  		return XT_ABORT;  	}  	/* We know that this is a fatal error. If it's a "conflict" error, we  	   should turn off auto-reconnect to make sure we won't get some nasty  	   infinite loop! */ -	if( strcmp( type, "conflict" ) == 0 ) +	if( strcmp( err->code, "conflict" ) == 0 )  	{  		imcb_error( ic, "Account and resource used from a different location" );  		allow_reconnect = FALSE;  	}  	else  	{ -		imcb_error( ic, "Stream error: %s%s%s", type, text ? ": " : "", text ? text : "" ); +		imcb_error( ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "", +		            err->text ? err->text : "" );  	} +	jabber_error_free( err );  	imc_logout( ic, allow_reconnect );  	return XT_ABORT;  } +static xt_status jabber_xmlconsole( struct xt_node *node, gpointer data ) +{ +	struct im_connection *ic = data; +	struct jabber_data *jd = ic->proto_data; +	 +	if( jd->flags & JFLAG_XMLCONSOLE ) +	{ +		char *msg, *pkt; +		 +		pkt = xt_to_string( node ); +		msg = g_strdup_printf( "RX: %s", pkt ); +		imcb_buddy_msg( ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0 ); +		g_free( msg ); +		g_free( pkt ); +	} +	 +	return XT_NEXT; +} +  static const struct xt_handler_entry jabber_handlers[] = { +	{ NULL,                 "stream:stream",        jabber_xmlconsole },  	{ "stream:stream",      "<root>",               jabber_end_of_stream },  	{ "message",            "stream:stream",        jabber_pkt_message },  	{ "presence",           "stream:stream",        jabber_pkt_presence }, diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index 2aa9d432..4738817a 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -98,26 +98,25 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  		}  		else if( strcmp( s, XMLNS_DISCOVER ) == 0 )  		{ +			const char *features[] = { XMLNS_VERSION, +			                           XMLNS_TIME, +			                           XMLNS_CHATSTATES, +			                           XMLNS_MUC, +			                           NULL }; +			const char **f; +			  			c = xt_new_node( "identity", NULL, NULL );  			xt_add_attr( c, "category", "client" );  			xt_add_attr( c, "type", "pc" );  			xt_add_attr( c, "name", "BitlBee" );  			xt_add_child( reply, c ); -			c = xt_new_node( "feature", NULL, NULL ); -			xt_add_attr( c, "var", XMLNS_VERSION ); -			xt_add_child( reply, c ); -			 -			c = xt_new_node( "feature", NULL, NULL ); -			xt_add_attr( c, "var", XMLNS_TIME ); -			xt_add_child( reply, c ); -			 -			c = xt_new_node( "feature", NULL, NULL ); -			xt_add_attr( c, "var", XMLNS_CHATSTATES ); -			xt_add_child( reply, c ); -			 -			/* Later this can be useful to announce things like -			   MUC support. */ +			for( f = features; *f; f ++ ) +			{ +				c = xt_new_node( "feature", NULL, NULL ); +				xt_add_attr( c, "var", *f ); +				xt_add_child( reply, c ); +			}  		}  		else  		{ @@ -372,15 +371,13 @@ static xt_status jabber_parse_roster( struct im_connection *ic, struct xt_node *  					imcb_add_buddy( ic, jid, ( group && group->text_len ) ?  					                           group->text : NULL ); -				imcb_rename_buddy( ic, jid, name ); +				if( name ) +					imcb_rename_buddy( ic, jid, name );  			}  			else if( strcmp( sub, "remove" ) == 0 )  			{ -				/* Don't have any API call for this yet! So let's -				   just try to handle this as well as we can. */  				jabber_buddy_remove_bare( ic, jid ); -				imcb_buddy_status( ic, jid, 0, NULL, NULL ); -				/* FIXME! */ +				imcb_remove_buddy( ic, jid, NULL );  			}  		} diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c index edad5dbd..ab26efc9 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -54,6 +54,9 @@ static void jabber_init( account_t *acc )  	s = set_add( &acc->set, "tls", "try", set_eval_tls, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; +	 +	s = set_add( &acc->set, "xmlconsole", "false", set_eval_bool, acc ); +	s->flags |= ACC_SET_OFFLINE_ONLY;  }  static void jabber_login( account_t *acc ) @@ -188,6 +191,14 @@ static void jabber_login( account_t *acc )  		imcb_error( ic, "Could not connect to server" );  		imc_logout( ic, TRUE );  	} +	 +	if( set_getbool( &acc->set, "xmlconsole" ) ) +	{ +		jd->flags |= JFLAG_XMLCONSOLE; +		/* Shouldn't really do this at this stage already, maybe. But +		   I think this shouldn't break anything. */ +		imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); +	}  }  static void jabber_logout( struct im_connection *ic ) @@ -196,6 +207,9 @@ static void jabber_logout( struct im_connection *ic )  	jabber_end_stream( ic ); +	while( ic->groupchats ) +		jabber_chat_free( ic->groupchats ); +	  	if( jd->r_inpa >= 0 )  		b_event_remove( jd->r_inpa );  	if( jd->w_inpa >= 0 ) @@ -223,9 +237,16 @@ static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message,  	struct jabber_data *jd = ic->proto_data;  	struct jabber_buddy *bud;  	struct xt_node *node; +	char *s;  	int st; -	bud = jabber_buddy_by_jid( ic, who, 0 ); +	if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) +		return jabber_write( ic, message, strlen( message ) ); +	 +	if( ( s = strchr( who, '=' ) ) && jabber_chat_by_name( ic, s + 1 ) ) +		bud = jabber_buddy_by_ext_jid( ic, who, 0 ); +	else +		bud = jabber_buddy_by_jid( ic, who, 0 );  	node = xt_new_node( "body", message, NULL );  	node = jabber_make_packet( "message", "chat", bud ? bud->full_jid : who, node ); @@ -310,12 +331,34 @@ static void jabber_set_away( struct im_connection *ic, char *state_txt, char *me  static void jabber_add_buddy( struct im_connection *ic, char *who, char *group )  { +	struct jabber_data *jd = ic->proto_data; +	 +	if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) +	{ +		jd->flags |= JFLAG_XMLCONSOLE; +		imcb_add_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); +		return; +	} +	  	if( jabber_add_to_roster( ic, who, NULL ) )  		presence_send_request( ic, who, "subscribe" );  }  static void jabber_remove_buddy( struct im_connection *ic, char *who, char *group )  { +	struct jabber_data *jd = ic->proto_data; +	 +	if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 ) +	{ +		jd->flags &= ~JFLAG_XMLCONSOLE; +		/* Not necessary for now. And for now the code isn't too +		   happy if the buddy is completely gone right after calling +		   this function already. +		imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL ); +		*/ +		return; +	} +	  	/* We should always do this part. Clean up our administration a little bit. */  	jabber_buddy_remove_bare( ic, who ); @@ -323,6 +366,30 @@ static void jabber_remove_buddy( struct im_connection *ic, char *who, char *grou  		presence_send_request( ic, who, "unsubscribe" );  } +static struct groupchat *jabber_chat_join_( struct im_connection *ic, char *room, char *nick, char *password ) +{ +	if( strchr( room, '@' ) == NULL ) +		imcb_error( ic, "Invalid room name: %s", room ); +	else if( jabber_chat_by_name( ic, room ) ) +		imcb_error( ic, "Already present in chat `%s'", room ); +	else +		return jabber_chat_join( ic, room, nick, password ); +	 +	return NULL; +} + +static void jabber_chat_msg_( struct groupchat *c, char *message, int flags ) +{ +	if( c && message ) +		jabber_chat_msg( c, message, flags ); +} + +static void jabber_chat_leave_( struct groupchat *c ) +{ +	if( c ) +		jabber_chat_leave( c, NULL ); +} +  static void jabber_keepalive( struct im_connection *ic )  {  	/* Just any whitespace character is enough as a keepalive for XMPP sessions. */ @@ -387,16 +454,15 @@ void jabber_initmodule()  	ret->logout = jabber_logout;  	ret->buddy_msg = jabber_buddy_msg;  	ret->away_states = jabber_away_states; -//	ret->get_status_string = jabber_get_status_string;  	ret->set_away = jabber_set_away;  //	ret->set_info = jabber_set_info;  	ret->get_info = jabber_get_info;  	ret->add_buddy = jabber_add_buddy;  	ret->remove_buddy = jabber_remove_buddy; -//	ret->chat_msg = jabber_chat_msg; +	ret->chat_msg = jabber_chat_msg_;  //	ret->chat_invite = jabber_chat_invite; -//	ret->chat_leave = jabber_chat_leave; -//	ret->chat_open = jabber_chat_open; +	ret->chat_leave = jabber_chat_leave_; +	ret->chat_join = jabber_chat_join_;  	ret->keepalive = jabber_keepalive;  	ret->send_typing = jabber_send_typing;  	ret->handle_cmp = g_strcasecmp; diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index 42f57ae1..7af7f98e 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -31,16 +31,17 @@  typedef enum  { -	JFLAG_STREAM_STARTED = 1,	/* Set when we detected the beginning of the stream +	JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream  	                                   and want to do auth. */ -	JFLAG_AUTHENTICATED = 2,	/* Set when we're successfully authenticatd. */ -	JFLAG_STREAM_RESTART = 4,	/* Set when we want to restart the stream (after +	JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */ +	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after  	                                   SASL or TLS). */ -	JFLAG_WAIT_SESSION = 8,		/* Set if we sent a <session> tag and need a reply +	JFLAG_WAIT_SESSION = 8,	        /* Set if we sent a <session> tag and need a reply  	                                   before we continue. */ -	JFLAG_WAIT_BIND = 16,		/* ... for <bind> tag. */ -	JFLAG_WANT_TYPING = 32,		/* Set if we ever sent a typing notification, this +	JFLAG_WAIT_BIND = 16,           /* ... for <bind> tag. */ +	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this  	                                   activates all XEP-85 related code. */ +	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */  } jabber_flags_t;  typedef enum @@ -49,12 +50,12 @@ typedef enum  	                                   sure it gets sent only once. */  	JBFLAG_DOES_XEP85 = 2,		/* Set this when the resource seems to support  	                                   XEP85 (typing notification shite). */ +	JBFLAG_IS_CHATROOM = 4,		/* It's convenient to use this JID thingy for +	                                   groupchat state info too. */ +	JBFLAG_IS_ANONYMOUS = 8,	/* For anonymous chatrooms, when we don't have +	                                   have a real JID. */  } jabber_buddy_flags_t; -#define JABBER_PORT_DEFAULT "5222" -#define JABBER_PORT_MIN 5220 -#define JABBER_PORT_MAX 5229 -  struct jabber_data  {  	struct im_connection *ic; @@ -100,6 +101,9 @@ struct jabber_buddy  	char *full_jid;  	char *resource; +	char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */ +	               /* otherwise something similar to the conference JID. */ +	  	int priority;  	struct jabber_away_state *away_state;  	char *away_message; @@ -110,6 +114,20 @@ struct jabber_buddy  	struct jabber_buddy *next;  }; +struct jabber_chat +{ +	int flags; +	char *name; +	char *my_full_jid; /* Separate copy because of case sensitivity. */ +	struct jabber_buddy *me; +}; + +#define JABBER_XMLCONSOLE_HANDLE "xmlconsole" + +#define JABBER_PORT_DEFAULT "5222" +#define JABBER_PORT_MIN 5220 +#define JABBER_PORT_MAX 5229 +  /* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the     first one should be used, but when storing a packet in the cache, a     "special" kind of ID is assigned to make it easier later to figure out @@ -131,8 +149,11 @@ struct jabber_buddy  #define XMLNS_VERSION      "jabber:iq:version"                  /* XEP-0092 */  #define XMLNS_TIME         "jabber:iq:time"                     /* XEP-0090 */  #define XMLNS_VCARD        "vcard-temp"                         /* XEP-0054 */ +#define XMLNS_DELAY        "jabber:x:delay"                     /* XEP-0091 */  #define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"  /* 0085 */  #define XMLNS_DISCOVER     "http://jabber.org/protocol/disco#info"  /* 0030 */ +#define XMLNS_MUC          "http://jabber.org/protocol/muc"     /* XEP-0045 */ +#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"/* XEP-0045 */  /* iq.c */  xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); @@ -163,18 +184,29 @@ void jabber_cache_clean( struct im_connection *ic );  const struct jabber_away_state *jabber_away_state_by_code( char *code );  const struct jabber_away_state *jabber_away_state_by_name( char *name );  void jabber_buddy_ask( struct im_connection *ic, char *handle ); -char *jabber_normalize( char *orig ); +char *jabber_normalize( const char *orig );  typedef enum  {  	GET_BUDDY_CREAT = 1,	/* Try to create it, if necessary. */ -	GET_BUDDY_EXACT = 2,	/* Get an exact message (only makes sense with bare JIDs). */ +	GET_BUDDY_EXACT = 2,	/* Get an exact match (only makes sense with bare JIDs). */ +	GET_BUDDY_FIRST = 4,	/* No selection, simply get the first resource for this JID. */  } get_buddy_flags_t; +struct jabber_error +{ +	char *code, *text, *type; +}; +  struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid );  struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags ); +struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid, get_buddy_flags_t flags );  int jabber_buddy_remove( struct im_connection *ic, char *full_jid );  int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid ); +struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name ); +time_t jabber_get_timestamp( struct xt_node *xt ); +struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns ); +void jabber_error_free( struct jabber_error *err );  extern const struct jabber_away_state jabber_away_state_list[]; @@ -192,4 +224,12 @@ xt_status sasl_pkt_challenge( struct xt_node *node, gpointer data );  xt_status sasl_pkt_result( struct xt_node *node, gpointer data );  gboolean sasl_supported( struct im_connection *ic ); +/* conference.c */ +struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *nick, char *password ); +void jabber_chat_free( struct groupchat *c ); +int jabber_chat_msg( struct groupchat *ic, char *message, int flags ); +int jabber_chat_leave( struct groupchat *c, const char *reason ); +void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); +void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node ); +  #endif diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 3c0e71f4..56491c4f 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -47,7 +47,7 @@ char *set_eval_priority( set_t *set, char *value )  		   convenient, they have one disadvantage: If I would just  		   call p_s_u() now to send the new prio setting, it would  		   send the old setting because the set->value gets changed -		   when the eval returns a non-NULL value. +		   after the (this) eval returns a non-NULL value.  		   So now I can choose between implementing post-set  		   functions next to evals, or just do this little hack: */ @@ -128,7 +128,7 @@ struct xt_node *jabber_make_error_packet( struct xt_node *orig, char *err_cond,  /* Cache a node/packet for later use. Mainly useful for IQ packets if you need     them when you receive the response. Use this BEFORE sending the packet so -   it'll get a new id= tag, and do NOT free() the packet after writing it! */ +   it'll get a new id= tag, and do NOT free() the packet after sending it! */  void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_cache_event func )  {  	struct jabber_data *jd = ic->proto_data; @@ -251,7 +251,7 @@ void jabber_buddy_ask( struct im_connection *ic, char *handle )  }  /* Returns a new string. Don't leak it! */ -char *jabber_normalize( char *orig ) +char *jabber_normalize( const char *orig )  {  	int len, i;  	char *new; @@ -319,6 +319,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_  	}  	else  	{ +		/* Keep in mind that full_jid currently isn't really +		   a full JID... */  		new->bare_jid = g_strdup( full_jid );  		g_hash_table_insert( jd->buddies, new->bare_jid, new );  	} @@ -332,7 +334,8 @@ struct jabber_buddy *jabber_buddy_add( struct im_connection *ic, char *full_jid_  	else  	{  		/* Let's waste some more bytes of RAM instead of to make -		   memory management a total disaster here.. */ +		   memory management a total disaster here. And it saves +		   me one g_free() call in this function. :-P */  		new->full_jid = full_jid;  	} @@ -352,6 +355,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  	if( ( s = strchr( jid, '/' ) ) )  	{ +		int none_found = 0; +		  		*s = 0;  		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )  		{ @@ -369,8 +374,16 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  						break;  			}  		} +		else +		{ +			/* This hack is there to make sure that O_CREAT will +			   work if there's already another resouce present +			   for this JID, even if it's an unknown buddy. This +			   is done to handle conferences properly. */ +			none_found = 1; +		} -		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && imcb_find_buddy( ic, jid ) ) +		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) )  		{  			*s = '/';  			bud = jabber_buddy_add( ic, jid ); @@ -417,6 +430,38 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  	}  } +/* I'm keeping a separate ext_jid attribute to save a JID that makes sense +   to export to BitlBee. This is mainly for groupchats right now. It's +   a bit of a hack, but I just think having the user nickname in the hostname +   part of the hostmask doesn't look nice on IRC. Normally you can convert +   a normal JID to ext_jid by swapping the part before and after the / and +   replacing the / with a =. But there should be some stripping (@s are +   allowed in Jabber nicks...). */ +struct jabber_buddy *jabber_buddy_by_ext_jid( struct im_connection *ic, char *jid_, get_buddy_flags_t flags ) +{ +	struct jabber_buddy *bud; +	char *s, *jid; +	 +	jid = jabber_normalize( jid_ ); +	 +	if( ( s = strchr( jid, '=' ) ) == NULL ) +		return NULL; +	 +	for( bud = jabber_buddy_by_jid( ic, s + 1, GET_BUDDY_FIRST ); bud; bud = bud->next ) +	{ +		/* Hmmm, could happen if not all people in the chat are anonymized? */ +		if( bud->ext_jid == NULL ) +			continue; +		 +		if( strcmp( bud->ext_jid, jid ) == 0 ) +			break; +	} +	 +	g_free( jid ); +	 +	return bud; +} +  /* Remove one specific full JID from our list. Use this when a buddy goes     off-line (because (s)he can still be online from a different location.     XXX: See above, we should accept bare JIDs too... */ @@ -440,6 +485,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )  		{  			g_hash_table_remove( jd->buddies, bud->bare_jid );  			g_free( bud->bare_jid ); +			g_free( bud->ext_jid );  			g_free( bud->full_jid );  			g_free( bud->away_message );  			g_free( bud ); @@ -472,6 +518,7 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )  					   item, because we're removing the first. */  					g_hash_table_replace( jd->buddies, bi->bare_jid, bi->next ); +				g_free( bi->ext_jid );  				g_free( bi->full_jid );  				g_free( bi->away_message );  				g_free( bi ); @@ -494,39 +541,144 @@ int jabber_buddy_remove( struct im_connection *ic, char *full_jid_ )  /* Remove a buddy completely; removes all resources that belong to the     specified bare JID. Use this when removing someone from the contact     list, for example. */ -int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid_ ) +int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid )  {  	struct jabber_data *jd = ic->proto_data;  	struct jabber_buddy *bud, *next; -	char *bare_jid; -	if( strchr( bare_jid_, '/' ) ) +	if( strchr( bare_jid, '/' ) )  		return 0; -	bare_jid = jabber_normalize( bare_jid_ ); -	 -	if( ( bud = g_hash_table_lookup( jd->buddies, bare_jid ) ) ) +	if( ( bud = jabber_buddy_by_jid( ic, bare_jid, GET_BUDDY_FIRST ) ) )  	{  		/* Most important: Remove the hash reference. We don't know  		   this buddy anymore. */  		g_hash_table_remove( jd->buddies, bud->bare_jid ); +		g_free( bud->bare_jid );  		/* Deallocate the linked list of resources. */  		while( bud )  		{ +			/* ext_jid && anonymous means that this buddy is +			   specific to one groupchat (the one we're +			   currently cleaning up) so it can be deleted +			   completely. */ +			if( bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS ) +				imcb_remove_buddy( ic, bud->ext_jid, NULL ); +			  			next = bud->next; +			g_free( bud->ext_jid );  			g_free( bud->full_jid );  			g_free( bud->away_message );  			g_free( bud );  			bud = next;  		} -		g_free( bare_jid );  		return 1;  	}  	else  	{ -		g_free( bare_jid );  		return 0;  	}  } + +struct groupchat *jabber_chat_by_name( struct im_connection *ic, const char *name ) +{ +	char *normalized = jabber_normalize( name ); +	struct groupchat *ret; +	struct jabber_chat *jc; +	 +	for( ret = ic->groupchats; ret; ret = ret->next ) +	{ +		jc = ret->data; +		if( strcmp( normalized, jc->name ) == 0 ) +			break; +	} +	g_free( normalized ); +	 +	return ret; +} + +time_t jabber_get_timestamp( struct xt_node *xt ) +{ +	struct tm tp, utc; +	struct xt_node *c; +	time_t res, tres; +	char *s = NULL; +	 +	for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next ) +	{ +		if( ( s = xt_find_attr( c, "xmlns" ) ) && strcmp( s, XMLNS_DELAY ) == 0 ) +			break; +	} +	 +	if( !c || !( s = xt_find_attr( c, "stamp" ) ) ) +		return 0; +	 +	memset( &tp, 0, sizeof( tp ) ); +	if( sscanf( s, "%4d%2d%2dT%2d:%2d:%2d", &tp.tm_year, &tp.tm_mon, &tp.tm_mday, +	                                        &tp.tm_hour, &tp.tm_min, &tp.tm_sec ) != 6 ) +		return 0; +	 +	tp.tm_year -= 1900; +	tp.tm_mon --; +	tp.tm_isdst = -1; /* GRRRRRRRRRRR */ +	 +	res = mktime( &tp ); +	/* Problem is, mktime() just gave us the GMT timestamp for the +	   given local time... While the given time WAS NOT local. So +	   we should fix this now. +	 +	   Now I could choose between messing with environment variables +	   (kludgy) or using timegm() (not portable)... Or doing the +	   following, which I actually prefer... */ +	gmtime_r( &res, &utc ); +	utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */ +	if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min ) +		/* Sweet! We're in UTC right now... */ +		return res; +	 +	tres = mktime( &utc ); +	res += res - tres; +	 +	/* Yes, this is a hack. And it will go wrong around DST changes. +	   BUT this is more likely to be threadsafe than messing with +	   environment variables, and possibly more portable... */ +	 +	return res; +} + +struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns ) +{ +	struct jabber_error *err = g_new0( struct jabber_error, 1 ); +	struct xt_node *c; +	char *s; +	 +	err->type = xt_find_attr( node, "type" ); +	 +	for( c = node->children; c; c = c->next ) +	{ +		if( !( s = xt_find_attr( c, "xmlns" ) ) || +		    strcmp( s, xmlns ) != 0 ) +			continue; +		 +		if( strcmp( c->name, "text" ) != 0 ) +		{ +			err->code = c->name; +		} +		/* Only use the text if it doesn't have an xml:lang attribute, +		   if it's empty or if it's set to something English. */ +		else if( !( s = xt_find_attr( c, "xml:lang" ) ) || +		         !*s || strncmp( s, "en", 2 ) == 0 ) +		{ +			err->text = c->text; +		} +	} +	 +	return err; +} + +void jabber_error_free( struct jabber_error *err ) +{ +	g_free( err ); +} diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c index 19edbdfd..fab62a91 100644 --- a/protocols/jabber/message.c +++ b/protocols/jabber/message.c @@ -29,25 +29,33 @@ xt_status jabber_pkt_message( struct xt_node *node, gpointer data )  	char *from = xt_find_attr( node, "from" );  	char *type = xt_find_attr( node, "type" );  	struct xt_node *body = xt_find_node( node->children, "body" ), *c; +	struct jabber_buddy *bud = NULL;  	char *s; +	if( !from ) +		return XT_HANDLED; /* Consider this packet corrupted. */ +	 +	bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ); +	  	if( type && strcmp( type, "error" ) == 0 )  	{  		/* Handle type=error packet. */  	} -	else if( type && strcmp( type, "groupchat" ) == 0 ) +	else if( type && from && strcmp( type, "groupchat" ) == 0 )  	{ -		/* TODO! */ +		jabber_chat_pkt_message( ic, bud, node );  	}  	else /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */  	{ -		struct jabber_buddy *bud = NULL;  		GString *fullmsg = g_string_new( "" );  		if( ( s = strchr( from, '/' ) ) )  		{ -			if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) ) +			if( bud ) +			{  				bud->last_act = time( NULL ); +				from = bud->ext_jid ? : bud->bare_jid; +			}  			else  				*s = 0; /* We need to generate a bare JID now. */  		} @@ -75,26 +83,31 @@ xt_status jabber_pkt_message( struct xt_node *node, gpointer data )  			fullmsg = g_string_append( fullmsg, body->text );  		if( fullmsg->len > 0 ) -			imcb_buddy_msg( ic, bud ? bud->bare_jid : from, fullmsg->str, 0, 0 ); +			imcb_buddy_msg( ic, from, fullmsg->str, +			                0, jabber_get_timestamp( node ) );  		g_string_free( fullmsg, TRUE );  		/* Handling of incoming typing notifications. */ -		if( xt_find_node( node->children, "composing" ) ) +		if( bud == NULL ) +		{ +			/* Can't handle these for unknown buddies. */ +		} +		else if( xt_find_node( node->children, "composing" ) )  		{  			bud->flags |= JBFLAG_DOES_XEP85; -			imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_TYPING ); +			imcb_buddy_typing( ic, from, OPT_TYPING );  		}  		/* No need to send a "stopped typing" signal when there's a message. */  		else if( xt_find_node( node->children, "active" ) && ( body == NULL ) )  		{  			bud->flags |= JBFLAG_DOES_XEP85; -			imcb_buddy_typing( ic, bud ? bud->bare_jid : from, 0 ); +			imcb_buddy_typing( ic, from, 0 );  		}  		else if( xt_find_node( node->children, "paused" ) )  		{  			bud->flags |= JBFLAG_DOES_XEP85; -			imcb_buddy_typing( ic, bud ? bud->bare_jid : from, OPT_THINKING ); +			imcb_buddy_typing( ic, from, OPT_THINKING );  		}  		if( s ) diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 49c66a3d..71a044b5 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -30,15 +30,22 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	char *type = xt_find_attr( node, "type" );	/* NULL should mean the person is online. */  	struct xt_node *c;  	struct jabber_buddy *bud; +	int is_chat = 0, is_away = 0;  	char *s;  	if( !from )  		return XT_HANDLED; +	if( ( s = strchr( from, '/' ) ) ) +	{ +		*s = 0; +		if( jabber_chat_by_name( ic, from ) ) +			is_chat = 1; +		*s = '/'; +	} +	  	if( type == NULL )  	{ -		int is_away = 0; -		  		if( !( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT ) ) )  		{  			if( set_getbool( &ic->irc->set, "debug" ) ) @@ -71,30 +78,55 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  		else  			bud->priority = 0; -		if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) ) +		if( is_chat ) +			jabber_chat_pkt_presence( ic, bud, node ); +		else if( bud == jabber_buddy_by_jid( ic, bud->bare_jid, 0 ) )  			imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away,  			                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL,  			                   bud->away_message );  	}  	else if( strcmp( type, "unavailable" ) == 0 )  	{ -		if( jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) == NULL ) +		if( ( bud = jabber_buddy_by_jid( ic, from, GET_BUDDY_EXACT ) ) == NULL )  		{  			if( set_getbool( &ic->irc->set, "debug" ) )  				imcb_log( ic, "WARNING: Received presence information from unknown JID: %s", from );  			return XT_HANDLED;  		} +		/* Handle this before we delete the JID. */ +		if( is_chat ) +		{ +			jabber_chat_pkt_presence( ic, bud, node ); +		} +		  		jabber_buddy_remove( ic, from ); -		if( ( s = strchr( from, '/' ) ) ) +		if( is_chat ) +		{ +			/* Nothing else to do for now? */ +		} +		else if( ( s = strchr( from, '/' ) ) )  		{  			*s = 0; -			/* Only count this as offline if there's no other resource -			   available anymore. */ -			if( jabber_buddy_by_jid( ic, from, 0 ) == NULL ) +			/* If another resource is still available, send its presence +			   information. */ +			if( ( bud = jabber_buddy_by_jid( ic, from, 0 ) ) ) +			{ +				if( bud->away_state && ( *bud->away_state->code == 0 || +				    strcmp( bud->away_state->code, "chat" ) == 0 ) ) +					is_away = OPT_AWAY; +				 +				imcb_buddy_status( ic, bud->bare_jid, OPT_LOGGED_IN | is_away, +				                   ( is_away && bud->away_state ) ? bud->away_state->full_name : NULL, +				                   bud->away_message ); +			} +			else +			{ +				/* Otherwise, count him/her as offline now. */  				imcb_buddy_status( ic, from, 0, NULL, NULL ); +			}  			*s = '/';  		} @@ -125,7 +157,17 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	}  	else if( strcmp( type, "error" ) == 0 )  	{ -		/* What to do with it? */ +		struct jabber_error *err; +		 +		if( ( c = xt_find_node( node->children, "error" ) ) ) +		{ +			err = jabber_error_parse( c, XMLNS_STANZA_ERROR ); +			imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name, +			            err->code, err->text ? ": " : "", +			            err->text ? err->text : "" ); +			jabber_error_free( err ); +		} +		/* What else to do with it? */  	}  	return XT_HANDLED; @@ -139,6 +181,7 @@ int presence_send_update( struct im_connection *ic )  	struct xt_node *node;  	char *show = jd->away_state->code;  	char *status = jd->away_message; +	struct groupchat *c;  	int st;  	node = jabber_make_packet( "presence", NULL, NULL, NULL ); @@ -150,6 +193,16 @@ int presence_send_update( struct im_connection *ic )  	st = jabber_write_packet( ic, node ); +	/* Have to send this update to all groupchats too, the server won't +	   do this automatically. */ +	for( c = ic->groupchats; c && st; c = c->next ) +	{ +		struct jabber_chat *jc = c->data; +		 +		xt_add_attr( node, "to", jc->my_full_jid ); +		st = jabber_write_packet( ic, node ); +	} +	  	xt_free_node( node );  	return st;  } diff --git a/protocols/jabber/xmltree.c b/protocols/jabber/xmltree.c index 7e74cccb..62549eb5 100644 --- a/protocols/jabber/xmltree.c +++ b/protocols/jabber/xmltree.c @@ -443,7 +443,7 @@ void xt_free( struct xt_parser *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, char *name ) +struct xt_node *xt_find_node( struct xt_node *node, const char *name )  {  	while( node )  	{ @@ -456,7 +456,7 @@ struct xt_node *xt_find_node( struct xt_node *node, char *name )  	return node;  } -char *xt_find_attr( struct xt_node *node, char *key ) +char *xt_find_attr( struct xt_node *node, const char *key )  {  	int i; @@ -525,7 +525,7 @@ void xt_add_child( struct xt_node *parent, struct xt_node *child )  	}  } -void xt_add_attr( struct xt_node *node, char *key, char *value ) +void xt_add_attr( struct xt_node *node, const char *key, const char *value )  {  	int i; @@ -552,7 +552,7 @@ void xt_add_attr( struct xt_node *node, char *key, char *value )  	node->attr[i].value = g_strdup( value );  } -int xt_remove_attr( struct xt_node *node, char *key ) +int xt_remove_attr( struct xt_node *node, const char *key )  {  	int i, last; diff --git a/protocols/jabber/xmltree.h b/protocols/jabber/xmltree.h index 70850c1d..b8b61641 100644 --- a/protocols/jabber/xmltree.h +++ b/protocols/jabber/xmltree.h @@ -86,12 +86,12 @@ 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, char *name ); -char *xt_find_attr( struct xt_node *node, char *key ); +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, char *key, char *value ); -int xt_remove_attr( struct xt_node *node, char *key ); +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 | 
