diff options
Diffstat (limited to 'utils')
| -rw-r--r-- | utils/README | 35 | ||||
| -rwxr-xr-x | utils/bitlbee-ctl.pl | 59 | ||||
| -rw-r--r-- | utils/bitlbeed.c | 455 | ||||
| -rwxr-xr-x | utils/convert_purple.py | 113 | 
4 files changed, 662 insertions, 0 deletions
| diff --git a/utils/README b/utils/README new file mode 100644 index 00000000..7e875af9 --- /dev/null +++ b/utils/README @@ -0,0 +1,35 @@ +This directory contains tiny additional programs which you might just like +or need to run BitlBee: + + +* bitlbeed.c + +If you want to run BitlBee on a machine you don't have root access to, this +utility will help you. Compiling it is easy: 'gcc bitlbeed.c -o bitlbeed', +you don't need any special flags. Use 'bitlbeed -h' to get more help. + +For example, 'bitlbeed -p6669 -n1 /home/wilmer/bin/bitlbee' will start +listening on TCP port 6669 (on any interface, you might not want that!) +and connect the specified BitlBee program to this socket as soon as +someone connects. The -n1 makes sure only one person can be connected +at once. + +Of course this program can be used for other programs too, not just BitlBee. + + +* convert_purple.py + +Converts libpurple configs into something BitlBee can use, so you don't +have to re-add all your accounts by hand. + + +* BitlBee-specific Irssi scripts for: tab completion, typing notifica- +tions, auto-away and more, by Tijmen Ruizendaal <tijmen.ruizendaal@gmail.com>. + +There are too many scripts to include them all with BitlBee (and keep +them up-to-date), so you should get them from Tijmen's site: + +http://the-timing.nl/stuff/irssi-bitlbee + + +Please do send your sources if you write anything useful for the Bee! diff --git a/utils/bitlbee-ctl.pl b/utils/bitlbee-ctl.pl new file mode 100755 index 00000000..32f0a81e --- /dev/null +++ b/utils/bitlbee-ctl.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl +# Simple front-end to BitlBee's administration commands +# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> + +use IO::Socket; +use Getopt::Long; +use strict; +use warnings; + +my $opt_help; +my $opt_socketfile = "/var/run/bitlbee"; + +sub ShowHelp  +{ +	print  +"bitlbee-ctl.pl [options] command ... + +Available options: + +  --ipc-socket=SOCKET	Override path to IPC socket [$opt_socketfile] +  --help				Show this help message + +Available commands: +	 +	die + +"; +	exit (0); +} + +GetOptions ( +	    'help|h|?' => \&ShowHelp, +		'ipc-socket=s' => \$opt_socketfile +	    ) or exit(1); + +my $client = IO::Socket::UNIX->new(Peer => $opt_socketfile, +								Type => SOCK_STREAM, +								Timeout => 10); +								 +if (not $client) { +	print "Error connecting to $opt_socketfile: $@\n"; +	exit(1); +} + +my $cmd = shift @ARGV; + +if (not defined($cmd)) { +	print "Usage: bitlbee-ctl.pl [options] command ...\n"; +	exit(1); +} + +if ($cmd eq "die") { +	$client->send("DIE\r\n"); +} else { +	print "No such command: $cmd\n"; +	exit(1); +} + +$client->close(); diff --git a/utils/bitlbeed.c b/utils/bitlbeed.c new file mode 100644 index 00000000..82bd0879 --- /dev/null +++ b/utils/bitlbeed.c @@ -0,0 +1,455 @@ +/****************************************************************\ +*                                                                * +*  bitlbeed.c                                                    * +*                                                                * +*  A tiny daemon to allow you to run The Bee as a non-root user  * +*  (without access to /etc/inetd.conf or whatever)               * +*                                                                * +*  Copyright 2002-2004 Wilmer van der Gaast <wilmer@gaast.net>   * +*                                                                * +*  Licensed under the GNU General Public License                 * +*                                                                * +*  Modified by M. Dennis, 20040627                               * +\****************************************************************/ + +/*  +   ChangeLog: +    +   2004-06-27: Added support for AF_LOCAL (UNIX domain) sockets +               Renamed log to do_log to fix conflict warning +               Changed protocol to 0 (6 is not supported?) +               Added error check for socket() +               Added a no-fork (debug) mode +   2004-05-15: Added rate limiting +   2003-12-26: Added the SO_REUSEADDR sockopt, logging and CPU-time limiting +               for clients using setrlimit(), fixed the execv() call +   2002-11-29: Added the timeout so old child processes clean up faster +   2002-11-28: First version +*/ + +#define SELECT_TIMEOUT 2 +#define MAX_LOG_LEN 128 + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdarg.h> +#include <time.h> + +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/resource.h> +#include <sys/stat.h> + +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +typedef struct settings +{ +	char local; +	char debug; +	char *interface; +	signed int port; +	 +	unsigned char max_conn; +	int seconds; +	 +	int rate_seconds; +	int rate_times; +	int rate_ignore; +	 +	char **call; +} settings_t; + +typedef struct ipstats +{ +	unsigned int ip; +	 +	time_t rate_start; +	int rate_times; +	time_t rate_ignore; +	 +	struct ipstats *next; +} ipstats_t; + +FILE *logfile; +ipstats_t *ipstats; + +settings_t *set_load( int argc, char *argv[] ); +void do_log( char *fmt, ... ); +ipstats_t *ip_get( char *ip_txt ); + +int main( int argc, char *argv[] ) +{ +	const int rebind_on = 1; +	settings_t *set; +	 +	int serv_fd, serv_len; +	struct sockaddr_in serv_addr; +	struct sockaddr_un local_addr; +	 +	pid_t st; +	 +	if( !( set = set_load( argc, argv ) ) ) +		return( 1 ); +	 +	if( !logfile ) +		if( !( logfile = fopen( "/dev/null", "w" ) ) ) +		{ +			perror( "fopen" ); +			return( 1 ); +		} +	 +	fcntl( fileno( logfile ), F_SETFD, FD_CLOEXEC ); +	 +	if( set->local ) +		serv_fd = socket( PF_LOCAL, SOCK_STREAM, 0 ); +	else +		serv_fd = socket( PF_INET, SOCK_STREAM, 0 ); +	if( serv_fd < 0 ) +	{ +		perror( "socket" ); +		return( 1 ); +	} +	setsockopt( serv_fd, SOL_SOCKET, SO_REUSEADDR, &rebind_on, sizeof( rebind_on ) ); +	fcntl( serv_fd, F_SETFD, FD_CLOEXEC ); +	if (set->local) { +		local_addr.sun_family = AF_LOCAL; +		strncpy( local_addr.sun_path, set->interface, sizeof( local_addr.sun_path ) - 1 ); +		local_addr.sun_path[sizeof( local_addr.sun_path ) - 1] = '\0'; +		 +		/* warning - don't let untrusted users run this program if it +		   is setuid/setgid! Arbitrary file deletion risk! */ +		unlink( set->interface ); +		if( bind( serv_fd, (struct sockaddr *) &local_addr, SUN_LEN( &local_addr ) ) != 0 ) +		{ +			perror( "bind" ); +			return( 1 ); +		} +		chmod( set->interface, S_IRWXO|S_IRWXG|S_IRWXU ); + +	} else { +		serv_addr.sin_family = AF_INET; +		serv_addr.sin_addr.s_addr = inet_addr( set->interface ); +		serv_addr.sin_port = htons( set->port ); +		serv_len = sizeof( serv_addr ); +	 +		if( bind( serv_fd, (struct sockaddr *) &serv_addr, serv_len ) != 0 ) +		{ +			perror( "bind" ); +			return( 1 ); +		} +	} +	 +	if( listen( serv_fd, set->max_conn ) != 0 ) +	{ +		perror( "listen" ); +		return( 1 ); +	} +	 +	if ( ! set->debug ) { +		st = fork(); +		if( st < 0 ) +		{ +			perror( "fork" ); +			return( 1 ); +		} +		else if( st > 0 ) +		{ +			return( 0 ); +		} +		 +		setsid(); +		close( 0 ); +		close( 1 ); +		close( 2 ); +	} +	 +	do_log( "bitlbeed running" ); +	 +	/* The Daemon */ +	while( 1 ) +	{ +		int cli_fd, cli_len, i, st; +		struct sockaddr_in cli_addr; +		struct sockaddr_un cli_local; +		ipstats_t *ip; +		char *cli_txt; +		pid_t child; +		 +		static int running = 0; +		 +		fd_set rd; +		struct timeval tm; +		 +		/* accept() only returns after someone connects. To clean up old +		   processes (by running waitpid()) it's better to use select() +		   with a timeout. */ +		FD_ZERO( &rd ); +		FD_SET( serv_fd, &rd ); +		tm.tv_sec = SELECT_TIMEOUT; +		tm.tv_usec = 0; +		if( select( serv_fd + 1, &rd, NULL, NULL, &tm ) > 0 ) +		{ +			if (set->local) { +				cli_len = SUN_LEN( &cli_local ); +				cli_fd = accept( serv_fd, (struct sockaddr *) &cli_local, &cli_len ); +				cli_txt = "127.0.0.1"; +			} else { +				cli_len = sizeof( cli_addr ); +				cli_fd = accept( serv_fd, (struct sockaddr *) &cli_addr, &cli_len ); +				cli_txt = inet_ntoa( cli_addr.sin_addr ); +			} +			 +			ip = ip_get( cli_txt ); +			 +			if( set->rate_times == 0 || time( NULL ) > ip->rate_ignore ) +			{ +				/* We want this socket on stdout and stderr too! */ +				dup( cli_fd ); dup( cli_fd ); +				 +				if( ( child = fork() ) == 0 ) +				{ +					if( set->seconds ) +					{ +						struct rlimit li; +						 +						li.rlim_cur = (rlim_t) set->seconds; +						li.rlim_max = (rlim_t) set->seconds + 1; +						setrlimit( RLIMIT_CPU, &li ); +					} +					execv( set->call[0], set->call ); +					do_log( "Error while executing %s!", set->call[0] ); +					return( 1 ); +				} +				 +				running ++; +				close( 0 ); +				close( 1 ); +				close( 2 ); +				 +				do_log( "Started child process for client %s (PID=%d), got %d clients now", cli_txt, child, running ); +				 +				if( time( NULL ) < ( ip->rate_start + set->rate_seconds ) ) +				{ +					ip->rate_times ++; +					if( ip->rate_times >= set->rate_times ) +					{ +						do_log( "Client %s crossed the limit; ignoring for the next %d seconds", cli_txt, set->rate_ignore ); +						ip->rate_ignore = time( NULL ) + set->rate_ignore; +						ip->rate_start = 0; +					} +				} +				else +				{ +					ip->rate_start = time( NULL ); +					ip->rate_times = 1; +				} +			} +			else +			{ +				do_log( "Ignoring connection from %s", cli_txt ); +				close( cli_fd ); +			} +		} +		 +		/* If the max. number of connection is reached, don't accept +		   new connections until one expires -> Not always WNOHANG +		    +		   Cleaning up child processes is a good idea anyway... :-) */ +		while( ( i = waitpid( 0, &st, ( ( running < set->max_conn ) || ( set->max_conn == 0 ) ) ? WNOHANG : 0 ) ) > 0 ) +		{ +			running --; +			if( WIFEXITED( st ) ) +			{ +				do_log( "Child process (PID=%d) exited normally with status %d. %d Clients left now", +				     i, WEXITSTATUS( st ), running ); +			} +			else if( WIFSIGNALED( st ) ) +			{ +				do_log( "Child process (PID=%d) killed by signal %d. %d Clients left now", +				     i, WTERMSIG( st ), running ); +			} +			else +			{ +				/* Should not happen AFAIK... */ +				do_log( "Child process (PID=%d) stopped for unknown reason, %d clients left now", +				     i, running ); +			} +		} +	} +	 +	return( 0 ); +} + +settings_t *set_load( int argc, char *argv[] ) +{ +	settings_t *set; +	int opt, i; +	 +	set = malloc( sizeof( settings_t ) ); +	memset( set, 0, sizeof( settings_t ) ); +	set->interface = NULL;		/* will be filled in later */ +	set->port = 6667; +	set->local = 0; +	set->debug = 0; +	 +	set->rate_seconds = 600; +	set->rate_times = 5; +	set->rate_ignore = 900; +	 +	while( ( opt = getopt( argc, argv, "i:p:n:t:l:r:hud" ) ) >= 0 ) +	{ +		if( opt == 'i' ) +		{ +			set->interface = strdup( optarg ); +		} +		else if( opt == 'p' ) +		{ +			if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i <= 0 ) || ( i > 65535 ) ) +			{ +				fprintf( stderr, "Invalid port number: %s\n", optarg ); +				return( NULL ); +			} +			set->port = i; +		} +		else if( opt == 'n' ) +		{ +			if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) ) +			{ +				fprintf( stderr, "Invalid number of connections: %s\n", optarg ); +				return( NULL ); +			} +			set->max_conn = i; +		} +		else if( opt == 't' ) +		{ +			if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) || ( i > 600 ) ) +			{ +				fprintf( stderr, "Invalid number of seconds: %s\n", optarg ); +				return( NULL ); +			} +			set->seconds = i; +		} +		else if( opt == 'l' ) +		{ +			if( !( logfile = fopen( optarg, "a" ) ) ) +			{ +				perror( "fopen" ); +				fprintf( stderr, "Error opening logfile, giving up.\n" ); +				return( NULL ); +			} +			setbuf( logfile, NULL ); +		} +		else if( opt == 'r' ) +		{ +			if( sscanf( optarg, "%d,%d,%d", &set->rate_seconds, &set->rate_times, &set->rate_ignore ) != 3 ) +			{ +				fprintf( stderr, "Invalid argument to -r.\n" ); +				return( NULL ); +			} +		} +		else if( opt == 'u' ) +			set->local = 1; +		else if( opt == 'd' ) +			set->debug = 1; +		else if( opt == 'h' ) +		{ +			printf( "Usage: %s [-i <interface>] [-p <port>] [-n <num>] [-r x,y,z] ...\n" +			        "          ... <command> <args...>\n" +			        "A simple inetd-like daemon to have a program listening on a TCP socket without\n" +			        "needing root access to the machine\n" +			        "\n" +			        "  -i  Specify the interface (by IP address) to listen on.\n" +			        "      (Default: 0.0.0.0 (any interface))\n" +			        "  -p  Port number to listen on. (Default: 6667)\n" +			        "  -n  Maximum number of connections. (Default: 0 (unlimited))\n" +			        "  -t  Specify the maximum number of CPU seconds per process.\n" +			        "      (Default: 0 (unlimited))\n" +			        "  -l  Specify a logfile. (Default: none)\n" +			        "  -r  Rate limiting: Ignore a host for z seconds when it connects for more\n" +			        "      than y times in x seconds. (Default: 600,5,900. Disable: 0,0,0)\n" +				"  -u  Use a local socket, by default /tmp/bitlbee (override with -i <filename>)\n" +				"  -d  Don't fork for listening (for debugging purposes)\n" +			        "  -h  This information\n", argv[0] ); +			return( NULL ); +		} +	} +	 +	if( set->interface == NULL ) +		set->interface = (set->local) ? "/tmp/bitlbee" : "0.0.0.0"; +	 +	if( optind == argc ) +	{ +		fprintf( stderr, "Missing program parameter!\n" ); +		return( NULL ); +	} +	 +	/* The remaining arguments are the executable and its arguments */ +	set->call = malloc( ( argc - optind + 1 ) * sizeof( char* ) ); +	memcpy( set->call, argv + optind, sizeof( char* ) * ( argc - optind ) ); +	set->call[argc-optind] = NULL; +	 +	return( set ); +} + +void do_log( char *fmt, ... ) +{ +	va_list params; +	char line[MAX_LOG_LEN]; +	time_t tm; +	int l; +	 +	memset( line, 0, MAX_LOG_LEN ); +	 +	tm = time( NULL ); +	strcpy( line, ctime( &tm ) ); +	l = strlen( line ); +	line[l-1] = ' '; +	 +	va_start( params, fmt ); +	vsnprintf( line + l, MAX_LOG_LEN - l - 2, fmt, params ); +	va_end( params ); +	strcat( line, "\n" ); +	 +	fprintf( logfile, "%s", line ); +} + +ipstats_t *ip_get( char *ip_txt ) +{ +	unsigned int ip; +	ipstats_t *l; +	int p[4]; +	 +	sscanf( ip_txt, "%d.%d.%d.%d", p + 0, p + 1, p + 2, p + 3 ); +	ip = ( p[0] << 24 ) | ( p[1] << 16 ) | ( p[2] << 8 ) | ( p[3] ); +	 +	for( l = ipstats; l; l = l->next ) +	{ +		if( l->ip == ip ) +			return( l ); +	} +	 +	if( ipstats ) +	{ +		for( l = ipstats; l->next; l = l->next ); +		 +		l->next = malloc( sizeof( ipstats_t ) ); +		l = l->next; +	} +	else +	{ +		l = malloc( sizeof( ipstats_t ) ); +		ipstats = l; +	} +	memset( l, 0, sizeof( ipstats_t ) ); +	 +	l->ip = ip; +	 +	return( l ); +} diff --git a/utils/convert_purple.py b/utils/convert_purple.py new file mode 100755 index 00000000..85433119 --- /dev/null +++ b/utils/convert_purple.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +# +# Part of BitlBee. Reads a libpurple accounts.xml file and generates some +# commands/XML that BitlBee understands. For easy migration from Pidgin/ +# Finch/whatever to BitlBee, be it a public server or your own. +# +# Licensed under the GPL2 like the rest of BitlBee. +# +# Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> +# + +import getopt +import getpass +import os +import subprocess +import sys + +import xml.dom.minidom + +BITLBEE = '/usr/sbin/bitlbee' + +def parse_purple(f): +	protomap = { +		'msn-pecan': 'msn', +		'aim': 'oscar', +		'icq': 'oscar', +	} +	supported = ('msn', 'jabber', 'oscar', 'yahoo', 'twitter') +	accs = list() +	 +	if os.path.isdir(f): +		f = f + '/accounts.xml' +	xt = xml.dom.minidom.parse(f) +	for acc in xt.getElementsByTagName('account')[1:]: +		protocol = acc.getElementsByTagName('protocol')[0].firstChild.wholeText +		name = acc.getElementsByTagName('name')[0].firstChild.wholeText +		password = acc.getElementsByTagName('password')[0].firstChild.wholeText +		if protocol.startswith('prpl-'): +			protocol = protocol[5:] +		if name.endswith('/'): +			name = name[:-1] +		if protocol in protomap: +			protocol = protomap[protocol] +		if protocol not in supported: +			print 'Warning: protocol probably not supported by BitlBee: ' + protocol +		accs.append((protocol, name, password)) +	 +	return accs + +def print_commands(accs): +	print 'To copy all your Pidgin accounts to BitlBee, just copy-paste the following' +	print 'commands into your &bitlbee channel:' +	print +	for acc in accs: +		print 'account add %s %s %s' % acc + +def bitlbee_x(*args): +	bb = subprocess.Popen([BITLBEE, '-x'] + list(args), stdout=subprocess.PIPE) +	return bb.stdout.read().strip() + +def print_xml(accs): +	try: +		bitlbee_x('hash', 'blaataap') +	except: +		print "Can't find/use BitlBee binary. It has to be a 1.2.5 binary or higher." +		print +		usage() +	 +	print 'BitlBee .xml files are encrypted using the identify password. Please type your' +	print 'preferred identify password.' +	user = getpass.getuser() +	pwd = getpass.getpass() +	 +	root = xml.dom.minidom.Element('user') +	root.setAttribute('nick', user) +	root.setAttribute('password', bitlbee_x('hash', pwd)) +	root.setAttribute('version', '1') +	for acc in accs: +		accx = xml.dom.minidom.Element('account') +		accx.setAttribute('protocol', acc[0]) +		accx.setAttribute('handle', acc[1]) +		accx.setAttribute('password', bitlbee_x('enc', pwd, acc[2])) +		accx.setAttribute('autoconnect', '1') +		root.appendChild(accx) +	 +	print +	print 'Write the following XML data to a file called %s.xml (rename it if' +	print 'you want to use a different nickname). It should be in the directory where' +	print 'your BitlBee account files are stored (most likely /var/lib/bitlbee).' +	print +	print root.toprettyxml() + +def usage(): +	print 'Usage: %s [-f <purple accounts file>] [-b <bitlbee executable>] [-x]' % sys.argv[0] +	print +	print 'Generates "account add" commands by default. -x generates a .xml file instead.' +	print 'The accounts file can normally be found in ~/.purple/.' +	sys.exit(os.EX_USAGE) + +try: +	flags = dict(getopt.getopt(sys.argv[1:], 'f:b:x')[0]) +except getopt.GetoptError: +	usage() +if '-f' not in flags: +	usage() +if '-b' in flags: +	BITLBEE = flags['-b'] + +parsed = parse_purple(flags['-f']) +if '-x' in flags: +	print_xml(parsed) +else: +	print_commands(parsed) | 
