diff options
| -rw-r--r-- | Makefile | 1 | ||||
| -rw-r--r-- | bitlbee.c | 6 | ||||
| -rw-r--r-- | bitlbee.h | 9 | ||||
| -rw-r--r-- | conf.c | 45 | ||||
| -rw-r--r-- | crypting.c | 5 | ||||
| -rw-r--r-- | debian/watch | 2 | ||||
| -rw-r--r-- | help.c | 66 | ||||
| -rw-r--r-- | help.h | 5 | ||||
| -rw-r--r-- | irc.c | 16 | ||||
| -rw-r--r-- | irc.h | 1 | ||||
| -rw-r--r-- | irc_commands.c | 4 | ||||
| -rw-r--r-- | lib/Makefile | 2 | ||||
| -rw-r--r-- | protocols/Makefile | 2 | ||||
| -rw-r--r-- | protocols/jabber/Makefile | 2 | ||||
| -rw-r--r-- | protocols/jabber/conference.c | 11 | ||||
| -rw-r--r-- | protocols/jabber/iq.c | 3 | ||||
| -rw-r--r-- | protocols/jabber/jabber.h | 20 | ||||
| -rw-r--r-- | protocols/jabber/jabber_util.c | 16 | ||||
| -rw-r--r-- | protocols/jabber/presence.c | 34 | ||||
| -rw-r--r-- | protocols/jabber/sasl.c | 27 | ||||
| -rw-r--r-- | protocols/msn/Makefile | 2 | ||||
| -rw-r--r-- | protocols/msn/ns.c | 2 | ||||
| -rw-r--r-- | protocols/msn/sb.c | 2 | ||||
| -rw-r--r-- | protocols/oscar/Makefile | 2 | ||||
| -rw-r--r-- | protocols/oscar/oscar.c | 10 | ||||
| -rw-r--r-- | protocols/yahoo/Makefile | 2 | ||||
| -rw-r--r-- | root_commands.c | 11 | ||||
| -rw-r--r-- | storage_text.c | 8 | ||||
| -rw-r--r-- | storage_xml.c | 4 | ||||
| -rw-r--r-- | tests/Makefile | 4 | ||||
| -rw-r--r-- | tests/check.c | 4 | ||||
| -rw-r--r-- | tests/check_arc.c | 5 | ||||
| -rw-r--r-- | tests/check_jabber_sasl.c | 117 | ||||
| -rw-r--r-- | unix.c | 7 | 
34 files changed, 332 insertions, 125 deletions
| @@ -49,7 +49,6 @@ distclean: clean $(subdirs)  check: all  	$(MAKE) -C tests -lcov:  gcov: check  	gcov *.c @@ -47,7 +47,11 @@ int bitlbee_daemon_init()  	memset( &hints, 0, sizeof( hints ) );  	hints.ai_family = PF_UNSPEC;  	hints.ai_socktype = SOCK_STREAM; -	hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; +	hints.ai_flags = AI_PASSIVE +#ifdef AI_ADDRCONFIG +	               | AI_ADDRCONFIG +#endif +	;  	i = getaddrinfo( global.conf->iface, global.conf->port, &hints, &addrinfo_bind );  	if( i ) @@ -98,6 +98,12 @@  #define F_OK 0  #endif +#ifndef G_GNUC_MALLOC +/* Doesn't exist in GLib <=2.4 while everything else in BitlBee should +   work with it, so let's fake this one. */ +#define G_GNUC_MALLOC +#endif +  #define _( x ) x  #define ROOT_NICK "root" @@ -115,8 +121,6 @@  #define HELP_FILE VARDIR "help.txt"  #define CONF_FILE_DEF ETCDIR "bitlbee.conf" -extern char *CONF_FILE; -  #include "irc.h"  #include "storage.h"  #include "set.h" @@ -139,6 +143,7 @@ typedef struct global {  	int listen_socket;  	gint listen_watch_source_id;  	help_t *help; +	char *conf_file;  	conf_t *conf;  	GList *storage; /* The first backend in the list will be used for saving */  	char *helpfile; @@ -35,14 +35,12 @@  #include "proxy.h" -char *CONF_FILE; -  static int conf_loadini( conf_t *conf, char *file );  conf_t *conf_load( int argc, char *argv[] )  {  	conf_t *conf; -	int opt, i; +	int opt, i, config_missing = 0;  	conf = g_new0( conf_t, 1 ); @@ -66,15 +64,17 @@ conf_t *conf_load( int argc, char *argv[] )  	conf->user = NULL;  	proxytype = 0; -	i = conf_loadini( conf, CONF_FILE ); +	i = conf_loadini( conf, global.conf_file );  	if( i == 0 )  	{ -		fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", CONF_FILE ); -		return( NULL ); +		fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file ); +		return NULL;  	}  	else if( i == -1 )  	{ -		fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE ); +		config_missing ++; +		/* Whine after parsing the options if there was no -c pointing +		   at a *valid* configuration file. */  	}  	while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 ) @@ -106,16 +106,16 @@ conf_t *conf_load( int argc, char *argv[] )  			conf->runmode = RUNMODE_FORKDAEMON;  		else if( opt == 'c' )  		{ -			if( strcmp( CONF_FILE, optarg ) != 0 ) +			if( strcmp( global.conf_file, optarg ) != 0 )  			{ -				g_free( CONF_FILE ); -				CONF_FILE = g_strdup( optarg ); +				g_free( global.conf_file ); +				global.conf_file = g_strdup( optarg );  				g_free( conf );  				/* Re-evaluate arguments. Don't use this option twice,   				   you'll end up in an infinite loop! Hope this trick  				   works with all libcs BTW.. */  				optind = 1; -				return( conf_load( argc, argv ) ); +				return conf_load( argc, argv );  			}  		}  		else if( opt == 'd' ) @@ -143,7 +143,7 @@ conf_t *conf_load( int argc, char *argv[] )  			        "  -c  Load alternative configuration file\n"  			        "  -d  Specify alternative user configuration directory\n"  			        "  -h  Show this help page.\n" ); -			return( NULL ); +			return NULL;  		}  		else if( opt == 'R' )  		{ @@ -169,7 +169,10 @@ conf_t *conf_load( int argc, char *argv[] )  		conf->configdir = s;  	} -	return( conf ); +	if( config_missing ) +		fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file ); +	 +	return conf;  }  static int conf_loadini( conf_t *conf, char *file ) @@ -178,7 +181,7 @@ static int conf_loadini( conf_t *conf, char *file )  	int i;  	ini = ini_open( file ); -	if( ini == NULL ) return( -1 ); +	if( ini == NULL ) return -1;  	while( ini_read( ini ) )  	{  		if( g_strcasecmp( ini->section, "settings" ) == 0 ) @@ -261,7 +264,7 @@ static int conf_loadini( conf_t *conf, char *file )  				if( sscanf( ini->value, "%d", &i ) != 1 )  				{  					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); -					return( 0 ); +					return 0;  				}  				conf->ping_interval = i;  			} @@ -270,7 +273,7 @@ static int conf_loadini( conf_t *conf, char *file )  				if( sscanf( ini->value, "%d", &i ) != 1 )  				{  					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); -					return( 0 ); +					return 0;  				}  				conf->ping_timeout = i;  			} @@ -282,7 +285,7 @@ static int conf_loadini( conf_t *conf, char *file )  				{  					fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );  					g_free( url ); -					return( 0 ); +					return 0;  				}  				strncpy( proxyhost, url->host, sizeof( proxyhost ) ); @@ -306,7 +309,7 @@ static int conf_loadini( conf_t *conf, char *file )  			else  			{  				fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key ); -				return( 0 ); +				return 0;  				/* For now just ignore unknown keys... */  			}  		} @@ -314,19 +317,19 @@ static int conf_loadini( conf_t *conf, char *file )  		{  			fprintf( stderr, "Error: Unknown section [%s] in configuration file. "  			                 "BitlBee configuration must be put in a [settings] section!\n", ini->section ); -			return( 0 ); +			return 0;  		}  	}  	ini_close( ini ); -	return( 1 ); +	return 1;  }  void conf_loaddefaults( irc_t *irc )  {  	ini_t *ini; -	ini = ini_open( CONF_FILE ); +	ini = ini_open( global.conf_file );  	if( ini == NULL ) return;  	while( ini_read( ini ) )  	{ @@ -28,10 +28,7 @@     included if CRYPTING_MAIN is defined. Or just do "make decode" and     the programs will be built. */ -#include <string.h> -#include <stdio.h> -#include <stdlib.h> -#include <glib.h> +#include <bitlbee.h>  #include "md5.h"  #include "crypting.h" diff --git a/debian/watch b/debian/watch new file mode 100644 index 00000000..66ab4504 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=2 +http://get.bitlbee.org/src/bitlbee-(.*).tar.gz @@ -70,21 +70,20 @@ help_t *help_init( help_t **help, const char *helpfile )  		if( !( t = strstr( s, "\n%\n" ) ) || s[0] != '?' )  		{  			/* FIXME: Clean up */ -//			help_close( *help ); -			*help = NULL; +			help_free( help );  			g_free( s ); -			return( NULL ); +			return NULL;  		}  		i = strchr( s, '\n' ) - s; -		if( h->string ) +		if( h->title )  		{  			h = h->next = g_new0( help_t, 1 );  		} -		h->string = g_new ( char, i ); +		h->title = g_new ( char, i ); -		strncpy( h->string, s + 1, i - 1 ); -		h->string[i-1] = 0; +		strncpy( h->title, s + 1, i - 1 ); +		h->title[i-1] = 0;  		h->fd = (*help)->fd;  		h->offset.file_offset = lseek( h->fd, 0, SEEK_CUR ) - buflen + i + 1;  		h->length = t - s - i - 1; @@ -102,7 +101,31 @@ help_t *help_init( help_t **help, const char *helpfile )  	return( *help );  } -char *help_get( help_t **help, char *string ) +void help_free( help_t **help ) +{ +	help_t *h, *oh; +	int last_fd = -1; /* Weak de-dupe */ +	 +	if( help == NULL || *help == NULL ) +		return; +	 +	h = *help; +	while( h ) +	{ +		if( h->fd != last_fd ) +		{ +			close( h->fd ); +			last_fd = h->fd; +		} +		g_free( h->title ); +		h = (oh=h)->next; +		g_free( oh ); +	} +	 +	*help = NULL; +} + +char *help_get( help_t **help, char *title )  {  	time_t mtime;  	struct stat stat[1]; @@ -110,28 +133,29 @@ char *help_get( help_t **help, char *string )  	for( h = *help; h; h = h->next )  	{ -		if( h->string != NULL &&  -			g_strcasecmp( h->string, string ) == 0 )  +		if( h->title != NULL && g_strcasecmp( h->title, title ) == 0 )  			break;  	}  	if( h && h->length > 0 )  	{  		char *s = g_new( char, h->length + 1 ); -		if( fstat( h->fd, stat ) != 0 ) -		{ -			g_free( h ); -			*help = NULL; -			return NULL; -		} -		mtime = stat->st_mtime; -		 -		if( mtime > h->mtime ) -			return NULL; -		  		s[h->length] = 0;  		if( h->fd >= 0 )  		{ +			if( fstat( h->fd, stat ) != 0 ) +			{ +				g_free( s ); +				return NULL; +			} +			mtime = stat->st_mtime; +		 +			if( mtime > h->mtime ) +			{ +				g_free( s ); +				return NULL; +			} +			  			lseek( h->fd, h->offset.file_offset, SEEK_SET );  			read( h->fd, s, h->length );  		} @@ -36,13 +36,14 @@ typedef struct help  {  	int fd;  	time_t mtime; -	char *string; +	char *title;  	help_off_t offset;  	int length;  	struct help *next;  } help_t;  G_GNUC_MALLOC help_t *help_init( help_t **help, const char *helpfile ); -char *help_get( help_t **help, char *string ); +void help_free( help_t **help ); +char *help_get( help_t **help, char *title );  #endif @@ -76,7 +76,7 @@ irc_t *irc_new( int fd )  		char buf[NI_MAXHOST+1];  		if( getnameinfo( (struct sockaddr *) &sock, socklen, buf, -		                 NI_MAXHOST, NULL, -1, 0 ) == 0 ) +		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )  		{  			irc->myhost = g_strdup( ipv6_unwrap( buf ) );  		} @@ -87,7 +87,7 @@ irc_t *irc_new( int fd )  		char buf[NI_MAXHOST+1];  		if( getnameinfo( (struct sockaddr *)&sock, socklen, buf, -		                 NI_MAXHOST, NULL, -1, 0 ) == 0 ) +		                 NI_MAXHOST, NULL, 0, 0 ) == 0 )  		{  			irc->host = g_strdup( ipv6_unwrap( buf ) );  		} @@ -198,7 +198,6 @@ void irc_free(irc_t * irc)  {  	account_t *account;  	user_t *user, *usertmp; -	help_t *helpnode, *helpnodetmp;  	log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); @@ -275,17 +274,6 @@ void irc_free(irc_t * irc)  	g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);  	g_hash_table_destroy(irc->watches); -	if (irc->help != NULL) { -		helpnode = irc->help; -		while (helpnode != NULL) { -			g_free(helpnode->string); -			 -			helpnodetmp = helpnode; -			helpnode = helpnode->next; -			g_free(helpnodetmp); -		} -	} -	  	otr_free(irc->otr);  	g_free(irc); @@ -90,7 +90,6 @@ typedef struct irc  	GHashTable *userhash;  	GHashTable *watches;  	struct __NICK *nicks; -	struct help *help;  	struct set *set;  	gint r_watch_source_id; diff --git a/irc_commands.c b/irc_commands.c index 65f0d6c6..68db4617 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -555,7 +555,7 @@ static void irc_cmd_completions( irc_t *irc, char **cmd )  		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command );  	for( h = global.help; h; h = h->next ) -		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->string ); +		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title );  	for( s = irc->set; s; s = s->next )  		irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); @@ -570,7 +570,7 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd )  	else  		ipc_to_master( cmd ); -	irc_reply( irc, 382, "%s :Rehashing", CONF_FILE ); +	irc_reply( irc, 382, "%s :Rehashing", global.conf_file );  }  static const command_t irc_commands[] = { diff --git a/lib/Makefile b/lib/Makefile index a79f7c4c..975deceb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: lib.o  check: all -lcov: +lcov: check  gcov:  	gcov *.c diff --git a/protocols/Makefile b/protocols/Makefile index f7d76e0f..18d79e8d 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -26,7 +26,7 @@ LFLAGS += -r  # [SH] Phony targets  all: protocols.o  check: all -lcov: +lcov: check  gcov:  	gcov *.c diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index e042f812..3ce78127 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: jabber_mod.o  check: all -lcov: +lcov: check  gcov:   	gcov *.c diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 515194fc..79fdd053 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -36,6 +36,8 @@ struct groupchat *jabber_chat_join( struct im_connection *ic, char *room, char *  	node = xt_new_node( "x", NULL, NULL );  	xt_add_attr( node, "xmlns", XMLNS_MUC );  	node = jabber_make_packet( "presence", NULL, roomjid, node ); +	if( password ) +		xt_add_child( node, xt_new_node( "password", password, NULL ) );  	jabber_cache_add( ic, node, jabber_chat_join_failed );  	if( !jabber_write_packet( ic, node ) ) @@ -122,6 +124,8 @@ int jabber_chat_msg( struct groupchat *c, char *message, int flags )  	struct jabber_chat *jc = c->data;  	struct xt_node *node; +	jc->flags |= JCFLAG_MESSAGE_SENT; +	  	node = xt_new_node( "body", message, NULL );  	node = jabber_make_packet( "message", "groupchat", jc->name, node ); @@ -294,10 +298,11 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud  {  	struct xt_node *subject = xt_find_node( node->children, "subject" );  	struct xt_node *body = xt_find_node( node->children, "body" ); -	struct groupchat *chat = NULL; +	struct groupchat *chat = bud ? jabber_chat_by_jid( ic, bud->bare_jid ) : NULL; +	struct jabber_chat *jc = chat ? chat->data : NULL;  	char *s; -	if( bud == NULL ) +	if( bud == NULL || ( jc && ~jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me ) )  	{  		char *nick; @@ -345,7 +350,7 @@ void jabber_chat_pkt_message( struct im_connection *ic, struct jabber_buddy *bud  		return;  	} -	else if( ( chat = jabber_chat_by_jid( ic, bud->bare_jid ) ) == NULL ) +	else if( chat == NULL )  	{  		/* How could this happen?? We could do kill( self, 11 )  		   now or just wait for the OS to do it. :-) */ diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c index e1bab29e..c88bc0b0 100644 --- a/protocols/jabber/iq.c +++ b/protocols/jabber/iq.c @@ -91,7 +91,8 @@ xt_status jabber_pkt_iq( struct xt_node *node, gpointer data )  		}  		else if( strcmp( s, XMLNS_DISCOVER ) == 0 )  		{ -			const char *features[] = { XMLNS_VERSION, +			const char *features[] = { XMLNS_DISCOVER, +			                           XMLNS_VERSION,  			                           XMLNS_TIME,  			                           XMLNS_CHATSTATES,  			                           XMLNS_MUC, diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index cf0f8e6a..1ff0e8dd 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -48,16 +48,22 @@ typedef enum  typedef enum  { -	JBFLAG_PROBED_XEP85 = 1,	/* Set this when we sent our probe packet to make +	JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make  	                                   sure it gets sent only once. */ -	JBFLAG_DOES_XEP85 = 2,		/* Set this when the resource seems to support +	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 +	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 +	JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have  	                                   have a real JID. */  } jabber_buddy_flags_t; +typedef enum +{ +	JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so +	                                   we can detect echoes/backlogs. */ +} jabber_chat_flags_t; +  struct jabber_data  {  	struct im_connection *ic; @@ -94,6 +100,7 @@ typedef xt_status (*jabber_cache_event) ( struct im_connection *ic, struct xt_no  struct jabber_cache_entry  { +	time_t saved_at;  	struct xt_node *node;  	jabber_cache_event func;  }; @@ -140,6 +147,10 @@ struct jabber_chat  #define JABBER_PACKET_ID "BeeP"  #define JABBER_CACHED_ID "BeeC" +/* The number of seconds to keep cached packets before garbage collecting +   them. This gc is done on every keepalive (every minute). */ +#define JABBER_CACHE_MAX_AGE 600 +  /* RFC 392[01] stuff */  #define XMLNS_TLS          "urn:ietf:params:xml:ns:xmpp-tls"  #define XMLNS_SASL         "urn:ietf:params:xml:ns:xmpp-sasl" @@ -160,6 +171,7 @@ struct jabber_chat  #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 */ +#define XMLNS_CAPS         "http://jabber.org/protocol/caps"    /* XEP-0115 */  /* iq.c */  xt_status jabber_pkt_iq( struct xt_node *node, gpointer data ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index 794a1040..6e872040 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -141,6 +141,7 @@ void jabber_cache_add( struct im_connection *ic, struct xt_node *node, jabber_ca  	entry->node = node;  	entry->func = func; +	entry->saved_at = time( NULL );  	g_hash_table_insert( jd->node_cache, xt_find_attr( node, "id" ), entry );  } @@ -162,22 +163,17 @@ gboolean jabber_cache_clean_entry( gpointer key, gpointer entry, gpointer nullpo  void jabber_cache_clean( struct im_connection *ic )  {  	struct jabber_data *jd = ic->proto_data; +	time_t threshold = time( NULL ) - JABBER_CACHE_MAX_AGE; -	g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, NULL ); +	g_hash_table_foreach_remove( jd->node_cache, jabber_cache_clean_entry, &threshold );  } -gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer nullpointer ) +gboolean jabber_cache_clean_entry( gpointer key, gpointer entry_, gpointer threshold_ )  {  	struct jabber_cache_entry *entry = entry_; -	struct xt_node *node = entry->node; +	time_t *threshold = threshold_; -	if( node->flags & XT_SEEN ) -		return TRUE; -	else -	{ -		node->flags |= XT_SEEN; -		return FALSE; -	} +	return entry->saved_at < *threshold;  }  xt_status jabber_cache_handle_packet( struct im_connection *ic, struct xt_node *node ) diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index 2c49b800..6fc360b7 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -28,7 +28,7 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  	struct im_connection *ic = data;  	char *from = xt_find_attr( node, "from" );  	char *type = xt_find_attr( node, "type" );	/* NULL should mean the person is online. */ -	struct xt_node *c; +	struct xt_node *c, *cap;  	struct jabber_buddy *bud, *send_presence = NULL;  	int is_chat = 0;  	char *s; @@ -76,6 +76,26 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  		else  			bud->priority = 0; +		if( bud && ( cap = xt_find_node( node->children, "c" ) ) && +		    ( s = xt_find_attr( cap, "xmlns" ) ) && strcmp( s, XMLNS_CAPS ) == 0 ) +		{ +			/* This <presence> stanza includes an XEP-0115 +			   capabilities part. Not too interesting, but we can +			   see if it has an ext= attribute. */ +			s = xt_find_attr( cap, "ext" ); +			if( s && ( strstr( s, "cstates" ) || strstr( s, "chatstate" ) ) ) +				bud->flags |= JBFLAG_DOES_XEP85; +			 +			/* This field can contain more information like xhtml +			   support, but we don't support that ourselves. +			   Officially the ext= tag was deprecated, but enough +			   clients do send it. +			    +			   (I'm aware that this is not the right way to use +			   this field.) See for an explanation of ext=: +			   http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/ +		} +		  		if( is_chat )  			jabber_chat_pkt_presence( ic, bud, node );  		else @@ -185,7 +205,7 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  int presence_send_update( struct im_connection *ic )  {  	struct jabber_data *jd = ic->proto_data; -	struct xt_node *node; +	struct xt_node *node, *cap;  	char *show = jd->away_state->code;  	char *status = jd->away_message;  	struct groupchat *c; @@ -198,6 +218,16 @@ int presence_send_update( struct im_connection *ic )  	if( status )  		xt_add_child( node, xt_new_node( "status", status, NULL ) ); +	/* This makes the packet slightly bigger, but clients interested in +	   capabilities can now cache the discovery info. This reduces the +	   usual post-login iq-flood. See XEP-0115. At least libpurple and +	   Trillian seem to do this right. */ +	cap = xt_new_node( "c", NULL, NULL ); +	xt_add_attr( cap, "xmlns", XMLNS_CAPS ); +	xt_add_attr( cap, "node", "http://bitlbee.org/xmpp/caps" ); +	xt_add_attr( cap, "ver", BITLBEE_VERSION ); /* The XEP wants this hashed, but nobody's doing that. */ +	xt_add_child( node, cap ); +	  	st = jabber_write_packet( ic, node );  	/* Have to send this update to all groupchats too, the server won't diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c index 87059051..53248ef3 100644 --- a/protocols/jabber/sasl.c +++ b/protocols/jabber/sasl.c @@ -21,6 +21,8 @@  *                                                                           *  \***************************************************************************/ +#include <ctype.h> +  #include "jabber.h"  #include "base64.h" @@ -106,12 +108,17 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )  	return XT_HANDLED;  } -static char *sasl_get_part( char *data, char *field ) +/* Non-static function, but not mentioned in jabber.h because it's for internal +   use, just that the unittest should be able to reach it... */ +char *sasl_get_part( char *data, char *field )  {  	int i, len;  	len = strlen( field ); +	while( isspace( *data ) || *data == ',' ) +		data ++; +	  	if( g_strncasecmp( data, field, len ) == 0 && data[len] == '=' )  	{  		i = strlen( field ) + 1; @@ -128,13 +135,19 @@ static char *sasl_get_part( char *data, char *field )  					i ++;  			} -			/* If we got a comma, we got a new field. Check it. */ -			if( data[i] == ',' && -			    g_strncasecmp( data + i + 1, field, len ) == 0 && -			    data[i+len+1] == '=' ) +			/* If we got a comma, we got a new field. Check it, +			   find the next key after it. */ +			if( data[i] == ',' )  			{ -				i += len + 2; -				break; +				while( isspace( data[i] ) || data[i] == ',' ) +					i ++; +				 +				if( g_strncasecmp( data + i, field, len ) == 0 && +				    data[i+len] == '=' ) +				{ +					i += len + 1; +					break; +				}  			}  		}  	} diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 3440658d..6a588613 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: msn_mod.o  check: all -lcov: +lcov: check  gcov:   	gcov *.c diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 3735aad6..0bb84a74 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -583,7 +583,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )  	}  	else  	{ -		debug( "Received unknown command from main server: %s", cmd[0] ); +		/* debug( "Received unknown command from main server: %s", cmd[0] ); */  	}  	return( 1 ); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index cdf2e8ad..18c41ef5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -593,7 +593,7 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts )  	}  	else  	{ -		debug( "Received unknown command from switchboard server: %s", cmd[0] ); +		/* debug( "Received unknown command from switchboard server: %s", cmd[0] ); */  	}  	return( 1 ); diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile index 95e85ec2..2792f22a 100644 --- a/protocols/oscar/Makefile +++ b/protocols/oscar/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: oscar_mod.o  check: all -lcov: +lcov: check  gcov:  	gcov *.c diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 9167f6a3..120ebc3e 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -1065,12 +1065,14 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_  	} else if (args->mpmsg.numparts == 0) {  		g_snprintf(tmp, BUF_LONG, "%s", args->msg);  	} else { -		int i; +		aim_mpmsg_section_t *part;  		*tmp = 0; -		for (i = 0; i < args->mpmsg.numparts; i ++) { -			g_strlcat(tmp, (char*) args->mpmsg.parts[i].data, BUF_LONG); -			g_strlcat(tmp, "\n", BUF_LONG); +		for (part = args->mpmsg.parts; part; part = part->next) { +			if (part->data) { +				g_strlcat(tmp, (char*) part->data, BUF_LONG); +				g_strlcat(tmp, "\n", BUF_LONG); +			}  		}  	} diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index 2cfd147b..b4fe56e2 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -17,7 +17,7 @@ LFLAGS += -r  # [SH] Phony targets  all: yahoo_mod.o  check: all -lcov: +lcov: check  gcov:   	gcov *.c diff --git a/root_commands.c b/root_commands.c index d47a8b1d..5b64052f 100644 --- a/root_commands.c +++ b/root_commands.c @@ -453,7 +453,7 @@ static void cmd_add( irc_t *irc, char **cmd )  	if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )  	{  		add_on_server = 0; -		cmd ++;		/* So evil... :-D */ +		cmd ++;  	}  	if( !( a = account_get( irc, cmd[1] ) ) ) @@ -485,12 +485,13 @@ static void cmd_add( irc_t *irc, char **cmd )  		}  	} -	/* By making this optional, you can talk to people without having to -	   add them to your *real* (server-side) contact list. */  	if( add_on_server )  		a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL ); -	 -	/* add_buddy( a->ic, NULL, cmd[2], cmd[2] ); */ +	else +		/* Yeah, officially this is a call-*back*... So if we just +		   called add_buddy, we'll wait for the IM server to respond +		   before we do this. */ +		imcb_add_buddy( a->ic, cmd[2], NULL );  	irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2]  );  } diff --git a/storage_text.c b/storage_text.c index 7c29d95a..5ee6438d 100644 --- a/storage_text.c +++ b/storage_text.c @@ -29,10 +29,10 @@  static void text_init (void)  { -	if( access( global.conf->configdir, F_OK ) != 0 ) -		log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", global.conf->configdir ); -	else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 ) -		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir ); +	/* Don't complain about the configuration directory anymore, leave it +	   up to the XML storage module, which uses the same directory for it +	   anyway. Nobody should be using just the text plugin anymore since +	   it's read only! */  }  static storage_status_t text_load ( const char *my_nick, const char* password, irc_t *irc ) diff --git a/storage_xml.c b/storage_xml.c index 4c372cde..19070a74 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -262,9 +262,9 @@ GMarkupParser xml_parser =  static void xml_init( void )  {  	if( access( global.conf->configdir, F_OK ) != 0 ) -		log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", global.conf->configdir ); +		log_message( LOGLVL_WARNING, "The configuration directory `%s' does not exist. Configuration won't be saved.", global.conf->configdir );  	else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 ) -		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir ); +		log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.", global.conf->configdir );  }  static storage_status_t xml_load_real( const char *my_nick, const char *password, irc_t *irc, xml_pass_st action ) diff --git a/tests/Makefile b/tests/Makefile index 5bc3fbde..ae76fef5 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -10,9 +10,9 @@ clean:  distclean: clean -main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o  +main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o -test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o +test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_crypting.o check_set.o check_jabber_sasl.o  check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o  	@echo '*' Linking $@ diff --git a/tests/check.c b/tests/check.c index 043889d6..b3ffb957 100644 --- a/tests/check.c +++ b/tests/check.c @@ -65,6 +65,9 @@ Suite *crypting_suite(void);  /* From check_set.c */  Suite *set_suite(void); +/* From check_jabber_sasl.c */ +Suite *jabber_sasl_suite(void); +  int main (int argc, char **argv)  {  	int nf; @@ -110,6 +113,7 @@ int main (int argc, char **argv)  	srunner_add_suite(sr, user_suite());  	srunner_add_suite(sr, crypting_suite());  	srunner_add_suite(sr, set_suite()); +	srunner_add_suite(sr, jabber_sasl_suite());  	if (no_fork)  		srunner_set_fork_status(sr, CK_NOFORK);  	srunner_run_all (sr, verbose?CK_VERBOSE:CK_NORMAL); diff --git a/tests/check_arc.c b/tests/check_arc.c index 989a0a66..a430f899 100644 --- a/tests/check_arc.c +++ b/tests/check_arc.c @@ -61,14 +61,15 @@ struct  			0xb6, 0x92, 0x59, 0xe4, 0xf9, 0xc1, 0x7a, 0xf6, 0xf3, 0x18, 0xea, 0x28,  			0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63  		}, 24, "OSCAR is creepy..." -	} +	}, +	{ "", 0, NULL }  };  static void check_decod(int l)  {  	int i; -	for( i = 0; clear_tests[i]; i++ ) +	for( i = 0; decrypt_tests[i].len; i++ )  	{    		tcase_fn_start (decrypt_tests[i].decrypted, __FILE__, __LINE__);  		char *decrypted; diff --git a/tests/check_jabber_sasl.c b/tests/check_jabber_sasl.c new file mode 100644 index 00000000..96c05837 --- /dev/null +++ b/tests/check_jabber_sasl.c @@ -0,0 +1,117 @@ +#include <stdlib.h> +#include <glib.h> +#include <gmodule.h> +#include <check.h> +#include <string.h> +#include <stdio.h> +#include "arc.h" + +char *sasl_get_part( char *data, char *field ); + +#define challenge1 "nonce=\"1669585310\",qop=\"auth\",charset=utf-8,algorithm=md5-sess," \ +                   "something=\"Not \\\"standardized\\\"\"" +#define challenge2 "realm=\"quadpoint.org\", nonce=\"NPotlQpQf9RNYodOwierkQ==\", " \ +                   "qop=\"auth, auth-int\", charset=utf-8, algorithm=md5-sess" +#define challenge3 ", realm=\"localhost\", nonce=\"LlBV2txnO8RbB5hgs3KgiQ==\", " \ +                   "qop=\"auth, auth-int, \", ,\n, charset=utf-8, algorithm=md5-sess," + +struct +{ +	const char *challenge; +	char *key; +	char *value; +} get_part_tests[] = { +	{ +		challenge1, +		"nonce", +		"1669585310" +	}, +	{ +		challenge1, +		"charset", +		"utf-8" +	}, +	{ +		challenge1, +		"harset", +		NULL +	}, +	{ +		challenge1, +		"something", +		"Not \"standardized\"" +	}, +	{ +		challenge1, +		"something_else", +		NULL +	}, +	{ +		challenge2, +		"realm", +		"quadpoint.org", +	}, +	{ +		challenge2, +		"real", +		NULL +	}, +	{ +		challenge2, +		"qop", +		"auth, auth-int" +	}, +	{ +		challenge3, +		"realm", +		"localhost" +	}, +	{ +		challenge3, +		"qop", +		"auth, auth-int, " +	}, +	{ +		challenge3, +		"charset", +		"utf-8" +	}, +	{ NULL, NULL, NULL } +}; + +static void check_get_part(int l) +{ +	int i; +	 +	for( i = 0; get_part_tests[i].key; i++ ) +	{ +  		tcase_fn_start( get_part_tests[i].key, __FILE__, i ); +		char *res; +		int len; +		 +		res = sasl_get_part( get_part_tests[i].challenge, +		                     get_part_tests[i].key ); +		 +		if( get_part_tests[i].value == NULL ) +			fail_if( res != NULL, "Found key %s in %s while it shouldn't be there!", +			         get_part_tests[i].key, get_part_tests[i].challenge ); +		else if( res ) +			fail_unless( strcmp( res, get_part_tests[i].value ) == 0, +			             "Incorrect value for key %s in %s: %s", +			             get_part_tests[i].key, get_part_tests[i].challenge, res ); +		else +			fail( "Could not find key %s in %s", +			      get_part_tests[i].key, get_part_tests[i].challenge ); +		 +		g_free( res ); +	} +} + +Suite *jabber_sasl_suite (void) +{ +	Suite *s = suite_create("jabber/sasl"); +	TCase *tc_core = tcase_create("Core"); +	suite_add_tcase (s, tc_core); +	tcase_add_test (tc_core, check_get_part); +	return s; +} @@ -47,7 +47,7 @@ int main( int argc, char *argv[], char **envp )  	struct sigaction sig, old;  	log_init(); -	CONF_FILE = g_strdup( CONF_FILE_DEF ); +	global.conf_file = g_strdup( CONF_FILE_DEF );  	global.conf = conf_load( argc, argv );  	if( global.conf == NULL )  		return( 1 ); @@ -125,11 +125,14 @@ int main( int argc, char *argv[], char **envp )  	if( !getuid() || !geteuid() )  		log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); -	if( help_init( &(global.help), global.helpfile ) == NULL ) +	if( help_init( &global.help, global.helpfile ) == NULL )  		log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE );  	b_main_run(); +	/* Mainly good for restarting, to make sure we close the help.txt fd. */ +	help_free( &global.help ); +	  	if( global.restart )  	{  		char *fn = ipc_master_save_state(); | 
