diff options
41 files changed, 1113 insertions, 636 deletions
| @@ -16,3 +16,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 crypting.o help.o ini.o irc.o nick.o query.o set.o url.o user.o log.o +objects = account.o bitlbee.o commands.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o url.o user.o storage_text.o storage.o  subdirs = protocols  ifeq ($(ARCH),Windows) @@ -123,12 +123,6 @@ void account_on( irc_t *irc, account_t *a )  		return;  	} -	if (a->prpl == NULL ) -	{ -		irc_usermsg( irc, "Support for protocol %s is not included in this BitlBee", a->prpl->name ); -		return; -	} -	  	cancel_auto_reconnect( a );  	u = g_new0 ( struct aim_user, 1 ); @@ -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> @@ -158,7 +157,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;  } @@ -229,251 +236,6 @@ gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condi  	}  } -/* DO NOT USE THIS FUNCTION IN NEW CODE. This  - * function is here merely because the save/load code still uses  - * ids rather then names */ -struct prpl *find_protocol_by_id(int id) -{ -	switch (id) { -	case 1: return find_protocol("oscar"); -	case 4: return find_protocol("msn"); -	case 2: return find_protocol("yahoo"); -	case 8: return find_protocol("jabber"); -	default: break; -	} -	return NULL; -} - -int bitlbee_load( irc_t *irc, char* password ) -{ -	char s[512]; -	char *line; -	char proto[20]; -	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 %s %s", s, proto, nick ) > 0 ) -	{ -		struct prpl *prpl; - -		prpl = find_protocol(proto); - -		/* Older files saved the protocol number rather then the protocol name */ -		if (!prpl && atoi(proto)) { -			prpl = find_protocol_by_id(atoi(proto)); -		} - -		if (!prpl) -			continue; - -		http_decode( s ); -		nick_set( irc, s, prpl, 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 ), " %s %s", n->proto->name, 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( !strcmp( a->prpl->name, "oscar" ) ) -			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\"", -			            a->prpl->name, 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 ) @@ -622,7 +604,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; @@ -56,7 +56,6 @@ Option		Description				Default  --debug=0/1	Disable/enable debugging		$debug  --strip=0/1	Disable/enable binary stripping		$strip ---flood=0/1	Flood protection			$flood  --ipv6=0/1	IPv6 socket support			$ipv6  --ssl=...	SSL library to use (gnutls, nss, openssl, bogus, auto) @@ -286,7 +285,11 @@ else  fi  if [ "$flood" = 1 ]; then -	echo '#define FLOOD_SEND' >> config.h +	# echo '#define FLOOD_SEND' >> config.h +	echo 'Flood protection is disabled in this release because of too many bugs.' 2> /dev/stderr +	rm config.h +	rm Makefile.settings +	exit 1  fi  if [ -n "$BITLBEE_VERSION" ]; then @@ -344,33 +347,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 @@ -393,11 +391,11 @@ if [ "$msn" = "1" ]; then  	echo '  Using SSL library: '$ssl;  fi -if [ "$flood" = "0" ]; then -	echo '  Flood protection disabled.'; -else -	echo '  Flood protection enabled.'; -fi +#if [ "$flood" = "0" ]; then +#	echo '  Flood protection disabled.'; +#else +#	echo '  Flood protection enabled.'; +#fi  if [ -n "$protocols" ]; then  	echo '  Building with these protocols:' $protocols; @@ -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 2981a378..7b95e8cb 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -10,8 +10,26 @@ Version 1.0:  - Cleaned up some unnecessary code in the Jabber module, and implemented    handlers for headline messages (which allows you to use RSS-to-Jabber    gateways). - -Finished ... +- Lowered the line splitting limit a bit to fix data loss issues. +- The $proto($handle) format used for messages specific to one IM-connection +  now only include the ($handle) part when there's more than one $proto- +  connection. +- Fix for a crash-bug on broken Jabber/SSL connections. +- Incoming typing notifications now also come in as CTCP TYPING messages, for +  better consistency. Don't forget to update your scripts! +- AIM typing notifications are supported now. +- 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. +- 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 @@ -38,30 +38,18 @@ A: 'root' is just the name for the most powerful user in BitlBee. Just like     artistic creativity.  Q: When is $random_feature going to be implemented? -A: Please do consult doc/TODO (preferably in a development snapshot, which -   is more up-to-date than a TODO in a release version) before asking. -   Please also check the documentation. You'd not be the first one to request -   a feature which already exists! -    -   If your fabulous feature seems not to be requested before, just join -   #bitlbee on irc.oftc.net and tell us the news. -    -   If your feature request is already in the TODO list, of course you can -   still request it again/make us know that you'd like to see the feature as -   well. But when the feature is in the "post-1.0" list, it's probably not -   going to help. Most of the features in this list are low-priority because -   we (the developers) don't need (or even want) them. (File transfers are a -   good example here.) -   Hence, they'll only be implemented when we really got too much spare -   time. Obviously, if you're willing to help (i.e. submit a patch), you're -   always welcome. +A: It depends on the feature. We keep a list of all wishlist "bugs" in our +   Bug Tracking system at http://bugs.bitlbee.org/  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/TODO b/doc/TODO deleted file mode 100644 index 3bd241ef..00000000 --- a/doc/TODO +++ /dev/null @@ -1,40 +0,0 @@ -TODO for BitlBee: - -We're trying to keep a better TODO list now. The priorities here are somewhat -random, maybe. Or at least they change from time to time. The "After 1.0" -changes are very low-priority, the "Before 1.0" are "highest" priority, the -others are changes we'll do when we feel like doing them. They're usually not -as critical as the "Before 1.0" changes, but still a bit more important than -"After 1.0". - -KNOWN BUGS TO FIX BEFORE 1.0: -- 100% cpu usage bugs. -  -> They're still there... :-( -- Check if the IRC send flood protection is reliable now. -  -> Probably not, at least it still causes troubles on logging in for some -     people. Maybe the thresholds aren't okay yet. Logins are just floody, -     nothing we can do about that. - -Some time: (mainly features) -- Rewrite Jabber module - the current one sucks. -- Test Yahoo! groupchats a bit better, because they still seem to be a bit -  flakey. -  -> There are bug reports from time to time, but we can't do much about -     them, possibly it's more a libyahoo2 problem. -- Groupchats -  -> Make them work on other nets than MSN/Yahoo as well. -- Make usernames case-insensitive. (On case-insensitive filesystems this -  change isn't necessary. This one is going to suck with backward-compati- -  bilty...) -  -> We'll probably combine this with the introduction of a better file -     format for userdata. -- Remind the user of unanswered questions after some time. -- Maybe a way to send global messages. (for server shutdowns, for example) -- Away-auto-replies. -- Allow nick changes. - -After 1.0: (mainly toys) -- File transfers -> DCC -- SSL support? Persistent connections? Things you can do with a bouncer/proxy. -- Support for buddy groups. -- What else? diff --git a/doc/user-guide/user-guide.xml b/doc/user-guide/user-guide.xml index eff5998e..5b881fb2 100644 --- a/doc/user-guide/user-guide.xml +++ b/doc/user-guide/user-guide.xml @@ -20,24 +20,13 @@  	</author>  	<legalnotice id="legalnotice"> -     <para> -         Permission is granted to copy, distribute and/or modify this  -         document under the terms of the <ulink type="help" -         url="gnome-help:fdl"><citetitle>GNU Free Documentation  -         License</citetitle></ulink>, Version 1.1 or any later version  -         published by the Free Software Foundation with no Invariant  -         Sections, no Front-Cover Texts, and no Back-Cover Texts. You  -         may obtain a copy of the <citetitle>GNU Free Documentation  -         License</citetitle> from the Free Software Foundation by  -         visiting <ulink type="http" url="http://www.fsf.org">their  -         Web site</ulink> or by writing to: Free Software Foundation,  -         Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,  -         USA. -       </para>      </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;	 @@ -1509,7 +1530,7 @@ int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char  			else  			{  				irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, -				           type, to, prefix, line ); +				           type, to, prefix ? prefix : "", line );  			}  			line = s + 1;  		} @@ -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, struct prpl *proto, char *nick ) +void nick_set( irc_t *irc, const char *handle, struct prpl *proto, const char *nick )  {  	nick_t *m = NULL, *n = irc->nicks; @@ -55,7 +55,7 @@ void nick_set( irc_t *irc, char *handle, struct prpl *proto, char *nick )  	nick_strip( n->nick );  } -char *nick_get( irc_t *irc, char *handle, struct prpl *proto, const char *realname ) +char *nick_get( irc_t *irc, const char *handle, struct prpl *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, struct prpl *proto, const char *realna  	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, struct prpl *proto, char *nick ); -char *nick_get( irc_t *irc, char *handle, struct prpl *proto, const char *realname ); -void nick_del( irc_t *irc, char *nick ); +void nick_set( irc_t *irc, const char *handle, struct prpl *proto, const char *nick ); +char *nick_get( irc_t *irc, const char *handle, struct prpl *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/jabber/jabber.c b/protocols/jabber/jabber.c index 413f77ef..fc419124 100644 --- a/protocols/jabber/jabber.c +++ b/protocols/jabber/jabber.c @@ -21,10 +21,6 @@   *   */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -  #ifndef _WIN32  #include <sys/utsname.h>  #endif @@ -58,6 +54,8 @@  #define DEFAULT_GROUPCHAT "conference.jabber.org"  #define DEFAULT_PORT 5222  #define DEFAULT_PORT_SSL 5223 +#define JABBER_PORT_MIN 5220 +#define JABBER_PORT_MAX 5229  #define JABBER_GROUP "Friends" @@ -540,11 +538,6 @@ static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition c  	struct jabber_data *jd;  	gjconn gjc; -	if (!g_slist_find(get_connections(), gc)) { -		ssl_disconnect(source); -		return; -	} -	  	jd = gc->proto_data;  	gjc = jd->gjc; @@ -553,6 +546,11 @@ static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition c  		return;  	} +	if (!g_slist_find(get_connections(), gc)) { +		ssl_disconnect(source); +		return; +	} +	  	gjab_connected(data, gjc->fd, cond);  } @@ -588,6 +586,11 @@ static void gjab_start(gjconn gjc)  		port = DEFAULT_PORT;  	else if (port == -1 && ssl)  		port = DEFAULT_PORT_SSL; +	else if (port < JABBER_PORT_MIN || port > JABBER_PORT_MAX) { +		serv_got_crap(GJ_GC(gjc), "For security reasons, the Jabber port number must be in the %d-%d range.", JABBER_PORT_MIN, JABBER_PORT_MAX); +		STATE_EVT(JCONN_STATE_OFF) +		return; +	}  	if (server == NULL)  		server = g_strdup(gjc->user->server); diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index 793a881e..2f4d05d5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -643,7 +643,7 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int  			if( who )  			{ -				serv_got_typing( gc, who, 5 ); +				serv_got_typing( gc, who, 5, 1 );  				g_free( who );  			} diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 474b91b2..4966a76f 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -54,6 +54,7 @@ static int remove_chat_buddy_silent( struct conversation *b, char *handle );  GSList *connections; +#ifdef WITH_PLUGINS  gboolean load_plugin(char *path)  {  	void (*init_function) (void); @@ -75,6 +76,34 @@ gboolean load_plugin(char *path)  	return TRUE;  } +void load_plugins(void) +{ +	GDir *dir; +	GError *error = NULL; + +	dir = g_dir_open(PLUGINDIR, 0, &error); + +	if (dir) { +		const gchar *entry; +		char *path; + +		while ((entry = g_dir_read_name(dir))) { +			path = g_build_filename(PLUGINDIR, entry, NULL); +			if(!path) { +				log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry); +				continue; +			} + +			load_plugin(path); + +			g_free(path); +		} + +		g_dir_close(dir); +	} +} +#endif +  /* nogaim.c */  GList *protocols = NULL; @@ -100,49 +129,30 @@ struct prpl *find_protocol(const char *name)  /* nogaim.c */  void nogaim_init()  { -	GDir *dir; -	GError *error = NULL; +	extern void msn_init(); +	extern void oscar_init(); +	extern void byahoo_init(); +	extern void jabber_init();  #ifdef WITH_MSN -	extern void msn_init();  	msn_init();  #endif  #ifdef WITH_OSCAR -	extern void oscar_init();  	oscar_init();  #endif  #ifdef WITH_YAHOO -	extern void byahoo_init();  	byahoo_init();  #endif  #ifdef WITH_JABBER -	extern void jabber_init();  	jabber_init();  #endif -	dir = g_dir_open(PLUGINDIR, 0, &error); - -	if (dir) { -		const gchar *entry; -		char *path; - -		while ((entry = g_dir_read_name(dir))) { -			path = g_build_filename(PLUGINDIR, entry, NULL); -			if(!path) { -				log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry); -				continue; -			} - -			load_plugin(path); - -			g_free(path); -		} - -		g_dir_close(dir); -	} +#ifdef WITH_PLUGINS +	load_plugins(); +#endif  }  GSList *get_connections() { return connections; } @@ -441,7 +451,14 @@ void signoff( struct gaim_connection *gc )  void do_error_dialog( struct gaim_connection *gc, char *msg, char *title )  { -	serv_got_crap( gc, "Error: %s", msg ); +	if( msg && title ) +		serv_got_crap( gc, "Error: %s: %s", title, msg ); +	else if( msg ) +		serv_got_crap( gc, "Error: %s", msg ); +	else if( title ) +		serv_got_crap( gc, "Error: %s", title ); +	else +		serv_got_crap( gc, "Error" );  }  void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont ) @@ -726,7 +743,7 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f  		/* If there's a newline/space in this string, split up there,  		   looks a bit prettier. */ -		if( ( nl = strrchr( msg, '\n' ) ) || ( nl = strchr( msg, ' ' ) ) ) +		if( ( nl = strrchr( msg, '\n' ) ) || ( nl = strrchr( msg, ' ' ) ) )  		{  			msg[425] = tmp;  			tmp = *nl; @@ -750,15 +767,25 @@ void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 f  	irc_msgfrom( irc, u->nick, msg );  } -void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ) +void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout, int type )  {  	user_t *u;  	if( !set_getint( gc->irc, "typing_notice" ) )  		return; -	if( ( u = user_findhandle( gc, handle ) ) ) -		irc_msgfrom( gc->irc, u->nick, "\1TYPING 1\1" ); +	if( ( u = user_findhandle( gc, handle ) ) ) { +		/* If type is: +		 * 0: user has stopped typing +		 * 1: user is actively typing +		 * 2: user has entered text, but is not actively typing +		 */ +		if (type == 0 || type == 1 || type == 2) { +			char buf[256];  +			g_snprintf(buf, 256, "\1TYPING %d\1", type);  +			irc_privmsg( gc->irc, u, "PRIVMSG", gc->irc->nick, NULL, buf ); +		} +	}  }  void serv_got_chat_left( struct gaim_connection *gc, int id ) diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 3d5006d9..8ec65347 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -306,7 +306,7 @@ G_MODULE_EXPORT void show_got_added( struct gaim_connection *gc, char *id, char  /* server.c */                      G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps );  G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ); -G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ); +G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout, int type );  G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data );  G_MODULE_EXPORT struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle );  G_MODULE_EXPORT void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime ); @@ -325,12 +325,6 @@ G_MODULE_EXPORT void strip_html( char *msg );  G_MODULE_EXPORT char * escape_html(const char *html);  G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value); -/* file transfers */ -G_MODULE_EXPORT void ft_progress( struct ft *, int); -G_MODULE_EXPORT void ft_incoming( struct ft_request * ); -G_MODULE_EXPORT void ft_accepted( struct ft_request *, struct ft *); -G_MODULE_EXPORT void ft_denied( struct ft_request *, const char *reason); -  /* prefs.c */  G_MODULE_EXPORT void build_block_list();  G_MODULE_EXPORT void build_allow_list(); 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 538f47ae..5f19f12b 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) { @@ -1073,30 +1072,38 @@ 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);  		GList *m = NULL; +		char txt[1024]; +		struct aim_chat_invitation * inv = g_new0(struct aim_chat_invitation, 1); +  		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); + +		g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", name, userinfo->sn, args->msg ); + +		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;  } @@ -2440,10 +2447,20 @@ int gaim_parsemtn(aim_session_t *sess, aim_frame_t *fr, ...)  	sn = va_arg(ap, char*);  	type2 = va_arg(ap, int);  	va_end(ap); - -	if(type2 == 0x0001 || type2 == 0x0002) -		serv_got_typing(gc, sn, 0); - +     +	if(type2 == 0x0002) { +		/* User is typing */ +		serv_got_typing(gc, sn, 0, 1); +	}  +	else if (type2 == 0x0001) { +		/* User has typed something, but is not actively typing (stale) */ +		serv_got_typing(gc, sn, 0, 2); +	} +	else { +		/* User has stopped typing */ +		serv_got_typing(gc, sn, 0, 0); +	}         +	  	return 1;  } @@ -2481,6 +2498,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; +	int ret; +	guint8 len = strlen(message); +	char *s; +	 +	if(!(ccon = find_oscar_chat(gc, id))) +		return -1; +	  	 +	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; +	int ret; +	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++); +   +	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); +} +  void oscar_init()   {  	struct prpl *ret = g_new0(struct prpl, 1); @@ -2494,6 +2643,10 @@ void oscar_init()  	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 6d450c92..0546f2d7 100644 --- a/protocols/proxy.c +++ b/protocols/proxy.c @@ -72,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; @@ -113,7 +113,7 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin  	return TRUE;  } -static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) +static void gaim_io_connected(gpointer data, gint source, GaimInputCondition cond)  {  	struct PHB *phb = data;  	unsigned int len; @@ -143,7 +143,7 @@ static void no_one_calls(gpointer data, gint source, GaimInputCondition cond)  	}  } -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; @@ -162,7 +162,7 @@ static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb)  	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) {  		if (sockerr_again()) { -			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); +			phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);  			phb->fd = fd;  		} else {  			closesocket(fd); @@ -270,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; @@ -354,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; @@ -536,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; @@ -577,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/protocols/yahoo/crypt.c b/protocols/yahoo/crypt.c index 00eed70b..5122e3af 100644 --- a/protocols/yahoo/crypt.c +++ b/protocols/yahoo/crypt.c @@ -22,10 +22,6 @@   * already had. isn't that lovely. people should just use linux or   * freebsd, crypt works properly on those systems. i hate solaris */ -#if HAVE_CONFIG_H -#  include <config.h> -#endif -  #if HAVE_STRING_H  #  include <string.h>  #elif HAVE_STRINGS_H diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c index 62ceb0af..c691f18b 100644 --- a/protocols/yahoo/libyahoo2.c +++ b/protocols/yahoo/libyahoo2.c @@ -43,10 +43,6 @@   *   */ -#if HAVE_CONFIG_H -# include <config.h> -#endif -  #ifndef _WIN32  #include <unistd.h>  #endif diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index e55b30af..832d1ab4 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -634,8 +634,14 @@ void ext_yahoo_got_file( int id, char *who, char *url, long expires, char *msg,  void ext_yahoo_typing_notify( int id, char *who, int stat )  {  	struct gaim_connection *gc = byahoo_get_gc_by_id( id ); -	 -	serv_got_typing( gc, who, 1 ); +	if (stat == 1) { +		/* User is typing */ +		serv_got_typing( gc, who, 1, 1 ); +	} +	else { +		/* User stopped typing */ +		serv_got_typing( gc, who, 1, 0 ); +	}  }  void ext_yahoo_system_message( int id, char *msg ) diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c index 41e31a23..dbbe2a84 100644 --- a/protocols/yahoo/yahoo_httplib.c +++ b/protocols/yahoo/yahoo_httplib.c @@ -19,10 +19,6 @@   *   */ -#if HAVE_CONFIG_H -# include <config.h> -#endif -  #include <stdio.h>  #include <stdlib.h> diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c index cb155e3c..3c99cf44 100644 --- a/protocols/yahoo/yahoo_util.c +++ b/protocols/yahoo/yahoo_util.c @@ -19,10 +19,6 @@   *   */ -#if HAVE_CONFIG_H -# include <config.h> -#endif -  #if STDC_HEADERS  # include <string.h>  #else 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..ed32b73f --- /dev/null +++ b/storage_text.c @@ -0,0 +1,347 @@ +  /********************************************************************\ +  * 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" + +/* DO NOT USE THIS FUNCTION IN NEW CODE. This  + * function is here merely because the save/load code still uses  + * ids rather than names */ +static struct prpl *find_protocol_by_id(int id) +{ +	switch (id) { +	case 1: return find_protocol("oscar"); +	case 4: return find_protocol("msn"); +	case 2: return find_protocol("yahoo"); +	case 8: return find_protocol("jabber"); +	default: break; +	} +	return NULL; +} + +static int find_protocol_id(const char *name) +{ +	if (!strcmp(name, "oscar")) return 1; +	if (!strcmp(name, "msn")) return 4; +	if (!strcmp(name, "yahoo")) return 2; +	if (!strcmp(name, "jabber")) return 8; + +	return -1; +} + + +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 ) +	{ +		struct prpl *prpl; + +		prpl = find_protocol_by_id(proto); + +		if (!prpl) +			continue; + +		http_decode( s ); +		nick_set( irc, s, prpl, 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", find_protocol_id(n->proto->name), 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( !strcmp(a->prpl->name, "oscar") ) +			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\"", +			            a->prpl->name, 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 +}; @@ -52,11 +52,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 )  	{  		log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); @@ -76,6 +77,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 ) ); @@ -93,10 +102,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 ); | 
