diff options
39 files changed, 781 insertions, 381 deletions
| @@ -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 ) @@ -278,7 +282,7 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition  			child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );  			child_list = g_slist_append( child_list, child ); -			log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", client_pid ); +			log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid );  			/* Close some things we don't need in the parent process. */  			close( new_socket ); @@ -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" @@ -138,6 +142,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 ); @@ -65,15 +63,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 ) @@ -105,16 +105,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' ) @@ -142,7 +142,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' )  		{ @@ -168,7 +168,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 ) @@ -177,7 +180,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 ) @@ -255,7 +258,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;  			} @@ -264,7 +267,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;  			} @@ -276,7 +279,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 ) ); @@ -300,7 +303,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... */  			}  		} @@ -308,19 +311,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 ) )  	{ @@ -188,7 +188,7 @@ else  fi  if [ "$events" = "libevent" ]; then -	if ! [ -e "${libevent}include/event.h" ]; then +	if ! [ -f "${libevent}include/event.h" ]; then  		echo  		echo 'Warning: Could not find event.h, you might have to install it and/or specify'  		echo 'its location using the --libevent= argument. (Example: If event.h is in' @@ -323,7 +323,7 @@ fi;  echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings  for i in /lib /usr/lib /usr/local/lib; do -	if [ -e $i/libresolv.a ]; then +	if [ -f $i/libresolv.a ]; then  		echo '#define HAVE_RESOLV_A' >> config.h  		echo 'EFLAGS+='$i'/libresolv.a' >> Makefile.settings  		break @@ -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/doc/user-guide/quickstart.xml b/doc/user-guide/quickstart.xml index fcb06c6b..7735a8d7 100644 --- a/doc/user-guide/quickstart.xml +++ b/doc/user-guide/quickstart.xml @@ -33,7 +33,7 @@ To add an account to the account list you will need to use the <emphasis>account  </para>  <para> -For instance, suppose you have an ICQ account with UIN <emphasis>72696705</emphasis> with password <emphasis>QuickStart</emphasis>, you would: +For instance, suppose you have a Jabber account at jabber.org with handle <emphasis>bitlbee@jabber.org</emphasis> with password <emphasis>QuickStart</emphasis>, you would:  </para>  <ircexample> @@ -42,7 +42,7 @@ For instance, suppose you have an ICQ account with UIN <emphasis>72696705</empha  </ircexample>  <para> -Other available IM protocols are msn, oscar, and yahoo. Oscar is the protocol used by ICQ and AOL. +Other available IM protocols are msn, oscar, and yahoo. OSCAR is the protocol used by ICQ and AOL. For more information about the <emphasis>account add</emphasis> command, see <emphasis>help account add</emphasis>.  </para>  <para> @@ -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 @@ -51,7 +51,7 @@ static void ipc_master_cmd_client( irc_t *data, char **cmd )  	if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )  		ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n", -		                     child ? child->pid : -1, cmd[2], cmd[1], cmd[3] ); +		                     (int) ( child ? child->pid : -1 ), cmd[2], cmd[1], cmd[3] );  }  static void ipc_master_cmd_die( irc_t *data, char **cmd ) @@ -445,7 +445,7 @@ char *ipc_master_save_state()  	fprintf( fp, "%d\n", i );  	for( l = child_list; l; l = l->next ) -		fprintf( fp, "%d %d\n", ((struct bitlbee_child*)l->data)->pid, +		fprintf( fp, "%d %d\n", (int) ((struct bitlbee_child*)l->data)->pid,  		                        ((struct bitlbee_child*)l->data)->ipc_fd );  	if( fclose( fp ) == 0 ) @@ -550,7 +550,7 @@ int ipc_master_load_state()  	{  		child = g_new0( struct bitlbee_child, 1 ); -		if( fscanf( fp, "%d %d", &child->pid, &child->ipc_fd ) != 2 ) +		if( fscanf( fp, "%d %d", (int *) &child->pid, &child->ipc_fd ) != 2 )  		{  			log_message( LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i );  			g_free( child ); @@ -73,7 +73,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 ) );  		} @@ -84,7 +84,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 ) );  		} @@ -188,7 +188,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 ); @@ -265,16 +264,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); -		} -	}  	g_free(irc);  	if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON ) @@ -324,7 +313,10 @@ void irc_process( irc_t *irc )  				conv[IRC_MAX_LINE] = 0;  				if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -1 )  				{ +					/* GLib can do strange things if things are not in the expected charset, +					   so let's be a little bit paranoid here: */  					if( irc->status & USTATUS_LOGGED_IN ) +					{  						irc_usermsg( irc, "Error: Charset mismatch detected. The charset "  						                  "setting is currently set to %s, so please make "  						                  "sure your IRC client will send and accept text in " @@ -332,7 +324,18 @@ void irc_process( irc_t *irc )  						                  "expect by changing the charset setting. See "  						                  "`help set charset' for more information. Your "  						                  "message was ignored.", cs ); -					*conv = 0; +						*conv = 0; +					} +					else +					{ +						irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, +						           "Warning: invalid (non-UTF8) characters received at login time." ); +						 +						strncpy( conv, lines[i], IRC_MAX_LINE ); +						for( temp = conv; *temp; temp ++ ) +							if( *temp & 0x80 ) +								*temp = '?'; +					}  				}  				lines[i] = conv;  			} @@ -88,7 +88,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[] = { @@ -25,7 +25,7 @@  #include <string.h>		/* for memcpy() */  #include "md5.h" -static void md5_transform(u_int32_t buf[4], u_int32_t const in[16]); +static void md5_transform(uint32_t buf[4], uint32_t const in[16]);  /*   * Wrapper function for all-in-one MD5 @@ -34,6 +34,29 @@ static void md5_transform(u_int32_t buf[4], u_int32_t const in[16]);   * 20021120   */ +/* Turns out MD5 was designed for little-endian machines. If we're running +   on a big-endian machines, we have to swap some bytes. Since detecting +   endianness at compile time reliably seems pretty hard, let's do it at +   run-time. It's not like we're going to checksum megabytes of data... */ +static uint32_t cvt32(uint32_t val) +{ +	static int little_endian = -1; +	 +	if (little_endian == -1) +	{ +		little_endian = 1; +		little_endian = *((char*) &little_endian); +	} +	 +	if (little_endian) +		return val; +	else +		return (val >> 24) | +		       ((val >> 8) & 0xff00) | +		       ((val << 8) & 0xff0000) | +		       (val << 24); +} +  void md5_init(struct MD5Context *ctx)  {  	ctx->buf[0] = 0x67452301; @@ -52,12 +75,12 @@ void md5_init(struct MD5Context *ctx)  void md5_append(struct MD5Context *ctx, const md5_byte_t *buf,  		unsigned int len)  { -	u_int32_t t; +	uint32_t t;  	/* Update bitcount */  	t = ctx->bits[0]; -	if ((ctx->bits[0] = t + ((u_int32_t) len << 3)) < t) +	if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)  		ctx->bits[1]++;	/* Carry from low to high */  	ctx->bits[1] += len >> 29; @@ -74,7 +97,7 @@ void md5_append(struct MD5Context *ctx, const md5_byte_t *buf,  			return;  		}  		memcpy(p, buf, t); -		md5_transform(ctx->buf, (u_int32_t *) ctx->in); +		md5_transform(ctx->buf, (uint32_t *) ctx->in);  		buf += t;  		len -= t;  	} @@ -82,7 +105,7 @@ void md5_append(struct MD5Context *ctx, const md5_byte_t *buf,  	while (len >= 64) {  		memcpy(ctx->in, buf, 64); -		md5_transform(ctx->buf, (u_int32_t *) ctx->in); +		md5_transform(ctx->buf, (uint32_t *) ctx->in);  		buf += 64;  		len -= 64;  	} @@ -116,7 +139,7 @@ void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16])  	if (count < 8) {  		/* Two lots of padding:  Pad the first block to 64 bytes */  		memset(p, 0, count); -		md5_transform(ctx->buf, (u_int32_t *) ctx->in); +		md5_transform(ctx->buf, (uint32_t *) ctx->in);  		/* Now fill the next block with 56 bytes */  		memset(ctx->in, 0, 56); @@ -126,10 +149,14 @@ void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16])  	}  	/* Append length in bits and transform */ -	((u_int32_t *) ctx->in)[14] = ctx->bits[0]; -	((u_int32_t *) ctx->in)[15] = ctx->bits[1]; - -	md5_transform(ctx->buf, (u_int32_t *) ctx->in); +	((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]); +	((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]); + +	md5_transform(ctx->buf, (uint32_t *) ctx->in); +	ctx->buf[0] = cvt32(ctx->buf[0]); +	ctx->buf[1] = cvt32(ctx->buf[1]); +	ctx->buf[2] = cvt32(ctx->buf[2]); +	ctx->buf[3] = cvt32(ctx->buf[3]);  	memcpy(digest, ctx->buf, 16);  	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */  } @@ -151,82 +178,82 @@ void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16])   * reflect the addition of 16 longwords of new data.  MD5Update blocks   * the data and converts bytes into longwords for this routine.   */ -static void md5_transform(u_int32_t buf[4], u_int32_t const in[16]) +static void md5_transform(uint32_t buf[4], uint32_t const in[16])  { -	register u_int32_t a, b, c, d; +	register uint32_t a, b, c, d;  	a = buf[0];  	b = buf[1];  	c = buf[2];  	d = buf[3]; -	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); -	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); -	MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); -	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); -	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); -	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); -	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); -	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); -	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); -	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); -	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); -	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); -	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); -	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); -	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); -	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - -	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); -	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); -	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); -	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); -	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); -	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); -	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); -	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); -	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); -	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); -	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); -	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); -	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); -	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); -	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); -	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - -	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); -	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); -	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); -	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); -	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); -	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); -	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); -	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); -	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); -	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); -	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); -	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); -	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); -	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); -	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); -	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - -	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); -	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); -	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); -	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); -	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); -	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); -	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); -	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); -	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); -	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); -	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); -	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); -	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); -	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); -	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); -	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); +	MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22); + +	MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20); + +	MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23); + +	MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21);  	buf[0] += a;  	buf[1] += b; @@ -26,11 +26,12 @@  #include <sys/types.h>  #include <gmodule.h> +#include <stdint.h> -typedef u_int8_t md5_byte_t; +typedef uint8_t md5_byte_t;  typedef struct MD5Context { -	u_int32_t buf[4]; -	u_int32_t bits[2]; +	uint32_t buf[4]; +	uint32_t bits[2];  	unsigned char in[64];  } md5_state_t; @@ -89,12 +89,14 @@ time_t get_time(int year, int month, int day, int hour, int min, int sec)  {  	struct tm tm; +	memset(&tm, 0, sizeof(struct tm));  	tm.tm_year = year - 1900;  	tm.tm_mon = month - 1;  	tm.tm_mday = day;  	tm.tm_hour = hour;  	tm.tm_min = min;  	tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; +	  	return mktime(&tm);  } @@ -244,12 +246,6 @@ char *escape_html( const char *html )  	return( str );  } -void info_string_append(GString *str, char *newline, char *name, char *value) -{ -	if( value && value[0] ) -		g_string_sprintfa( str, "%s%s: %s", newline, name, value ); -} -  /* Decode%20a%20file%20name						*/  void http_decode( char *s )  { @@ -41,7 +41,6 @@ G_MODULE_EXPORT void strip_linefeed( gchar *text );  G_MODULE_EXPORT char *add_cr( char *text );  G_MODULE_EXPORT char *strip_newlines(char *source);  G_MODULE_EXPORT char *normalize( const char *s ); -G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value );  G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );  double gettime( void ); diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index 074412ec..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 ) ) @@ -72,17 +74,16 @@ static xt_status jabber_chat_join_failed( struct im_connection *ic, struct xt_no  	char *room;  	room = xt_find_attr( orig, "to" ); -	if( ( bud = jabber_buddy_by_jid( ic, room, 0 ) ) ) -		jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) ); -	 +	bud = jabber_buddy_by_jid( ic, room, 0 );  	err = jabber_error_parse( xt_find_node( node->children, "error" ), XMLNS_STANZA_ERROR );  	if( err )  	{ -		imcb_error( ic, "Error joining groupchat %s: %s%s%s", -		            bud->bare_jid, err->code, err->text ? ": " : "", -		            err->text ? err->text : "" ); +		imcb_error( ic, "Error joining groupchat %s: %s%s%s", room, err->code, +		            err->text ? ": " : "", err->text ? err->text : "" );  		jabber_error_free( err );  	} +	if( bud ) +		jabber_chat_free( jabber_chat_by_jid( ic, bud->bare_jid ) );  	return XT_HANDLED;  } @@ -123,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 ); @@ -295,22 +298,59 @@ 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; +	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; +		 +		if( body == NULL || body->text_len == 0 ) +			/* Meh. Empty messages aren't very interesting, no matter +			   how much some servers love to send them. */ +			return; +		  		s = xt_find_attr( node, "from" ); /* pkt_message() already NULL-checked this one. */ -		if( strchr( s, '/' ) == NULL ) +		nick = strchr( s, '/' ); +		if( nick ) +		{ +			/* If this message included a resource/nick we don't know, +			   we might still know the groupchat itself. */ +			*nick = 0; +			chat = jabber_chat_by_jid( ic, s ); +			*nick = '/'; +			 +			nick ++; +		} +		else +		{ +			/* message.c uses the EXACT_JID option, so bud should +			   always be NULL here for bare JIDs. */ +			chat = jabber_chat_by_jid( ic, s ); +		} +		 +		if( nick == 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" ); +			if( chat ) +				imcb_chat_log( chat, "From conference server: %s", body->text ); +			else +				imcb_log( ic, "System message from unknown groupchat %s: %s", s, body->text ); +		}  		else -			/* This, however, isn't fine! */ -			imcb_log( ic, "Groupchat message from unknown participant %s: %s", s, body ? body->text : "NULL" ); +		{ +			/* This can happen too, at least when receiving a backlog when +			   just joining a channel. */ +			if( chat ) +				imcb_chat_log( chat, "Message from unknown participant %s: %s", nick, body->text ); +			else +				imcb_log( ic, "Groupchat message from unknown JID %s: %s", s, body->text ); +		}  		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/io.c b/protocols/jabber/io.c index 29561b86..86c216ef 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -248,6 +248,9 @@ gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition c  {  	struct im_connection *ic = data; +	if( g_slist_find( jabber_connections, ic ) == NULL ) +		return FALSE; +	  	if( source == -1 )  	{  		imcb_error( ic, "Could not connect to server" ); @@ -263,7 +266,12 @@ gboolean jabber_connected_plain( gpointer data, gint source, b_input_condition c  gboolean jabber_connected_ssl( gpointer data, void *source, b_input_condition cond )  {  	struct im_connection *ic = data; -	struct jabber_data *jd = ic->proto_data; +	struct jabber_data *jd; +	 +	if( g_slist_find( jabber_connections, ic ) == NULL ) +		return FALSE; +	 +	jd = ic->proto_data;  	if( source == NULL )  	{ 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.c b/protocols/jabber/jabber.c index 264a0308..243ed7fd 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -34,6 +34,8 @@  #include "md5.h"  #include "base64.h" +GSList *jabber_connections; +  static void jabber_init( account_t *acc )  {  	set_t *s; @@ -70,6 +72,11 @@ static void jabber_login( account_t *acc )  	struct ns_srv_reply *srv = NULL;  	char *connect_to, *s; +	/* For now this is needed in the _connected() handlers if using +	   GLib event handling, to make sure we're not handling events +	   on dead connections. */ +	jabber_connections = g_slist_prepend( jabber_connections, ic ); +	  	jd->ic = ic;  	ic->proto_data = jd; @@ -262,6 +269,8 @@ static void jabber_logout( struct im_connection *ic )  	g_free( jd->away_message );  	g_free( jd->username );  	g_free( jd ); +	 +	jabber_connections = g_slist_remove( jabber_connections, ic );  }  static int jabber_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h index f673ee0d..1ff0e8dd 100644 --- a/protocols/jabber/jabber.h +++ b/protocols/jabber/jabber.h @@ -29,6 +29,8 @@  #include "xmltree.h"  #include "bitlbee.h" +extern GSList *jabber_connections; +  typedef enum  {  	JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream @@ -46,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; @@ -92,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;  }; @@ -138,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" @@ -158,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 b84be49b..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 ) @@ -391,19 +387,26 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  		*s = 0;  		if( ( bud = g_hash_table_lookup( jd->buddies, jid ) ) )  		{ +			/* Just return the first one for this bare JID. */ +			if( flags & GET_BUDDY_FIRST ) +			{ +				*s = '/'; +				g_free( jid ); +				return bud; +			} +			  			/* Is this one of those no-resource buddies? */  			if( bud->resource == NULL )  			{ +				*s = '/';  				g_free( jid );  				return NULL;  			} -			else -			{ -				/* See if there's an exact match. */ -				for( ; bud; bud = bud->next ) -					if( g_strcasecmp( bud->resource, s + 1 ) == 0 ) -						break; -			} +			 +			/* See if there's an exact match. */ +			for( ; bud; bud = bud->next ) +				if( g_strcasecmp( bud->resource, s + 1 ) == 0 ) +					break;  		}  		else  		{ @@ -412,6 +415,8 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  			   for this JID, even if it's an unknown buddy. This  			   is done to handle conferences properly. */  			none_found = 1; +			/* TODO(wilmer): Find out what I was thinking when I +			   wrote this??? And then fix it. This makes me sad... */  		}  		if( bud == NULL && ( flags & GET_BUDDY_CREAT ) && ( imcb_find_buddy( ic, jid ) || !none_found ) ) @@ -442,6 +447,9 @@ struct jabber_buddy *jabber_buddy_by_jid( struct im_connection *ic, char *jid_,  		else if( ( bud->resource == NULL || bud->next == NULL ) )  			/* No need for selection if there's only one option. */  			return bud; +		else if( flags & GET_BUDDY_FIRST ) +			/* Looks like the caller doesn't care about details. */ +			return bud;  		best_prio = best_time = bud;  		for( ; bud; bud = bud->next ) diff --git a/protocols/jabber/presence.c b/protocols/jabber/presence.c index f2dca26c..6fc360b7 100644 --- a/protocols/jabber/presence.c +++ b/protocols/jabber/presence.c @@ -28,9 +28,9 @@ 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 jabber_buddy *bud; -	int is_chat = 0, is_away = 0; +	struct xt_node *c, *cap; +	struct jabber_buddy *bud, *send_presence = NULL; +	int is_chat = 0;  	char *s;  	if( !from ) @@ -62,8 +62,6 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  		if( ( c = xt_find_node( node->children, "show" ) ) && c->text_len > 0 )  		{  			bud->away_state = (void*) jabber_away_state_by_code( c->text ); -			if( strcmp( c->text, "chat" ) != 0 ) -				is_away = OPT_AWAY;  		}  		else  		{ @@ -78,12 +76,30 @@ 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 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 +			send_presence = jabber_buddy_by_jid( ic, bud->bare_jid, 0 );  	}  	else if( strcmp( type, "unavailable" ) == 0 )  	{ @@ -118,17 +134,7 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  			/* 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 +			if( ( send_presence = jabber_buddy_by_jid( ic, from, 0 ) ) == NULL )  			{  				/* Otherwise, count him/her as offline now. */  				imcb_buddy_status( ic, from, 0, NULL, NULL ); @@ -176,6 +182,20 @@ xt_status jabber_pkt_presence( struct xt_node *node, gpointer data )  			jabber_error_free( err );  		} */  	} + +	if( send_presence ) +	{ +		int is_away = 0; + +		if( send_presence->away_state && !( *send_presence->away_state->code == 0 || +		    strcmp( send_presence->away_state->code, "chat" ) == 0 ) ) +			is_away = OPT_AWAY; + +		imcb_buddy_status( ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away, +		                   ( is_away && send_presence->away_state ) ? +		                   send_presence->away_state->full_name : NULL, +		                   send_presence->away_message ); +	}  	return XT_HANDLED;  } @@ -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/msn.h b/protocols/msn/msn.h index 746606a0..c8f4f4c6 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -28,11 +28,9 @@  #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r"  #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r" -#ifdef _WIN32 -#define debug  +#ifdef DEBUG +#define debug( text... ) imcb_log( ic, text );  #else -#define debug( text... ) irc_usermsg( IRC, text ); -#undef debug  #define debug( text... )  #endif @@ -65,8 +63,10 @@ struct msn_data  	GSList *msgq;  	GSList *switchboards; -	const struct msn_away_state *away_state; +	int sb_failures; +	time_t first_sb_failure; +	const struct msn_away_state *away_state;  	int buddycount;  	int groupcount;  	char **grouplist; 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 8c0afca6..18c41ef5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -276,16 +276,59 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond )  static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition cond )  {  	struct msn_switchboard *sb = data; +	struct im_connection *ic = sb->ic; +	struct msn_data *md = ic->proto_data;  	if( msn_handler( sb->handler ) == -1 )  	{ +		time_t now = time( NULL ); +		 +		if( now - md->first_sb_failure > 600 ) +		{ +			/* It's not really the first one, but the start of this "series". +			   With this, the warning below will be shown only if this happens +			   at least three times in ten minutes. This algorithm isn't +			   perfect, but for this purpose it will do. */ +			md->first_sb_failure = now; +			md->sb_failures = 0; +		} +		  		debug( "Error: Switchboard died" ); +		if( ++ md->sb_failures >= 3 ) +			imcb_log( ic, "Warning: Many switchboard failures on MSN connection. " +			              "There might be problems delivering your messages." ); +		 +		if( sb->msgq != NULL ) +		{ +			char buf[1024]; +			 +			if( md->msgq == NULL ) +			{ +				md->msgq = sb->msgq; +			} +			else +			{ +				GSList *l; +				 +				for( l = md->msgq; l->next; l = l->next ); +				l->next = sb->msgq; +			} +			sb->msgq = NULL; +			 +			debug( "Moved queued messages back to the main queue, creating a new switchboard to retry." ); +			g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); +			if( !msn_write( ic, buf, strlen( buf ) ) ) +				return FALSE; +		} +		  		msn_sb_destroy( sb );  		return FALSE;  	}  	else +	{  		return TRUE; +	}  }  static int msn_sb_command( gpointer data, char **cmd, int num_parts ) @@ -550,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/nogaim.c b/protocols/nogaim.c index 5e698902..3ce15166 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -624,7 +624,7 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags,  	}  } -void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at ) +void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at )  {  	irc_t *irc = ic->irc;  	char *wrapped; @@ -675,7 +675,7 @@ void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_  	g_free( wrapped );  } -void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags ) +void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags )  {  	user_t *u; @@ -691,6 +691,31 @@ void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags  	}  } +struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ) +{ +	struct groupchat *c; +	 +	/* This one just creates the conversation structure, user won't see anything yet */ +	 +	if( ic->groupchats ) +	{ +		for( c = ic->groupchats; c->next; c = c->next ); +		c = c->next = g_new0( struct groupchat, 1 ); +	} +	else +		ic->groupchats = c = g_new0( struct groupchat, 1 ); +	 +	c->ic = ic; +	c->title = g_strdup( handle ); +	c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); +	c->topic = g_strdup_printf( "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->title ); +	 +	if( set_getbool( &ic->irc->set, "debug" ) ) +		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); +	 +	return c; +} +  void imcb_chat_free( struct groupchat *c )  {  	struct im_connection *ic = c->ic; @@ -727,11 +752,12 @@ void imcb_chat_free( struct groupchat *c )  		g_list_free( c->in_room );  		g_free( c->channel );  		g_free( c->title ); +		g_free( c->topic );  		g_free( c );  	}  } -void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at ) +void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at )  {  	struct im_connection *ic = c->ic;  	char *wrapped; @@ -759,6 +785,24 @@ void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags,  	g_free( wrapped );  } +void imcb_chat_log( struct groupchat *c, char *format, ... ) +{ +	irc_t *irc = c->ic->irc; +	va_list params; +	char *text; +	user_t *u; +	 +	va_start( params, format ); +	text = g_strdup_vprintf( format, params ); +	va_end( params ); +	 +	u = user_find( irc, irc->mynick ); +	 +	irc_privmsg( irc, u, "PRIVMSG", c->channel, "System message: ", text ); +	 +	g_free( text ); +} +  void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at )  {  	struct im_connection *ic = c->ic; @@ -782,31 +826,6 @@ void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at  		irc_write( ic->irc, ":%s!%s@%s TOPIC %s :%s", u->nick, u->user, u->host, c->channel, topic );  } -struct groupchat *imcb_chat_new( struct im_connection *ic, char *handle ) -{ -	struct groupchat *c; -	 -	/* This one just creates the conversation structure, user won't see anything yet */ -	 -	if( ic->groupchats ) -	{ -		for( c = ic->groupchats; c->next; c = c->next ); -		c = c->next = g_new0( struct groupchat, 1 ); -	} -	else -		ic->groupchats = c = g_new0( struct groupchat, 1 ); -	 -	c->ic = ic; -	c->title = g_strdup( handle ); -	c->channel = g_strdup_printf( "&chat_%03d", ic->irc->c_id++ ); -	c->topic = g_strdup_printf( "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", c->channel, c->title ); -	 -	if( set_getbool( &ic->irc->set, "debug" ) ) -		imcb_log( ic, "Creating new conversation: (id=%p,handle=%s)", c, handle ); -	 -	return c; -} -  /* buddy_chat.c */ diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 0e890464..7d391edd 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -67,7 +67,7 @@  struct im_connection  {  	account_t *acc; -	u_int32_t flags; +	uint32_t flags;  	/* each connection then can have its own protocol-specific data */  	void *proto_data; @@ -281,8 +281,8 @@ G_MODULE_EXPORT void imcb_buddy_nick_hint( struct im_connection *ic, char *handl  G_MODULE_EXPORT void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, const char *state, const char *message );  /* Not implemented yet! */ G_MODULE_EXPORT void imcb_buddy_times( struct im_connection *ic, const char *handle, time_t login, time_t idle );  /* Call when a handle says something. 'flags' and 'sent_at may be just 0. */ -G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, u_int32_t flags, time_t sent_at ); -G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, u_int32_t flags ); +G_MODULE_EXPORT void imcb_buddy_msg( struct im_connection *ic, char *handle, char *msg, uint32_t flags, time_t sent_at ); +G_MODULE_EXPORT void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags );  G_MODULE_EXPORT void imcb_clean_handle( struct im_connection *ic, char *handle );  /* Groupchats */ @@ -298,7 +298,9 @@ G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, char *handle );  /* To remove a handle from a group chat. Reason can be NULL. */  G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, char *handle, char *reason );  /* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */ -G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, u_int32_t flags, time_t sent_at ); +G_MODULE_EXPORT void imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ); +/* System messages specific to a groupchat, so they can be displayed in the right context. */ +G_MODULE_EXPORT void imcb_chat_log( struct groupchat *c, char *format, ... ) G_GNUC_PRINTF( 2, 3 );  /* To tell BitlBee 'who' changed the topic of 'c' to 'topic'. */  G_MODULE_EXPORT void imcb_chat_topic( struct groupchat *c, char *who, char *topic, time_t set_at );  G_MODULE_EXPORT void imcb_chat_free( struct groupchat *c ); diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index c4683046..9e5de70a 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -90,6 +90,8 @@ struct oscar_data {  	gboolean killme;  	gboolean icq;  	GSList *evilhack; +	 +	GHashTable *ips;  	struct {  		guint maxbuddies; /* max users you can watch */ @@ -355,9 +357,10 @@ static void oscar_login(account_t *acc) {  	struct im_connection *ic = imcb_new(acc);  	struct oscar_data *odata = ic->proto_data = g_new0(struct oscar_data, 1); -	if (!isdigit(acc->user[0])) { +	if (isdigit(acc->user[0])) +		odata->icq = TRUE; +	else  		ic->flags |= OPT_DOES_HTML; -	}  	sess = g_new0(aim_session_t, 1); @@ -410,6 +413,8 @@ static void oscar_logout(struct im_connection *ic) {  		odata->create_rooms = g_slist_remove(odata->create_rooms, cr);  		g_free(cr);  	} +	if (odata->ips) +		g_hash_table_destroy(odata->ips);  	if (odata->email)  		g_free(odata->email);  	if (odata->newp) @@ -986,6 +991,16 @@ static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) {  	if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN)  		signon = time(NULL) - info->sessionlen; +	if (info->present & AIM_USERINFO_PRESENT_ICQIPADDR) { +		uint32_t *uin = g_new0(uint32_t, 1); +		 +		if (od->ips == NULL) +			od->ips = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL); +		 +		if (sscanf(info->sn, "%d", uin) == 1) +			g_hash_table_insert(od->ips, uin, (gpointer) (long) info->icqinfo.ipaddr); +	} +  	tmp = g_strdup(normalize(ic->acc->user));  	if (!strcmp(tmp, normalize(info->sn)))  		g_snprintf(ic->displayname, sizeof(ic->displayname), "%s", info->sn); @@ -1050,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); +			}  		}  	} @@ -2218,87 +2235,94 @@ static GList *oscar_away_states(struct im_connection *ic)  static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...)  { -        struct im_connection *ic = sess->aux_data; -        gchar who[16]; -        GString *str; -        va_list ap; -        struct aim_icq_info *info; - -        va_start(ap, fr); -        info = va_arg(ap, struct aim_icq_info *); -        va_end(ap); - -        if (!info->uin) -                return 0; - -        str = g_string_sized_new(100); -        g_snprintf(who, sizeof(who), "%u", info->uin); - -        g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"),  -				info->nick ? info->nick : "-"); -        info_string_append(str, "\n", _("First Name"), info->first); -        info_string_append(str, "\n", _("Last Name"), info->last); -		info_string_append(str, "\n", _("Email Address"), info->email); -        if (info->numaddresses && info->email2) { -                int i; -                for (i = 0; i < info->numaddresses; i++) { -					info_string_append(str, "\n", _("Email Address"), info->email2[i]); -                } -        } -        info_string_append(str, "\n", _("Mobile Phone"), info->mobile); -        if (info->gender != 0) -        	info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male")); -        if (info->birthyear || info->birthmonth || info->birthday) { -                char date[30]; -                struct tm tm; -                tm.tm_mday = (int)info->birthday; -                tm.tm_mon = (int)info->birthmonth-1; -                tm.tm_year = (int)info->birthyear%100; -                strftime(date, sizeof(date), "%Y-%m-%d", &tm); -                info_string_append(str, "\n", _("Birthday"), date); -        } -        if (info->age) { -                char age[5]; -                g_snprintf(age, sizeof(age), "%hhd", info->age); -                info_string_append(str, "\n", _("Age"), age); -        } -		info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage); -        if (info->info && info->info[0]) { -                g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"),  -						info->info, _("End of Additional Information")); -        } -        g_string_sprintfa(str, "\n"); -        if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { -                g_string_sprintfa(str, "%s:", _("Home Address")); -                info_string_append(str, "\n", _("Address"), info->homeaddr); -                info_string_append(str, "\n", _("City"), info->homecity); -                info_string_append(str, "\n", _("State"), info->homestate);  -				info_string_append(str, "\n", _("Zip Code"), info->homezip); -                g_string_sprintfa(str, "\n"); -        } -        if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { -                g_string_sprintfa(str, "%s:", _("Work Address")); -                info_string_append(str, "\n", _("Address"), info->workaddr); -                info_string_append(str, "\n", _("City"), info->workcity); -                info_string_append(str, "\n", _("State"), info->workstate); -				info_string_append(str, "\n", _("Zip Code"), info->workzip); -                g_string_sprintfa(str, "\n"); -        } -        if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { -                g_string_sprintfa(str, "%s:", _("Work Information")); -                info_string_append(str, "\n", _("Company"), info->workcompany); -                info_string_append(str, "\n", _("Division"), info->workdivision); -                info_string_append(str, "\n", _("Position"), info->workposition); -                if (info->workwebpage && info->workwebpage[0]) { -                        info_string_append(str, "\n", _("Web Page"), info->workwebpage); -                } -                g_string_sprintfa(str, "\n"); -        } - -		imcb_log(ic, "%s\n%s", _("User Info"), str->str); -        g_string_free(str, TRUE); - -        return 1; +	struct im_connection *ic = sess->aux_data; +	struct oscar_data *od = ic->proto_data; +	gchar who[16]; +	GString *str; +	va_list ap; +	struct aim_icq_info *info; +	uint32_t ip; + +	va_start(ap, fr); +	info = va_arg(ap, struct aim_icq_info *); +	va_end(ap); + +	if (!info->uin) +		return 0; + +	str = g_string_sized_new(512); +	g_snprintf(who, sizeof(who), "%u", info->uin); + +	g_string_printf(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"),  +	info->nick ? info->nick : "-"); +	g_string_append_printf(str, "\n%s: %s", _("First Name"), info->first); +	g_string_append_printf(str, "\n%s: %s", _("Last Name"), info->last); +	g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email); +	if (info->numaddresses && info->email2) { +		int i; +		for (i = 0; i < info->numaddresses; i++) { +			g_string_append_printf(str, "\n%s: %s", _("Email Address"), info->email2[i]); +		} +	} +	if ((ip = (long) g_hash_table_lookup(od->ips, &info->uin)) != 0) { +		g_string_append_printf(str, "\n%s: %d.%d.%d.%d", _("Last used IP address"), +		                       (ip >> 24), (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff); +	} +	g_string_append_printf(str, "\n%s: %s", _("Mobile Phone"), info->mobile); +	if (info->gender != 0) +		g_string_append_printf(str, "\n%s: %s", _("Gender"), info->gender==1 ? _("Female") : _("Male")); +	if (info->birthyear || info->birthmonth || info->birthday) { +		char date[30]; +		struct tm tm; +		memset(&tm, 0, sizeof(struct tm)); +		tm.tm_mday = (int)info->birthday; +		tm.tm_mon = (int)info->birthmonth-1; +		tm.tm_year = (int)info->birthyear%100; +		strftime(date, sizeof(date), "%Y-%m-%d", &tm); +		g_string_append_printf(str, "\n%s: %s", _("Birthday"), date); +	} +	if (info->age) { +		char age[5]; +		g_snprintf(age, sizeof(age), "%hhd", info->age); +		g_string_append_printf(str, "\n%s: %s", _("Age"), age); +	} +	g_string_append_printf(str, "\n%s: %s", _("Personal Web Page"), info->personalwebpage); +	if (info->info && info->info[0]) { +		g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"),  +		info->info, _("End of Additional Information")); +	} +	g_string_append_c(str, '\n'); +	if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { +		g_string_append_printf(str, "%s:", _("Home Address")); +		g_string_append_printf(str, "\n%s: %s", _("Address"), info->homeaddr); +		g_string_append_printf(str, "\n%s: %s", _("City"), info->homecity); +		g_string_append_printf(str, "\n%s: %s", _("State"), info->homestate);  +		g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->homezip); +		g_string_append_c(str, '\n'); +	} +	if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { +		g_string_append_printf(str, "%s:", _("Work Address")); +		g_string_append_printf(str, "\n%s: %s", _("Address"), info->workaddr); +		g_string_append_printf(str, "\n%s: %s", _("City"), info->workcity); +		g_string_append_printf(str, "\n%s: %s", _("State"), info->workstate); +		g_string_append_printf(str, "\n%s: %s", _("Zip Code"), info->workzip); +		g_string_append_c(str, '\n'); +	} +	if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { +		g_string_append_printf(str, "%s:", _("Work Information")); +		g_string_append_printf(str, "\n%s: %s", _("Company"), info->workcompany); +		g_string_append_printf(str, "\n%s: %s", _("Division"), info->workdivision); +		g_string_append_printf(str, "\n%s: %s", _("Position"), info->workposition); +		if (info->workwebpage && info->workwebpage[0]) { +			g_string_append_printf(str, "\n%s: %s", _("Web Page"), info->workwebpage); +		} +		g_string_append_c(str, '\n'); +	} + +	imcb_log(ic, "%s\n%s", _("User Info"), str->str); +	g_string_free(str, TRUE); + +	return 1;  } @@ -2432,7 +2456,7 @@ int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)  	else {  		/* User has stopped typing */  		imcb_buddy_typing(ic, sn, 0); -	}         +	}  	return 1;  } diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index d413597a..9f9ffcf7 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -623,10 +623,14 @@ void ext_yahoo_status_changed( int id, const char *who, int stat, const char *ms  void ext_yahoo_got_im( int id, const char *me, const char *who, const char *msg, long tm, int stat, int utf8 )  {  	struct im_connection *ic = byahoo_get_ic_by_id( id ); -	char *m = byahoo_strip( msg ); +	char *m; -	imcb_buddy_msg( ic, (char*) who, (char*) m, 0, 0 ); -	g_free( m ); +	if( msg ) +	{ +		m = byahoo_strip( msg ); +		imcb_buddy_msg( ic, (char*) who, (char*) m, 0, 0 ); +		g_free( m ); +	}  }  void ext_yahoo_got_file( int id, diff --git a/root_commands.c b/root_commands.c index dc70feb4..8e315bd4 100644 --- a/root_commands.c +++ b/root_commands.c @@ -448,7 +448,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] ) ) ) @@ -480,12 +480,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]  );  } @@ -820,7 +821,7 @@ static void cmd_blist( irc_t *irc, char **cmd )  	{  		if( online == 1 )  		{ -			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name ); +			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );  			irc_usermsg( irc, format, u->nick, s, "Online" );  		} @@ -831,7 +832,7 @@ static void cmd_blist( irc_t *irc, char **cmd )  	{  		if( away == 1 )  		{ -			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name ); +			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );  			irc_usermsg( irc, format, u->nick, s, u->away );  		}  		n_away ++; @@ -841,7 +842,7 @@ static void cmd_blist( irc_t *irc, char **cmd )  	{  		if( offline == 1 )  		{ -			g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->ic->acc->prpl->name ); +			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );  			irc_usermsg( irc, format, u->nick, s, "Offline" );  		}  		n_offline ++; 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; +} @@ -46,7 +46,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 ); @@ -123,11 +123,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(); @@ -196,9 +199,9 @@ static void sighandler( int signal )  		while( ( pid = waitpid( 0, &st, WNOHANG ) ) > 0 )  		{  			if( WIFSIGNALED( st ) ) -				log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", pid, WEXITSTATUS( st ) ); +				log_message( LOGLVL_INFO, "Client %d terminated normally. (status = %d)", (int) pid, WEXITSTATUS( st ) );  			else if( WIFEXITED( st ) ) -				log_message( LOGLVL_INFO, "Client %d killed by signal %d.", pid, WTERMSIG( st ) ); +				log_message( LOGLVL_INFO, "Client %d killed by signal %d.", (int) pid, WTERMSIG( st ) );  		}  	}  	else if( signal != SIGPIPE ) | 
