diff options
| -rw-r--r-- | .bzrignore | 2 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | bitlbee.c | 229 | ||||
| -rw-r--r-- | bitlbee.h | 6 | ||||
| -rw-r--r-- | commands.c | 96 | ||||
| -rw-r--r-- | conf.c | 11 | ||||
| -rw-r--r-- | conf.h | 2 | ||||
| -rwxr-xr-x | configure | 9 | ||||
| -rw-r--r-- | crypting.c | 101 | ||||
| -rw-r--r-- | crypting.h | 10 | ||||
| -rw-r--r-- | doc/AUTHORS | 2 | ||||
| -rw-r--r-- | doc/CHANGES | 11 | ||||
| -rw-r--r-- | doc/FAQ | 7 | ||||
| -rw-r--r-- | doc/README | 17 | ||||
| -rw-r--r-- | doc/user-guide/user-guide.xml | 5 | ||||
| -rw-r--r-- | irc.c | 23 | ||||
| -rw-r--r-- | irc.h | 1 | ||||
| -rw-r--r-- | nick.c | 21 | ||||
| -rw-r--r-- | nick.h | 12 | ||||
| -rw-r--r-- | protocols/oscar/aim.h | 6 | ||||
| -rw-r--r-- | protocols/oscar/chat.c | 25 | ||||
| -rw-r--r-- | protocols/oscar/im.c | 26 | ||||
| -rw-r--r-- | protocols/oscar/oscar.c | 168 | ||||
| -rw-r--r-- | protocols/oscar/tlv.c | 25 | ||||
| -rw-r--r-- | protocols/proxy.c | 22 | ||||
| -rw-r--r-- | protocols/proxy.h | 2 | ||||
| -rw-r--r-- | storage.c | 185 | ||||
| -rw-r--r-- | storage.h | 66 | ||||
| -rw-r--r-- | storage_text.c | 314 | ||||
| -rw-r--r-- | unix.c | 17 | 
30 files changed, 961 insertions, 462 deletions
| @@ -7,3 +7,5 @@ help.txt  debian  build-arch-stamp  tags +decode +encode @@ -9,7 +9,7 @@  -include Makefile.settings  # Program variables -objects = account.o bitlbee.o commands.o conf.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o unix.o url.o user.o +objects = account.o bitlbee.o commands.o conf.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o unix.o url.o user.o storage_text.o storage.o  subdirs = protocols  # Expansion of variables @@ -26,7 +26,6 @@  #define BITLBEE_CORE  #include "bitlbee.h"  #include "commands.h" -#include "crypting.h"  #include "protocols/nogaim.h"  #include "help.h"  #include <signal.h> @@ -164,7 +163,15 @@ gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condit  		irc_free( irc );  		return FALSE;  	}  -		 +	 +	/* Very naughty, go read the RFCs! >:) */ +	if( irc->readbuffer && ( strlen( irc->readbuffer ) > 1024 ) ) +	{ +		log_message( LOGLVL_ERROR, "Maximum line length exceeded." ); +		irc_free( irc ); +		return FALSE; +	} +	  	return TRUE;  } @@ -235,224 +242,6 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi  	}  } -int bitlbee_load( irc_t *irc, char* password ) -{ -	char s[512]; -	char *line; -	int proto; -	char nick[MAX_NICK_LENGTH+1]; -	FILE *fp; -	user_t *ru = user_find( irc, ROOT_NICK ); -	 -	if( irc->status == USTATUS_IDENTIFIED ) -		return( 1 ); -	 -	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); -   	fp = fopen( s, "r" ); -   	if( !fp ) return( 0 ); -	 -	fscanf( fp, "%32[^\n]s", s ); -	if( setpass( irc, password, s ) < 0 ) -	{ -		fclose( fp ); -		return( -1 ); -	} -	 -	/* Do this now. If the user runs with AuthMode = Registered, the -	   account command will not work otherwise. */ -	irc->status = USTATUS_IDENTIFIED; -	 -	while( fscanf( fp, "%511[^\n]s", s ) > 0 ) -	{ -		fgetc( fp ); -		line = deobfucrypt( irc, s ); -		root_command_string( irc, ru, line, 0 ); -		g_free( line ); -	} -	fclose( fp ); -	 -	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); -	fp = fopen( s, "r" ); -	if( !fp ) return( 0 ); -	while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) -	{ -		http_decode( s ); -		nick_set( irc, s, proto, nick ); -	} -	fclose( fp ); -	 -	if( set_getint( irc, "auto_connect" ) ) -	{ -		strcpy( s, "account on" );	/* Can't do this directly because r_c_s alters the string */ -		root_command_string( irc, ru, s, 0 ); -	} -	 -	return( 1 ); -} - -int bitlbee_save( irc_t *irc ) -{ -	char s[512]; -	char path[512], new_path[512]; -	char *line; -	nick_t *n; -	set_t *set; -	mode_t ou = umask( 0077 ); -	account_t *a; -	FILE *fp; -	char *hash; -	 -	/*\ -	 *  [SH] Nothing should be saved if no password is set, because the -	 *  password is not set if it was wrong, or if one is not identified -	 *  yet. This means that a malicious user could easily overwrite -	 *  files owned by someone else: -	 *  a Bad Thing, methinks -	\*/ - -	/* [WVG] No? Really? */ - -	/*\ -	 *  [SH] Okay, okay, it wasn't really Wilmer who said that, it was -	 *  me. I just thought it was funny. -	\*/ -	 -	hash = hashpass( irc ); -	if( hash == NULL ) -	{ -		irc_usermsg( irc, "Please register yourself if you want to save your settings." ); -		return( 0 ); -	} -	 -	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" ); -	fp = fopen( path, "w" ); -	if( !fp ) return( 0 ); -	for( n = irc->nicks; n; n = n->next ) -	{ -		strcpy( s, n->handle ); -		s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */ -		http_encode( s ); -		g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", n->proto, n->nick ); -		if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 ) -		{ -			irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); -			fclose( fp ); -			return( 0 ); -		} -	} -	if( fclose( fp ) != 0 ) -	{ -		irc_usermsg( irc, "fclose() reported an error. Disk full?" ); -		return( 0 ); -	} -   -	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); -	if( unlink( new_path ) != 0 ) -	{ -		if( errno != ENOENT ) -		{ -			irc_usermsg( irc, "Error while removing old .nicks file" ); -			return( 0 ); -		} -	} -	if( rename( path, new_path ) != 0 ) -	{ -		irc_usermsg( irc, "Error while renaming new .nicks file" ); -		return( 0 ); -	} -	 -	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" ); -	fp = fopen( path, "w" ); -	if( !fp ) return( 0 ); -	if( fprintf( fp, "%s", hash ) != strlen( hash ) ) -	{ -		irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); -		fclose( fp ); -		return( 0 ); -	} -	g_free( hash ); - -	for( a = irc->accounts; a; a = a->next ) -	{ -		if( a->protocol == PROTO_OSCAR || a->protocol == PROTO_ICQ || a->protocol == PROTO_TOC ) -			g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server ); -		else -			g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"", -			            proto_name[a->protocol], a->user, a->pass, a->server ? a->server : "" ); -		 -		line = obfucrypt( irc, s ); -		if( *line ) -		{ -			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) -			{ -				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); -				fclose( fp ); -				return( 0 ); -			} -		} -		g_free( line ); -	} -	 -	for( set = irc->set; set; set = set->next ) -	{ -		if( set->value && set->def ) -		{ -			g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value ); -			line = obfucrypt( irc, s ); -			if( *line ) -			{ -				if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) -				{ -					irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); -					fclose( fp ); -					return( 0 ); -				} -			} -			g_free( line ); -		} -	} -	 -	if( strcmp( irc->mynick, ROOT_NICK ) != 0 ) -	{ -		g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick ); -		line = obfucrypt( irc, s ); -		if( *line ) -		{ -			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) -			{ -				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); -				fclose( fp ); -				return( 0 ); -			} -		} -		g_free( line ); -	} -	if( fclose( fp ) != 0 ) -	{ -		irc_usermsg( irc, "fclose() reported an error. Disk full?" ); -		return( 0 ); -	} -	 - 	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); - 	if( unlink( new_path ) != 0 ) -	{ -		if( errno != ENOENT ) -		{ -			irc_usermsg( irc, "Error while removing old .accounts file" ); -			return( 0 ); -		} -	} -	if( rename( path, new_path ) != 0 ) -	{ -		irc_usermsg( irc, "Error while renaming new .accounts file" ); -		return( 0 ); -	} -	 -	umask( ou ); -	 -	return( 1 ); -} -  void bitlbee_shutdown( gpointer data )  {  	/* Try to save data for all active connections (if desired). */ @@ -29,7 +29,7 @@  #define _GNU_SOURCE /* Stupid GNU :-P */  #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.0pre" +#define BITLBEE_VERSION "BZR"  #define VERSION BITLBEE_VERSION  #define MAX_STRING 128 @@ -99,6 +99,7 @@  extern char *CONF_FILE;  #include "irc.h" +#include "storage.h"  #include "set.h"  #include "protocols/nogaim.h"  #include "commands.h" @@ -114,6 +115,7 @@ typedef struct global_t {  	int listen_socket;  	help_t *help;  	conf_t *conf; +	GList *storage; /* The first backend in the list will be used for saving */  	char *helpfile;  	GMainLoop *loop;  } global_t; @@ -126,8 +128,6 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi  int root_command_string( irc_t *irc, user_t *u, char *command, int flags );  int root_command( irc_t *irc, char *command[] ); -int bitlbee_load( irc_t *irc, char *password ); -int bitlbee_save( irc_t *irc );  void bitlbee_shutdown( gpointer data );  double gettime( void );  G_MODULE_EXPORT void http_encode( char *s ); @@ -85,54 +85,47 @@ int cmd_help( irc_t *irc, char **cmd )  int cmd_identify( irc_t *irc, char **cmd )  { -	int checkie = bitlbee_load( irc, cmd[1] ); +	storage_status_t status = storage_load( irc->nick, cmd[1], irc ); -	if( checkie == -1 ) -	{ +	switch (status) { +	case STORAGE_INVALID_PASSWORD:  		irc_usermsg( irc, "Incorrect password" ); -	} -	else if( checkie == 0 ) -	{ +		break; +	case STORAGE_NO_SUCH_USER:  		irc_usermsg( irc, "The nick is (probably) not registered" ); -	} -	else if( checkie == 1 ) -	{ +		break; +	case STORAGE_OK:  		irc_usermsg( irc, "Password accepted" ); -	} -	else -	{ +		break; +	default:  		irc_usermsg( irc, "Something very weird happened" ); +		break;  	} -	 +  	return( 0 );  }  int cmd_register( irc_t *irc, char **cmd )  { -	int checkie; -	char path[512]; -	  	if( global.conf->authmode == AUTHMODE_REGISTERED )  	{  		irc_usermsg( irc, "This server does not allow registering new accounts" );  		return( 0 );  	} -	 -	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); -	checkie = access( path, F_OK ); -	 -	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); -	checkie += access( path, F_OK ); -	 -	if( checkie == -2 ) -	{ -		setpassnc( irc, cmd[1] ); -		root_command_string( irc, user_find( irc, irc->mynick ), "save", 0 ); -		irc->status = USTATUS_IDENTIFIED; -	} -	else -	{ -		irc_usermsg( irc, "Nick is already registered" ); + +	irc_setpass( irc, cmd[1] ); +	switch( storage_save( irc, FALSE )) { +		case STORAGE_ALREADY_EXISTS: +			irc_usermsg( irc, "Nick is already registered" ); +			break; +			 +		case STORAGE_OK: +			irc->status = USTATUS_IDENTIFIED; +			break; + +		default: +			irc_usermsg( irc, "Error registering" ); +			break;  	}  	return( 0 ); @@ -140,35 +133,24 @@ int cmd_register( irc_t *irc, char **cmd )  int cmd_drop( irc_t *irc, char **cmd )  { -	char s[512]; -	FILE *fp; +	storage_status_t status; -	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); -	fp = fopen( s, "r" ); -	if( !fp ) -	{ +	status = storage_remove (irc->nick, cmd[1]); +	switch (status) { +	case STORAGE_NO_SUCH_USER:  		irc_usermsg( irc, "That account does not exist" );  		return( 0 ); -	} -	 -	fscanf( fp, "%32[^\n]s", s ); -	fclose( fp ); -	if( setpass( irc, cmd[1], s ) < 0 ) -	{ -		irc_usermsg( irc, "Incorrect password" ); +	case STORAGE_INVALID_PASSWORD: +		irc_usermsg( irc, "Password invalid" ); +		return( 0 ); +	case STORAGE_OK: +		irc_setpass( irc, NULL ); +		irc_usermsg( irc, "Account `%s' removed", irc->nick ); +		return( 0 ); +	default: +		irc_usermsg( irc, "Error: '%d'", status );  		return( 0 );  	} -	 -	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); -	unlink( s ); -	 -	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); -	unlink( s ); -	 -	setpassnc( irc, NULL ); -	irc_usermsg( irc, "Files belonging to account `%s' removed", irc->nick ); -	 -	return( 0 );  }  int cmd_account( irc_t *irc, char **cmd ) @@ -633,7 +615,7 @@ int cmd_set( irc_t *irc, char **cmd )  int cmd_save( irc_t *irc, char **cmd )  { -	if( bitlbee_save( irc ) ) +	if( storage_save( irc, TRUE ) == STORAGE_OK )  		irc_usermsg( irc, "Configuration saved" );  	else  		irc_usermsg( irc, "Configuration could not be saved!" ); @@ -49,6 +49,7 @@ conf_t *conf_load( int argc, char *argv[] )  	conf->port = 6667;  	conf->nofork = 0;  	conf->verbose = 0; +	conf->primary_storage = "text";  	conf->runmode = RUNMODE_INETD;  	conf->authmode = AUTHMODE_OPEN;  	conf->password = NULL; @@ -197,6 +198,16 @@ static int conf_loadini( conf_t *conf, char *file )  				g_free( conf->motdfile );  				conf->motdfile = g_strdup( ini->value );  			} +			else if( g_strcasecmp( ini->key, "account_storage" ) == 0 ) +			{ +				g_free( conf->primary_storage ); +				conf->primary_storage = g_strdup( ini->value ); +			} +			else if( g_strcasecmp( ini->key, "account_storage_migrate" ) == 0 ) +			{ +				g_strfreev( conf->migrate_storage ); +				conf->migrate_storage = g_strsplit( ini->value, " \t,;", -1 ); +			}  			else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 )  			{  				if( sscanf( ini->value, "%d", &i ) != 1 ) @@ -41,6 +41,8 @@ typedef struct conf  	char *hostname;  	char *configdir;  	char *motdfile; +	char *primary_storage; +	char **migrate_storage;  	int ping_interval;  	int ping_timeout;  } conf_t; @@ -342,33 +342,28 @@ echo  echo Architecture: $arch  case "$arch" in  Linux ) -	echo 'Linux.'  ;;  GNU/* ) -	echo 'Debian with non-Linux kernel?'  ;;  *BSD ) -	echo '*BSD.'  	echo 'EFLAGS+=-liconv' >> Makefile.settings;  ;;  SunOS ) -	echo 'Solaris.'  	echo 'EFLAGS+=-lresolv -lnsl -lsocket' >> Makefile.settings  	echo 'STRIP=\# skip strip' >> Makefile.settings  	echo 'EFLAGS+=-liconv' >> Makefile.settings;  ;;  Darwin ) -	echo 'Darwin/Mac OS X.'  	echo 'EFLAGS+=-liconv' >> Makefile.settings;  ;;  IRIX ) -	echo 'IRIX.'  ;;  CYGWIN* )  	echo 'Cygwin is not officially supported.'  ;;  * ) -	echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV. Please report any problems to <wilmer@gaast.net>.' +	echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV.' +	echo 'Please report any problems at http://bugs.bitlbee.org/.'  ;;  esac @@ -28,64 +28,20 @@     included if CRYPTING_MAIN is defined. Or just do "make decode" and     the programs will be built. */ -#ifndef CRYPTING_MAIN -#define BITLBEE_CORE -#include "bitlbee.h" -#include "irc.h"  #include "md5.h"  #include "crypting.h"  #include <string.h>  #include <stdio.h>  #include <stdlib.h> -#else - -typedef struct irc -{ -	char *password; -} irc_t; - -#define set_add( a, b, c, d ) -#define set_find( a, b ) NULL - -#include "md5.h" -#include "crypting.h" -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -#define irc_usermsg - -#endif -  /*\   * [SH] Do _not_ call this if it's not entirely sure that it will not cause   * harm to another users file, since this does not check the password for   * correctness.  \*/ -/* USE WITH CAUTION! -   Sets pass without checking */ -void setpassnc (irc_t *irc, char *pass) { -	if (!set_find (irc, "password")) -		set_add (irc, "password", NULL, passchange); -	 -	if (irc->password) g_free (irc->password); -	 -	if (pass) { -		irc->password = g_strdup (pass); -		irc_usermsg (irc, "Password successfully changed"); -	} else { -		irc->password = NULL; -	} -} - -char *passchange (irc_t *irc, void *set, char *value) { -	setpassnc (irc, value); -	return (NULL); -} - -int setpass (irc_t *irc, char *pass, char* md5sum) { +int checkpass (const char *pass, const char *md5sum) +{  	md5_state_t md5state;  	md5_byte_t digest[16];  	int i, j; @@ -102,27 +58,25 @@ int setpass (irc_t *irc, char *pass, char* md5sum) {  		if (digits[0] != md5sum[j]) return (-1);  		if (digits[1] != md5sum[j + 1]) return (-1);  	} -	 -	/* If pass is correct, we end up here and we set the pass */ -	setpassnc (irc, pass); -	 -	return (0); + +	return( 0 );  } -char *hashpass (irc_t *irc) { + +char *hashpass (const char *password) +{  	md5_state_t md5state;  	md5_byte_t digest[16];  	int i;  	char digits[3];  	char *rv; -	if (irc->password == NULL) return (NULL); +	if (password == NULL) return (NULL); -	rv = (char *)g_malloc (33); -	memset (rv, 0, 33); +	rv = g_new0 (char, 33);  	md5_init (&md5state); -	md5_append (&md5state, (unsigned char *)irc->password, strlen (irc->password)); +	md5_append (&md5state, (const unsigned char *)password, strlen (password));  	md5_finish (&md5state, digest);  	for (i = 0; i < 16; i++) { @@ -134,47 +88,46 @@ char *hashpass (irc_t *irc) {  	return (rv);  } -char *obfucrypt (irc_t *irc, char *line) { +char *obfucrypt (char *line, const char *password)  +{  	int i, j;  	char *rv; -	if (irc->password == NULL) return (NULL); +	if (password == NULL) return (NULL); -	rv = (char *)g_malloc (strlen (line) + 1); -	memset (rv, '\0', strlen (line) + 1); +	rv = g_new0 (char, strlen (line) + 1);  	i = j = 0;  	while (*line) {  		/* Encrypt/obfuscate the line, using the password */  		if (*(signed char*)line < 0) *line = - (*line); -		if (((signed char*)irc->password)[i] < 0) irc->password[i] = - irc->password[i]; -		rv[j] = *line + irc->password[i]; /* Overflow intended */ +		rv[j] = *line + password[i]; /* Overflow intended */  		line++; -		if (!irc->password[++i]) i = 0; +		if (!password[++i]) i = 0;  		j++;  	}  	return (rv);  } -char *deobfucrypt (irc_t *irc, char *line) { +char *deobfucrypt (char *line, const char *password)  +{  	int i, j;  	char *rv; -	if (irc->password == NULL) return (NULL); +	if (password == NULL) return (NULL); -	rv = (char *)g_malloc (strlen (line) + 1); -	memset (rv, '\0', strlen (line) + 1); +	rv = g_new0 (char, strlen (line) + 1);  	i = j = 0;  	while (*line) {  		/* Decrypt/deobfuscate the line, using the pass */ -		rv[j] = *line - irc->password[i]; /* Overflow intended */ +		rv[j] = *line - password[i]; /* Overflow intended */  		line++; -		if (!irc->password[++i]) i = 0; +		if (!password[++i]) i = 0;  		j++;  	} @@ -188,9 +141,8 @@ char *deobfucrypt (irc_t *irc, char *line) {  int main( int argc, char *argv[] )  { -	irc_t *irc = g_malloc( sizeof( irc_t ) );  	char *hash, *action, line[256]; -	char* (*func)( irc_t *, char * ); +	char* (*func)( char *, const char * );  	if( argc < 2 )  	{ @@ -200,10 +152,7 @@ int main( int argc, char *argv[] )  		return( 1 );  	} -	memset( irc, 0, sizeof( irc_t ) ); -	irc->password = g_strdup( argv[1] ); -	 -	hash = hashpass( irc ); +	hash = hashpass( argv[1] );  	action = argv[0] + strlen( argv[0] ) - strlen( "encode" );  	if( strcmp( action, "encode" ) == 0 ) @@ -235,7 +184,7 @@ int main( int argc, char *argv[] )  		/* Flush the newline */  		fgetc( stdin ); -		out = func( irc, line ); +		out = func( line, argv[1] );  		printf( "%s\n", out );  		g_free( out );  	} @@ -23,9 +23,7 @@    Suite 330, Boston, MA  02111-1307  USA  */ -void setpassnc (irc_t *irc, char *pass); /* USE WITH CAUTION! */ -char *passchange (irc_t *irc, void *set, char *value); -int setpass (irc_t *irc, char *pass, char* md5sum); -char *hashpass (irc_t *irc); -char *obfucrypt (irc_t *irc, char *line); -char *deobfucrypt (irc_t *irc, char *line); +int checkpass (const char *password, const char *md5sum); +char *hashpass (const char *password); +char *obfucrypt (char *line, const char *password); +char *deobfucrypt (char *line, const char *password); diff --git a/doc/AUTHORS b/doc/AUTHORS index 82d81862..6dab1040 100644 --- a/doc/AUTHORS +++ b/doc/AUTHORS @@ -3,7 +3,7 @@ Current developers:  Wilmer van der Gaast <wilmer@gaast.net>  	Main developer -Jelmer 'ctrlsoft' Vernooij <jelmer@nl.linux.org> +Jelmer 'ctrlsoft' Vernooij <jelmer@samba.org>  	Documentation, general hacking, Win32 port  Maurits Dijkstra <mauritsd@xs4all.nl> diff --git a/doc/CHANGES b/doc/CHANGES index 9ccf77c0..7b95e8cb 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -21,8 +21,15 @@ Version 1.0:  - Jabber module only accepts ports 5220-5229 now, to prevent people from    abusing it as a port scanner. We aren't aware of any Jabber server that    runs on other ports than those. If you are, please warn us. - -Finished ... +- Send flood protection can't be enabled anymore. It was disabled by default +  for a good reason for some time already, but some package maintainers +  turned it back on while it's way too unreliable and trigger-happy to be +  used. +- Removed TODO file, the current to-do list is always in the on-line bug +  tracking system. +- Fixed a potential DoS bug in input handling. + +Finished 4 Dec 2005  Version 0.99:  - Fixed memory initialization bug in OSCAR module that caused crashes on @@ -45,8 +45,11 @@ Q: The messages I send and/or receive look weird. I see weird characters and     annoying HTML codes. Or, BitlBee does evil things when I send messages with     non-ASCII characters!  A: You probably have to change some settings. To get rid of HTML in messages, -   see "help set html". If you seem to have problems with your charset, see -   "help set charset". +   see "help set strip_html". If you seem to have problems with your charset, +   see "help set charset". +    +   Although actually most of these problems should be gone by now. So if you +   can't get things to work well, you might have found a bug.  Q: Is BitlBee forked from Gaim?  A: BitlBee 0.7 was, sort-of. It contained a lot of code from Gaim 0.58 @@ -107,11 +107,10 @@ not daemons).  See utils/bitlbeed.c for more information about the program. -Just a little note: We run our public server im.bitlbee.org for a couple of -months now, and so far we haven't experienced this problem yet. The only -BitlBee processes killed because of CPU-time overuse were running for a long -time already, they were usually killed during the MSN login process (which -is quite CPU-time consuming). +Just a little note: Now that we reach version 1.0, this shouldn't be that +much of an issue anymore. However, on a public server, especially if you +also use it for other things, it can't hurt to protect yourself against +possible problems.  USAGE @@ -145,6 +144,12 @@ WEBSITE  You can find new releases of BitlBee at:  http://www.bitlbee.org/ +The bug tracking system: +http://bugs.bitlbee.org/ + +Our version control system is Bazaar-NG. Our repository is at: +http://code.bitlbee.org/ +  A NOTE ON ENCRYPTION  ==================== @@ -186,5 +191,5 @@ also licensed under the GPL.  	BitlBee - An IRC to other chat networks gateway   	          <http://www.bitlbee.org/> -	Copyright (C) 2002-2004  Wilmer van der Gaast <wilmer@gaast.net> +	Copyright (C) 2002-2005  Wilmer van der Gaast <wilmer@gaast.net>  	                         and others diff --git a/doc/user-guide/user-guide.xml b/doc/user-guide/user-guide.xml index e6a6cd66..5b881fb2 100644 --- a/doc/user-guide/user-guide.xml +++ b/doc/user-guide/user-guide.xml @@ -23,7 +23,10 @@      </legalnotice>     <releaseinfo> -   		This is the initial release of the BitlBee User Guide. +   		This is the BitlBee User Guide. For now, the on-line help is +		the most up-to-date documentation. Although this document shares +		some parts with the on-line help system, other parts might be +		very outdated.     </releaseinfo>    </bookinfo> @@ -31,6 +31,12 @@ static gboolean irc_userping( gpointer _irc );  GSList *irc_connection_list = NULL; +static char *passchange (irc_t *irc, void *set, char *value)  +{ +	irc_setpass (irc, value); +	return (NULL); +} +  irc_t *irc_new( int fd )  {  	irc_t *irc = g_new0( irc_t, 1 ); @@ -128,6 +134,7 @@ irc_t *irc_new( int fd )  	set_add( irc, "strip_html", "true", NULL );  	set_add( irc, "to_char", ": ", set_eval_to_char );  	set_add( irc, "typing_notice", "false", set_eval_bool ); +	set_add( irc, "password", NULL, passchange);  	conf_loaddefaults( irc ); @@ -153,7 +160,7 @@ void irc_free(irc_t * irc)  	log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );  	if( irc->status >= USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) )  -		if( !bitlbee_save( irc ) ) +		if( storage_save( irc, TRUE ) != STORAGE_OK )  			irc_usermsg( irc, "Error while saving settings!" );  	if( irc->ping_source_id > 0 ) @@ -260,6 +267,20 @@ void irc_free(irc_t * irc)  		g_main_quit( global.loop );  } +/* USE WITH CAUTION! +   Sets pass without checking */ +void irc_setpass (irc_t *irc, const char *pass)  +{ +	if (irc->password) g_free (irc->password); +	 +	if (pass) { +		irc->password = g_strdup (pass); +		irc_usermsg (irc, "Password successfully changed"); +	} else { +		irc->password = NULL; +	} +} +  int irc_process( irc_t *irc )  {  	char **lines, *temp;	 @@ -136,6 +136,7 @@ void irc_kill( irc_t *irc, user_t *u );  void irc_invite( irc_t *irc, char *nick, char *channel );  void irc_whois( irc_t *irc, char *nick );  int irc_away( irc_t *irc, char *away ); +void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */  int irc_send( irc_t *irc, char *nick, char *s, int flags );  int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); @@ -26,7 +26,7 @@  #define BITLBEE_CORE  #include "bitlbee.h" -void nick_set( irc_t *irc, char *handle, int proto, char *nick ) +void nick_set( irc_t *irc, const char *handle, int proto, const char *nick )  {  	nick_t *m = NULL, *n = irc->nicks; @@ -55,7 +55,7 @@ void nick_set( irc_t *irc, char *handle, int proto, char *nick )  	nick_strip( n->nick );  } -char *nick_get( irc_t *irc, char *handle, int proto, const char *realname ) +char *nick_get( irc_t *irc, const char *handle, int proto, const char *realname )  {  	static char nick[MAX_NICK_LENGTH+1];  	nick_t *n = irc->nicks; @@ -128,7 +128,7 @@ char *nick_get( irc_t *irc, char *handle, int proto, const char *realname )  	return( nick );  } -void nick_del( irc_t *irc, char *nick ) +void nick_del( irc_t *irc, const char *nick )  {  	nick_t *l = NULL, *n = irc->nicks; @@ -175,9 +175,9 @@ void nick_strip( char * nick )  		nick[j++] = '\0';  } -int nick_ok( char *nick ) +int nick_ok( const char *nick )  { -	char *s; +	const char *s;  	/* Empty/long nicks are not allowed */  	if( !*nick || strlen( nick ) > MAX_NICK_LENGTH ) @@ -236,7 +236,7 @@ int nick_uc( char *nick )  	return( 1 );  } -int nick_cmp( char *a, char *b ) +int nick_cmp( const char *a, const char *b )  {  	char aa[1024] = "", bb[1024] = ""; @@ -252,12 +252,7 @@ int nick_cmp( char *a, char *b )  	}  } -char *nick_dup( char *nick ) +char *nick_dup( const char *nick )  { -	char *cp; -	 -	cp = g_new0 ( char, MAX_NICK_LENGTH + 1 ); -	strncpy( cp, nick, MAX_NICK_LENGTH ); -	 -	return( cp ); +	return g_strndup( nick, MAX_NICK_LENGTH );  } @@ -31,13 +31,13 @@ typedef struct __NICK  	struct __NICK *next;  } nick_t; -void nick_set( irc_t *irc, char *handle, int proto, char *nick ); -char *nick_get( irc_t *irc, char *handle, int proto, const char *realname ); -void nick_del( irc_t *irc, char *nick ); +void nick_set( irc_t *irc, const char *handle, int proto, const char *nick ); +char *nick_get( irc_t *irc, const char *handle, int proto, const char *realname ); +void nick_del( irc_t *irc, const char *nick );  void nick_strip( char *nick ); -int nick_ok( char *nick ); +int nick_ok( const char *nick );  int nick_lc( char *nick );  int nick_uc( char *nick ); -int nick_cmp( char *a, char *b ); -char *nick_dup( char *nick ); +int nick_cmp( const char *a, const char *b ); +char *nick_dup( const char *nick ); diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h index f7bf1a8e..24cd7730 100644 --- a/protocols/oscar/aim.h +++ b/protocols/oscar/aim.h @@ -465,6 +465,7 @@ int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 type, const c  int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v);  int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps);  int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type); +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance);  int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui);  int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl);  int aim_counttlvchain(aim_tlvlist_t **list); @@ -571,6 +572,11 @@ struct aim_chat_roominfo {  	unsigned short instance;  }; +struct aim_chat_invitation { +	struct gaim_connection * gc; +	char * name; +	guint8 exchange; +};  #define AIM_VISIBILITYCHANGE_PERMITADD    0x05  #define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 diff --git a/protocols/oscar/chat.c b/protocols/oscar/chat.c index 60aabc79..033c2577 100644 --- a/protocols/oscar/chat.c +++ b/protocols/oscar/chat.c @@ -183,31 +183,6 @@ int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const  	return 0;  } -static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) -{ -	guint8 *buf; -	int buflen; -	aim_bstream_t bs; - -	buflen = 2 + 1 + strlen(roomname) + 2; -	 -	if (!(buf = g_malloc(buflen))) -		return 0; - -	aim_bstream_init(&bs, buf, buflen); - -	aimbs_put16(&bs, exchange); -	aimbs_put8(&bs, strlen(roomname)); -	aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); -	aimbs_put16(&bs, instance); - -	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); - -	g_free(buf); - -	return 0; -} -  /*   * Join a room of name roomname.  This is the first step to joining an    * already created room.  It's basically a Service Request for  diff --git a/protocols/oscar/im.c b/protocols/oscar/im.c index 085687e0..c829d409 100644 --- a/protocols/oscar/im.c +++ b/protocols/oscar/im.c @@ -1368,6 +1368,30 @@ static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r  	return ret;  } + +static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + +	/* XXX aim_chat_roominfo_free() */ +	g_free(args->info.chat.roominfo.name); + +	return; +} + +static void incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + +	/* +	 * Chat room info. +	 */ +	if (servdata) +		aim_chat_readroominfo(servdata, &args->info.chat.roominfo); + +	args->destructor = (void *)incomingim_ch2_chat_free; + +	return; +} +  static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)  { @@ -1616,6 +1640,8 @@ static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r  	if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)  		incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); +	else if (args.reqclass & AIM_CAPS_CHAT) +		incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr);  	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 76599d8c..240bab14 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -61,7 +61,7 @@  /* Don't know if support for UTF8 is really working. For now it's UTF16 here.     static int gaim_caps = AIM_CAPS_UTF8; */ -static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY; +static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY | AIM_CAPS_CHAT;  static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02};  struct oscar_data { @@ -155,7 +155,6 @@ static char *extract_name(const char *name) {  	return tmp;  } -#if 0  static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) {  	GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats;  	struct chat_connection *c = NULL; @@ -170,7 +169,7 @@ static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int i  	return c;  } -#endif +  static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc,  							aim_conn_t *conn) { @@ -1075,14 +1074,15 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_  	return 1;  } +void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv); +void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv); +	  static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { -#if 0  	struct gaim_connection *gc = sess->aux_data; -#endif  	if (args->status != AIM_RENDEZVOUS_PROPOSE)  		return 1; -#if 0 +  	if (args->reqclass & AIM_CAPS_CHAT) {  		char *name = extract_name(args->info.chat.roominfo.name);  		int *exch = g_new0(int, 1); @@ -1090,15 +1090,23 @@ static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_  		m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name));  		*exch = args->info.chat.roominfo.exchange;  		m = g_list_append(m, exch); -		serv_got_chat_invite(gc, -				     name ? name : args->info.chat.roominfo.name, -				     userinfo->sn, -				     (char *)args->msg, -				     m); + +		char txt[1024]; + +		g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg ); + +		struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1); + +		inv->gc = gc; +		inv->exchange = *exch; +		inv->name = g_strdup(name); +		 +		do_ask_dialog( gc, txt, inv, oscar_accept_chat, oscar_reject_chat); +	  		if (name)  			g_free(name);  	} -#endif +  	return 1;  } @@ -2493,6 +2501,138 @@ int oscar_send_typing(struct gaim_connection *gc, char * who, int typing)  	return( aim_im_sendmtn(od->sess, 1, who, typing ? 0x0002 : 0x0000) );  } +int oscar_chat_send(struct gaim_connection * gc, int id, char *message) +{ +	struct oscar_data * od = (struct oscar_data*)gc->proto_data; +	struct chat_connection * ccon; +	 +	if(!(ccon = find_oscar_chat(gc, id))) +		return -1; +	 +	int ret; +	guint8 len = strlen(message); +	char *s; +	  	 +	for (s = message; *s; s++) +		if (*s & 128) +			break; +	  	 +	/* Message contains high ASCII chars, time for some translation! */ +	if (*s) { +		s = g_malloc(BUF_LONG); +		/* Try if we can put it in an ISO8859-1 string first. +		   If we can't, fall back to UTF16. */ +		if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) { +			len = ret; +		} else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { +			len = ret; +		} else { +			/* OOF, translation failed... Oh well.. */ +			g_free( s ); +			s = message; +		} +	} else { +		s = message; +	} +	  	 +	ret = aim_chat_send_im(od->sess, ccon->conn, AIM_CHATFLAGS_NOREFLECT, s, len); +	  	 +	if (s != message) {	 +		g_free(s); +  } +   +  return (ret >= 0); +} + +void oscar_chat_invite(struct gaim_connection * gc, int id, char *message, char *who) +{ +	struct oscar_data * od = (struct oscar_data *)gc->proto_data; +	struct chat_connection *ccon = find_oscar_chat(gc, id); +	 +	if (ccon == NULL) +		return; +	 +	aim_chat_invite(od->sess, od->conn, who, message ? message : "", +					ccon->exchange, ccon->name, 0x0); +} + +void oscar_chat_kill(struct gaim_connection *gc, struct chat_connection *cc) +{ +	struct oscar_data *od = (struct oscar_data *)gc->proto_data; + +	/* Notify the conversation window that we've left the chat */ +	serv_got_chat_left(gc, cc->id); + +	/* Destroy the chat_connection */ +	od->oscar_chats = g_slist_remove(od->oscar_chats, cc); +	if (cc->inpa > 0) +		gaim_input_remove(cc->inpa); +	aim_conn_kill(od->sess, &cc->conn); +	g_free(cc->name); +	g_free(cc->show); +	g_free(cc); +} + +void oscar_chat_leave(struct gaim_connection * gc, int id) +{ +	struct chat_connection * ccon = find_oscar_chat(gc, id); + +	if(ccon == NULL) +		return; + +	oscar_chat_kill(gc, ccon); +} + +int oscar_chat_join(struct gaim_connection * gc, char * name) +{ +    struct oscar_data * od = (struct oscar_data *)gc->proto_data; +	 +	aim_conn_t * cur; + +	if((cur = aim_getconn_type(od->sess, AIM_CONN_TYPE_CHATNAV))) { +	 +		return (aim_chatnav_createroom(od->sess, cur, name, 4) == 0); +	 +	} else { +		struct create_room * cr = g_new0(struct create_room, 1); +		cr->exchange = 4; +		cr->name = g_strdup(name); +		od->create_rooms = g_slist_append(od->create_rooms, cr); +		aim_reqservice(od->sess, od->conn, AIM_CONN_TYPE_CHATNAV); +		return 1; +	} +} + +int oscar_chat_open(struct gaim_connection * gc, char *who) +{ +	struct oscar_data * od = (struct oscar_data *)gc->proto_data; + +	static int chat_id = 0; +	char * chatname = g_new0(char, strlen(gc->username)+4); +	g_snprintf(chatname, strlen(gc->username) + 4, "%s%d", gc->username, chat_id++); +   +	int ret = oscar_chat_join(gc, chatname); + +	aim_chat_invite(od->sess, od->conn, who, "", 4, chatname, 0x0); + +	g_free(chatname); +	 +	return ret; +} + +void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv) +{ +	oscar_chat_join(inv->gc, inv->name); +	g_free(inv->name); +	g_free(inv); +} + +void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv) +{ +	g_free(inv->name); +	g_free(inv); +} +  static struct prpl *my_protocol = NULL;  void oscar_init(struct prpl *ret) { @@ -2506,6 +2646,10 @@ void oscar_init(struct prpl *ret) {  	ret->get_away = oscar_get_away;  	ret->add_buddy = oscar_add_buddy;  	ret->remove_buddy = oscar_remove_buddy; +	ret->chat_send = oscar_chat_send; +	ret->chat_invite = oscar_chat_invite; +	ret->chat_leave = oscar_chat_leave; +	ret->chat_open = oscar_chat_open;  	ret->add_permit = oscar_add_permit;  	ret->add_deny = oscar_add_deny;  	ret->rem_permit = oscar_rem_permit; diff --git a/protocols/oscar/tlv.c b/protocols/oscar/tlv.c index 11b89758..9d827caf 100644 --- a/protocols/oscar/tlv.c +++ b/protocols/oscar/tlv.c @@ -339,6 +339,31 @@ int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvl  	return buflen;  } +int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) +{ +	guint8 *buf; +	int buflen; +	aim_bstream_t bs; + +	buflen = 2 + 1 + strlen(roomname) + 2; +	 +	if (!(buf = g_malloc(buflen))) +		return 0; + +	aim_bstream_init(&bs, buf, buflen); + +	aimbs_put16(&bs, exchange); +	aimbs_put8(&bs, strlen(roomname)); +	aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); +	aimbs_put16(&bs, instance); + +	aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); + +	g_free(buf); + +	return 0; +} +  /**   * aim_writetlvchain - Write a TLV chain into a data buffer.   * @buf: Destination buffer diff --git a/protocols/proxy.c b/protocols/proxy.c index fb87e356..0546f2d7 100644 --- a/protocols/proxy.c +++ b/protocols/proxy.c @@ -49,16 +49,6 @@  #define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)  #define GAIM_ERR_COND   (G_IO_HUP | G_IO_ERR | G_IO_NVAL) -/*FIXME*		 -	#ifndef _WIN32 -		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { -			closesocket(fd); -			g_free(phb); -			return -1; -		} -		fcntl(fd, F_SETFL, 0); -#endif*/ -  char proxyhost[128] = "";  int proxyport = 0;  int proxytype = PROXY_NONE; @@ -82,7 +72,7 @@ typedef struct _GaimIOClosure { -static struct sockaddr_in *gaim_gethostbyname(char *host, int port) +static struct sockaddr_in *gaim_gethostbyname(const char *host, int port)  {  	static struct sockaddr_in sin; @@ -153,7 +143,7 @@ static void gaim_io_connected(gpointer data, gint source, GaimInputCondition con  	}  } -static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)  {  	struct sockaddr_in *sin;  	int fd = -1; @@ -280,7 +270,7 @@ static void http_canwrite(gpointer data, gint source, GaimInputCondition cond)  	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb);  } -static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb)  {  	phb->host = g_strdup(host);  	phb->port = port; @@ -364,7 +354,7 @@ static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond)  	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb);  } -static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb)  {  	phb->host = g_strdup(host);  	phb->port = port; @@ -546,7 +536,7 @@ static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond)  	phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb);  } -static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb) +static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb)  {  	phb->host = g_strdup(host);  	phb->port = port; @@ -587,7 +577,7 @@ void gaim_input_remove(gint tag)  		g_source_remove(tag);  } -int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) +int proxy_connect(const char *host, int port, GaimInputFunction func, gpointer data)  {  	struct PHB *phb; diff --git a/protocols/proxy.h b/protocols/proxy.h index 7c34fc40..47c966d2 100644 --- a/protocols/proxy.h +++ b/protocols/proxy.h @@ -55,6 +55,6 @@ typedef void (*GaimInputFunction)(gpointer, gint, GaimInputCondition);  G_MODULE_EXPORT gint gaim_input_add(int, GaimInputCondition, GaimInputFunction, gpointer);  G_MODULE_EXPORT void gaim_input_remove(gint); -G_MODULE_EXPORT int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data); +G_MODULE_EXPORT int proxy_connect(const char *host, int port, GaimInputFunction func, gpointer data);  #endif /* _PROXY_H_ */ diff --git a/storage.c b/storage.c new file mode 100644 index 00000000..b766c9e3 --- /dev/null +++ b/storage.c @@ -0,0 +1,185 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Support for multiple storage backends */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "crypting.h" + +extern storage_t storage_text; + +static GList text_entry = { &storage_text, NULL, NULL }; +static GList *storage_backends = &text_entry; + +void register_storage_backend(storage_t *backend) +{ +	storage_backends = g_list_append(storage_backends, backend); +} + +static storage_t *storage_init_single(const char *name) +{ +	GList *gl; +	storage_t *st; + +	for (gl = storage_backends; gl; gl = gl->next) { +		st = gl->data; +		if (strcmp(st->name, name) == 0) +			break; +	} + +	if (gl == NULL)  +		return NULL; + +	if (st->init) +		st->init(); + +	return st; +} + +GList *storage_init(const char *primary, char **migrate) +{ +	GList *ret = NULL; +	int i; +	storage_t *storage; + +	storage = storage_init_single(primary); +	if (storage == NULL) +		return NULL; + +	ret = g_list_append(ret, storage); + +	for (i = 0; migrate && migrate[i]; i++) { +		storage = storage_init_single(migrate[i]); +	 +		if (storage) +			ret = g_list_append(ret, storage); +	} + +	return ret; +} + +storage_status_t storage_check_pass (const char *nick, const char *password) +{ +	GList *gl; +	 +	/* Loop until we don't get NO_SUCH_USER */ + +	for (gl = global.storage; gl; gl = gl->next) { +		storage_t *st = gl->data; +		storage_status_t status; + +		status = st->check_pass(nick, password); +		if (status != STORAGE_NO_SUCH_USER) +			return status; +	} +	 +	return STORAGE_NO_SUCH_USER; +} + +storage_status_t storage_load (const char *nick, const char *password, irc_t * irc) +{ +	GList *gl; +	 +	/* Loop until we don't get NO_SUCH_USER */ +	for (gl = global.storage; gl; gl = gl->next) { +		storage_t *st = gl->data; +		storage_status_t status; + +		status = st->load(nick, password, irc); +		if (status == STORAGE_OK) { +			irc_setpass(irc, password); +			return status; +		} +		 +		if (status != STORAGE_NO_SUCH_USER)  +			return status; +	} +	 +	return STORAGE_NO_SUCH_USER; +} + +storage_status_t storage_save (irc_t *irc, int overwrite) +{ +	return ((storage_t *)global.storage->data)->save(irc, overwrite); +} + +storage_status_t storage_remove (const char *nick, const char *password) +{ +	GList *gl; +	storage_status_t ret = STORAGE_OK; +	 +	/* Remove this account from all storage backends. If this isn't  +	 * done, the account will still be usable, it'd just be  +	 * loaded from a different backend. */ +	for (gl = global.storage; gl; gl = gl->next) { +		storage_t *st = gl->data; +		storage_status_t status; + +		status = st->remove(nick, password); +		if (status != STORAGE_NO_SUCH_USER &&  +			status != STORAGE_OK) +			ret = status; +	} +	 +	return ret; +} + +storage_status_t storage_rename (const char *onick, const char *nnick, const char *password) +{ +	storage_status_t status; +	GList *gl = global.storage; +	storage_t *primary_storage = gl->data; +	irc_t *irc; + +	/* First, try to rename in the current write backend, assuming onick  +	 * is stored there */ +	status = primary_storage->rename(onick, nnick, password); +	if (status != STORAGE_NO_SUCH_USER) +		return status; + +	/* Try to load from a migration backend and save to the current backend.  +	 * Explicitly remove the account from the migration backend as otherwise  +	 * it'd still be usable under the old name */ +	 +	irc = g_new0(irc_t, 1); +	status = storage_load(onick, password, irc); +	if (status != STORAGE_OK) { +		irc_free(irc); +		return status; +	} + +	g_free(irc->nick); +	irc->nick = g_strdup(nnick); + +	status = storage_save(irc, FALSE); +	if (status != STORAGE_OK) { +		irc_free(irc); +		return status; +	} +	irc_free(irc); + +	storage_remove(onick, password); + +	return STORAGE_OK; +} diff --git a/storage.h b/storage.h new file mode 100644 index 00000000..301b424c --- /dev/null +++ b/storage.h @@ -0,0 +1,66 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Layer for retrieving and storing buddy information */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef __STORAGE_H__ +#define __STORAGE_H__ + +typedef enum { +	STORAGE_OK = 0, +	STORAGE_NO_SUCH_USER, +	STORAGE_INVALID_PASSWORD, +	STORAGE_ALREADY_EXISTS, +	STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as  +						   a database that is unreachable. log() will be  +						   used for the exact error message */ +} storage_status_t; + +typedef struct { +	const char *name; +	 +	/* May be set to NULL if not required */ +	void (*init) (void); + +	storage_status_t (*check_pass) (const char *nick, const char *password); + +	storage_status_t (*load) (const char *nick, const char *password, irc_t * irc); +	storage_status_t (*save) (irc_t *irc, int overwrite); +	storage_status_t (*remove) (const char *nick, const char *password); + +	/* May be NULL if not supported by backend */ +	storage_status_t (*rename) (const char *onick, const char *nnick, const char *password); +} storage_t; + +storage_status_t storage_check_pass (const char *nick, const char *password); + +storage_status_t storage_load (const char *nick, const char *password, irc_t * irc); +storage_status_t storage_save (irc_t *irc, int overwrite); +storage_status_t storage_remove (const char *nick, const char *password); + +storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); + +void register_storage_backend(storage_t *); +GList *storage_init(const char *primary, char **migrate); + +#endif /* __STORAGE_H__ */ diff --git a/storage_text.c b/storage_text.c new file mode 100644 index 00000000..5909ff63 --- /dev/null +++ b/storage_text.c @@ -0,0 +1,314 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Storage backend that uses the same file format as <=1.0 */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "crypting.h" + +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.", CONFIG ); +	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 ); +} + +static storage_status_t text_load ( const char *my_nick, const char* password, irc_t *irc ) +{ +	char s[512]; +	char *line; +	int proto; +	char nick[MAX_NICK_LENGTH+1]; +	FILE *fp; +	user_t *ru = user_find( irc, ROOT_NICK ); +	 +	if( irc->status == USTATUS_IDENTIFIED ) +		return( 1 ); +	 +	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" ); +   	fp = fopen( s, "r" ); +   	if( !fp ) return STORAGE_NO_SUCH_USER; +	 +	fscanf( fp, "%32[^\n]s", s ); + +	if (checkpass (password, s) != 0)  +	{ +		fclose( fp ); +		return STORAGE_INVALID_PASSWORD; +	} +	 +	/* Do this now. If the user runs with AuthMode = Registered, the +	   account command will not work otherwise. */ +	irc->status = USTATUS_IDENTIFIED; +	 +	while( fscanf( fp, "%511[^\n]s", s ) > 0 ) +	{ +		fgetc( fp ); +		line = deobfucrypt( s, password ); +		if (line == NULL) return STORAGE_OTHER_ERROR; +		root_command_string( irc, ru, line, 0 ); +		g_free( line ); +	} +	fclose( fp ); +	 +	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" ); +	fp = fopen( s, "r" ); +	if( !fp ) return STORAGE_NO_SUCH_USER; +	while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) +	{ +		http_decode( s ); +		nick_set( irc, s, proto, nick ); +	} +	fclose( fp ); +	 +	if( set_getint( irc, "auto_connect" ) ) +	{ +		strcpy( s, "account on" );	/* Can't do this directly because r_c_s alters the string */ +		root_command_string( irc, ru, s, 0 ); +	} +	 +	return STORAGE_OK; +} + +static storage_status_t text_save( irc_t *irc, int overwrite ) +{ +	char s[512]; +	char path[512], new_path[512]; +	char *line; +	nick_t *n; +	set_t *set; +	mode_t ou = umask( 0077 ); +	account_t *a; +	FILE *fp; +	char *hash; + +	if (!overwrite) { +		g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); +		if (access( path, F_OK ) != -1) +			return STORAGE_ALREADY_EXISTS; +	 +		g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); +		if (access( path, F_OK ) != -1) +			return STORAGE_ALREADY_EXISTS; +	} +	 +	/*\ +	 *  [SH] Nothing should be saved if no password is set, because the +	 *  password is not set if it was wrong, or if one is not identified +	 *  yet. This means that a malicious user could easily overwrite +	 *  files owned by someone else: +	 *  a Bad Thing, methinks +	\*/ + +	/* [WVG] No? Really? */ + +	/*\ +	 *  [SH] Okay, okay, it wasn't really Wilmer who said that, it was +	 *  me. I just thought it was funny. +	\*/ +	 +	hash = hashpass( irc->password ); +	if( hash == NULL ) +	{ +		irc_usermsg( irc, "Please register yourself if you want to save your settings." ); +		return STORAGE_OTHER_ERROR; +	} +	 +	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" ); +	fp = fopen( path, "w" ); +	if( !fp ) return STORAGE_OTHER_ERROR; +	for( n = irc->nicks; n; n = n->next ) +	{ +		strcpy( s, n->handle ); +		s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */ +		http_encode( s ); +		g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", n->proto, n->nick ); +		if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 ) +		{ +			irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); +			fclose( fp ); +			return STORAGE_OTHER_ERROR; +		} +	} +	if( fclose( fp ) != 0 ) +	{ +		irc_usermsg( irc, "fclose() reported an error. Disk full?" ); +		return STORAGE_OTHER_ERROR; +	} +   +	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); +	if( unlink( new_path ) != 0 ) +	{ +		if( errno != ENOENT ) +		{ +			irc_usermsg( irc, "Error while removing old .nicks file" ); +			return STORAGE_OTHER_ERROR; +		} +	} +	if( rename( path, new_path ) != 0 ) +	{ +		irc_usermsg( irc, "Error while renaming new .nicks file" ); +		return STORAGE_OTHER_ERROR; +	} +	 +	g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" ); +	fp = fopen( path, "w" ); +	if( !fp ) return STORAGE_OTHER_ERROR; +	if( fprintf( fp, "%s", hash ) != strlen( hash ) ) +	{ +		irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); +		fclose( fp ); +		return STORAGE_OTHER_ERROR; +	} +	g_free( hash ); + +	for( a = irc->accounts; a; a = a->next ) +	{ +		if( a->protocol == PROTO_OSCAR || a->protocol == PROTO_ICQ || a->protocol == PROTO_TOC ) +			g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server ); +		else +			g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"", +			            proto_name[a->protocol], a->user, a->pass, a->server ? a->server : "" ); +		 +		line = obfucrypt( s, irc->password ); +		if( *line ) +		{ +			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) +			{ +				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); +				fclose( fp ); +				return STORAGE_OTHER_ERROR; +			} +		} +		g_free( line ); +	} +	 +	for( set = irc->set; set; set = set->next ) +	{ +		if( set->value && set->def ) +		{ +			g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value ); +			line = obfucrypt( s, irc->password ); +			if( *line ) +			{ +				if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) +				{ +					irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); +					fclose( fp ); +					return STORAGE_OTHER_ERROR; +				} +			} +			g_free( line ); +		} +	} +	 +	if( strcmp( irc->mynick, ROOT_NICK ) != 0 ) +	{ +		g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick ); +		line = obfucrypt( s, irc->password ); +		if( *line ) +		{ +			if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) +			{ +				irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); +				fclose( fp ); +				return STORAGE_OTHER_ERROR; +			} +		} +		g_free( line ); +	} +	if( fclose( fp ) != 0 ) +	{ +		irc_usermsg( irc, "fclose() reported an error. Disk full?" ); +		return STORAGE_OTHER_ERROR; +	} +	 + 	g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + 	if( unlink( new_path ) != 0 ) +	{ +		if( errno != ENOENT ) +		{ +			irc_usermsg( irc, "Error while removing old .accounts file" ); +			return STORAGE_OTHER_ERROR; +		} +	} +	if( rename( path, new_path ) != 0 ) +	{ +		irc_usermsg( irc, "Error while renaming new .accounts file" ); +		return STORAGE_OTHER_ERROR; +	} +	 +	umask( ou ); +	 +	return STORAGE_OK; +} + +static storage_status_t text_check_pass( const char *nick, const char *password ) +{ +	char s[512]; +	FILE *fp; +	 +	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); +	fp = fopen( s, "r" ); +	if (!fp) +		return STORAGE_NO_SUCH_USER; + +	fscanf( fp, "%32[^\n]s", s ); +	fclose( fp ); + +	if (checkpass( password, s) == -1) +		return STORAGE_INVALID_PASSWORD; + +	return STORAGE_OK; +} + +static storage_status_t text_remove( const char *nick, const char *password ) +{ +	char s[512]; +	storage_status_t status; + +	status = text_check_pass( nick, password ); +	if (status != STORAGE_OK) +		return status; + +	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" ); +	if (unlink( s ) == -1) +		return STORAGE_OTHER_ERROR; +	 +	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".nicks" ); +	if (unlink( s ) == -1) +		return STORAGE_OTHER_ERROR; + +	return STORAGE_OK; +} + +storage_t storage_text = { +	.name = "text", +	.init = text_init, +	.check_pass = text_check_pass, +	.remove = text_remove, +	.load = text_load, +	.save = text_save +}; @@ -51,11 +51,12 @@ int main( int argc, char *argv[] )  	CONF_FILE = g_strdup( CONF_FILE_DEF );  	global.helpfile = g_strdup( HELP_FILE ); -	 +  	global.conf = conf_load( argc, argv );  	if( global.conf == NULL )  		return( 1 ); -	 + +  	if( global.conf->runmode == RUNMODE_INETD )  	{  		i = bitlbee_inetd_init(); @@ -69,6 +70,14 @@ int main( int argc, char *argv[] )  	}  	if( i != 0 )  		return( i ); + +	global.storage = storage_init( global.conf->primary_storage,  +								   global.conf->migrate_storage ); +	if ( global.storage == NULL) { +		log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); +		return( 1 ); +	} +	  	/* Catch some signals to tell the user what's happening before quitting */  	memset( &sig, 0, sizeof( sig ) ); @@ -86,10 +95,6 @@ int main( int argc, char *argv[] )  	if( !getuid() || !geteuid() )  		log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); -	if( access( global.conf->configdir, F_OK ) != 0 ) -		log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG ); -	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 );  	if( help_init( &(global.help) ) == NULL )  		log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); | 
