diff options
| author | Wilmer van der Gaast <wilmer@gaast.net> | 2005-11-06 19:23:18 +0100 | 
|---|---|---|
| committer | Wilmer van der Gaast <wilmer@gaast.net> | 2005-11-06 19:23:18 +0100 | 
| commit | b7d3cc34f68dab7b8f7d0777711317b334fc2219 (patch) | |
| tree | 6aa4d6332c96654fda79fe18993ab0e35d36a52b /bitlbee.c | |
Initial repository (0.99 release tree)0.99
Diffstat (limited to 'bitlbee.c')
| -rw-r--r-- | bitlbee.c | 601 | 
1 files changed, 601 insertions, 0 deletions
| diff --git a/bitlbee.c b/bitlbee.c new file mode 100644 index 00000000..66552130 --- /dev/null +++ b/bitlbee.c @@ -0,0 +1,601 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Main file                                                            */ + +/* +  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 "commands.h" +#include "crypting.h" +#include "protocols/nogaim.h" +#include "help.h" +#include <signal.h> +#include <stdio.h> +#include <errno.h> + +gboolean bitlbee_io_new_client( GIOChannel *source, GIOCondition condition, gpointer data ) +{ +	size_t size = sizeof( struct sockaddr_in ); +	struct sockaddr_in conn_info; +	int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info,  +		                     &size ); +	 +	count_io_event(source, "main"); +	 +	log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket ); +	irc_new( new_socket ); + +	return TRUE; +} +  + + +int bitlbee_daemon_init() +{ +	struct sockaddr_in listen_addr; +	int i; +	GIOChannel *ch; +	 +	log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); +	log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); +	 +	global.listen_socket = socket( AF_INET, SOCK_STREAM, 0 ); +	if( global.listen_socket == -1 ) +	{ +		log_error( "socket" ); +		return( -1 ); +	} +	listen_addr.sin_family = AF_INET; +	listen_addr.sin_port = htons( global.conf->port ); +	listen_addr.sin_addr.s_addr = inet_addr( global.conf->iface ); + +	i = bind( global.listen_socket, (struct sockaddr *) &listen_addr, sizeof( struct sockaddr ) ); +	if( i == -1 ) +	{ +		log_error( "bind" ); +		return( -1 ); +	} + +	i = listen( global.listen_socket, 10 ); +	if( i == -1 ) +	{ +		log_error( "listen" ); +		return( -1 ); +	} + +	ch = g_io_channel_unix_new( global.listen_socket ); +	g_io_add_watch( ch, G_IO_IN, bitlbee_io_new_client, NULL ); + +#ifndef _WIN32 +	if( !global.conf->nofork ) +	{ +		i = fork(); +		if( i == -1 ) +		{ +			log_error( "fork" ); +			return( -1 ); +		} +		else if( i != 0 )  +			exit( 0 ); +		close( 0 ); +		close( 1 ); +		close( 2 ); +		chdir( "/" ); +	} +#endif +	 +	return( 0 ); +} +  +int bitlbee_inetd_init() +{ +	if( !irc_new( 0 ) ) +		return( 1 ); +	 +	log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); +	log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); +	 +	return( 0 ); +} + +gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condition, gpointer data ) +{ +	irc_t *irc = data; +	char line[513]; +	int st; +	 +	count_io_event(source, "main"); + +	if( condition & G_IO_ERR || condition & G_IO_HUP ) +	{ +		irc_free( irc ); +		return FALSE; +	} +	 +	st = read( irc->fd, line, sizeof( line ) - 1 ); +	if( st == 0 ) +	{ +		irc_free( irc ); +		return FALSE; +	} +	else if( st < 0 ) +	{ +		if( sockerr_again() ) +		{ +			return TRUE; +		} +		else +		{ +			irc_free( irc ); +			return FALSE; +		} +	} +	 +	line[st] = '\0'; +	if( irc->readbuffer == NULL )  +	{ +		irc->readbuffer = g_strdup( line ); +	} +	else  +	{ +		irc->readbuffer = g_renew( char, irc->readbuffer, strlen( irc->readbuffer ) + strlen ( line ) + 1 ); +		strcpy( ( irc->readbuffer + strlen( irc->readbuffer ) ), line ); +	} +	 +	if( !irc_process( irc ) ) +	{ +		log_message( LOGLVL_INFO, "Destroying connection with fd %d.", irc->fd ); +		irc_free( irc ); +		return FALSE; +	}  +		 +	return TRUE; +} + +gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data ) +{ +	irc_t *irc = data; +	int st, size; +	char *temp; +#ifdef FLOOD_SEND +	time_t newtime; +#endif + +	count_io_event(source, "main"); + +#ifdef FLOOD_SEND	 +	newtime = time( NULL ); +	if( ( newtime - irc->oldtime ) > FLOOD_SEND_INTERVAL ) +	{ +		irc->sentbytes = 0; +		irc->oldtime = newtime; +	} +#endif +	 +	if( irc->sendbuffer == NULL ) +		return( FALSE ); +	 +	size = strlen( irc->sendbuffer ); +	 +#ifdef FLOOD_SEND +	if( ( FLOOD_SEND_BYTES - irc->sentbytes ) > size ) +		st = write( irc->fd, irc->sendbuffer, size ); +	else +		st = write( irc->fd, irc->sendbuffer, ( FLOOD_SEND_BYTES - irc->sentbytes ) ); +#else +	st = write( irc->fd, irc->sendbuffer, size ); +#endif +	 +	if( st <= 0 ) +	{ +		if( sockerr_again() ) +		{ +			return TRUE; +		} +		else +		{ +			irc_free( irc ); +			return FALSE; +		} +	} +	 +#ifdef FLOOD_SEND +	irc->sentbytes += st; +#endif		 +	 +	if( st == size ) +	{ +		g_free( irc->sendbuffer ); +		irc->sendbuffer = NULL; +		 +		irc->w_watch_source_id = 0; +		return( FALSE ); +	} +	else +	{ +		temp = g_strdup( irc->sendbuffer + st ); +		g_free( irc->sendbuffer ); +		irc->sendbuffer = temp; +		 +		return( TRUE ); +	} +} + +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). */ +	while( irc_connection_list != NULL ) +		irc_free( irc_connection_list->data ); +	 +	/* We'll only reach this point when not running in inetd mode: */ +	g_main_quit( global.loop ); +} + +int root_command_string( irc_t *irc, user_t *u, char *command, int flags ) +{ +	char *cmd[IRC_MAX_ARGS]; +	char *s; +	int k; +	char q = 0; +	 +	memset( cmd, 0, sizeof( cmd ) ); +	cmd[0] = command; +	k = 1; +	for( s = command; *s && k < ( IRC_MAX_ARGS - 1 ); s ++ ) +		if( *s == ' ' && !q ) +		{ +			*s = 0; +			while( *++s == ' ' ); +			if( *s == '"' || *s == '\'' ) +			{ +				q = *s; +				s ++; +			} +			if( *s ) +			{ +				cmd[k++] = s; +				s --; +			} +		} +		else if( *s == q ) +		{ +			q = *s = 0; +		} +	cmd[k] = NULL; +	 +	return( root_command( irc, cmd ) ); +} + +int root_command( irc_t *irc, char *cmd[] ) +{	 +	int i; +	 +	if( !cmd[0] ) +		return( 0 ); +	 +	for( i = 0; commands[i].command; i++ ) +		if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) +		{ +			if( !cmd[commands[i].required_parameters] ) +			{ +				irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); +				return( 0 ); +			} +			commands[i].execute( irc, cmd ); +			return( 1 ); +		} +	 +	irc_usermsg( irc, "Unknown command: %s. Please use help commands to get a list of available commands.", cmd[0] ); +	 +	return( 1 ); +} + +/* Decode%20a%20file%20name						*/ +void http_decode( char *s ) +{ +	char *t; +	int i, j, k; +	 +	t = g_new( char, strlen( s ) + 1 ); +	 +	for( i = j = 0; s[i]; i ++, j ++ ) +	{ +		if( s[i] == '%' ) +		{ +			if( sscanf( s + i + 1, "%2x", &k ) ) +			{ +				t[j] = k; +				i += 2; +			} +			else +			{ +				*t = 0; +				break; +			} +		} +		else +		{ +			t[j] = s[i]; +		} +	} +	t[j] = 0; +	 +	strcpy( s, t ); +	g_free( t ); +} + +/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ +/* This fuction is safe, but make sure you call it safely as well! */ +void http_encode( char *s ) +{ +	char *t; +	int i, j; +	 +	t = g_strdup( s ); +	 +	for( i = j = 0; t[i]; i ++, j ++ ) +	{ +		if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) +		{ +			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); +			j += 2; +		} +		else +		{ +			s[j] = t[i]; +		} +	} +	s[j] = 0; +	 +	g_free( t ); +} + +/* Strip newlines from a string. Modifies the string passed to it. */  +char *strip_newlines( char *source ) +{ +	int i;	 + +	for( i = 0; source[i] != '\0'; i ++ ) +		if( source[i] == '\n' || source[i] == '\r' ) +			source[i] = 32; +	 +	return source; +} | 
