aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2005-11-06 19:23:18 +0100
committerWilmer van der Gaast <wilmer@gaast.net>2005-11-06 19:23:18 +0100
commitb7d3cc34f68dab7b8f7d0777711317b334fc2219 (patch)
tree6aa4d6332c96654fda79fe18993ab0e35d36a52b /utils
Initial repository (0.99 release tree)0.99
Diffstat (limited to 'utils')
-rw-r--r--utils/README58
-rw-r--r--utils/bitlbeed.c455
-rwxr-xr-xutils/centericq2bitlbee.sh115
-rw-r--r--utils/convert_gnomeicu.txt7
-rwxr-xr-xutils/create_nicksfile.pl210
5 files changed, 845 insertions, 0 deletions
diff --git a/utils/README b/utils/README
new file mode 100644
index 00000000..92b0348b
--- /dev/null
+++ b/utils/README
@@ -0,0 +1,58 @@
+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.
+
+
+* create_nicksfile.pl (Christian Friedl <christian.friedl@chello.at>)
+
+This program reads your ~/.licq/ configuration data and convert it to a
+correct .nicks file. This program can be extended to read other contact
+list file formats as well.
+
+
+* centericq2bitlbee.sh (geno <geno@xenyon.com>)
+
+Converter script for CenterICQ ICQ contact lists. See the documentation
+for more information.
+
+
+* convert_gnomeicu.txt
+
+Not a program, but this one contains a regex which should correctly
+convert GnomeICU configuration files into the BitlBee format.
+
+
+* Dynamic MOTD for BitlBee (Geert Hauwaerts <geert@hauwaerts.be>)
+
+Originally, I wanted to put this program here, but Geert put it online
+on his own server, with docs and stuff, so I guess it's better to put
+a link here. dmotd is a little script which generates a motd with some
+nice statistics, especially nice for servers with many people on it.
+
+See http://dmotd.hauwaerts.be/ for more information.
+
+
+* BitlBee-specific Irssi scripts for: tab completion, typing notifica-
+tions, auto-away and more, by Tijmen Ruizendaal <tijmen@fokdat.nl>.
+
+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://fokdat.nl/~tijmen/software/irssi-bitlbee.html
+
+
+Please do send your sources if you write anything useful for the Bee!
diff --git a/utils/bitlbeed.c b/utils/bitlbeed.c
new file mode 100644
index 00000000..b8db348e
--- /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 <lintux@debian.org> *
+* *
+* 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/centericq2bitlbee.sh b/utils/centericq2bitlbee.sh
new file mode 100755
index 00000000..b8c134e8
--- /dev/null
+++ b/utils/centericq2bitlbee.sh
@@ -0,0 +1,115 @@
+#!/bin/bash
+#
+# Author geno, <geno@xenyon.com>
+# Date 2004-04-24
+# Version 0.1c
+#
+
+show_help()
+{
+cat << _EOF_
+
+This script converts your CenterICQ contacts (AIM/ICQ) to BitlBee's contacts.
+The use of this script is on you own risk. You agree by using this script. :-)
+
+SYNTAX: `basename $0` <protoname> [<add_proto_tag>]
+
+ protoname - Choose the protocol you want to get your contacts from
+ by using "aim" or "icq" here.
+
+ add_proto_tag - This is optional and adds a suffix to each nickname.
+ For an AIM contact it will look like this: geno|aim
+ For an ICQ contact it will be |icq , WOW! :-D
+ To enable this option use "on".
+
+NOTE:
+ After the conversion of one protocol is done you will find a file
+ called bitlbee_[protoname] in ~/.centericq . Append the content of
+ this file to /var/lib/bitlbee/[username].nicks .
+
+ [username] is your username you use to talk to the BitlBee Server.
+ You will have to be root to edit this file!
+
+CREDITS:
+ This script was written by geno (geno@xenyon.com).
+ I hope it will help you to make the switch to BitlBee a bit easier. :-)
+
+_EOF_
+exit 0
+}
+
+case $1 in
+ "") show_help ;;
+ "icq")
+ nick_protocol="[1-9]*/"
+ protocol_const="3"
+ ;;
+
+ "aim")
+ nick_protocol="a*/"
+ protocol_const="1"
+ ;;
+
+ *) show_help ;;
+esac
+
+# can we see CenterICQ's directory ?
+if [ ! -d ~/.centericq ]; then
+ echo "The directory of CenterICQ (~/.centericq) was not found!"
+ echo "Maybe you are logged in with the wrong username."
+ exit 1
+fi
+
+# change to the center of all evil ;)
+cd ~/.centericq
+
+# get the listing of all nicks
+nick_listing=`ls -d $nick_protocol | sed 's/\ /_DuMmY_/g' | sed 's/\/_DuMmY_/\/ /g'`
+
+echo -e "\nConverting ...\n"
+
+# remove old conversion
+rm -f ~/.centericq/bitlbee_$1
+
+for nick_accountname in $nick_listing; do
+ # get rid of the slash and replace _DuMmY_ with space
+ nick_accountname=`echo "$nick_accountname" | sed 's/\/$//' | sed 's/_DuMmY_/\ /g'`
+
+ # find centericq alias
+ nick_cicq_alias=`cat "$nick_accountname/info" | sed '46!d'`
+
+ # if the centericq alias is the same as the account's name then
+ # it's not a real alias; search for account nickname
+ if [ "$nick_accountname" == "$nick_cicq_alias" ]; then
+ nick_accountalias=`cat "$nick_accountname/info" | sed '1!d'`
+ fi
+
+ # save the best nickname for conversion
+ if [ "x$nick_accountalias" == "x" ]; then
+ nick="$nick_cicq_alias"
+ else
+ nick="$nick_accountalias"
+ fi
+
+ # cut off the prefix 'a' of the accountname
+ if [ "$1" == "aim" ]; then
+ nick_accountname=`echo "$nick_accountname" | sed 's/^a//'`
+ fi
+
+ # replace each space with an underscore (spaces are not allowed in irc nicknames)
+ nick=`echo "$nick" | sed 's/\ /_/g'`
+
+ # if tags are wanted we will add them here
+ if [ "$2" == "on" ]; then
+ nick=`echo "$nick"\|$1`
+ fi
+
+ # print output to std
+ echo "Found '$nick_accountname' with alias '$nick'"
+ # save output to file
+ echo "$nick_accountname" $protocol_const "$nick" >> ~/.centericq/bitlbee_$1
+done
+
+echo -e "\nYou can find this list as a file in ~/.centericq/bitlbee_$1."
+echo -e "See help if you don't know what you have to do next.\n"
+
diff --git a/utils/convert_gnomeicu.txt b/utils/convert_gnomeicu.txt
new file mode 100644
index 00000000..e2bd1377
--- /dev/null
+++ b/utils/convert_gnomeicu.txt
@@ -0,0 +1,7 @@
+15:03:38 zoo| wilmer: watch this:
+15:03:40 zoo| cat ~/.icq/contacts.xml | sed "s/<\/user>/\n/g" |
+ sed "s/^.*<uin>//g" | sed "s/<\/nick>//" | sed "s/ /_/g" |
+ sed "s/<\/uin><nick>/ 3 /g" | grep -v -e "^<"
+15:04:23 zoo| it does output your gnomeicu nicks to stdout
+
+Thanks to Claas Langbehn. Use this at your own risk, it's not tested by us.
diff --git a/utils/create_nicksfile.pl b/utils/create_nicksfile.pl
new file mode 100755
index 00000000..abd6d3d2
--- /dev/null
+++ b/utils/create_nicksfile.pl
@@ -0,0 +1,210 @@
+#!/usr/bin/perl
+use strict;
+use Getopt::Long;
+
+
+my $conn = undef;
+my %readline_funcs = ( 'licq' => \&import_readline_licq );
+my %open_funcs = ( 'licq' => \&import_open_licq );
+my %close_funcs = ( 'licq' => \&import_close_licq );
+my $funcname = undef;
+my $dirname = undef;
+my $filename = undef;
+my $imported = 0;
+my $not_imported = 0;
+my $debug = 0;
+
+main();
+exit(0);
+
+
+sub main {
+ my ($server,$port,$nick,$pass,$func,$dir,$file,$outfile);
+ my $dirfile;
+ if (($dirfile=pop @ARGV) =~ /^\-/ || !$dirfile) {
+ tell_usage();
+ exit(0);
+ }
+ if ($dirfile =~ m|/|) {
+ $dirfile =~ m|^(.*)(/.+)$|;
+ $dir=$1;
+ $file=$2;
+ } else {
+ $dir=undef;
+ $file = $dirfile;
+ }
+ GetOptions(
+ 'from=s', => \$func,
+ 'of=s', => \$outfile,
+ 'debug', => \$debug
+ );
+ if (!import_start($func,$dir,$file,$outfile,$debug)) {
+ tell_usage();
+ }
+}
+
+sub tell_usage {
+ print "Usage: create_nicksfile.pl [--from=FROM] [--of=OUTPUTFILE] [--debug] FILENAME\n";
+ print " FROM defines which application we import from.\n",
+ print " Note that currently the only valid value for FROM is licq.\n";
+ print " For further information, you might want to do perldoc create_nicksfile.pl\n";
+}
+
+sub import_start {
+ $funcname = (shift) || 'licq';
+ $dirname = shift;
+ $filename = shift;
+ my $outfile = shift || 'bitlbee.nicks';
+ $debug = shift;
+ my ($alias,$protocol,$name,$found);
+ open(OUT,'>'.$outfile) || die "unable to open $outfile";
+ if (defined $open_funcs{$funcname}) {
+ if (&{$open_funcs{$funcname}}($dirname,$filename)) {
+ do {
+ ($alias,$protocol,$name,$found)=&{$readline_funcs{$funcname}}();
+ print OUT "$alias $protocol $name\n" if $found;
+ } while ($found);
+ } else {
+ import_err('Unable to open '.$filename);
+ return 0;
+ }
+ } else {
+ import_err($funcname.' is no defined import function.');
+ return 0;
+ }
+ close OUT;
+ &{$close_funcs{$funcname}}();
+ return 1;
+}
+
+sub import_err {
+ my $msg=shift;
+ print "\nError: $msg\n";
+}
+
+sub import_open_licq {
+ my ($dir,$name)=@_;
+ return open(IN,'<'.$dir.'/users.conf');
+}
+sub import_close_licq {
+ close IN;
+}
+sub import_readline_licq {
+ my ($uin,$alias);
+ my $line;
+GETLINE:
+ $line=<IN>;
+ if ($line) {
+ while ($line && $line !~ /^User\d+/) {
+ $line=<IN>;
+ }
+ if ($line) {
+ if ($line =~ /^User\d+\s*=\s*(\d+)(\.Licq)?$/) { # getting UIN
+ $uin=$1;
+ open(ALIAS,'<'.$dirname.'/users/'.$uin.'.Licq') ||
+ open(ALIAS,'<'.$dirname.'/users/'.$uin.'.uin') || do {
+ warn "unable to open userfile for $uin";
+ return (undef,undef,0);
+ };
+ while (<ALIAS>) {
+ if (/^Alias\s*=\s*(.*)$/) {
+ $alias=$1;
+ $alias =~ s/\s+/_/g;
+ last;
+ }
+ }
+ close ALIAS;
+ $imported++;
+ return ($uin,3,$alias,1);
+ } else {
+ warn('Unknown line format: '.$line);
+ $not_imported++;
+ goto GETLINE; #### grrrr, sometimes there are negative uins in licq files...
+ }
+ } else {
+ return (undef,undef,0);
+ }
+ } else {
+ return undef;
+ }
+}
+
+__END__
+
+=head1 NAME
+
+create_nicksfile.pl - Create a valid bitlbee .nicks file
+
+=head1 SYNOPSIS
+
+create_nicksfile.pl [--from=FROM] [--of=OUTPUTFILE] [--debug] FILENAME
+
+ FROM defines which application we import from.
+ Note that currently the only valid value for FROM
+ is licq.
+
+ If of is missing, we write to bitlbee.nicks.
+
+=head1 DESCRIPTION
+
+We run thru the
+files where the contacts reside and create
+a bitlbee .nicks-file from them.
+
+=head1 DEPENDENCIES
+
+On the perlside, we need Getopt::Long.
+
+=head1 CAVEATS
+
+=head1 TODO
+
+&import_readline_... should take a filehandle as argument.
+
+Add more import functions. If you are interested,
+to do so, you need to write the following functions:
+
+=over
+
+=item *
+
+import_open_<WHATEVER>(DIR,FILENAME)
+
+=item *
+
+import_close_<WHATEVER>()
+
+=item *
+
+import_readline_<WHATEVER>()
+
+=back
+
+and add them to the hashes
+
+=over
+
+=item *
+
+%readline_funcs
+
+=item *
+
+%open_funcs
+
+=item *
+
+%close_funcs
+
+=back
+
+at the top of this script.
+
+
+=head1 AUTHORS
+
+Christian Friedl <vijeno@chello.at>
+
+Updated for the new Licq list firmat by Hugo Buddelmeijer <kmail@hugo.doemaarwat.nl>
+
+=cut