Changes in / [ee6cc94:2528cda]


Ignore:
Files:
43 added
16 deleted
98 edited

Legend:

Unmodified
Added
Removed
  • Makefile

    ree6cc94 r2528cda  
    1010
    1111# Program variables
    12 objects = account.o bitlbee.o chat.o crypting.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o
    13 headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/nogaim.h
     12objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS)
     13headers = bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/account.h protocols/bee.h protocols/ft.h protocols/nogaim.h
    1414subdirs = lib protocols
    1515
     
    2626# Expansion of variables
    2727subdirobjs = $(foreach dir,$(subdirs),$(dir)/$(dir).o)
    28 CFLAGS += -Wall
    2928
    3029all: $(OUTFILE)
     
    8281install-dev:
    8382        mkdir -p $(DESTDIR)$(INCLUDEDIR)
    84         install -m 0644 $(headers) $(DESTDIR)$(INCLUDEDIR)
     83        install -m 0644 config.h $(DESTDIR)$(INCLUDEDIR)
     84        for i in $(headers); do install -m 0644 $(SRCDIR)$$i $(DESTDIR)$(INCLUDEDIR); done
    8585        mkdir -p $(DESTDIR)$(PCDIR)
    8686        install -m 0644 bitlbee.pc $(DESTDIR)$(PCDIR)
     
    9393install-etc:
    9494        mkdir -p $(DESTDIR)$(ETCDIR)
    95         install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
    96         install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
     95        install -m 0644 $(SRCDIR)motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
     96        install -m 0644 $(SRCDIR)bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
    9797
    9898uninstall-etc:
     
    110110        @$(MAKE) -C $@ $(MAKECMDGOALS)
    111111
    112 $(objects): %.o: %.c
     112$(objects): %.o: $(SRCDIR)%.c
    113113        @echo '*' Compiling $<
    114114        @$(CC) -c $(CFLAGS) $< -o $@
     
    126126ctags:
    127127        ctags `find . -name "*.c"` `find . -name "*.h"`
     128
     129# Using this as a bogus Make target to test if a GNU-compatible version of
     130# make is available.
     131helloworld:
     132        @echo Hello World
  • bitlbee.c

    ree6cc94 r2528cda  
    3636static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition condition );
    3737
     38static gboolean try_listen( struct addrinfo *res )
     39{
     40        int i;
     41       
     42        global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
     43        if( global.listen_socket < 0 )
     44        {
     45                log_error( "socket" );
     46                return FALSE;
     47        }
     48
     49#ifdef IPV6_V6ONLY             
     50        if( res->ai_family == AF_INET6 )
     51        {
     52                i = 0;
     53                setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
     54                            (char *) &i, sizeof( i ) );
     55        }
     56#endif
     57
     58        /* TIME_WAIT (?) sucks.. */
     59        i = 1;
     60        setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
     61
     62        i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen );
     63        if( i == -1 )
     64        {
     65                closesocket( global.listen_socket );
     66                global.listen_socket = -1;
     67               
     68                log_error( "bind" );
     69                return FALSE;
     70        }
     71       
     72        return TRUE;
     73}
     74
    3875int bitlbee_daemon_init()
    3976{
     
    4279        FILE *fp;
    4380       
    44         log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG );
    45         log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG );
     81        log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE );
     82        log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE );
    4683       
    4784        memset( &hints, 0, sizeof( hints ) );
     
    63100
    64101        global.listen_socket = -1;
    65 
     102       
     103        /* Try IPv6 first (which will become an IPv6+IPv4 socket). */
    66104        for( res = addrinfo_bind; res; res = res->ai_next )
    67         {
    68                 global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol );
    69                 if( global.listen_socket < 0 )
    70                         continue;
    71 
    72                 /* TIME_WAIT (?) sucks.. */
    73                 i = 1;
    74                 setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) );
    75 
    76                 i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen );
    77                 if( i == -1 )
    78                 {
    79                         log_error( "bind" );
    80                         return( -1 );
    81                 }
    82 
    83                 break;
    84         }
    85 
     105                if( res->ai_family == AF_INET6 && try_listen( res ) )
     106                        break;
     107       
     108        /* The rest (so IPv4, I guess). */
     109        if( res == NULL )
     110                for( res = addrinfo_bind; res; res = res->ai_next )
     111                        if( res->ai_family != AF_INET6 && try_listen( res ) )
     112                                break;
     113       
    86114        freeaddrinfo( addrinfo_bind );
    87115
     
    93121        }
    94122       
    95         global.listen_watch_source_id = b_input_add( global.listen_socket, GAIM_INPUT_READ, bitlbee_io_new_client, NULL );
     123        global.listen_watch_source_id = b_input_add( global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL );
    96124       
    97125#ifndef _WIN32
     
    107135                        exit( 0 );
    108136               
     137                setsid();
    109138                chdir( "/" );
    110139               
     
    137166#endif
    138167       
     168        if( !global.conf->nofork )
     169        {
     170                log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG );
     171                log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG );
     172        }
     173       
    139174        return( 0 );
    140175}
     
    283318                        struct bitlbee_child *child;
    284319                       
     320                        /* TODO: Stuff like this belongs in ipc.c. */
    285321                        child = g_new0( struct bitlbee_child, 1 );
    286322                        child->pid = client_pid;
    287323                        child->ipc_fd = fds[0];
    288                         child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );
     324                        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     325                        child->to_fd = -1;
    289326                        child_list = g_slist_append( child_list, child );
    290327                       
     
    314351                        /* We can store the IPC fd there now. */
    315352                        global.listen_socket = fds[1];
    316                         global.listen_watch_source_id = b_input_add( fds[1], GAIM_INPUT_READ, ipc_child_read, irc );
     353                        global.listen_watch_source_id = b_input_add( fds[1], B_EV_IO_READ, ipc_child_read, irc );
    317354                       
    318355                        close( fds[0] );
     
    335372        /* Try to save data for all active connections (if desired). */
    336373        while( irc_connection_list != NULL )
    337                 irc_free( irc_connection_list->data );
     374                irc_abort( irc_connection_list->data, TRUE,
     375                           "BitlBee server shutting down" );
    338376       
    339377        /* We'll only reach this point when not running in inetd mode: */
  • bitlbee.h

    ree6cc94 r2528cda  
    3535
    3636#define PACKAGE "BitlBee"
    37 #define BITLBEE_VERSION "1.2.5"
     37#define BITLBEE_VERSION "1.3dev"
    3838#define VERSION BITLBEE_VERSION
     39#define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c))
     40#define BITLBEE_VERSION_CODE BITLBEE_VER(1, 3, 0)
    3941
    4042#define MAX_STRING 511
    4143
    4244#if HAVE_CONFIG_H
    43 #include "config.h"
     45#include <config.h>
    4446#endif
    4547
     
    124126#define CONF_FILE_DEF ETCDIR "bitlbee.conf"
    125127
     128#include "bee.h"
    126129#include "irc.h"
    127130#include "storage.h"
     
    158161gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond );
    159162
    160 void root_command_string( irc_t *irc, user_t *u, char *command, int flags );
     163void root_command_string( irc_t *irc, char *command );
    161164void root_command( irc_t *irc, char *command[] );
     165gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond );
    162166gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
    163167
  • conf.c

    ree6cc94 r2528cda  
    6363        conf->ping_timeout = 300;
    6464        conf->user = NULL;
     65        conf->ft_max_size = SIZE_MAX;
     66        conf->ft_max_kbps = G_MAXUINT;
     67        conf->ft_listen = NULL;
    6568        conf->protocols = NULL;
    6669        proxytype = 0;
     
    7982        }
    8083       
    81         while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 )
     84        while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hu:V" ) ) >= 0 )
    8285        /*     ^^^^ Just to make sure we skip this step from the REHASH handler. */
    8386        {
     
    145148                                "  -d  Specify alternative user configuration directory\n"
    146149                                "  -x  Command-line interface to password encryption/hashing\n"
    147                                 "  -h  Show this help page.\n" );
     150                                "  -h  Show this help page.\n"
     151                                "  -V  Show version info.\n" );
    148152                        return NULL;
    149153                }
    150                 else if( opt == 'R' )
    151                 {
    152                         /* Backward compatibility; older BitlBees passed this
    153                            info using a command-line flag. Allow people to
    154                            upgrade from such a version for now. */
    155                         setenv( "_BITLBEE_RESTART_STATE", optarg, 0 );
     154                else if( opt == 'V' )
     155                {
     156                        printf( "BitlBee %s\nAPI version %06x\n",
     157                                BITLBEE_VERSION, BITLBEE_VERSION_CODE );
     158                        return NULL;
    156159                }
    157160                else if( opt == 'u' )
     
    308311                                conf->user = g_strdup( ini->value );
    309312                        }
     313                        else if( g_strcasecmp( ini->key, "ft_max_size" ) == 0 )
     314                        {
     315                                size_t ft_max_size;
     316                                if( sscanf( ini->value, "%zu", &ft_max_size ) != 1 )
     317                                {
     318                                        fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
     319                                        return 0;
     320                                }
     321                                conf->ft_max_size = ft_max_size;
     322                        }
     323                        else if( g_strcasecmp( ini->key, "ft_max_kbps" ) == 0 )
     324                        {
     325                                if( sscanf( ini->value, "%d", &i ) != 1 )
     326                                {
     327                                        fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
     328                                        return 0;
     329                                }
     330                                conf->ft_max_kbps = i;
     331                        }
     332                        else if( g_strcasecmp( ini->key, "ft_listen" ) == 0 )
     333                        {
     334                                g_free( conf->ft_listen );
     335                                conf->ft_listen = g_strdup( ini->value );
     336                        }
    310337                        else if( g_strcasecmp( ini->key, "protocols" ) == 0 )
    311338                        {
     
    342369                if( g_strcasecmp( ini->section, "defaults" ) == 0 )
    343370                {
    344                         set_t *s = set_find( &irc->set, ini->key );
     371                        set_t *s = set_find( &irc->b->set, ini->key );
    345372                       
    346373                        if( s )
  • conf.h

    ree6cc94 r2528cda  
    5050        int ping_timeout;
    5151        char *user;
     52        size_t ft_max_size;
     53        int ft_max_kbps;
     54        char *ft_listen;
    5255        char **protocols;
    5356} conf_t;
  • configure

    ree6cc94 r2528cda  
    2626oscar=1
    2727yahoo=1
     28twitter=1
     29twitter=1
     30purple=0
    2831
    2932debug=0
     
    6669--oscar=0/1     Disable/enable Oscar part (ICQ, AIM)    $oscar
    6770--yahoo=0/1     Disable/enable Yahoo part               $yahoo
     71--twitter=0/1   Disable/enable Twitter part             $twitter
     72
     73--purple=0/1    Disable/enable libpurple support        $purple
    6874
    6975--debug=0/1     Disable/enable debugging                $debug
     
    119125EOF
    120126
     127srcdir="$(dirname $0)"
     128if [ "$srcdir" != "." ]; then
     129        echo
     130        echo "configure script run from a different directory. Will create some symlinks..."
     131        if [ ! -e Makefile -o -L Makefile ]; then
     132                COPYDIRS="doc lib protocols tests utils"
     133                mkdir -p $(cd "$srcdir"; find $COPYDIRS -type d)
     134                find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null
     135                dst="$PWD"
     136                cd "$srcdir"
     137                for i in $(find . -name Makefile -type f); do
     138                        ln -s "$PWD${i#.}" "$dst/$i";
     139                done
     140                cd "$dst"
     141                rm -rf .bzr
     142        fi
     143       
     144        echo "SRCDIR=$srcdir/" >> Makefile.settings
     145        CFLAGS="$CFLAGS -I${dst}"
     146else
     147        srcdir=$PWD
     148fi
     149
    121150cat<<EOF>config.h
    122151/* BitlBee settings, generated by configure
     
    155184fi
    156185
    157 echo CFLAGS=$CFLAGS >> Makefile.settings
    158 echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings
     186echo CFLAGS=$CFLAGS $CPPFLAGS >> Makefile.settings
     187echo CFLAGS+=-I${srcdir} -I${srcdir}/lib -I${srcdir}/protocols -I. >> Makefile.settings
    159188
    160189echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings
     
    172201
    173202echo "CC=$CC" >> Makefile.settings;
     203if echo $CC | grep -qw gcc; then
     204        # Apparently -Wall is gcc-specific?
     205        echo CFLAGS+=-Wall >> Makefile.settings
     206fi
    174207
    175208if [ -z "$LD" ]; then
     
    267300detect_ldap()
    268301{
    269         TMPFILE=$(mktemp)
     302        TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
    270303        if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
    271304                cat<<EOF>>Makefile.settings
     
    295328detect_resolv_dynamic()
    296329{
    297         TMPFILE=$(mktemp)
     330        TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
    298331        ret=1
    299332        echo "$RESOLV_TESTCODE" | $CC -o $TMPFILE -x c - -lresolv >/dev/null 2>/dev/null
     
    309342detect_resolv_static()
    310343{
    311         TMPFILE=$(mktemp)
     344        TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
    312345        ret=1
    313346        for i in $systemlibdirs; do
     
    396429fi
    397430
    398 STORAGES="text xml"
     431STORAGES="xml"
    399432
    400433if [ "$ldap" = "auto" ]; then
     
    476509fi
    477510
     511if ! make helloworld > /dev/null 2>&1; then
     512        echo "WARNING: Your version of make (BSD make?) does not support BitlBee's makefiles."
     513        echo "BitlBee needs GNU make to build properly. On most systems GNU make is available"
     514        echo "under the name 'gmake'."
     515        echo
     516        if gmake helloworld > /dev/null 2>&1; then
     517                echo "gmake seems to be available on your machine, great."
     518                echo
     519        else
     520                echo "gmake is not installed (or not working). Please try to install it."
     521                echo
     522        fi
     523fi
     524
    478525cat <<EOF>bitlbee.pc
    479526prefix=$prefix
     
    492539protoobjs=''
    493540
     541if [ "$purple" = 0 ]; then
     542        echo '#undef WITH_PURPLE' >> config.h
     543else
     544        if ! $PKG_CONFIG purple; then
     545                echo
     546                echo 'Cannot find libpurple development libraries, aborting. (Install libpurple-dev?)'
     547                exit 1
     548        fi
     549        echo '#define WITH_PURPLE' >> config.h
     550        cat<<EOF>>Makefile.settings
     551EFLAGS += $($PKG_CONFIG purple --libs)
     552PURPLE_CFLAGS += $($PKG_CONFIG purple --cflags)
     553EOF
     554        protocols=$protocols'purple '
     555        protoobjs=$protoobjs'purple_mod.o '
     556
     557        # Having both libpurple and native IM modules in one binary may
     558        # do strange things. Let's not do that.
     559        msn=0
     560        jabber=0
     561        oscar=0
     562        yahoo=0
     563        twitter=0
     564       
     565        if [ "$events" = "libevent" ]; then
     566                echo
     567                echo 'Warning: Some libpurple modules (including msn-pecan) do their event handling'
     568                echo 'outside libpurple, talking to GLib directly. At least for now the combination'
     569                echo 'libpurple + libevent is *not* recommended!'
     570        fi
     571fi
     572
    494573if [ "$msn" = 0 ]; then
    495574        echo '#undef WITH_MSN' >> config.h
     
    524603fi
    525604
     605if [ "$twitter" = 0 ]; then
     606        echo '#undef WITH_TWITTER' >> config.h
     607else
     608        echo '#define WITH_TWITTER' >> config.h
     609        protocols=$protocols'twitter '
     610        protoobjs=$protoobjs'twitter_mod.o '
     611fi
     612
    526613if [ "$protocols" = "PROTOCOLS = " ]; then
    527614        echo "Warning: You haven't selected any communication protocol to compile!"
  • debian/bitlbee.init

    • Property mode changed from 100755 to 100644
    ree6cc94 r2528cda  
    3838d_start() {
    3939        # Make sure BitlBee can actually write its PID...
    40         touch /var/run/bitlbee.pid
    41         chown bitlbee: /var/run/bitlbee.pid
     40        touch $PIDFILE
     41        chown bitlbee: $PIDFILE
    4242       
    4343        start-stop-daemon --start --quiet --pidfile $PIDFILE \
  • debian/changelog

    ree6cc94 r2528cda  
     1bitlbee (1.3-0) unstable; urgency=low
     2
     3  * Setting some bogus version number, fix that later.
     4  * Now using debhelper to improve maintainability.
     5  * Added a bitlbee-libpurple package, and split off docs and stuff into
     6    bitlbee-common.
     7
     8 -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 05 Jun 2010 15:16:38 +0100
     9
     10bitlbee (1.2.8-1) unstable; urgency=low
     11
     12  * New upstream version.
     13
     14 -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 10 Jul 2010 13:54:55 +0100
     15
     16bitlbee (1.2.7-1) unstable; urgency=high
     17
     18  * New upstream version.
     19  * Fixes MSN Messenger login issues, so uploading at high priority.
     20
     21 -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 15 May 2010 16:15:57 +0100
     22
     23bitlbee (1.2.6a-1) unstable; urgency=low
     24
     25  * New upstream version.
     26  * Native support for Twitter.
     27  * Fixed /WHOIS response format. (Closes: #576120)
     28  * Problems with bitlbee-skype are solved by now. (Closes: #575572)
     29
     30 -- Wilmer van der Gaast <wilmer@peer.gaast.net>  Tue, 20 Apr 2010 00:34:51 +0200
     31
    132bitlbee (1.2.5-1) unstable; urgency=low
    233
  • debian/control

    ree6cc94 r2528cda  
    44Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
    55Uploaders: Jelmer Vernooij <jelmer@samba.org>
    6 Standards-Version: 3.8.0
    7 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
     6Standards-Version: 3.8.4
     7Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), po-debconf, libpurple-dev, debhelper (>= 6)
    88Homepage: http://www.bitlbee.org/
    99Vcs-Bzr: http://code.bitlbee.org/bitlbee/
     
    1212Package: bitlbee
    1313Architecture: any
    14 Depends: ${shlibs:Depends}, adduser, net-tools, ${debconf-depends}, debianutils (>= 1.16)
    15 Description: An IRC to other chat networks gateway
     14Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version})
     15Conflicts: bitlbee-libpurple
     16Replaces: bitlbee-libpurple
     17Description: An IRC to other chat networks gateway (default version)
    1618 This program can be used as an IRC server which forwards everything you
    17  say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo.
     19 say to people on other chat networks: Jabber (which includes Google Talk
     20 and Facebook Chat), ICQ, AIM, MSN, Yahoo! and Twitter/Identica/Status.net.
     21
     22Package: bitlbee-libpurple
     23Architecture: any
     24Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version})
     25Conflicts: bitlbee
     26Replaces: bitlbee
     27Description: An IRC to other chat networks gateway (using libpurple)
     28 This program can be used as an IRC server which forwards everything you
     29 say to people on other chat networks: Jabber (which includes Google Talk
     30 and Facebook Chat), ICQ, AIM, MSN, Yahoo! and Twitter/Identica/Status.net.
     31 .
     32 This package contains a version of BitlBee that uses the libpurple instant
     33 messaging library instead of built-in code, which adds support for more IM
     34 protocols (all protocols supported by Pidgin/Finch) and features (like file
     35 transfers), at the price of being less lightweight.
     36 .
     37 This variant may not be very suitable for BitlBee instances used by many
     38 (tens or hundreds) of clients.
     39
     40Package: bitlbee-common
     41Architecture: all
     42Depends: ${misc:Depends}, net-tools
     43Replaces: bitlbee
     44Description: An IRC to other chat networks gateway (common files/docs)
     45 This program can be used as an IRC server which forwards everything you
     46 say to people on other chat networks: Jabber (which includes Google Talk
     47 and Facebook Chat), ICQ, AIM, MSN, Yahoo! and Twitter/Identica/Status.net.
     48 .
     49 This package contains common files (mostly documentation) for bitlbee and
     50 bitlbee-libpurple.
    1851
    1952Package: bitlbee-dev
    2053Architecture: all
    21 Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~)
    22 Description: An IRC to other chat networks gateway
     54Depends: ${misc:Depends}, bitlbee (>= ${bee:Version}), bitlbee (<< ${bee:Version}.1~)
     55Description: An IRC to other chat networks gateway (dev files)
    2356 This program can be used as an IRC server which forwards everything you
    24  say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo.
     57 say to people on other chat networks: Jabber (which includes Google Talk
     58 and Facebook Chat), ICQ, AIM, MSN, Yahoo! and Twitter/Identica/Status.net.
    2559 .
    2660 This package holds development stuff for compiling plug-ins.
  • debian/patches/bitlbee.conf.diff

    ree6cc94 r2528cda  
    1 --- debian/bitlbee/etc/bitlbee/bitlbee.conf     2009-06-01 00:20:24.000000000 +0100
    2 +++ debian/bitlbee/etc/bitlbee/bitlbee.conf     2009-06-07 21:16:19.000000000 +0100
     1--- bitlbee.conf        2009-06-01 00:20:24.000000000 +0100
     2+++ bitlbee.conf        2009-06-07 21:16:19.000000000 +0100
    33@@ -23,13 +23,18 @@
    44 ## If BitlBee is started by root as a daemon, it can drop root privileges,
  • debian/po/POTFILES.in

    ree6cc94 r2528cda  
    1 [type: gettext/rfc822deb] templates
     1[type: gettext/rfc822deb] bitlbee-common.templates
  • debian/rules

    ree6cc94 r2528cda  
    11#!/usr/bin/make -f
     2#
     3# Finally switching to debhelper.
     4#
     5# Not using debhelper was an exercise suggested to me by my AM (Gergely
     6# Nagy). It was educating at the time but I finally decided that the
     7# exercise is over now.
     8#
    29
     10BITLBEE_CONFIGURE_FLAGS ?=
    311DEBUG ?= 0
    412
    5 ifdef BITLBEE_VERSION
    6 BITLBEE_FORCE_VERSION=1
    7 else
     13ifndef BITLBEE_VERSION
    814# Want to use the full package version number instead of just the release.
    9 BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')"
    10 export BITLBEE_VERSION
     15BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION=\"$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')\"
    1116endif
    1217
    13 build-arch: build-arch-stamp
    14 build-arch-stamp:
    15         [ -d debian ]
    16         ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent
    17         $(MAKE)
    18 #       $(MAKE) -C doc/ all
    19         touch build-arch-stamp
     18build: build-stamp
     19build-stamp:
     20        dh_testdir
     21
     22        mkdir -p debian/build-native
     23        ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS)
     24        $(MAKE) -C debian/build-native
     25
     26        mkdir -p debian/build-libpurple
     27        ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS)
     28        $(MAKE) -C debian/build-libpurple
     29
     30        $(MAKE) -C doc
     31
     32        touch build-stamp
    2033
    2134clean:
    22         [ "`whoami`" = "root" -a -d debian ]
    23         rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files debian/bitlbee-dev
     35        dh_testdir
     36        dh_testroot
     37        rm -f build-stamp
     38
     39        rm -rf build-arch-stamp debian/build-*
    2440        $(MAKE) distclean
    25 #       -$(MAKE) -C doc/ clean
    26                                
    2741
    28 install-arch: build-arch
    29         [ "`whoami`" = "root" -a -d debian ]
    30         mkdir -p debian/bitlbee/DEBIAN/
    31         $(MAKE) install install-etc DESTDIR=`pwd`/debian/bitlbee
     42        dh_clean
    3243
    33         mkdir -p debian/bitlbee/usr/share/doc/bitlbee/
    34         cp doc/user-guide/user-guide.txt debian/bitlbee/usr/share/doc/bitlbee/
    35         cp doc/user-guide/user-guide.html debian/bitlbee/usr/share/doc/bitlbee/
     44install: build
     45        dh_testdir
     46        dh_testroot
     47        dh_clean -k
     48        dh_installdirs
    3649
    37 install-indep: install-arch
    38         [ "`whoami`" = "root" -a -d debian ]
    39         mkdir -p debian/bitlbee-dev/DEBIAN/
    40         $(MAKE) install-dev DESTDIR=`pwd`/debian/bitlbee-dev
     50        $(MAKE) -C debian/build-native install install-etc DESTDIR=`pwd`/debian/bitlbee
     51        $(MAKE) -C debian/build-libpurple install install-etc DESTDIR=`pwd`/debian/bitlbee-libpurple
     52        $(MAKE) -C debian/build-native install-dev DESTDIR=`pwd`/debian/bitlbee-dev
    4153
    42         mkdir -p debian/bitlbee-dev/usr/share/doc/bitlbee-dev/
     54        patch debian/bitlbee/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff
     55        patch debian/bitlbee-libpurple/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff
    4356
    44 binary-arch: build-arch install-arch
    45         [ "`whoami`" = "root" -a -d debian ]
     57        mkdir -p debian/bitlbee-common/usr
     58        mv debian/bitlbee/usr/share debian/bitlbee-common/usr
     59        rm -rf debian/bitlbee-libpurple/usr/share
    4660
    47         chmod 755 debian/post* debian/pre* debian/config debian/bitlbee.init
     61binary-common:
     62        dh_testdir
     63        dh_testroot
    4864
    49         mkdir -p debian/bitlbee/usr/share/doc/bitlbee/examples/ debian/bitlbee/etc/init.d/
    50         -cp doc/RELEASE-SPEECH* debian/bitlbee/usr/share/doc/bitlbee/ && gzip -9 debian/bitlbee/usr/share/doc/bitlbee/RELEASE-SPEECH*
    51         cp doc/CREDITS doc/AUTHORS doc/README doc/FAQ debian/README.Debian debian/bitlbee/usr/share/doc/bitlbee/
    52         cp debian/changelog debian/bitlbee/usr/share/doc/bitlbee/changelog.Debian
    53         cp debian/copyright debian/bitlbee/usr/share/doc/bitlbee/copyright
    54         cp doc/CHANGES debian/bitlbee/usr/share/doc/bitlbee/changelog
    55         cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/
    56         cp debian/bitlbee.init debian/bitlbee/etc/init.d/bitlbee
    57         patch -p0 < debian/patches/bitlbee.conf.diff
    58         cd debian/bitlbee/usr/share/; \
    59                 gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \
    60                         doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5
    61        
    62         chown -R root:root debian/bitlbee/
    63         find debian/bitlbee/usr/share/ -type d -exec chmod 755 {} \;
    64         find debian/bitlbee/usr/share/ -type f -exec chmod 644 {} \;
    65        
    66         cp debian/prerm debian/bitlbee/DEBIAN/
    67         cp debian/postinst debian/bitlbee/DEBIAN/
    68         cp debian/postrm debian/bitlbee/DEBIAN/
    69         cp debian/config debian/bitlbee/DEBIAN/
     65        dh_installchangelogs doc/CHANGES
     66        dh_installexamples
     67        dh_installdocs #--link-doc=bitlbee-common
     68        # TODO: Restore --link-doc up here and remove the hack below once
     69        # Hardy and Lenny are deprecated.
     70        for p in bitlbee bitlbee-libpurple bitlbee-dev; do rm -rf debian/$$p/usr/share/doc/$$p; ln -s bitlbee-common debian/$$p/usr/share/doc/$$p; done
     71        dh_installdebconf
     72        dh_installinit
     73ifeq ($(DH_OPTIONS),-a)
     74        cp -a debian/bitlbee/etc debian/bitlbee-libpurple
     75endif
     76        dh_installman
     77        dh_strip
     78        dh_link
     79        dh_compress
     80        dh_fixperms
     81        dh_installdeb
     82ifeq ($(DH_OPTIONS),-a)
     83        cp -a debian/bitlbee/DEBIAN/post* debian/bitlbee/DEBIAN/pre* debian/bitlbee-libpurple/DEBIAN
     84endif
     85        dh_shlibdeps
     86ifdef BITLBEE_VERSION
     87        dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0  -Vbee:Version=1:$(BITLBEE_VERSION)-0
     88else
     89        dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+b[0-9]\+$$//')
     90endif
     91        dh_md5sums
     92        dh_builddeb
    7093
    71         po2debconf debian/templates > debian/bitlbee/DEBIAN/templates
    72         cp debian/conffiles debian/bitlbee/DEBIAN/
    73        
    74         if [ "$(DEBUG)" = "0" ]; then strip -R .comment -R .note debian/bitlbee/usr/sbin/bitlbee; fi
     94binary-indep: build install
     95        $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common
    7596
    76         cd debian/bitlbee; \
    77                 find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
    78         dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee
    79 ifdef BITLBEE_FORCE_VERSION
    80         dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
    81 else
    82         dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
    83 endif
     97binary-arch: build install
     98        $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common
    8499
    85         dpkg --build debian/bitlbee ..
     100binary-%: build install
     101        make -f debian/rules binary-common DH_OPTIONS=-p$*
    86102
    87 binary-indep: install-indep
    88         [ "`whoami`" = "root" -a -d debian ]
    89 
    90         chown -R root.root debian/bitlbee-dev/
    91         find debian/bitlbee-dev/usr/share/ -type d -exec chmod 755 {} \;
    92         find debian/bitlbee-dev/usr/share/ -type f -exec chmod 644 {} \;
    93 
    94         cp debian/changelog debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian
    95         gzip -9 debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian
    96         cp debian/copyright debian/bitlbee-dev/usr/share/doc/bitlbee-dev/copyright
    97 
    98         cd debian/bitlbee-dev; \
    99                 find usr -type f -exec md5sum {} \; > DEBIAN/md5sums
    100 
    101         dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev
    102 
    103         dpkg --build debian/bitlbee-dev ..
    104 
    105 binary: binary-arch binary-indep
    106 build: build-arch
    107 install: install-arch install-indep
    108 
    109 .PHONY: build-arch build clean binary-arch binary install-arch install binary-indep install-indep
     103binary: binary-indep binary-arch
     104.PHONY: build clean binary-indep binary-arch binary-common binary install
  • doc/CHANGES

    ree6cc94 r2528cda  
    33
    44http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
     5
     6Version 1.3dev:
     7- For the first time since 2007, a dev snapshot. Like then, this is pretty
     8  stable already (running on testing.bitlbee.org for weeks by now), but not
     9  all planned features for the next major release are ready yet, and there
     10  may be some rough edges in the interface and documentation.
     11- Loads of new stuff, mostly ready for a new major release, but starting with
     12  a dev snapshot. A few changes that were planned for a long time already:
     13- Rewrote the IRC core, which brings:
     14  * Support for multiple (control) channels, so you can have one channel per
     15    buddy group, per IM account/protocol, or for example a &offline with all
     16    offline contacts. See "help channels" for information on how to use this.
     17  * You can also leave and rejoin all channels. Being in &bitlbee is no
     18    longer required.
     19  * Now you can start groupchats by just joining a new channel and inviting
     20    contacts. (The "chat with" command still works as well.)
     21  * You can change your nickname, whenever you want.
     22  * Root commands:
     23    + To hopefully resolve confusion about the "account set" syntax, the
     24      ordering was changed slightly: "account set acc/setting value"
     25      becomes "account acc set setting value". Obviously the same order
     26      should now be used for other account subcommands.
     27    + Shortcuts: Instead of "account list" you can say "acc li".
     28  * /whois shows idle/login times of your contacts when available.
     29  * paste_buffer (previously known as buddy_sendbuffer) now works for chats
     30    as well.
     31  * The nick_source setting was replaced with a nick_format setting, which
     32    looks more or less like a format string, and lets you tweak how nicknames
     33    for contacts are generated in more detail. It also tries to convert non-
     34    ASCII characters (i.e. accents) properly.
     35  * A per-channel show_users setting lets you configure exactly which kinds
     36    of contacts (online/away/offline) should show up in a channel and what
     37    modes (none/voice/etc) they should have. Like show_offline, but together
     38    with other new features it can for example create an &offline channel
     39    with just offline contacts.
     40  * If you connect (and identify) to a BitlBee server you're already
     41    connected to, you can take over the existing session instead of starting
     42    a new one.
     43  * More correct validation of channel names: They can contain pretty much
     44    any character, unlike nicknames.
     45- Support for using libpurple instead of BitlBee's built-in IM protocol
     46  modules. This can be enabled by passing --purple=1 to the configure script.
     47  * This adds support for many more IM protocols to BitlBee.
     48  * And new functionality to existing protocols.
     49  * This is and will always be optional and using it on public servers is
     50    *not* recommended. It should be pretty stable, but costs more RAM/etc.
     51  * Switching to libpurple should be pretty transparent. See "help purple"
     52    for a list of supported protocols (works only in libpurple-enabled
     53    binaries).
     54- Support for file transfers, in and out. /DCC SEND a file to a contact and
     55  it becomes a file transfer, and incoming file transfers become /DCC SENDs
     56  to you. Note that this is mostly useful when combined with libpurple, as
     57  only the Jabber native protocol module currently supports file transfers.
     58- Updated Yahoo! module to be in sync again with libyahoo2. This mostly
     59  fixes issues with authorization requests.
     60
     61Finished 6 Aug 2010
     62
     63Version 1.2.8:
     64- Now always using the AIM-style authentication method for OSCAR connections,
     65  even when connecting to ICQ. This solves login issues some people were
     66  having. (If you have problems, try changing the old_icq_auth setting.)
     67- Twitter:
     68  * Allow changing the Twitter API base URL so the module can also be used
     69    for identi.ca or any other compatible network.
     70  * Fetch the full list of Twitter contacts instead of slowly adding all
     71    contacts as they post a message.
     72  * Fixed message length counting.
     73  * Allow following/unfollowing people using the usual add/remove commands.
     74  * Better error reporting.
     75- Added a user_agent setting to the Jabber module to get around artificial
     76  client restrictions.
     77- Allow nick changes (although only before register/identify).
     78- Some more minor bugfixes/etc.
     79
     80Finished 4 Jul 2010
     81
     82Version 1.2.7:
     83- Fixed problems with MSN Messenger authentication. ("Could not parse
     84  Passport server response")
     85- Fixed broken typing notifications when talking to GTalk contacts.
     86- Fixed an issue with non-anonymous Jabber chatrooms polluting the nick
     87  namespace, sometimes generating odd warning messages.
     88- Restored ability to start groupchats on ICQ.
     89- Added show_offline setting that will also show offline contacts in the
     90  control channel.
     91- OAuth support for Twitter: This means the module will keep working after
     92  June (this also changes "via API" under your tweets into "via BitlBee").
     93
     94Finished 15 May 2010
     95
     96Version 1.2.6a:
     97- Fixed a typo that renders the Twitter groupchat mode unusable. A last-
     98  minute change that came a few minutes late.
     99
     100Finished 19 Apr 2010
     101
     102Version 1.2.6:
     103- Native (very basic) support for Twitter, implemented by Geert Mulders.
     104  Currently supported are posting tweets, reading the ones of people you
     105  follow, and sending (not yet receiving!) direct messages.
     106- Fixed format of status messages in /WHOIS to improve IRC client
     107  compatibility.
     108- Show timestamps of offline messages/channel backlogs.
     109- Allow saving MSN display names locally since sometimes this stuff breaks
     110  server-side. (Use the local_display_name per-account setting.)
     111- Suppress empty "Headline:" messages for certain new XMPP broadcast
     112  messages.
     113- Better handling of XMPP contacts with multiple resources on-line. Default
     114  behaviour now is to write to wherever the last message came from, or to
     115  the bare JID (usually becomes a broadcast) if there wasn't any recent msg.
     116- Added a switchboard_keepalives setting which should solve some issues with
     117  talking to offline MSN contacts. (Although full support for offline
     118  messages is not ready yet!)
     119- The usual misc. bug fixes.
     120
     121Finished 19 Apr 2010
    5122
    6123Version 1.2.5:
     
    23140  the main client).
    24141
    25 Fixed 17 Mar 2010
     142Finished 17 Mar 2010
    26143
    27144Version 1.2.4:
  • doc/Makefile

    ree6cc94 r2528cda  
    11-include ../Makefile.settings
     2ifdef SRCDIR
     3SRCDIR := $(SRCDIR)doc/
     4endif
    25
    36all:
     
    710install:
    811        mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(DESTDIR)$(MANDIR)/man5/
    9         install -m 0644 bitlbee.8 $(DESTDIR)$(MANDIR)/man8/
    10         install -m 0644 bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/
     12        install -m 0644 $(SRCDIR)bitlbee.8 $(DESTDIR)$(MANDIR)/man8/
     13        install -m 0644 $(SRCDIR)bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/
    1114        $(MAKE) -C user-guide $@
    1215
  • doc/README

    ree6cc94 r2528cda  
    99make install will move all the files to the right places.
    1010
    11 --- inetd installation
     11--- (Fork)Daemon mode
     12
     13These days ForkDaemon mode is the recommended way of running BitlBee. The
     14difference between Daemon and ForkDaemon mode is that in the latter, a
     15separate process is spawned for every user. This costs a little bit more
     16memory, but means that if one user hits a bug in the code, not all other
     17users get disconnected with him/her.
     18
     19To use BitlBee in any daemon mode, just start it with the right flags or
     20enable it in bitlbee.conf (see the RunMode option). You probably want to
     21write an init script to start BitlBee automatically after a reboot. (This
     22is where you realise using a package from your distro would've been a better
     23idea. :-P)
     24
     25Please do make sure that the user BitlBee runs as (not root, please!) is
     26able to read from and write to the /var/lib/bitlbee directory to save your
     27settings!
     28
     29--- inetd installation (more or less deprecated)
    1230
    1331After installation you have to set up inetd (you got that one running,
     
    2745you choose), add it and use that name in the inetd.conf entry.
    2846
    29 -- xinetd installation
     47-- xinetd installation (equally deprecated)
    3048
    3149Most machines use xinetd instead of inetd these days. If your machine runs
     
    4159by default) and chown it to the UID BitlBee is running as. Make sure this
    4260directory is read-/writable by this user only.
    43 
    44 --- (Fork)Daemon mode
    45 
    46 If you don't want to run any inetd daemon, you can run BitlBee in Daemon
    47 mode. Right now, daemon mode may be a bad idea on servers with multiple
    48 users, since possible fatal BitlBee bugs will crash the BitlBee process and
    49 disconnect all connected users at once. Instead, you can use ForkDaemon
    50 mode, which serves every user from a separate process, without depending on
    51 an inetd daemon.
    52 
    53 To use BitlBee in daemon mode, just start it with the right flags or enable
    54 it in bitlbee.conf. You probably want to write an init script to start
    55 BitlBee automatically after a reboot. (This is where you realise using
    56 a package from your distro would've been a better idea. :-P)
    57 
    58 Note that the BitlBee code is getting stable enough for daemon mode to be
    59 useful. Some public servers use it, and it saves a lot of memory by serving
    60 tens of users from a single process. One crash affects all users, but these
    61 are becoming quite rare.
    6261
    6362
     
    139138http://bugs.bitlbee.org/
    140139
    141 Our version control system is Bazaar-NG. Our repository is at:
     140Our version control system is Bazaar. Our repository is at:
    142141http://code.bitlbee.org/
     142
     143More documentation on the Wiki:
     144http://wiki.bitlbee.org/
    143145
    144146
     
    186188        BitlBee - An IRC to other chat networks gateway
    187189                  <http://www.bitlbee.org/>
    188         Copyright (C) 2002-2007  Wilmer van der Gaast <wilmer@gaast.net>
     190        Copyright (C) 2002-2010  Wilmer van der Gaast <wilmer@gaast.net>
    189191                                 and others
  • doc/bitlbee.8

    ree6cc94 r2528cda  
    3939networks and acts as a gateway. Users can connect to the server
    4040with any normal IRC client and see their 'buddy list' in
    41 &bitlbee. BitlBee's protocol support is based on the gaim
    42 protocol plugins. BitlBee currently supports Oscar (aim and icq),
    43 MSN, Jabber and Yahoo.
     41&bitlbee. It currently supports Oscar (AIM and ICQ),
     42MSN, Jabber, Yahoo! and Twitter.
    4443
    4544\fBbitlbee\fP should be called by
  • doc/user-guide/Makefile

    ree6cc94 r2528cda  
    11-include ../../Makefile.settings
     2ifdef SRCDIR
     3SRCDIR := $(SRCDIR)doc/user-guide/
     4endif
     5
    26EXTRAPARANEWLINE = 1
    37# EXTRAPARANEWLINE = 0
     
    3842        chmod 0755 $(DESTDIR)$(DATADIR)
    3943        rm -f $(DESTDIR)$(DATADIR)/help.txt # Prevent help function from breaking in running sessions
    40         install -m 0644 help.txt $(DESTDIR)$(DATADIR)/help.txt
     44        install -m 0644 $(SRCDIR)help.txt $(DESTDIR)$(DATADIR)/help.txt
    4145
    4246uninstall:
  • doc/user-guide/commands.xml

    ree6cc94 r2528cda  
    66        <bitlbee-command name="account">
    77                <short-description>IM-account list maintenance</short-description>
    8                 <syntax>account &lt;action&gt; [&lt;arguments&gt;]</syntax>
     8                <syntax>account [&lt;account id&gt;] &lt;action&gt; [&lt;arguments&gt;]</syntax>
    99
    1010                <description>
     
    2121                        <description>
    2222                                <para>
    23                                         Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see <emphasis>help account add &lt;protocol&gt;</emphasis>.
     23                                        Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ), Yahoo and Twitter. For more information about adding an account, see <emphasis>help account add &lt;protocol&gt;</emphasis>.
    2424                                </para>
    2525                        </description>
     
    6363                                </ircexample>
    6464                        </bitlbee-command>
     65                       
     66                        <bitlbee-command name="twitter">
     67                                <syntax>account add twitter &lt;handle&gt; &lt;password&gt;</syntax>
     68
     69                                <description>
     70                                        <para>
     71                                                This module gives you simple access to Twitter. Although it uses the Twitter API, only Twitter itself is supported at the moment.
     72                                        </para>
     73                                       
     74                                        <para>
     75                                                By default all your Twitter contacts will come from a contact called twitter_(yourusername). You can change this behaviour using the <emphasis>mode</emphasis> setting (see <emphasis>help set mode</emphasis>).
     76                                        </para>
     77                                       
     78                                        <para>
     79                                                To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option.
     80                                        </para>
     81
     82                                        <para>
     83                                                Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See <emphasis>help set oauth</emphasis>.)
     84                                        </para>
     85                                </description>
     86                        </bitlbee-command>
    6587
    6688                        <bitlbee-command name="yahoo">
     
    7799
    78100                <bitlbee-command name="del">
    79                         <syntax>account del &lt;account id&gt;</syntax>
     101                        <syntax>account &lt;account id&gt; del</syntax>
    80102
    81103                        <description>
     
    86108
    87109                                <para>
    88                                         The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
     110                                        The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
    89111                                </para>
    90112                        </description>
     
    92114
    93115                <bitlbee-command name="on">
    94                         <syntax>account on [&lt;account id&gt;]</syntax>
     116                        <syntax>account [&lt;account id&gt;] on</syntax>
    95117
    96118                        <description>
     
    100122
    101123                                <para>
    102                                         The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
     124                                        The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
    103125                                </para>
    104126                        </description>
     
    107129
    108130                <bitlbee-command name="off">
    109                         <syntax>account off [&lt;account id&gt;]</syntax>
     131                        <syntax>account [&lt;account id&gt;] off</syntax>
    110132
    111133                        <description>
     
    115137
    116138                                <para>
    117                                         The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
     139                                        The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
    118140                                </para>
    119141                        </description>
     
    131153
    132154                <bitlbee-command name="set">
    133                         <syntax>account set &lt;account id&gt;</syntax>
    134                         <syntax>account set &lt;account id&gt;/&lt;setting&gt;</syntax>
    135                         <syntax>account set &lt;account id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
    136                         <syntax>account set -del &lt;account id&gt;/&lt;setting&gt;</syntax>
     155                        <syntax>account &lt;account id&gt; set</syntax>
     156                        <syntax>account &lt;account id&gt; set &lt;setting&gt;</syntax>
     157                        <syntax>account &lt;account id&gt; set &lt;setting&gt; &lt;value&gt;</syntax>
     158                        <syntax>account &lt;account id&gt; set -del &lt;setting&gt;</syntax>
    137159
    138160                        <description>
    139161                                <para>
    140                                         This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account set &lt;account id&gt;</emphasis>.
     162                                        This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account &lt;account id&gt; set</emphasis>.
    141163                                </para>
    142164                               
     
    146168                               
    147169                                <para>
    148                                         The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
     170                                        The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
    149171                                </para>
    150172                        </description>
    151173                </bitlbee-command>
     174        </bitlbee-command>
     175
     176        <bitlbee-command name="channel">
     177                <short-description>Channel list maintenance</short-description>
     178                <syntax>channel [&lt;account id&gt;] &lt;action&gt; [&lt;arguments&gt;]</syntax>
     179
     180                <description>
     181                        <para>
     182                                Available actions: del, list, set. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
     183                        </para>
     184                       
     185                        <para>
     186                                There is no <emphasis>channel add</emphasis> command. To create a new channel, just use the IRC <emphasis>/join</emphasis> command. See also <emphasis>help channels</emphasis> and <emphasis>help groupchats</emphasis>.
     187                        </para>
     188                </description>
     189
     190                <bitlbee-command name="del">
     191                        <syntax>channel &lt;channel id&gt; del</syntax>
     192
     193                        <description>
     194                                <para>
     195                                        Remove a channel and forget all its settings. You can only remove channels you're not currently in, and can't remove the main control channel. (You can, however, leave it.)
     196                                </para>
     197                        </description>
     198
     199                </bitlbee-command>
     200
     201                <bitlbee-command name="list">
     202                        <syntax>channel list</syntax>
     203
     204                        <description>
     205                                <para>
     206                                        This command gives you a list of all the channels you configured.
     207                                </para>
     208                        </description>
     209
     210                </bitlbee-command>
     211
     212                <bitlbee-command name="set">
     213                        <syntax>channel [&lt;channel id&gt;] set</syntax>
     214                        <syntax>channel [&lt;channel id&gt;] set &lt;setting&gt;</syntax>
     215                        <syntax>channel [&lt;channel id&gt;] set &lt;setting&gt; &lt;value&gt;</syntax>
     216                        <syntax>channel [&lt;channel id&gt;] set -del &lt;setting&gt;</syntax>
     217
     218                        <description>
     219                                <para>
     220                                        This command can be used to change various settings for channels. Different channel types support different settings. You can see the settings available for a channel by typing <emphasis>channel &lt;channel id&gt; set</emphasis>.
     221                                </para>
     222                               
     223                                <para>
     224                                        For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
     225                                </para>
     226                               
     227                                <para>
     228                                        The channel ID can be a number (see <emphasis>channel list</emphasis>), or (part of) its name, as long as it matches only one channel. If you want to change settings of the current channel, you can omit the channel ID.
     229                                </para>
     230                        </description>
     231                </bitlbee-command>
     232
    152233        </bitlbee-command>
    153234
     
    159240
    160241                        <para>
    161                                 Available actions: add, del, list, with and set. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
     242                                Available actions: add, with. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
    162243                        </para>
    163244
     
    165246
    166247                <bitlbee-command name="add">
    167                         <syntax>chat add &lt;account&gt; &lt;room&gt; [&lt;channel&gt;]</syntax>
     248                        <syntax>chat add &lt;account id&gt; &lt;room&gt; [&lt;channel&gt;]</syntax>
    168249
    169250                        <description>
     
    183264                </bitlbee-command>
    184265
    185                 <bitlbee-command name="del">
    186                         <syntax>chat del &lt;chat id&gt;</syntax>
     266                <bitlbee-command name="with">
     267                        <syntax>chat with &lt;nickname&gt;</syntax>
    187268
    188269                        <description>
    189270                                <para>
    190                                         This commands deletes an chatroom from your list.
    191                                 </para>
    192 
    193                                 <para>
    194                                         The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel.
     271                                        While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions.
    195272                                </para>
    196273                        </description>
    197274                </bitlbee-command>
    198 
    199                 <bitlbee-command name="list">
    200                         <syntax>chat list</syntax>
    201 
    202                         <description>
    203                                 <para>
    204                                         This command gives you a list of all the chatrooms known by BitlBee.
    205                                 </para>
    206                         </description>
    207                 </bitlbee-command>
    208 
    209                 <bitlbee-command name="with">
    210                         <syntax>chat with &lt;nickname&gt;</syntax>
    211 
    212                         <description>
    213                                 <para>
    214                                         While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions.
    215                                 </para>
    216                         </description>
    217                 </bitlbee-command>
    218 
    219                 <bitlbee-command name="set">
    220                         <syntax>chat set &lt;chat id&gt;</syntax>
    221                         <syntax>chat set &lt;chat id&gt;/&lt;setting&gt;</syntax>
    222                         <syntax>chat set &lt;chat id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
    223                         <syntax>chat set -del &lt;chat id&gt;/&lt;setting&gt;</syntax>
    224 
    225                         <description>
    226                                 <para>
    227                                         This command can be used to change various settings for chatrooms.
    228                                 </para>
    229                                
    230                                 <para>
    231                                         For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
    232                                 </para>
    233                                
    234                                 <para>
    235                                         The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel.
    236                                 </para>
    237                         </description>
    238                 </bitlbee-command>
    239275        </bitlbee-command>
    240276
    241277        <bitlbee-command name="add">
    242278                <short-description>Add a buddy to your contact list</short-description>
    243                 <syntax>add &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
    244                 <syntax>add -tmp &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
     279                <syntax>add &lt;account id&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
     280                <syntax>add -tmp &lt;account id&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
    245281
    246282                <description>
     
    251287                        <para>
    252288                                If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications.
     289                        </para>
     290
     291                        <para>
     292                                If you use this command in a control channel containing people from only one group, the new contact will be added to that group automatically.
    253293                        </para>
    254294                </description>
     
    376416        </bitlbee-command>
    377417
     418        <bitlbee-setting name="account" type="string" scope="channel">
     419
     420                <description>
     421                        <para>
     422                                For control channels with <emphasis>fill_by</emphasis> set to <emphasis>account</emphasis>: Set this setting to the account id (numeric, or part of the username) of the account containing the contacts you want to see in this channel.
     423                        </para>
     424                </description>
     425        </bitlbee-setting>
     426
     427        <bitlbee-setting name="allow_takeover" type="boolean" scope="global">
     428                <default>true</default>
     429
     430                <description>
     431                        <para>
     432                                When you're already connected to a BitlBee server and you connect (and identify) again, BitlBee will offer to migrate your existing session to the new connection. If for whatever reason you don't want this, you can disable this setting.
     433                        </para>
     434                </description>
     435        </bitlbee-setting>
     436
    378437        <bitlbee-setting name="auto_connect" type="boolean" scope="both">
    379438                <default>true</default>
     
    390449        </bitlbee-setting>
    391450
    392         <bitlbee-setting name="auto_join" type="boolean" scope="chat">
     451        <bitlbee-setting name="auto_join" type="boolean" scope="channel">
    393452                <default>false</default>
    394453
    395454                <description>
    396455                        <para>
    397                                 With this option enabled, BitlBee will automatically join this chatroom when you log in.
     456                                With this option enabled, BitlBee will automatically join this channel when you log in.
    398457                        </para>
    399458                </description>
     
    401460
    402461        <bitlbee-setting name="auto_reconnect" type="boolean" scope="both">
    403                 <default>false</default>
     462                <default>true</default>
    404463
    405464                <description>
     
    436495        </bitlbee-setting>
    437496
     497        <bitlbee-setting name="auto_reply_timeout" type="integer" scope="account">
     498                <default>10800</default>
     499
     500                <description>
     501                        <para>
     502                                For Twitter accounts: If you respond to Tweets IRC-style (like "nickname: reply"), this will automatically be converted to the usual Twitter format ("@screenname reply").
     503                        </para>
     504
     505                        <para>
     506                                By default, BitlBee will then also add a reference to that person's most recent Tweet, unless that message is older than the value of this setting in seconds.
     507                        </para>
     508
     509                        <para>
     510                                If you want to disable this feature, just set this to 0. Alternatively, if you want to write a message once that is <emphasis>not</emphasis> a reply, use the Twitter reply syntax (@screenname).
     511                        </para>
     512                </description>
     513        </bitlbee-setting>
     514
    438515        <bitlbee-setting name="away" type="string" scope="both">
    439516                <description>
     
    459536                                With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled.
    460537                        </para>
    461                 </description>
    462         </bitlbee-setting>
    463 
    464         <bitlbee-setting name="buddy_sendbuffer" type="boolean" scope="global">
    465                 <default>false</default>
    466 
    467                 <description>
    468                         <para>
    469                                 By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data.
    470                         </para>
    471 
    472                         <para>
    473                                 Using the <emphasis>buddy_sendbuffer_delay</emphasis> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent.
    474                         </para>
    475 
    476                         <para>
    477                                 Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases.
    478                         </para>
    479                 </description>
    480         </bitlbee-setting>
    481 
    482         <bitlbee-setting name="buddy_sendbuffer_delay" type="integer" scope="global">
    483                 <default>200</default>
    484 
    485                 <description>
    486 
    487                         <para>
    488                                 Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds.
    489                         </para>
    490 
    491                         <para>
    492                                 See also the <emphasis>buddy_sendbuffer</emphasis> setting.
     538                       
     539                        <para>
     540                                Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
     541                        </para>
     542                </description>
     543        </bitlbee-setting>
     544
     545        <bitlbee-setting name="away_reply_timeout" type="integer" scope="global">
     546                <default>3600</default>
     547
     548                <description>
     549                        <para>
     550                                Most IRC servers send a user's away message every time s/he gets a private message, to inform the sender that they may not get a response immediately. With this setting set to 0, BitlBee will also behave like this.
     551                        </para>
     552
     553                        <para>
     554                                Since not all IRC clients do an excellent job at suppressing these messages, this setting lets BitlBee do it instead. BitlBee will wait this many seconds (or until the away state/message changes) before re-informing you that the person's away.
     555                        </para>
     556                </description>
     557        </bitlbee-setting>
     558
     559        <bitlbee-setting name="base_url" type="string" scope="account">
     560                <default>http://twitter.com</default>
     561
     562                <description>
     563                        <para>
     564                                There are more services that understand the Twitter API than just Twitter.com. BitlBee can connect to all Twitter API implementations.
     565                        </para>
     566
     567                        <para>
     568                                For example, set this setting to <emphasis>http://identi.ca/api</emphasis> to use Identi.ca.
     569                        </para>
     570
     571                        <para>
     572                                Keep two things in mind: When not using Twitter, you <emphasis>must</emphasis> also disable the <emphasis>oauth</emphasis> setting as it currently only works with Twitter. If you're still having issues, make sure there is <emphasis>no</emphasis> slash at the end of the URL you enter here.
    493573                        </para>
    494574                </description>
     
    521601        </bitlbee-setting>
    522602
     603        <bitlbee-setting name="chat_type" type="string" scope="channel">
     604                <default>groupchat</default>
     605                <possible-values>groupchat, room</possible-values>
     606
     607                <description>
     608                        <para>
     609                                There are two kinds of chat channels: simple groupchats (basically normal IM chats with more than two participants) and names chatrooms, more similar to IRC channels.
     610                        </para>
     611                       
     612                        <para>
     613                                BitlBee supports both types. With this setting set to <emphasis>groupchat</emphasis> (the default), you can just invite people into the room and start talking.
     614                        </para>
     615                       
     616                        <para>
     617                                For setting up named chatrooms, it's currently easier to just use the <emphasis>chat add</emphasis> command.
     618                        </para>
     619                </description>
     620        </bitlbee-setting>
     621
     622        <bitlbee-setting name="commands" type="boolean" scope="account">
     623                <default>true</default>
     624
     625                <description>
     626                        <para>
     627                                With this setting enabled, you can use some commands in your Twitter channel/query. The commands are simple and not documented in too much detail:
     628                        </para>
     629
     630                        <variablelist>
     631                                <varlistentry><term>undo [&lt;id&gt;]</term><listitem><para>Delete your last Tweet (or one with the given ID)</para></listitem></varlistentry>
     632                                <varlistentry><term>rt &lt;screenname|id&gt;</term><listitem><para>Retweet someone's last Tweet (or one with the given ID)</para></listitem></varlistentry>
     633                                <varlistentry><term>follow &lt;screenname&gt;</term><listitem><para>Start following a person</para></listitem></varlistentry>
     634                                <varlistentry><term>unfollow &lt;screenname&gt;</term><listitem><para>Stop following a person</para></listitem></varlistentry>
     635                                <varlistentry><term>post &lt;message&gt;</term><listitem><para>Post a tweet</para></listitem></varlistentry>
     636                        </variablelist>
     637
     638                        <para>
     639                                Anything that doesn't look like a command will be treated as a tweet. Watch out for typos! :-)
     640                        </para>
     641                </description>
     642        </bitlbee-setting>
     643
    523644        <bitlbee-setting name="debug" type="boolean" scope="global">
    524645                <default>false</default>
     
    560681        </bitlbee-setting>
    561682
     683        <bitlbee-setting name="display_timestamps" type="boolean" scope="global">
     684                <default>true</default>
     685
     686                <description>
     687                        <para>
     688                                When incoming messages are old (i.e. offline messages and channel backlogs), BitlBee will prepend them with a timestamp. If you find them ugly or useless, you can use this setting to hide them.
     689                        </para>
     690                </description>
     691        </bitlbee-setting>
     692
     693        <bitlbee-setting name="fill_by" type="string" scope="channel">
     694                <default>all</default>
     695                <possible-values>all, group, account, protocol</possible-values>
     696
     697                <description>
     698                        <para>
     699                                For control channels only: This setting determines which contacts the channel gets populated with.
     700                        </para>
     701
     702                        <para>
     703                                By default, control channels will contain all your contacts. You instead select contacts by buddy group, IM account or IM protocol.
     704                        </para>
     705                       
     706                        <para>
     707                                Change this setting and the corresponding <emphasis>account</emphasis>/<emphasis>group</emphasis>/<emphasis>protocol</emphasis> setting to set up this selection.
     708                        </para>
     709                       
     710                        <para>
     711                                Note that, when creating a new channel, BitlBee will try to preconfigure the channel for you, based on the channel name. See <emphasis>help channels</emphasis>.
     712                        </para>
     713                </description>
     714        </bitlbee-setting>
     715
     716        <bitlbee-setting name="group" type="string" scope="channel">
     717
     718                <description>
     719                        <para>
     720                                For control channels with <emphasis>fill_by</emphasis> set to <emphasis>group</emphasis>: Set this setting to the name of the group containing the contacts you want to see in this channel.
     721                        </para>
     722                </description>
     723        </bitlbee-setting>
     724
    562725        <bitlbee-setting name="handle_unknown" type="string" scope="global">
    563                 <default>root</default>
     726                <default>add_channel</default>
    564727                <possible-values>root, add, add_private, add_channel, ignore</possible-values>
    565728
     
    587750        </bitlbee-setting>
    588751
     752        <bitlbee-setting name="ignore_auth_requests" type="boolean" scope="account">
     753                <default>false</default>
     754
     755                <description>
     756                        <para>
     757                                Only supported by OSCAR so far, you can use this setting to ignore ICQ authorization requests, which are hardly used for legitimate (i.e. non-spam) reasons anymore.
     758                        </para>
     759                </description>
     760
     761        </bitlbee-setting>
     762
    589763        <bitlbee-setting name="lcnicks" type="boolean" scope="global">
    590764                <default>true</default>
     
    598772        </bitlbee-setting>
    599773
     774        <bitlbee-setting name="local_display_name" type="boolean" scope="account">
     775                <default>false</default>
     776
     777                <description>
     778                        <para>
     779                                Mostly meant to work around a bug in MSN servers (forgetting the display name set by the user), this setting tells BitlBee to store your display name locally and set this name on the MSN servers when connecting.
     780                        </para>
     781                </description>
     782
     783        </bitlbee-setting>
     784
    600785        <bitlbee-setting name="mail_notifications" type="boolean" scope="account">
    601786                <default>false</default>
     
    609794        </bitlbee-setting>
    610795
     796        <bitlbee-setting name="message_length" type="integer" scope="account">
     797                <default>140</default>
     798
     799                <description>
     800                        <para>
     801                                Since Twitter rejects messages longer than 140 characters, BitlBee can count message length and emit a warning instead of waiting for Twitter to reject it.
     802                        </para>
     803
     804                        <para>
     805                                You can change this limit here but this won't disable length checks on Twitter's side. You can also set it to 0 to disable the check in case you believe BitlBee doesn't count the characters correctly.
     806                        </para>
     807                </description>
     808
     809        </bitlbee-setting>
     810
     811        <bitlbee-setting name="mode" type="string" scope="account">
     812                <possible-values>one, many, chat</possible-values>
     813                <default>one</default>
     814
     815                <description>
     816                        <para>
     817                                By default, everything from the Twitter module will come from one nick, twitter_(yourusername). If you prefer to have individual nicks for everyone, you can set this setting to "many" instead.
     818                        </para>
     819                       
     820                        <para>
     821                                If you prefer to have all your Twitter things in a separate channel, you can set this setting to "chat".
     822                        </para>
     823                       
     824                        <para>
     825                                In the last two modes, you can send direct messages by /msg'ing your contacts directly. Note, however, that incoming DMs are not fetched yet.
     826                        </para>
     827                </description>
     828
     829        </bitlbee-setting>
     830
    611831        <bitlbee-setting name="nick" type="string" scope="chat">
    612 
    613832                <description>
    614833                        <para>
    615834                                You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname.
     835                        </para>
     836                </description>
     837        </bitlbee-setting>
     838
     839        <bitlbee-setting name="nick_format" type="string" scope="both">
     840                <default>%-@nick</default>
     841
     842                <description>
     843                        <para>
     844                                By default, BitlBee tries to derive sensible nicknames for all your contacts from their IM handles. In some cases, IM modules (ICQ for example) will provide a nickname suggestion, which will then be used instead. This setting lets you change this behaviour.
     845                        </para>
     846
     847                        <para>
     848                                Whenever this setting is set for an account, it will be used for all its contacts. If it's not set, the global value will be used.
     849                        </para>
     850
     851                        <para>
     852                                It's easier to describe this setting using a few examples:
     853                        </para>
     854
     855                        <para>
     856                                FB-%full_name will make all nicknames start with "FB-", followed by the person's full name. For example you can set this format for your Facebook account so all Facebook contacts are clearly marked.
     857                        </para>
     858
     859                        <para>
     860                                [%group]%-@nick will make all nicknames start with the group the contact is in between square brackets, followed by the nickname suggestions from the IM module if available, or otherwise the handle. Because of the "-@" part, everything from the first @ will be stripped.
     861                        </para>
     862
     863                        <para>
     864                                See <emphasis>help nick_format</emphasis> for more information.
    616865                        </para>
    617866                </description>
     
    633882        </bitlbee-setting>
    634883
     884        <bitlbee-setting name="oauth" type="boolean" scope="account">
     885                <default>true</default>
     886
     887                <description>
     888                        <para>
     889                                This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory.
     890                        </para>
     891
     892                        <para>
     893                                With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process.
     894                        </para>
     895
     896                        <para>
     897                                The resulting access token will be saved permanently, so you have to do this only once.
     898                        </para>
     899                </description>
     900
     901        </bitlbee-setting>
     902
    635903        <bitlbee-setting name="ops" type="string" scope="global">
    636904                <default>both</default>
     
    663931                </description>
    664932        </bitlbee-setting>
     933
     934        <bitlbee-setting name="paste_buffer" type="boolean" scope="global">
     935                <default>false</default>
     936
     937                <description>
     938                        <para>
     939                                By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data.
     940                        </para>
     941
     942                        <para>
     943                                Using the <emphasis>paste_buffer_delay</emphasis> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent.
     944                        </para>
     945
     946                        <para>
     947                                Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases.
     948                        </para>
     949                </description>
     950        </bitlbee-setting>
     951
     952        <bitlbee-setting name="paste_buffer_delay" type="integer" scope="global">
     953                <default>200</default>
     954
     955                <description>
     956
     957                        <para>
     958                                Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds.
     959                        </para>
     960
     961                        <para>
     962                                See also the <emphasis>paste_buffer</emphasis> setting.
     963                        </para>
     964                </description>
     965        </bitlbee-setting>
    665966       
    666967        <bitlbee-setting name="port" type="integer" scope="account">
     
    696997                        <para>
    697998                                This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect.
     999                        </para>
     1000                </description>
     1001        </bitlbee-setting>
     1002
     1003        <bitlbee-setting name="protocol" type="string" scope="channel">
     1004
     1005                <description>
     1006                        <para>
     1007                                For control channels with <emphasis>fill_by</emphasis> set to <emphasis>protocol</emphasis>: Set this setting to the name of the IM protocol of all contacts you want to see in this channel.
    6981008                        </para>
    6991009                </description>
     
    7681078        </bitlbee-setting>
    7691079
     1080        <bitlbee-setting name="show_offline" type="boolean" scope="global">
     1081                <default>false</default>
     1082
     1083                <description>
     1084                        <para>
     1085                                If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect.
     1086                        </para>
     1087                       
     1088                        <para>
     1089                                Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
     1090                        </para>
     1091                </description>
     1092        </bitlbee-setting>
     1093
     1094        <bitlbee-setting name="show_users" type="string" scope="channel">
     1095                <default>online+,away</default>
     1096
     1097                <description>
     1098                        <para>
     1099                                Comma-separated list of statuses of users you want in the channel,
     1100                                and any modes they should have. The following statuses are currently
     1101                                recognised: <emphasis>online</emphasis> (i.e. available, not
     1102                                away), <emphasis>away</emphasis>, and <emphasis>offline</emphasis>.
     1103                        </para>
     1104                       
     1105                        <para>
     1106                                If a status is followed by a valid channel mode character
     1107                                (@, % or +), it will be given to users with that status.
     1108                                For example, <emphasis>online@,away+,offline</emphasis> will
     1109                                show all users in the channel. Online people will
     1110                                have +o, people who are online but away will have +v,
     1111                                and others will have no special modes.
     1112                        </para>
     1113                </description>
     1114        </bitlbee-setting>
     1115
    7701116        <bitlbee-setting name="simulate_netsplit" type="boolean" scope="global">
    7711117                <default>true</default>
     
    8171163        </bitlbee-setting>
    8181164
     1165        <bitlbee-setting name="switchboard_keepalives" type="boolean" scope="account">
     1166                <default>false</default>
     1167
     1168                <description>
     1169                        <para>
     1170                                Turn on this flag if you have difficulties talking to offline/invisible contacts.
     1171                        </para>
     1172                       
     1173                        <para>
     1174                                With this setting enabled, BitlBee will send keepalives to MSN switchboards with offline/invisible contacts every twenty seconds. This should keep the server and client on the other side from shutting it down.
     1175                        </para>
     1176                       
     1177                        <para>
     1178                                This is useful because BitlBee doesn't support MSN offline messages yet and the MSN servers won't let the user reopen switchboards to offline users. Once offline messaging is supported, this flag might be removed.
     1179                        </para>
     1180                </description>
     1181        </bitlbee-setting>
     1182
     1183        <bitlbee-setting name="tag" type="string" scope="account">
     1184                <description>
     1185                        <para>
     1186                                For every account you have, you can set a tag you can use to uniquely identify that account. This tag can be used instead of the account number (or protocol name, or part of the screenname) when using commands like <emphasis>account</emphasis>, <emphasis>add</emphasis>, etc. You can't have two accounts with one and the same account tag.
     1187                        </para>
     1188
     1189                        <para>
     1190                                By default, it will be set to the name of the IM protocol. Once you add a second account on an IM network, a numeric suffix will be added, starting with 2.
     1191                        </para>
     1192                </description>
     1193        </bitlbee-setting>
     1194
     1195        <bitlbee-setting name="timezone" type="string" scope="global">
     1196                <default>local</default>
     1197                <possible-values>local, utc, gmt, timezone-spec</possible-values>
     1198
     1199                <description>
     1200                        <para>
     1201                                If message timestamps are available for offline messages or chatroom backlogs, BitlBee will display them as part of the message. By default it will use the local timezone. If you're not in the same timezone as the BitlBee server, you can adjust the timestamps using this setting.
     1202                        </para>
     1203
     1204                        <para>
     1205                                Values local/utc/gmt should be self-explanatory. timezone-spec is a time offset in hours:minutes, for example: -8 for Pacific Standard Time, +2 for Central European Summer Time, +5:30 for Indian Standard Time.
     1206                        </para>
     1207                </description>
     1208        </bitlbee-setting>
     1209
    8191210        <bitlbee-setting name="tls" type="boolean" scope="account">
    8201211                <default>try</default>
     
    8451236        </bitlbee-setting>
    8461237
     1238        <bitlbee-setting name="translate_to_nicks" type="boolean" scope="channel">
     1239                <default>true</default>
     1240
     1241                <description>
     1242                        <para>
     1243                                IRC's nickname namespace is quite limited compared to most IM protocols. Not any non-ASCII characters are allowed, in fact nicknames have to be mostly alpha-numeric. Also, BitlBee has to add underscores sometimes to avoid nickname collisions.
     1244                        </para>
     1245
     1246                        <para>
     1247                                While normally the BitlBee user is the only one seeing these names, they may be exposed to other chatroom participants for example when addressing someone in the channel (with or without tab completion). By default BitlBee will translate these stripped nicknames back to the original nick. If you don't want this, disable this setting.
     1248                        </para>
     1249                </description>
     1250        </bitlbee-setting>
     1251
     1252        <bitlbee-setting name="type" type="string" scope="channel">
     1253                <default>control</default>
     1254                <possible-values>control, chat</possible-values>
     1255
     1256                <description>
     1257                        <para>
     1258                                BitlBee supports two kinds of channels: control channels (usually with a name starting with a &amp;) and chatroom channels (name usually starts with a #).
     1259                        </para>
     1260
     1261                        <para>
     1262                                See <emphasis>help channels</emphasis> for a full description of channel types in BitlBee.
     1263                        </para>
     1264                </description>
     1265        </bitlbee-setting>
     1266
    8471267        <bitlbee-setting name="typing_notice" type="boolean" scope="global">
    8481268                <default>false</default>
     
    8511271                        <para>
    8521272                                Sends you a /notice when a user starts typing a message (if supported by the IM protocol and the user's client). To use this, you most likely want to use a script in your IRC client to show this information in a more sensible way.
     1273                        </para>
     1274                </description>
     1275        </bitlbee-setting>
     1276
     1277        <bitlbee-setting name="user_agent" type="string" scope="account">
     1278                <default>BitlBee</default>
     1279
     1280                <description>
     1281                        <para>
     1282                                Some Jabber servers are configured to only allow a few (or even just one) kinds of XMPP clients to connect to them.
     1283                        </para>
     1284                       
     1285                        <para>
     1286                                You can change this setting to make BitlBee present itself as a different client, so that you can still connect to these servers.
    8531287                        </para>
    8541288                </description>
     
    9631397
    9641398        <bitlbee-command name="identify">
    965                 <syntax>identify &lt;password&gt;</syntax>
     1399                <syntax>identify [-noload|-force] &lt;password&gt;</syntax>
    9661400                <short-description>Identify yourself with your password</short-description>
    9671401
     
    9731407                        <para>
    9741408                                Once you're registered, you can change your password using <emphasis>set password &lt;password&gt;</emphasis>.
     1409                        </para>
     1410
     1411                        <para>
     1412                                The <emphasis>-noload</emphasis> and <emphasis>-force</emphasis> flags can be used to identify when you're logged into some IM accounts already. <emphasis>-force</emphasis> will let you identify yourself and load all saved accounts (and keep the accounts you're logged into already).
     1413                        </para>
     1414                       
     1415                        <para>
     1416                                <emphasis>-noload</emphasis> will log you in but not load any accounts and settings saved under your current nickname. These will be overwritten once you save your settings (i.e. when you disconnect).
    9751417                        </para>
    9761418                </description>
     
    10001442        </bitlbee-command>
    10011443
    1002         <bitlbee-command name="nick">
    1003                 <short-description>Change friendly name, nick</short-description>
    1004                 <syntax>nick &lt;connection&gt; [&lt;new nick&gt;]</syntax>
    1005                 <syntax>nick &lt;connection&gt;</syntax>
    1006 
    1007                 <description>
    1008                         <para>
    1009                                 Deprecated: Use the per-account <emphasis>display_name</emphasis> setting to read and change this information.
    1010                         </para>
    1011                 </description>
    1012 
    1013                 <ircexample>
    1014                         <ircline nick="wouter">account set 1/display_name "The majestik møøse"</ircline>
    1015                         <ircline nick="root">display_name = `The majestik møøse'</ircline>
    1016                 </ircexample>
    1017 
    1018         </bitlbee-command>
     1444        <bitlbee-command name="group">
     1445                <short-description>Contact group management</short-description>
     1446                <syntax>group list</syntax>
     1447
     1448                <description>
     1449                        <para>
     1450                                Only the <emphasis>group list</emphasis> command is supported at the moment, which shows a list of all groups defined so far.
     1451                        </para>
     1452                </description>
     1453        </bitlbee-command>
     1454       
     1455        <bitlbee-command name="transfers">
     1456                <short-description>Monitor, cancel, or reject file transfers</short-description>
     1457                <syntax>transfers [&lt;cancel&gt; id | &lt;reject&gt;]</syntax>
     1458               
     1459                <description>
     1460                        <para>
     1461                                Without parameters the currently pending file transfers and their status will be listed. Available actions are <emphasis>cancel</emphasis> and <emphasis>reject</emphasis>. See <emphasis>help transfers &lt;action&gt;</emphasis> for more information.
     1462                        </para>
     1463
     1464                        <ircexample>
     1465                                <ircline nick="ulim">transfers</ircline>
     1466                        </ircexample>
     1467                </description>
     1468               
     1469                <bitlbee-command name="cancel">
     1470                        <short-description>Cancels the file transfer with the given id</short-description>
     1471                        <syntax>transfers &lt;cancel&gt; id</syntax>
     1472
     1473                        <description>
     1474                                <para>Cancels the file transfer with the given id</para>
     1475                        </description>
     1476
     1477                        <ircexample>
     1478                                <ircline nick="ulim">transfers cancel 1</ircline>
     1479                                <ircline nick="root">Canceling file transfer for test</ircline>
     1480                        </ircexample>
     1481                </bitlbee-command>
     1482
     1483                <bitlbee-command name="reject">
     1484                        <short-description>Rejects all incoming transfers</short-description>
     1485                        <syntax>transfers &lt;reject&gt;</syntax>
     1486
     1487                        <description>
     1488                                <para>Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is neccessary. Or is it?</para>
     1489                        </description>
     1490
     1491                        <ircexample>
     1492                                <ircline nick="ulim">transfers reject</ircline>
     1493                        </ircexample>
     1494                </bitlbee-command>
     1495        </bitlbee-command>
     1496       
    10191497</chapter>
  • doc/user-guide/help.xml

    ree6cc94 r2528cda  
    1414  <varlistentry><term>quickstart</term><listitem><para>A short introduction into BitlBee</para></listitem></varlistentry>
    1515  <varlistentry><term>commands</term><listitem><para>All available commands and settings</para></listitem></varlistentry>
     16  <varlistentry><term>channels</term><listitem><para>About creating and customizing channels</para></listitem></varlistentry>
    1617  <varlistentry><term>away</term><listitem><para>About setting away states</para></listitem></varlistentry>
     18  <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry>
     19  <varlistentry><term>nick_changes</term><listitem><para>Changing your nickname without losing any settings</para></listitem></varlistentry>
    1720  <varlistentry><term>smileys</term><listitem><para>A summary of some non-standard smileys you might find and fail to understand</para></listitem></varlistentry>
    18   <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry>
    1921</variablelist>
    2022
     
    4345  <varlistentry><term>quickstart</term><listitem><para>A short introduction into BitlBee</para></listitem></varlistentry>
    4446  <varlistentry><term>commands</term><listitem><para>All available commands and settings</para></listitem></varlistentry>
     47  <varlistentry><term>channels</term><listitem><para>About creating and customizing channels</para></listitem></varlistentry>
    4548  <varlistentry><term>away</term><listitem><para>About setting away states</para></listitem></varlistentry>
     49  <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry>
     50  <varlistentry><term>nick_changes</term><listitem><para>Changing your nickname without losing any settings</para></listitem></varlistentry>
    4651  <varlistentry><term>smileys</term><listitem><para>A summary of some non-standard smileys you might find and fail to understand</para></listitem></varlistentry>
    47   <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry>
    4852</variablelist>
    4953
  • doc/user-guide/misc.xml

    ree6cc94 r2528cda  
    117117</sect1>
    118118
     119<sect1 id="nick_changes">
     120<title>Changing your nickname</title>
     121
     122<para>
     123BitlBee now allows you to change your nickname. So far this was not possible because it made managing saved accounts more complicated.
     124</para>
     125
     126<para>
     127The restriction no longer exists now though. When you change your nick (just using the <emphasis>/nick</emphasis> command), your logged-in status will be reset, which means any changes made to your settings/accounts will not be saved.
     128</para>
     129
     130<para>
     131To restore your logged-in status, you need to either use the <emphasis>register</emphasis> command to create an account under the new nickname, or use <emphasis>identify -noload</emphasis> to re-identify yourself under the new nickname. The <emphasis>-noload</emphasis> flag tells the command to verify your password and log you in, but not load any new settings. See <emphasis>help identify</emphasis> for more information.
     132</para>
     133
     134</sect1>
     135
     136<sect1 id="channels">
     137<title>Dealing with channels</title>
     138
     139<para>
     140You can have as many channels in BitlBee as you want. You maintain your channel list using the <emphasis>channel</emphasis> command. You can create new channels by just joining them, like on regular IRC networks.
     141</para>
     142
     143<para>
     144You can create two kinds of channels. Control channels, and groupchat channels. By default, BitlBee will set up new channels as control channels if their name starts with an &amp;, and as chat channels if it starts with a #.
     145</para>
     146
     147<para>
     148Control channels are where you see your contacts. By default, you will have one control channel called &amp;bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list accross several channels.
     149</para>
     150
     151<para>
     152For example, you can have one channel with all contacts from your MSN Messenger account in it. Or all contacts from the group called "Work".
     153</para>
     154
     155<para>
     156Type <emphasis>help channels2</emphasis> to read more.
     157</para>
     158
     159</sect1>
     160
     161<sect1 id="channels2">
     162<title>Creating a channel</title>
     163
     164<para>
     165When you create a new channel, BitlBee will try to guess from its name which contacts to fill it with. For example, if the channel name (excluding the &amp;) matches the name of a group in which you have one or more contacts, the channel will contain all those contacts.
     166</para>
     167
     168<para>
     169Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &amp;msn, it will contain all your MSN contacts. And if you have a Facebook account set up, you can see its contacts by just joining &amp;facebook.
     170</para>
     171
     172<para>
     173To start a simple group chat, you simply join a channel which a name starting with #, and invite people into it. All people you invite have to be on the same IM network and contact list.
     174</para>
     175
     176<para>
     177If you want to configure your own channels, you can use the <emphasis>channel set</emphasis>.
     178</para>
     179
     180</sect1>
     181
     182<sect1 id="nick_format">
     183<title>Nickname formatting</title>
     184
     185<para>
     186The <emphasis>nick_format</emphasis> setting can be set globally using
     187the <emphasis>set</emphasis> command, or per account using <emphasis>account
     188set</emphasis> (so that you can set a per-account suffix/prefix or have
     189nicknames generated from full names for certain accounts).
     190</para>
     191
     192<para>
     193The setting is basically some kind of format string. It can contain normal
     194text that will be copied to the nick, combined with several variables:
     195</para>
     196
     197<variablelist>
     198        <varlistentry><term>%nick</term><listitem><para>Nickname suggested for this contact by the IM protocol, or just the handle if no nickname was suggested.</para></listitem></varlistentry>
     199        <varlistentry><term>%handle</term><listitem><para>The handle/screenname of the contact.</para></listitem></varlistentry>
     200        <varlistentry><term>%full_name</term><listitem><para>The full name of the contact.</para></listitem></varlistentry>
     201        <varlistentry><term>%first_name</term><listitem><para>The first name of the contact (the full name up to the first space).</para></listitem></varlistentry>
     202        <varlistentry><term>%group</term><listitem><para>The name of the group this contact is a member of</para></listitem></varlistentry>
     203        <varlistentry><term>%account</term><listitem><para>Account tag of the contact</para></listitem></varlistentry>
     204</variablelist>
     205
     206<para>
     207Invalid characters (like spaces) will always be stripped. Depending on your
     208locale settings, characters with accents will be converted to ASCII.
     209</para>
     210
     211<para>
     212See <emphasis>set nick_format2</emphasis> for some more information.
     213</para>
     214
     215</sect1>
     216
     217<sect1 id="nick_format2">
     218<title>Nickname formatting - modifiers</title>
     219
     220<para>
     221Two modifiers are currently available: You can include only the first few
     222characters of a variable by putting a number right after the %. For
     223example, <emphasis>[%3group]%-@nick</emphasis> will include only the first
     224three characters of the group name in the nick.
     225</para>
     226
     227<para>
     228Also, you can truncate variables from a certain character using
     229the <emphasis>-</emphasis> modifier. For example, you may want to leave out
     230everything after the @. <emphasis>%-@handle</emphasis> will expand to
     231everything in the handle up to the first @.
     232</para>
     233
     234</sect1>
     235
    119236</chapter>
  • doc/user-guide/quickstart.xml

    ree6cc94 r2528cda  
    33
    44<para>
    5 Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber and Yahoo Instant Messaging Systems.
     5Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber, Yahoo! and Twitter.
    66</para>
    77
     
    4343
    4444<para>
    45 Other available IM protocols are msn, oscar, and yahoo. OSCAR is the protocol used by ICQ and AOL. For more information about the <emphasis>account add</emphasis> command, see <emphasis>help account add</emphasis>.
     45Other available IM protocols are msn, oscar, yahoo and twitter. OSCAR is the protocol used by ICQ and AOL. For more information about the <emphasis>account add</emphasis> command, see <emphasis>help account add</emphasis>.
    4646</para>
    4747
  • help.c

    ree6cc94 r2528cda  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2005 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2009 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    169169        return NULL;
    170170}
     171
     172int help_add_mem( help_t **help, const char *title, const char *content )
     173{
     174        help_t *h, *l = NULL;
     175       
     176        for( h = *help; h; h = h->next )
     177        {
     178                if( g_strcasecmp( h->title, title ) == 0 )
     179                        return 0;
     180               
     181                l = h;
     182        }
     183       
     184        if( l )
     185                h = l->next = g_new0( struct help, 1 );
     186        else
     187                *help = h = g_new0( struct help, 1 );
     188        h->fd = -1;
     189        h->title = g_strdup( title );
     190        h->length = strlen( content );
     191        h->offset.mem_offset = g_strdup( content );
     192       
     193        return 1;
     194}
  • help.h

    ree6cc94 r2528cda  
    4646void help_free( help_t **help );
    4747char *help_get( help_t **help, char *title );
     48int help_add_mem( help_t **help, const char *title, const char *content_ );
    4849
    4950#endif
  • ipc.c

    ree6cc94 r2528cda  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2006 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    2929#include "commands.h"
    3030#ifndef _WIN32
     31#include <sys/uio.h>
    3132#include <sys/un.h>
    3233#endif
    3334
    3435GSList *child_list = NULL;
     36static int ipc_child_recv_fd = -1;
     37
     38static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both );
     39static gboolean ipc_send_fd( int fd, int send_fd );
    3540
    3641static void ipc_master_cmd_client( irc_t *data, char **cmd )
     
    4954        }
    5055       
     56        /* CLIENT == On initial connects, HELLO is after /RESTARTs. */
    5157        if( g_strcasecmp( cmd[0], "CLIENT" ) == 0 )
    5258                ipc_to_children_str( "OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
     
    5460}
    5561
     62static void ipc_master_cmd_nick( irc_t *data, char **cmd )
     63{
     64        struct bitlbee_child *child = (void*) data;
     65       
     66        if( child && cmd[1] )
     67        {
     68                g_free( child->nick );
     69                child->nick = g_strdup( cmd[1] );
     70        }
     71}
     72
    5673static void ipc_master_cmd_die( irc_t *data, char **cmd )
    5774{
     
    110127        global.restart = -1;
    111128        bitlbee_shutdown( NULL, -1, 0 );
     129}
     130
     131void ipc_master_cmd_identify( irc_t *data, char **cmd )
     132{
     133        struct bitlbee_child *child = (void*) data, *old = NULL;
     134        char *resp;
     135        GSList *l;
     136       
     137        if( !child || !child->nick || strcmp( child->nick, cmd[1] ) != 0 )
     138                return;
     139       
     140        g_free( child->password );
     141        child->password = g_strdup( cmd[2] );
     142       
     143        for( l = child_list; l; l = l->next )
     144        {
     145                old = l->data;
     146                if( child != old &&
     147                    old->nick && nick_cmp( old->nick, child->nick ) == 0 &&
     148                    old->password && strcmp( old->password, child->password ) == 0 )
     149                        break;
     150        }
     151       
     152        if( l && !child->to_child && !old->to_child )
     153        {
     154                resp = "TAKEOVER INIT\r\n";
     155                child->to_child = old;
     156                old->to_child = child;
     157        }
     158        else
     159        {
     160                /* Won't need the fd since we can't send it anywhere. */
     161                closesocket( child->to_fd );
     162                child->to_fd = -1;
     163                resp = "TAKEOVER NO\r\n";
     164        }
     165       
     166        if( write( child->ipc_fd, resp, strlen( resp ) ) != strlen( resp ) )
     167                ipc_master_free_one( child );
     168}
     169
     170
     171void ipc_master_cmd_takeover( irc_t *data, char **cmd )
     172{
     173        struct bitlbee_child *child = (void*) data;
     174        char *fwd = NULL;
     175       
     176        /* Normal daemon mode doesn't keep these and has simplified code for
     177           takeovers. */
     178        if( child == NULL )
     179                return;
     180       
     181        if( child->to_child == NULL ||
     182            g_slist_find( child_list, child->to_child ) == NULL )
     183                return ipc_master_takeover_fail( child, FALSE );
     184       
     185        if( strcmp( cmd[1], "AUTH" ) == 0 )
     186        {
     187                /* New connection -> Master */
     188                if( child->to_child &&
     189                    child->nick && child->to_child->nick && cmd[2] &&
     190                    child->password && child->to_child->password && cmd[3] &&
     191                    strcmp( child->nick, child->to_child->nick ) == 0 &&
     192                    strcmp( child->nick, cmd[2] ) == 0 &&
     193                    strcmp( child->password, child->to_child->password ) == 0 &&
     194                    strcmp( child->password, cmd[3] ) == 0 )
     195                {
     196                        ipc_send_fd( child->to_child->ipc_fd, child->to_fd );
     197                       
     198                        fwd = irc_build_line( cmd );
     199                        if( write( child->to_child->ipc_fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
     200                                ipc_master_free_one( child );
     201                        g_free( fwd );
     202                }
     203                else
     204                        return ipc_master_takeover_fail( child, TRUE );
     205        }
     206        else if( strcmp( cmd[1], "DONE" ) == 0 || strcmp( cmd[1], "FAIL" ) == 0 )
     207        {
     208                /* Old connection -> Master */
     209                int fd;
     210               
     211                /* The copy was successful (or not), we don't need it anymore. */
     212                closesocket( child->to_fd );
     213                child->to_fd = -1;
     214               
     215                /* Pass it through to the other party, and flush all state. */
     216                fwd = irc_build_line( cmd );
     217                fd = child->to_child->ipc_fd;
     218                child->to_child->to_child = NULL;
     219                child->to_child = NULL;
     220                if( write( fd, fwd, strlen( fwd ) ) != strlen( fwd ) )
     221                        ipc_master_free_one( child );
     222                g_free( fwd );
     223        }
    112224}
    113225
     
    115227        { "client",     3, ipc_master_cmd_client,     0 },
    116228        { "hello",      0, ipc_master_cmd_client,     0 },
     229        { "nick",       1, ipc_master_cmd_nick,       0 },
    117230        { "die",        0, ipc_master_cmd_die,        0 },
    118231        { "deaf",       0, ipc_master_cmd_deaf,       0 },
     
    123236        { "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
    124237        { "restart",    0, ipc_master_cmd_restart,    0 },
     238        { "identify",   2, ipc_master_cmd_identify,   0 },
     239        { "takeover",   1, ipc_master_cmd_takeover,   0 },
    125240        { NULL }
    126241};
     
    138253       
    139254        if( strchr( irc->umode, 'w' ) )
    140                 irc_write( irc, ":%s WALLOPS :%s", irc->myhost, cmd[1] );
     255                irc_write( irc, ":%s WALLOPS :%s", irc->root->host, cmd[1] );
    141256}
    142257
     
    147262       
    148263        if( strchr( irc->umode, 's' ) )
    149                 irc_write( irc, ":%s NOTICE %s :%s", irc->myhost, irc->nick, cmd[1] );
     264                irc_write( irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1] );
    150265}
    151266
     
    156271       
    157272        if( strchr( irc->umode, 'o' ) )
    158                 irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->myhost, irc->nick, cmd[1] );
     273                irc_write( irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1] );
    159274}
    160275
     
    176291                return;
    177292       
    178         if( nick_cmp( cmd[1], irc->nick ) != 0 )
     293        if( nick_cmp( cmd[1], irc->user->nick ) != 0 )
    179294                return;         /* It's not for us. */
    180295       
    181         irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->mynick, irc->mynick, irc->myhost, irc->nick, cmd[2] );
     296        irc_write( irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick, cmd[2] );
    182297        irc_abort( irc, 0, "Killed by operator: %s", cmd[2] );
    183298}
     
    188303                ipc_to_master_str( "HELLO\r\n" );
    189304        else
    190                 ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
     305                ipc_to_master_str( "HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
     306}
     307
     308static void ipc_child_cmd_takeover_yes( void *data );
     309static void ipc_child_cmd_takeover_no( void *data );
     310
     311static void ipc_child_cmd_takeover( irc_t *irc, char **cmd )
     312{
     313        if( strcmp( cmd[1], "NO" ) == 0 )
     314        {
     315                /* Master->New connection */
     316                /* No takeover, finish the login. */
     317        }
     318        else if( strcmp( cmd[1], "INIT" ) == 0 )
     319        {
     320                /* Master->New connection */
     321                if( !set_getbool( &irc->b->set, "allow_takeover" ) )
     322                {
     323                        ipc_child_cmd_takeover_no( irc );
     324                        return;
     325                }
     326               
     327                /* Offer to take over the old session, unless for some reason
     328                   we're already logging into IM connections. */
     329                if( irc->login_source_id != -1 )
     330                        query_add( irc, NULL,
     331                                   "You're already connected to this server. "
     332                                   "Would you like to take over this session?",
     333                                   ipc_child_cmd_takeover_yes,
     334                                   ipc_child_cmd_takeover_no, NULL, irc );
     335               
     336                /* This one's going to connect to accounts, avoid that. */
     337                b_event_remove( irc->login_source_id );
     338                irc->login_source_id = -1;
     339        }
     340        else if( strcmp( cmd[1], "AUTH" ) == 0 )
     341        {
     342                /* Master->Old connection */
     343                if( irc->password && cmd[2] && cmd[3] &&
     344                    ipc_child_recv_fd != -1 &&
     345                    strcmp( irc->user->nick, cmd[2] ) == 0 &&
     346                    strcmp( irc->password, cmd[3] ) == 0 &&
     347                    set_getbool( &irc->b->set, "allow_takeover" ) )
     348                {
     349                        irc_switch_fd( irc, ipc_child_recv_fd );
     350                        irc_sync( irc );
     351                        irc_usermsg( irc, "You've successfully taken over your old session" );
     352                        ipc_child_recv_fd = -1;
     353                       
     354                        ipc_to_master_str( "TAKEOVER DONE\r\n" );
     355                }
     356                else
     357                {
     358                        ipc_to_master_str( "TAKEOVER FAIL\r\n" );
     359                }
     360        }
     361        else if( strcmp( cmd[1], "DONE" ) == 0 )
     362        {
     363                /* Master->New connection (now taken over by old process) */
     364                irc_free( irc );
     365        }
     366        else if( strcmp( cmd[1], "FAIL" ) == 0 )
     367        {
     368                /* Master->New connection */
     369                irc_usermsg( irc, "Could not take over old session" );
     370        }
     371}
     372
     373static void ipc_child_cmd_takeover_yes( void *data )
     374{
     375        irc_t *irc = data, *old = NULL;
     376        char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL };
     377       
     378        /* Master->New connection */
     379        ipc_to_master_str( "TAKEOVER AUTH %s :%s\r\n",
     380                           irc->user->nick, irc->password );
     381       
     382        if( global.conf->runmode == RUNMODE_DAEMON )
     383        {
     384                GSList *l;
     385               
     386                for( l = irc_connection_list; l; l = l->next )
     387                {
     388                        old = l->data;
     389                       
     390                        if( irc != old &&
     391                            irc->user->nick && old->user->nick &&
     392                            irc->password && old->password &&
     393                            strcmp( irc->user->nick, old->user->nick ) == 0 &&
     394                            strcmp( irc->password, old->password ) == 0 )
     395                                break;
     396                }
     397                if( l == NULL )
     398                {
     399                        to_auth[1] = "FAIL";
     400                        ipc_child_cmd_takeover( irc, to_auth );
     401                        return;
     402                }
     403        }
     404       
     405        /* Drop credentials, we'll shut down soon and shouldn't overwrite
     406           any settings. */
     407        irc_usermsg( irc, "Trying to take over existing session" );
     408       
     409        irc_desync( irc );
     410       
     411        if( old )
     412        {
     413                ipc_child_recv_fd = dup( irc->fd );
     414                ipc_child_cmd_takeover( old, to_auth );
     415        }
     416       
     417        /* TODO: irc_setpass() should do all of this. */
     418        irc_setpass( irc, NULL );
     419        irc->status &= ~USTATUS_IDENTIFIED;
     420        irc_umode_set( irc, "-R", 1 );
     421       
     422        if( old )
     423                irc_abort( irc, FALSE, NULL );
     424}
     425
     426static void ipc_child_cmd_takeover_no( void *data )
     427{
     428        ipc_to_master_str( "TAKEOVER NO\r\n" );
     429        cmd_identify_finish( data, 0, 0 );
    191430}
    192431
     
    199438        { "kill",       2, ipc_child_cmd_kill,        0 },
    200439        { "hello",      0, ipc_child_cmd_hello,       0 },
     440        { "takeover",   1, ipc_child_cmd_takeover,    0 },
    201441        { NULL }
    202442};
    203443
     444gboolean ipc_child_identify( irc_t *irc )
     445{
     446        if( global.conf->runmode == RUNMODE_FORKDAEMON )
     447        {
     448                if( !ipc_send_fd( global.listen_socket, irc->fd ) )
     449                        ipc_child_disable();
     450       
     451                ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password );
     452               
     453                return TRUE;
     454        }
     455        else if( global.conf->runmode == RUNMODE_DAEMON )
     456        {
     457                GSList *l;
     458                irc_t *old;
     459                char *to_init[] = { "TAKEOVER", "INIT", NULL };
     460               
     461                for( l = irc_connection_list; l; l = l->next )
     462                {
     463                        old = l->data;
     464                       
     465                        if( irc != old &&
     466                            irc->user->nick && old->user->nick &&
     467                            irc->password && old->password &&
     468                            strcmp( irc->user->nick, old->user->nick ) == 0 &&
     469                            strcmp( irc->password, old->password ) == 0 )
     470                                break;
     471                }
     472                if( l == NULL ||
     473                    !set_getbool( &irc->b->set, "allow_takeover" ) ||
     474                    !set_getbool( &old->b->set, "allow_takeover" ) )
     475                        return FALSE;
     476               
     477                ipc_child_cmd_takeover( irc, to_init );
     478               
     479                return TRUE;
     480        }
     481        else
     482                return FALSE;
     483}
     484
     485static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both )
     486{
     487        if( child == NULL || g_slist_find( child_list, child ) == NULL )
     488                return;
     489       
     490        if( both && child->to_child != NULL )
     491                ipc_master_takeover_fail( child->to_child, FALSE );
     492       
     493        if( child->to_fd > -1 )
     494        {
     495                /* Send this error only to the new connection, which can be
     496                   recognised by to_fd being set. */
     497                if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 )
     498                {
     499                        ipc_master_free_one( child );
     500                        return;
     501                }
     502                close( child->to_fd );
     503                child->to_fd = -1;
     504        }
     505        child->to_child = NULL;
     506}
    204507
    205508static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
     
    230533/* Return just one line. Returns NULL if something broke, an empty string
    231534   on temporary "errors" (EAGAIN and friends). */
    232 static char *ipc_readline( int fd )
    233 {
     535static char *ipc_readline( int fd, int *recv_fd )
     536{
     537        struct msghdr msg;
     538        struct iovec iov;
     539        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
     540        struct cmsghdr *cmsg;
    234541        char buf[513], *eol;
    235542        int size;
     
    253560                size = eol - buf + 2;
    254561       
    255         if( recv( fd, buf, size, 0 ) != size )
     562        iov.iov_base = buf;
     563        iov.iov_len = size;
     564       
     565        memset( &msg, 0, sizeof( msg ) );
     566        msg.msg_iov = &iov;
     567        msg.msg_iovlen = 1;
     568        msg.msg_control = ccmsg;
     569        msg.msg_controllen = sizeof( ccmsg );
     570       
     571        if( recvmsg( fd, &msg, 0 ) != size )
    256572                return NULL;
    257         else
    258                 return g_strndup( buf, size - 2 );
     573       
     574        if( recv_fd )
     575                for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
     576                        if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
     577                        {
     578                                /* Getting more than one shouldn't happen but if it does,
     579                                   make sure we don't leave them around. */
     580                                if( *recv_fd != -1 )
     581                                        close( *recv_fd );
     582                               
     583                                *recv_fd = *(int*) CMSG_DATA( cmsg );
     584                                /*
     585                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
     586                                */
     587                        }
     588       
     589        /*
     590        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
     591        */
     592        return g_strndup( buf, size - 2 );
    259593}
    260594
    261595gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
    262596{
     597        struct bitlbee_child *child = data;
    263598        char *buf, **cmd;
    264599       
    265         if( ( buf = ipc_readline( source ) ) )
     600        if( ( buf = ipc_readline( source, &child->to_fd ) ) )
    266601        {
    267602                cmd = irc_parse_line( buf );
    268603                if( cmd )
    269604                {
    270                         ipc_command_exec( data, cmd, ipc_master_commands );
     605                        ipc_command_exec( child, cmd, ipc_master_commands );
    271606                        g_free( cmd );
    272607                }
     
    285620        char *buf, **cmd;
    286621       
    287         if( ( buf = ipc_readline( source ) ) )
     622        if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) )
    288623        {
    289624                cmd = irc_parse_line( buf );
     
    392727                        next = l->next;
    393728                        if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
    394                         {
    395729                                ipc_master_free_one( c );
    396                                 child_list = g_slist_remove( child_list, c );
    397                         }
    398730                }
    399731        }
     
    413745}
    414746
     747static gboolean ipc_send_fd( int fd, int send_fd )
     748{
     749        struct msghdr msg;
     750        struct iovec iov;
     751        char ccmsg[CMSG_SPACE(sizeof(fd))];
     752        struct cmsghdr *cmsg;
     753       
     754        memset( &msg, 0, sizeof( msg ) );
     755        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
     756        iov.iov_len = 6;
     757        msg.msg_iov = &iov;
     758        msg.msg_iovlen = 1;
     759       
     760        msg.msg_control = ccmsg;
     761        msg.msg_controllen = sizeof( ccmsg );
     762        cmsg = CMSG_FIRSTHDR( &msg );
     763        cmsg->cmsg_level = SOL_SOCKET;
     764        cmsg->cmsg_type = SCM_RIGHTS;
     765        cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
     766        *(int*)CMSG_DATA( cmsg ) = send_fd;
     767        msg.msg_controllen = cmsg->cmsg_len;
     768       
     769        return sendmsg( fd, &msg, 0 ) == 6;
     770}
     771
    415772void ipc_master_free_one( struct bitlbee_child *c )
    416773{
     774        GSList *l;
     775       
    417776        b_event_remove( c->ipc_inpa );
    418777        closesocket( c->ipc_fd );
     778       
     779        if( c->to_fd != -1 )
     780                close( c->to_fd );
    419781       
    420782        g_free( c->host );
    421783        g_free( c->nick );
    422784        g_free( c->realname );
     785        g_free( c->password );
    423786        g_free( c );
     787       
     788        child_list = g_slist_remove( child_list, c );
     789       
     790        /* Also, if any child has a reference to this one, remove it. */
     791        for( l = child_list; l; l = l->next )
     792        {
     793                struct bitlbee_child *oc = l->data;
     794               
     795                if( oc->to_child == c )
     796                        ipc_master_takeover_fail( oc, FALSE );
     797        }
    424798}
    425799
     
    435809                {
    436810                        ipc_master_free_one( c );
    437                         child_list = g_slist_remove( child_list, c );
    438811                        break;
    439812                }
     
    443816void ipc_master_free_all()
    444817{
    445         GSList *l;
    446        
    447         for( l = child_list; l; l = l->next )
    448                 ipc_master_free_one( l->data );
    449        
    450         g_slist_free( child_list );
    451         child_list = NULL;
     818        while( child_list )
     819                ipc_master_free_one( child_list->data );
    452820}
    453821
     
    506874        struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
    507875       
     876        child->to_fd = -1;
    508877        child->ipc_fd = accept( serversock, NULL, 0 );
    509        
    510878        if( child->ipc_fd == -1 )
    511879        {
     
    514882        }
    515883               
    516         child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );
    517        
    518         child_list = g_slist_append( child_list, child );
     884        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     885       
     886        child_list = g_slist_prepend( child_list, child );
    519887       
    520888        return TRUE;
     
    552920        }
    553921       
    554         b_input_add( serversock, GAIM_INPUT_READ, new_ipc_client, NULL );
     922        b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL );
    555923       
    556924        return 1;
     
    597965                        return 0;
    598966                }
    599                 child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child );
    600                
    601                 child_list = g_slist_append( child_list, child );
     967                child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     968                child->to_fd = -1;
     969               
     970                child_list = g_slist_prepend( child_list, child );
    602971        }
    603972       
  • ipc.h

    ree6cc94 r2528cda  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2004 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    3737        char *nick;
    3838        char *realname;
     39       
     40        char *password;
     41       
     42        /* For takeovers: */
     43        struct bitlbee_child *to_child;
     44        int to_fd;
    3945};
    4046
     
    4854
    4955void ipc_child_disable();
     56
     57gboolean ipc_child_identify( irc_t *irc );
    5058
    5159void ipc_to_master( char **cmd );
  • irc.c

    ree6cc94 r2528cda  
    55  \********************************************************************/
    66
    7 /* The big hairy IRCd part of the project                               */
     7/* The IRC-based UI (for now the only one)                              */
    88
    99/*
     
    2424*/
    2525
    26 #define BITLBEE_CORE
    2726#include "bitlbee.h"
    28 #include "sock.h"
    29 #include "crypting.h"
    3027#include "ipc.h"
    31 
    32 static gboolean irc_userping( gpointer _irc, int fd, b_input_condition cond );
    33 
    34 GSList *irc_connection_list = NULL;
    35 
    36 static char *set_eval_password( set_t *set, char *value )
    37 {
    38         irc_t *irc = set->data;
    39        
    40         if( irc->status & USTATUS_IDENTIFIED && value )
    41         {
    42                 irc_setpass( irc, value );
    43                 return NULL;
    44         }
    45         else
    46         {
    47                 return SET_INVALID;
    48         }
    49 }
    50 
    51 static char *set_eval_charset( set_t *set, char *value )
    52 {
    53         irc_t *irc = set->data;
    54         GIConv ic, oc;
    55 
    56         if( g_strcasecmp( value, "none" ) == 0 )
    57                 value = g_strdup( "utf-8" );
    58 
    59         if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
    60         {
    61                 return NULL;
    62         }
    63         if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
    64         {
    65                 g_iconv_close( ic );
    66                 return NULL;
    67         }
    68        
    69         if( irc->iconv != (GIConv) -1 )
    70                 g_iconv_close( irc->iconv );
    71         if( irc->oconv != (GIConv) -1 )
    72                 g_iconv_close( irc->oconv );
    73        
    74         irc->iconv = ic;
    75         irc->oconv = oc;
    76 
    77         return value;
    78 }
    79 
    80 static char *set_eval_away_status( set_t *set, char *value )
    81 {
    82         irc_t *irc = set->data;
    83         account_t *a;
    84        
    85         g_free( set->value );
    86         set->value = g_strdup( value );
    87        
    88         for( a = irc->accounts; a; a = a->next )
    89         {
    90                 struct im_connection *ic = a->ic;
    91                
    92                 if( ic && ic->flags & OPT_LOGGED_IN )
    93                         imc_away_send_update( ic );
    94         }
    95        
    96         return value;
    97 }
     28#include "dcc.h"
     29
     30GSList *irc_connection_list;
     31
     32static gboolean irc_userping( gpointer _irc, gint fd, b_input_condition cond );
     33static char *set_eval_charset( set_t *set, char *value );
     34static char *set_eval_password( set_t *set, char *value );
     35static char *set_eval_bw_compat( set_t *set, char *value );
    9836
    9937irc_t *irc_new( int fd )
     
    10240        struct sockaddr_storage sock;
    10341        socklen_t socklen = sizeof( sock );
     42        char *host = NULL, *myhost = NULL;
     43        irc_user_t *iu;
    10444        set_t *s;
     45        bee_t *b;
    10546       
    10647        irc = g_new0( irc_t, 1 );
     
    10950        sock_make_nonblocking( irc->fd );
    11051       
    111         irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc );
     52        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
    11253       
    11354        irc->status = USTATUS_OFFLINE;
    11455        irc->last_pong = gettime();
    11556       
    116         irc->userhash = g_hash_table_new( g_str_hash, g_str_equal );
     57        irc->nick_user_hash = g_hash_table_new( g_str_hash, g_str_equal );
    11758        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
    118        
    119         strcpy( irc->umode, UMODE );
    120         irc->mynick = g_strdup( ROOT_NICK );
    121         irc->channel = g_strdup( ROOT_CHAN );
    12259       
    12360        irc->iconv = (GIConv) -1;
     
    12663        if( global.conf->hostname )
    12764        {
    128                 irc->myhost = g_strdup( global.conf->hostname );
     65                myhost = g_strdup( global.conf->hostname );
    12966        }
    13067        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
     
    13572                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    13673                {
    137                         irc->myhost = g_strdup( ipv6_unwrap( buf ) );
     74                        myhost = g_strdup( ipv6_unwrap( buf ) );
    13875                }
    13976        }
     
    14683                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    14784                {
    148                         irc->host = g_strdup( ipv6_unwrap( buf ) );
    149                 }
    150         }
    151        
    152         if( irc->host == NULL )
    153                 irc->host = g_strdup( "localhost.localdomain" );
    154         if( irc->myhost == NULL )
    155                 irc->myhost = g_strdup( "localhost.localdomain" );
     85                        host = g_strdup( ipv6_unwrap( buf ) );
     86                }
     87        }
     88       
     89        if( host == NULL )
     90                host = g_strdup( "localhost.localdomain" );
     91        if( myhost == NULL )
     92                myhost = g_strdup( "localhost.localdomain" );
    15693       
    15794        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
    15895                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
    159        
    160         irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
    16196
    16297        irc_connection_list = g_slist_append( irc_connection_list, irc );
    16398       
    164         s = set_add( &irc->set, "away", NULL,  set_eval_away_status, irc );
     99        b = irc->b = bee_new();
     100        b->ui_data = irc;
     101        b->ui = &irc_ui_funcs;
     102       
     103        s = set_add( &b->set, "allow_takeover", "true", set_eval_bool, irc );
     104        s = set_add( &b->set, "away_devoice", "true", set_eval_bw_compat, irc );
     105        s = set_add( &b->set, "away_reply_timeout", "3600", set_eval_int, irc );
     106        s = set_add( &b->set, "charset", "utf-8", set_eval_charset, irc );
     107        s = set_add( &b->set, "default_target", "root", NULL, irc );
     108        s = set_add( &b->set, "display_namechanges", "false", set_eval_bool, irc );
     109        s = set_add( &b->set, "display_timestamps", "true", set_eval_bool, irc );
     110        s = set_add( &b->set, "handle_unknown", "add_channel", NULL, irc );
     111        s = set_add( &b->set, "lcnicks", "true", set_eval_bool, irc );
     112        s = set_add( &b->set, "nick_format", "%-@nick", NULL, irc );
     113        s = set_add( &b->set, "offline_user_quits", "true", set_eval_bool, irc );
     114        s = set_add( &b->set, "ops", "both", set_eval_irc_channel_ops, irc );
     115        s = set_add( &b->set, "paste_buffer", "false", set_eval_bool, irc );
     116        s->old_key = g_strdup( "buddy_sendbuffer" );
     117        s = set_add( &b->set, "paste_buffer_delay", "200", set_eval_int, irc );
     118        s->old_key = g_strdup( "buddy_sendbuffer_delay" );
     119        s = set_add( &b->set, "password", NULL, set_eval_password, irc );
    165120        s->flags |= SET_NULL_OK;
    166         s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
    167         s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
    168         s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
    169         s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
    170         s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
    171         s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
    172         s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
    173         s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
    174         s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
    175         s = set_add( &irc->set, "default_target", "root", NULL, irc );
    176         s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
    177         s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
    178         s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
    179         s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
    180         s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
    181         s->flags |= SET_NULL_OK;
    182         s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
    183         s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
    184         s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
    185         s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
    186         s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
    187         s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
    188         s->flags |= SET_NULL_OK;
    189         s = set_add( &irc->set, "strip_html", "true", NULL, irc );
    190         s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
    191         s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc );
     121        s = set_add( &b->set, "private", "true", set_eval_bool, irc );
     122        s = set_add( &b->set, "query_order", "lifo", NULL, irc );
     123        s = set_add( &b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc );
     124        s = set_add( &b->set, "show_offline", "false", set_eval_bw_compat, irc );
     125        s = set_add( &b->set, "simulate_netsplit", "true", set_eval_bool, irc );
     126        s = set_add( &b->set, "timezone", "local", set_eval_timezone, irc );
     127        s = set_add( &b->set, "to_char", ": ", set_eval_to_char, irc );
     128        s = set_add( &b->set, "typing_notice", "false", set_eval_bool, irc );
     129
     130        irc->root = iu = irc_user_new( irc, ROOT_NICK );
     131        iu->host = g_strdup( myhost );
     132        iu->fullname = g_strdup( ROOT_FN );
     133        iu->f = &irc_user_root_funcs;
     134       
     135        iu = irc_user_new( irc, NS_NICK );
     136        iu->host = g_strdup( myhost );
     137        iu->fullname = g_strdup( ROOT_FN );
     138        iu->f = &irc_user_root_funcs;
     139       
     140        irc->user = g_new0( irc_user_t, 1 );
     141        irc->user->host = g_strdup( host );
    192142       
    193143        conf_loaddefaults( irc );
    194144       
    195145        /* Evaluator sets the iconv/oconv structures. */
    196         set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
    197        
    198         return( irc );
     146        set_eval_charset( set_find( &b->set, "charset" ), set_getstr( &b->set, "charset" ) );
     147       
     148        irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host, "BitlBee-IRCd initialized, please go on" );
     149        if( isatty( irc->fd ) )
     150                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
     151                           "If you read this, you most likely accidentally "
     152                           "started BitlBee in inetd mode on the command line. "
     153                           "You probably want to run it in (Fork)Daemon mode. "
     154                           "See doc/README for more information." );
     155       
     156        g_free( myhost );
     157        g_free( host );
     158       
     159        nogaim_init();
     160       
     161        return irc;
    199162}
    200163
     
    204167void irc_abort( irc_t *irc, int immed, char *format, ... )
    205168{
     169        char *reason = NULL;
     170       
    206171        if( format != NULL )
    207172        {
    208173                va_list params;
    209                 char *reason;
    210174               
    211175                va_start( params, format );
    212176                reason = g_strdup_vprintf( format, params );
    213177                va_end( params );
    214                
    215                 if( !immed )
    216                         irc_write( irc, "ERROR :Closing link: %s", reason );
    217                
    218                 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    219                                    irc->nick ? irc->nick : "(NONE)", irc->host, reason );
    220                
    221                 g_free( reason );
     178        }
     179       
     180        irc_write( irc, "ERROR :Closing link: %s", reason ? : "" );
     181       
     182        ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
     183                           irc->user->nick ? irc->user->nick : "(NONE)",
     184                           irc->user->host, reason ? : "" );
     185       
     186        g_free( reason );
     187       
     188        irc_flush( irc );
     189        if( immed )
     190        {
     191                irc_free( irc );
    222192        }
    223193        else
    224194        {
    225                 if( !immed )
    226                         irc_write( irc, "ERROR :Closing link" );
    227                
    228                 ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    229                                    irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
    230         }
    231        
     195                b_event_remove( irc->ping_source_id );
     196                irc->ping_source_id = b_timeout_add( 1, (b_event_handler) irc_free, irc );
     197        }
     198}
     199
     200static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
     201
     202void irc_free( irc_t * irc )
     203{
    232204        irc->status |= USTATUS_SHUTDOWN;
    233         if( irc->sendbuffer && !immed )
    234         {
    235                 /* Set up a timeout event that should shut down the connection
    236                    in a second, just in case ..._write doesn't do it first. */
    237                
    238                 b_event_remove( irc->r_watch_source_id );
    239                 irc->r_watch_source_id = 0;
    240                
    241                 b_event_remove( irc->ping_source_id );
    242                 irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
    243         }
    244         else
    245         {
    246                 irc_free( irc );
    247         }
    248 }
    249 
    250 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
    251 {
    252         g_free( key );
    253        
    254         return( TRUE );
    255 }
    256 
    257 /* Because we have no garbage collection, this is quite annoying */
    258 void irc_free( irc_t * irc )
    259 {
    260         user_t *user, *usertmp;
    261205       
    262206        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
    263207       
    264         if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) )
     208        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) )
    265209                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
    266                         irc_usermsg( irc, "Error while saving settings!" );
     210                        log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick );
    267211       
    268212        irc_connection_list = g_slist_remove( irc_connection_list, irc );
    269        
    270         while( irc->accounts )
    271         {
    272                 if( irc->accounts->ic )
    273                         imc_logout( irc->accounts->ic, FALSE );
    274                 else if( irc->accounts->reconnect )
    275                         cancel_auto_reconnect( irc->accounts );
    276                
    277                 if( irc->accounts->ic == NULL )
    278                         account_del( irc, irc->accounts );
    279                 else
    280                         /* Nasty hack, but account_del() doesn't work in this
    281                            case and we don't want infinite loops, do we? ;-) */
    282                         irc->accounts = irc->accounts->next;
    283         }
    284213       
    285214        while( irc->queries != NULL )
    286215                query_del( irc, irc->queries );
    287216       
    288         while( irc->set )
    289                 set_del( &irc->set, irc->set->key );
    290        
    291         if (irc->users != NULL)
    292         {
    293                 user = irc->users;
    294                 while( user != NULL )
    295                 {
    296                         g_free( user->nick );
    297                         g_free( user->away );
    298                         g_free( user->handle );
    299                         if( user->user != user->nick ) g_free( user->user );
    300                         if( user->host != user->nick ) g_free( user->host );
    301                         if( user->realname != user->nick ) g_free( user->realname );
    302                         b_event_remove( user->sendbuf_timer );
    303                                        
    304                         usertmp = user;
    305                         user = user->next;
    306                         g_free( usertmp );
    307                 }
    308         }
     217        /* This is a little bit messy: bee_free() frees all b->users which
     218           calls us back to free the corresponding irc->users. So do this
     219           before we clear the remaining ones ourselves. */
     220        bee_free( irc->b );
     221       
     222        while( irc->users )
     223                irc_user_free( irc, (irc_user_t *) irc->users->data );
     224       
     225        while( irc->channels )
     226                irc_channel_free( irc->channels->data );
    309227       
    310228        if( irc->ping_source_id > 0 )
     
    318236        irc->fd = -1;
    319237       
    320         g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
    321         g_hash_table_destroy( irc->userhash );
     238        g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
     239        g_hash_table_destroy( irc->nick_user_hash );
    322240       
    323241        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
     
    331249        g_free( irc->sendbuffer );
    332250        g_free( irc->readbuffer );
    333        
    334         g_free( irc->nick );
    335         g_free( irc->user );
    336         g_free( irc->host );
    337         g_free( irc->realname );
    338251        g_free( irc->password );
    339        
    340         g_free( irc->myhost );
    341         g_free( irc->mynick );
    342        
    343         g_free( irc->channel );
    344        
    345         g_free( irc->last_target );
    346252       
    347253        g_free( irc );
     
    355261}
    356262
     263static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
     264{
     265        g_free( key );
     266       
     267        return( TRUE );
     268}
     269
    357270/* USE WITH CAUTION!
    358271   Sets pass without checking */
    359 void irc_setpass (irc_t *irc, const char *pass) 
     272void irc_setpass (irc_t *irc, const char *pass)
    360273{
    361274        g_free (irc->password);
     
    368281}
    369282
     283static char *set_eval_password( set_t *set, char *value )
     284{
     285        irc_t *irc = set->data;
     286       
     287        if( irc->status & USTATUS_IDENTIFIED && value )
     288        {
     289                irc_setpass( irc, value );
     290                return NULL;
     291        }
     292        else
     293        {
     294                return SET_INVALID;
     295        }
     296}
     297
     298static char **irc_splitlines( char *buffer );
     299
    370300void irc_process( irc_t *irc )
    371301{
     
    375305        if( irc->readbuffer != NULL )
    376306        {
    377                 lines = irc_tokenize( irc->readbuffer );
     307                lines = irc_splitlines( irc->readbuffer );
    378308               
    379309                for( i = 0; *lines[i] != '\0'; i ++ )
     
    412342                                                                  "`help set charset' for more information. Your "
    413343                                                                  "message was ignored.",
    414                                                                   set_getstr( &irc->set, "charset" ) );
     344                                                                  set_getstr( &irc->b->set, "charset" ) );
    415345                                               
    416346                                                g_free( conv );
     
    419349                                        else
    420350                                        {
    421                                                 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
     351                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
    422352                                                           "Warning: invalid characters received at login time." );
    423353                                               
     
    457387}
    458388
    459 /* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
    460    contains an incomplete line at the end, ends with an empty string. */
    461 char **irc_tokenize( char *buffer )
     389/* Splits a long string into separate lines. The array is NULL-terminated
     390   and, unless the string contains an incomplete line at the end, ends with
     391   an empty string. Could use g_strsplit() but this one does it in-place.
     392   (So yes, it's destructive.) */
     393static char **irc_splitlines( char *buffer )
    462394{
    463395        int i, j, n = 3;
     
    590522}
    591523
    592 void irc_reply( irc_t *irc, int code, char *format, ... )
    593 {
    594         char text[IRC_MAX_LINE];
    595         va_list params;
    596        
    597         va_start( params, format );
    598         g_vsnprintf( text, IRC_MAX_LINE, format, params );
    599         va_end( params );
    600         irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
    601        
    602         return;
    603 }
    604 
    605 int irc_usermsg( irc_t *irc, char *format, ... )
    606 {
    607         char text[1024];
    608         va_list params;
    609         char is_private = 0;
    610         user_t *u;
    611        
    612         u = user_find( irc, irc->mynick );
    613         is_private = u->is_private;
    614        
    615         va_start( params, format );
    616         g_vsnprintf( text, sizeof( text ), format, params );
    617         va_end( params );
    618        
    619         return( irc_msgfrom( irc, u->nick, text ) );
    620 }
    621 
    622524void irc_write( irc_t *irc, char *format, ... )
    623525{
     
    630532        return;
    631533}
     534
     535void irc_write_all( int now, char *format, ... )
     536{
     537        va_list params;
     538        GSList *temp;   
     539       
     540        va_start( params, format );
     541       
     542        temp = irc_connection_list;
     543        while( temp != NULL )
     544        {
     545                irc_t *irc = temp->data;
     546               
     547                if( now )
     548                {
     549                        g_free( irc->sendbuffer );
     550                        irc->sendbuffer = g_strdup( "\r\n" );
     551                }
     552                irc_vawrite( temp->data, format, params );
     553                if( now )
     554                {
     555                        bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE );
     556                }
     557                temp = temp->next;
     558        }
     559       
     560        va_end( params );
     561        return;
     562}
    632563
    633564void irc_vawrite( irc_t *irc, char *format, va_list params )
     
    677608                   in the event queue. */
    678609                /* Really can't be done as long as the code doesn't do error checking very well:
    679                 if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
     610                if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
    680611               
    681612                /* So just always do it via the event handler. */
    682                 irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
     613                irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc );
    683614        }
    684615       
     
    686617}
    687618
    688 void irc_write_all( int now, char *format, ... )
    689 {
    690         va_list params;
    691         GSList *temp;   
    692        
    693         va_start( params, format );
    694        
    695         temp = irc_connection_list;
    696         while( temp != NULL )
    697         {
    698                 irc_t *irc = temp->data;
    699                
    700                 if( now )
    701                 {
    702                         g_free( irc->sendbuffer );
    703                         irc->sendbuffer = g_strdup( "\r\n" );
    704                 }
    705                 irc_vawrite( temp->data, format, params );
    706                 if( now )
    707                 {
    708                         bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
    709                 }
    710                 temp = temp->next;
    711         }
    712        
    713         va_end( params );
    714         return;
    715 }
    716 
    717 void irc_names( irc_t *irc, char *channel )
    718 {
    719         user_t *u;
    720         char namelist[385] = "";
    721         struct groupchat *c = NULL;
    722         char *ops = set_getstr( &irc->set, "ops" );
    723        
    724         /* RFCs say there is no error reply allowed on NAMES, so when the
    725            channel is invalid, just give an empty reply. */
    726        
    727         if( g_strcasecmp( channel, irc->channel ) == 0 )
    728         {
    729                 for( u = irc->users; u; u = u->next ) if( u->online )
    730                 {
    731                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
     619/* Flush sendbuffer if you can. If it fails, fail silently and let some
     620   I/O event handler clean up. */
     621void irc_flush( irc_t *irc )
     622{
     623        ssize_t n;
     624        size_t len;
     625       
     626        if( irc->sendbuffer == NULL )
     627                return;
     628       
     629        len = strlen( irc->sendbuffer );
     630        if( ( n = send( irc->fd, irc->sendbuffer, len, 0 ) ) == len )
     631        {
     632                g_free( irc->sendbuffer );
     633                irc->sendbuffer = NULL;
     634               
     635                b_event_remove( irc->w_watch_source_id );
     636                irc->w_watch_source_id = 0;
     637        }
     638        else if( n > 0 )
     639        {
     640                char *s = g_strdup( irc->sendbuffer + n );
     641                g_free( irc->sendbuffer );
     642                irc->sendbuffer = s;
     643        }
     644        /* Otherwise something went wrong and we don't currently care
     645           what the error was. We may or may not succeed later, we
     646           were just trying to flush the buffer immediately. */
     647}
     648
     649/* Meant for takeover functionality. Transfer an IRC connection to a different
     650   socket. */
     651void irc_switch_fd( irc_t *irc, int fd )
     652{
     653        irc_write( irc, "ERROR :Transferring session to a new connection" );
     654        irc_flush( irc ); /* Write it now or forget about it forever. */
     655       
     656        if( irc->sendbuffer )
     657        {
     658                b_event_remove( irc->w_watch_source_id );
     659                irc->w_watch_source_id = 0;
     660                g_free( irc->sendbuffer );
     661                irc->sendbuffer = NULL;
     662        }
     663       
     664        b_event_remove( irc->r_watch_source_id );
     665        closesocket( irc->fd );
     666        irc->fd = fd;
     667        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
     668}
     669
     670void irc_sync( irc_t *irc )
     671{
     672        GSList *l;
     673       
     674        irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
     675                   irc->user->user, irc->user->host, irc->user->nick,
     676                   irc->umode );
     677       
     678        for( l = irc->channels; l; l = l->next )
     679        {
     680                irc_channel_t *ic = l->data;
     681                if( ic->flags & IRC_CHANNEL_JOINED )
     682                        irc_send_join( ic, irc->user );
     683        }
     684}
     685
     686void irc_desync( irc_t *irc )
     687{
     688        GSList *l;
     689       
     690        for( l = irc->channels; l; l = l->next )
     691                irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK,
     692                                      "Switching to old session" );
     693       
     694        irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
     695                   irc->user->user, irc->user->host, irc->user->nick,
     696                   irc->umode );
     697}
     698
     699int irc_check_login( irc_t *irc )
     700{
     701        if( irc->user->user && irc->user->nick )
     702        {
     703                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
     704                {
     705                        irc_send_num( irc, 464, ":This server is password-protected." );
     706                        return 0;
     707                }
     708                else
     709                {
     710                        irc_channel_t *ic;
     711                        irc_user_t *iu = irc->user;
     712                       
     713                        irc->user = irc_user_new( irc, iu->nick );
     714                        irc->user->user = iu->user;
     715                        irc->user->host = iu->host;
     716                        irc->user->fullname = iu->fullname;
     717                        irc->user->f = &irc_user_self_funcs;
     718                        g_free( iu->nick );
     719                        g_free( iu );
     720                       
     721                        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
     722                                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
     723                       
     724                        irc->status |= USTATUS_LOGGED_IN;
     725                       
     726                        irc_send_login( irc );
     727                       
     728                        irc->umode[0] = '\0';
     729                        irc_umode_set( irc, "+" UMODE, TRUE );
     730                       
     731                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
     732                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
     733                        set_setstr( &ic->set, "auto_join", "true" );
     734                        irc_channel_auto_joins( irc, NULL );
     735                       
     736                        irc->root->last_channel = irc->default_channel;
     737                       
     738                        irc_usermsg( irc,
     739                                     "Welcome to the BitlBee gateway!\n\n"
     740                                     "If you've never used BitlBee before, please do read the help "
     741                                     "information using the \x02help\x02 command. Lots of FAQs are "
     742                                     "answered there.\n"
     743                                     "If you already have an account on this server, just use the "
     744                                     "\x02identify\x02 command to identify yourself." );
     745                       
     746                        /* This is for bug #209 (use PASS to identify to NickServ). */
     747                        if( irc->password != NULL )
    732748                        {
    733                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    734                                 *namelist = 0;
     749                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
     750                               
     751                                irc_setpass( irc, NULL );
     752                                root_command( irc, send_cmd );
     753                                g_free( send_cmd[1] );
    735754                        }
    736755                       
    737                         if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
    738                                 strcat( namelist, "+" );
    739                         else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
    740                                  ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
    741                                 strcat( namelist, "@" );
    742                        
    743                         strcat( namelist, u->nick );
    744                         strcat( namelist, " " );
    745                 }
    746         }
    747         else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    748         {
    749                 GList *l;
    750                
    751                 /* root and the user aren't in the channel userlist but should
    752                    show up in /NAMES, so list them first: */
    753                 sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
    754                                                  strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
    755                
    756                 for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
    757                 {
    758                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
    759                         {
    760                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    761                                 *namelist = 0;
    762                         }
    763                        
    764                         strcat( namelist, u->nick );
    765                         strcat( namelist, " " );
    766                 }
    767         }
    768        
    769         if( *namelist )
    770                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    771        
    772         irc_reply( irc, 366, "%s :End of /NAMES list", channel );
    773 }
    774 
    775 int irc_check_login( irc_t *irc )
    776 {
    777         if( irc->user && irc->nick )
    778         {
    779                 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
    780                 {
    781                         irc_reply( irc, 464, ":This server is password-protected." );
    782                         return 0;
    783                 }
    784                 else
    785                 {
    786                         irc_login( irc );
    787756                        return 1;
    788757                }
     
    795764}
    796765
    797 void irc_login( irc_t *irc )
    798 {
    799         user_t *u;
    800        
    801         irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
    802         irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
    803         irc_reply( irc,   3, ":%s", IRCD_INFO );
    804         irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
    805         irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
    806                              "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
    807                              CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
    808         irc_motd( irc );
    809         irc->umode[0] = '\0';
    810         irc_umode_set( irc, "+" UMODE, 1 );
    811 
    812         u = user_add( irc, irc->mynick );
    813         u->host = g_strdup( irc->myhost );
    814         u->realname = g_strdup( ROOT_FN );
    815         u->online = 1;
    816         u->send_handler = root_command_string;
    817         u->is_private = 0; /* [SH] The channel is root's personal playground. */
    818         irc_spawn( irc, u );
    819        
    820         u = user_add( irc, NS_NICK );
    821         u->host = g_strdup( irc->myhost );
    822         u->realname = g_strdup( ROOT_FN );
    823         u->online = 0;
    824         u->send_handler = root_command_string;
    825         u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
    826        
    827         u = user_add( irc, irc->nick );
    828         u->user = g_strdup( irc->user );
    829         u->host = g_strdup( irc->host );
    830         u->realname = g_strdup( irc->realname );
    831         u->online = 1;
    832         irc_spawn( irc, u );
    833        
    834         irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
    835                           "If you've never used BitlBee before, please do read the help "
    836                           "information using the \x02help\x02 command. Lots of FAQs are "
    837                           "answered there.\n"
    838                           "If you already have an account on this server, just use the "
    839                           "\x02identify\x02 command to identify yourself." );
    840        
    841         if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
    842                 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
    843        
    844         irc->status |= USTATUS_LOGGED_IN;
    845        
    846         /* This is for bug #209 (use PASS to identify to NickServ). */
    847         if( irc->password != NULL )
    848         {
    849                 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
    850                
    851                 irc_setpass( irc, NULL );
    852                 root_command( irc, send_cmd );
    853                 g_free( send_cmd[1] );
    854         }
    855 }
    856 
    857 void irc_motd( irc_t *irc )
    858 {
    859         int fd;
    860        
    861         fd = open( global.conf->motdfile, O_RDONLY );
    862         if( fd == -1 )
    863         {
    864                 irc_reply( irc, 422, ":We don't need MOTDs." );
    865         }
    866         else
    867         {
    868                 char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
    869                 char *add, max;
    870                 int len;
    871                
    872                 linebuf[79] = len = 0;
    873                 max = sizeof( linebuf ) - 1;
    874                
    875                 irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
    876                 while( read( fd, linebuf + len, 1 ) == 1 )
    877                 {
    878                         if( linebuf[len] == '\n' || len == max )
    879                         {
    880                                 linebuf[len] = 0;
    881                                 irc_reply( irc, 372, ":- %s", linebuf );
    882                                 len = 0;
    883                         }
    884                         else if( linebuf[len] == '%' )
    885                         {
    886                                 read( fd, linebuf + len, 1 );
    887                                 if( linebuf[len] == 'h' )
    888                                         add = irc->myhost;
    889                                 else if( linebuf[len] == 'v' )
    890                                         add = BITLBEE_VERSION;
    891                                 else if( linebuf[len] == 'n' )
    892                                         add = irc->nick;
    893                                 else
    894                                         add = "%";
    895                                
    896                                 strncpy( linebuf + len, add, max - len );
    897                                 while( linebuf[++len] );
    898                         }
    899                         else if( len < max )
    900                         {
    901                                 len ++;
    902                         }
    903                 }
    904                 irc_reply( irc, 376, ":End of MOTD" );
    905                 close( fd );
    906         }
    907 }
    908 
    909 void irc_topic( irc_t *irc, char *channel )
    910 {
    911         struct groupchat *c = irc_chat_by_channel( irc, channel );
    912        
    913         if( c && c->topic )
    914                 irc_reply( irc, 332, "%s :%s", channel, c->topic );
    915         else if( g_strcasecmp( channel, irc->channel ) == 0 )
    916                 irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
    917         else
    918                 irc_reply( irc, 331, "%s :No topic for this channel", channel );
    919 }
    920 
    921 void irc_umode_set( irc_t *irc, char *s, int allow_priv )
     766/* TODO: This is a mess, but this function is a bit too complicated to be
     767   converted to something more generic. */
     768void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
    922769{
    923770        /* allow_priv: Set to 0 if s contains user input, 1 if you want
    924771           to set a "privileged" mode (+o, +R, etc). */
    925         char m[256], st = 1, *t;
     772        char m[128], st = 1;
     773        const char *t;
    926774        int i;
    927775        char changes[512], *p, st2 = 2;
     
    931779       
    932780        for( t = irc->umode; *t; t ++ )
    933                 m[(int)*t] = 1;
    934 
     781                if( *t < sizeof( m ) )
     782                        m[(int)*t] = 1;
     783       
    935784        p = changes;
    936785        for( t = s; *t; t ++ )
     
    938787                if( *t == '+' || *t == '-' )
    939788                        st = *t == '+';
    940                 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
     789                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
     790                         ( st == 1 && strchr( UMODES, *t ) ) ||
     791                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
    941792                {
    942793                        if( m[(int)*t] != st)
     
    955806        memset( irc->umode, 0, sizeof( irc->umode ) );
    956807       
    957         for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
     808        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
    958809                if( m[i] )
    959810                        irc->umode[strlen(irc->umode)] = i;
    960811       
    961812        if( badflag )
    962                 irc_reply( irc, 501, ":Unknown MODE flag" );
    963         /* Deliberately no !user@host on the prefix here */
     813                irc_send_num( irc, 501, ":Unknown MODE flag" );
    964814        if( *changes )
    965                 irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
    966 }
    967 
    968 void irc_spawn( irc_t *irc, user_t *u )
    969 {
    970         irc_join( irc, u, irc->channel );
    971 }
    972 
    973 void irc_join( irc_t *irc, user_t *u, char *channel )
    974 {
    975         char *nick;
    976        
    977         if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
    978                 irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
    979        
    980         if( nick_cmp( u->nick, irc->nick ) == 0 )
    981         {
    982                 irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
    983                 irc_names( irc, channel );
    984                 irc_topic( irc, channel );
    985         }
    986        
    987         nick = g_strdup( u->nick );
    988         nick_lc( nick );
    989         if( g_hash_table_lookup( irc->watches, nick ) )
    990         {
    991                 irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
    992         }
    993         g_free( nick );
    994 }
    995 
    996 void irc_part( irc_t *irc, user_t *u, char *channel )
    997 {
    998         irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
    999 }
    1000 
    1001 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
    1002 {
    1003         irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
    1004 }
    1005 
    1006 void irc_kill( irc_t *irc, user_t *u )
    1007 {
    1008         char *nick, *s;
    1009         char reason[128];
    1010        
    1011         if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
    1012         {
    1013                 if( u->ic->acc->server )
    1014                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1015                                     u->ic->acc->server );
    1016                 else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
    1017                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1018                                     s + 1 );
    1019                 else
    1020                         g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
    1021                                     u->ic->acc->prpl->name, irc->myhost );
    1022                
    1023                 /* proto_opt might contain garbage after the : */
    1024                 if( ( s = strchr( reason, ':' ) ) )
    1025                         *s = 0;
    1026         }
    1027         else
    1028         {
    1029                 strcpy( reason, "Leaving..." );
    1030         }
    1031        
    1032         irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
    1033        
    1034         nick = g_strdup( u->nick );
    1035         nick_lc( nick );
    1036         if( g_hash_table_lookup( irc->watches, nick ) )
    1037         {
    1038                 irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
    1039         }
    1040         g_free( nick );
    1041 }
    1042 
    1043 int irc_send( irc_t *irc, char *nick, char *s, int flags )
    1044 {
    1045         struct groupchat *c = NULL;
    1046         user_t *u = NULL;
    1047        
    1048         if( strchr( CTYPES, *nick ) )
    1049         {
    1050                 if( !( c = irc_chat_by_channel( irc, nick ) ) )
    1051                 {
    1052                         irc_reply( irc, 403, "%s :Channel does not exist", nick );
    1053                         return( 0 );
    1054                 }
    1055         }
    1056         else
    1057         {
    1058                 u = user_find( irc, nick );
    1059                
    1060                 if( !u )
    1061                 {
    1062                         if( irc->is_private )
    1063                                 irc_reply( irc, 401, "%s :Nick does not exist", nick );
    1064                         else
    1065                                 irc_usermsg( irc, "Nick `%s' does not exist!", nick );
    1066                         return( 0 );
    1067                 }
    1068         }
    1069        
    1070         if( *s == 1 && s[strlen(s)-1] == 1 )
    1071         {
    1072                 if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
    1073                 {
    1074                         if( s[7] == ' ' ) s ++;
    1075                         s += 3;
    1076                         *(s++) = '/';
    1077                         *(s++) = 'm';
    1078                         *(s++) = 'e';
    1079                         *(s++) = ' ';
    1080                         s -= 4;
    1081                         s[strlen(s)-1] = 0;
    1082                 }
    1083                 else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
    1084                 {
    1085                         u = user_find( irc, irc->mynick );
    1086                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
    1087                         return( 1 );
    1088                 }
    1089                 else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
    1090                 {
    1091                         u = user_find( irc, irc->mynick );
    1092                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
    1093                         return( 1 );
    1094                 }
    1095                 else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
    1096                 {
    1097                         if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
    1098                         {
    1099                                 time_t current_typing_notice = time( NULL );
    1100                                
    1101                                 if( current_typing_notice - u->last_typing_notice >= 5 )
    1102                                 {
    1103                                         u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
    1104                                         u->last_typing_notice = current_typing_notice;
    1105                                 }
    1106                         }
    1107                         return( 1 );
    1108                 }
    1109                 else
    1110                 {
    1111                         irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
    1112                         return( 0 );
    1113                 }
    1114         }
    1115        
    1116         if( u )
    1117         {
    1118                 /* For the next message, we probably do have to send new notices... */
    1119                 u->last_typing_notice = 0;
    1120                 u->is_private = irc->is_private;
    1121                
    1122                 if( u->is_private )
    1123                 {
    1124                         if( !u->online )
    1125                                 irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
    1126                         else if( u->away )
    1127                                 irc_reply( irc, 301, "%s :%s", u->nick, u->away );
    1128                 }
    1129                
    1130                 if( u->send_handler )
    1131                 {
    1132                         u->send_handler( irc, u, s, flags );
    1133                         return 1;
    1134                 }
    1135         }
    1136         else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
    1137         {
    1138                 return( imc_chat_msg( c, s, 0 ) );
    1139         }
    1140        
    1141         return( 0 );
    1142 }
    1143 
    1144 static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
    1145 {
    1146         user_t *u = data;
    1147        
    1148         /* Shouldn't happen, but just to be sure. */
    1149         if( u->sendbuf_len < 2 )
    1150                 return FALSE;
    1151        
    1152         u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
    1153         imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
    1154        
    1155         g_free( u->sendbuf );
    1156         u->sendbuf = NULL;
    1157         u->sendbuf_len = 0;
    1158         u->sendbuf_timer = 0;
    1159         u->sendbuf_flags = 0;
    1160        
    1161         return FALSE;
    1162 }
    1163 
    1164 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
    1165 {
    1166         if( !u || !u->ic ) return;
    1167        
    1168         if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
    1169         {
    1170                 int delay;
    1171                
    1172                 if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
    1173                 {
    1174                         /* Flush the buffer */
    1175                         b_event_remove( u->sendbuf_timer );
    1176                         buddy_send_handler_delayed( u, -1, 0 );
    1177                 }
    1178 
    1179                 if( u->sendbuf_len == 0 )
    1180                 {
    1181                         u->sendbuf_len = strlen( msg ) + 2;
    1182                         u->sendbuf = g_new( char, u->sendbuf_len );
    1183                         u->sendbuf[0] = 0;
    1184                         u->sendbuf_flags = flags;
    1185                 }
    1186                 else
    1187                 {
    1188                         u->sendbuf_len += strlen( msg ) + 1;
    1189                         u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
    1190                 }
    1191                
    1192                 strcat( u->sendbuf, msg );
    1193                 strcat( u->sendbuf, "\n" );
    1194                
    1195                 delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
    1196                 if( delay <= 5 )
    1197                         delay *= 1000;
    1198                
    1199                 if( u->sendbuf_timer > 0 )
    1200                         b_event_remove( u->sendbuf_timer );
    1201                 u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
    1202         }
    1203         else
    1204         {
    1205                 imc_buddy_msg( u->ic, u->handle, msg, flags );
    1206         }
    1207 }
    1208 
    1209 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
    1210 {
    1211         char last = 0;
    1212         char *s = msg, *line = msg;
    1213        
    1214         /* The almighty linesplitter .. woohoo!! */
    1215         while( !last )
    1216         {
    1217                 if( *s == '\r' && *(s+1) == '\n' )
    1218                         *(s++) = 0;
    1219                 if( *s == '\n' )
    1220                 {
    1221                         last = s[1] == 0;
    1222                         *s = 0;
    1223                 }
    1224                 else
    1225                 {
    1226                         last = s[0] == 0;
    1227                 }
    1228                 if( *s == 0 )
    1229                 {
    1230                         if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
    1231                         {
    1232                                 irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
    1233                                            type, to, line + 4 );
    1234                         }
    1235                         else
    1236                         {
    1237                                 irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
    1238                                            type, to, prefix ? prefix : "", line );
    1239                         }
    1240                         line = s + 1;
    1241                 }
    1242                 s ++;
    1243         }
    1244        
    1245         return( 1 );
    1246 }
    1247 
    1248 int irc_msgfrom( irc_t *irc, char *nick, char *msg )
    1249 {
    1250         user_t *u = user_find( irc, nick );
    1251         static char *prefix = NULL;
    1252        
    1253         if( !u ) return( 0 );
    1254         if( prefix && *prefix ) g_free( prefix );
    1255        
    1256         if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
    1257         {
    1258                 int len = strlen( irc->nick) + 3;
    1259                 prefix = g_new (char, len );
    1260                 g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
    1261                 prefix[len-1] = 0;
    1262         }
    1263         else
    1264         {
    1265                 prefix = "";
    1266         }
    1267        
    1268         return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
    1269 }
    1270 
    1271 int irc_noticefrom( irc_t *irc, char *nick, char *msg )
    1272 {
    1273         user_t *u = user_find( irc, nick );
    1274        
    1275         if( u )
    1276                 return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
    1277         else
    1278                 return( 0 );
     815                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
     816                           irc->user->user, irc->user->host, irc->user->nick,
     817                           changes );
    1279818}
    1280819
     
    1315854}
    1316855
    1317 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
    1318 {
    1319         struct groupchat *c;
    1320         account_t *a;
    1321        
    1322         /* This finds the connection which has a conversation which belongs to this channel */
    1323         for( a = irc->accounts; a; a = a->next )
    1324         {
    1325                 if( a->ic == NULL )
    1326                         continue;
    1327                
    1328                 c = a->ic->groupchats;
    1329                 while( c )
    1330                 {
    1331                         if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
    1332                                 return c;
    1333                        
    1334                         c = c->next;
    1335                 }
    1336         }
    1337        
    1338         return NULL;
    1339 }
     856static char *set_eval_charset( set_t *set, char *value )
     857{
     858        irc_t *irc = (irc_t*) set->data;
     859        char *test;
     860        gsize test_bytes = 0;
     861        GIConv ic, oc;
     862
     863        if( g_strcasecmp( value, "none" ) == 0 )
     864                value = g_strdup( "utf-8" );
     865
     866        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
     867        {
     868                return NULL;
     869        }
     870       
     871        /* Do a test iconv to see if the user picked an IRC-compatible
     872           charset (for example utf-16 goes *horribly* wrong). */
     873        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
     874            test_bytes > 1 )
     875        {
     876                g_free( test );
     877                g_iconv_close( oc );
     878                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
     879                                  "only supports 8-bit character sets." );
     880                return NULL;
     881        }
     882        g_free( test );
     883       
     884        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
     885        {
     886                g_iconv_close( oc );
     887                return NULL;
     888        }
     889       
     890        if( irc->iconv != (GIConv) -1 )
     891                g_iconv_close( irc->iconv );
     892        if( irc->oconv != (GIConv) -1 )
     893                g_iconv_close( irc->oconv );
     894       
     895        irc->iconv = ic;
     896        irc->oconv = oc;
     897
     898        return value;
     899}
     900
     901/* Mostly meant for upgrades. If one of these is set to the non-default,
     902   set show_users of all channels to something with the same effect. */
     903static char *set_eval_bw_compat( set_t *set, char *value )
     904{
     905        irc_t *irc = set->data;
     906        char *val;
     907        GSList *l;
     908       
     909        irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' "
     910                     "channel setting instead.", set->key );
     911       
     912        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
     913                val = "online,away";
     914        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
     915                val = "online@,away+,offline";
     916        else
     917                return SET_INVALID;
     918       
     919        for( l = irc->channels; l; l = l->next )
     920        {
     921                irc_channel_t *ic = l->data;
     922                /* No need to check channel type, if the setting doesn't exist it
     923                   will just be ignored. */
     924                set_setstr( &ic->set, "show_users", val );
     925        }
     926       
     927        return SET_INVALID;
     928}
  • irc.h

    ree6cc94 r2528cda  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2004 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
    7 /* The big hairy IRCd part of the project                               */
     7/* The IRC-based UI (for now the only one)                              */
    88
    99/*
     
    3333#define IRC_PING_STRING "PinglBee"
    3434
    35 #define UMODES "abisw"
    36 #define UMODES_PRIV "Ro"
    37 #define CMODES "nt"
    38 #define CMODE "t"
    39 #define UMODE "s"
    40 #define CTYPES "&#"
     35#define UMODES "abisw"     /* Allowed umodes (although they mostly do nothing) */
     36#define UMODES_PRIV "Ro"   /* Allowed, but not by user directly */
     37#define UMODES_KEEP "R"    /* Don't allow unsetting using /MODE */
     38#define CMODES "ntC"       /* Allowed modes */
     39#define CMODE "t"          /* Default mode */
     40#define UMODE "s"          /* Default mode */
     41
     42#define CTYPES "&#"        /* Valid channel name prefixes */
    4143
    4244typedef enum
    4345{
    4446        USTATUS_OFFLINE = 0,
    45         USTATUS_AUTHORIZED = 1,
    46         USTATUS_LOGGED_IN = 2,
    47         USTATUS_IDENTIFIED = 4,
    48         USTATUS_SHUTDOWN = 8
     47        USTATUS_AUTHORIZED = 1, /* Gave the correct server password (PASS). */
     48        USTATUS_LOGGED_IN = 2,  /* USER+NICK(+PASS) finished. */
     49        USTATUS_IDENTIFIED = 4, /* To NickServ (root). */
     50        USTATUS_SHUTDOWN = 8,   /* Now used to indicate we're shutting down.
     51                                   Currently just blocks irc_vawrite(). */
    4952} irc_status_t;
     53
     54struct irc_user;
    5055
    5156typedef struct irc
     
    5964        GIConv iconv, oconv;
    6065
    61         int sentbytes;
    62         time_t oldtime;
    63 
     66        struct irc_user *root;
     67        struct irc_user *user;
     68       
     69        char *password; /* HACK: Used to save the user's password, but before
     70                           logging in, this may contain a password we should
     71                           send to identify after USER/NICK are received. */
     72
     73        char umode[8];
     74       
     75        struct query *queries;
     76        GSList *file_transfers;
     77       
     78        GSList *users, *channels;
     79        struct irc_channel *default_channel;
     80        GHashTable *nick_user_hash;
     81        GHashTable *watches; /* See irc_cmd_watch() */
     82
     83        gint r_watch_source_id;
     84        gint w_watch_source_id;
     85        gint ping_source_id;
     86        gint login_source_id; /* To slightly delay some events at login time. */
     87       
     88        struct bee *b;
     89} irc_t;
     90
     91typedef enum
     92{
     93        /* Replaced with iu->last_channel IRC_USER_PRIVATE = 1, */
     94        IRC_USER_AWAY = 2,
     95} irc_user_flags_t;
     96
     97typedef struct irc_user
     98{
     99        irc_t *irc;
     100       
    64101        char *nick;
    65102        char *user;
    66103        char *host;
    67         char *realname;
    68         char *password; /* HACK: Used to save the user's password, but before
    69                            logging in, this may contain a password we should
    70                            send to identify after USER/NICK are received. */
    71 
    72         char umode[8];
    73        
    74         char *myhost;
    75         char *mynick;
    76 
    77         char *channel;
    78         int c_id;
    79 
    80         char is_private;                /* Not too nice... */
    81         char *last_target;
    82        
    83         struct query *queries;
    84         struct account *accounts;
    85         struct chat *chatrooms;
    86        
    87         struct __USER *users;
    88         GHashTable *userhash;
    89         GHashTable *watches;
    90         struct __NICK *nicks;
     104        char *fullname;
     105       
     106        /* Nickname in lowercase for case insensitive searches */
     107        char *key;
     108       
     109        irc_user_flags_t flags;
     110        struct irc_channel *last_channel;
     111       
     112        GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
     113        guint pastebuf_timer;
     114        time_t away_reply_timeout; /* Only send a 301 if this time passed. */
     115       
     116        struct bee_user *bu;
     117       
     118        const struct irc_user_funcs *f;
     119} irc_user_t;
     120
     121struct irc_user_funcs
     122{
     123        gboolean (*privmsg)( irc_user_t *iu, const char *msg );
     124        gboolean (*ctcp)( irc_user_t *iu, char * const* ctcp );
     125};
     126
     127extern const struct irc_user_funcs irc_user_root_funcs;
     128extern const struct irc_user_funcs irc_user_self_funcs;
     129
     130typedef enum
     131{
     132        IRC_CHANNEL_JOINED = 1, /* The user is currently in the channel. */
     133        IRC_CHANNEL_TEMP = 2,   /* Erase the channel when the user leaves,
     134                                   and don't save it. */
     135       
     136        /* Hack: Set this flag right before jumping into IM when we expect
     137           a call to imcb_chat_new(). */
     138        IRC_CHANNEL_CHAT_PICKME = 0x10000,
     139} irc_channel_flags_t;
     140
     141typedef struct irc_channel
     142{
     143        irc_t *irc;
     144        char *name;
     145        char mode[8];
     146        int flags;
     147       
     148        char *topic;
     149        char *topic_who;
     150        time_t topic_time;
     151       
     152        GSList *users; /* struct irc_channel_user */
     153        struct irc_user *last_target;
    91154        struct set *set;
    92 
    93         gint r_watch_source_id;
    94         gint w_watch_source_id;
    95         gint ping_source_id;
    96 } irc_t;
    97 
    98 #include "user.h"
    99 
     155       
     156        GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
     157        guint pastebuf_timer;
     158       
     159        const struct irc_channel_funcs *f;
     160        void *data;
     161} irc_channel_t;
     162
     163struct irc_channel_funcs
     164{
     165        gboolean (*privmsg)( irc_channel_t *ic, const char *msg );
     166        gboolean (*join)( irc_channel_t *ic );
     167        gboolean (*part)( irc_channel_t *ic, const char *msg );
     168        gboolean (*topic)( irc_channel_t *ic, const char *new );
     169        gboolean (*invite)( irc_channel_t *ic, irc_user_t *iu );
     170       
     171        gboolean (*_init)( irc_channel_t *ic );
     172        gboolean (*_free)( irc_channel_t *ic );
     173};
     174
     175typedef enum
     176{
     177        IRC_CHANNEL_USER_OP = 1,
     178        IRC_CHANNEL_USER_HALFOP = 2,
     179        IRC_CHANNEL_USER_VOICE = 4,
     180        IRC_CHANNEL_USER_NONE = 8,
     181} irc_channel_user_flags_t;
     182
     183typedef struct irc_channel_user
     184{
     185        irc_user_t *iu;
     186        int flags;
     187} irc_channel_user_t;
     188
     189typedef enum
     190{
     191        IRC_CC_TYPE_DEFAULT,
     192        IRC_CC_TYPE_REST,
     193        IRC_CC_TYPE_GROUP,
     194        IRC_CC_TYPE_ACCOUNT,
     195        IRC_CC_TYPE_PROTOCOL,
     196} irc_control_channel_type_t;
     197
     198struct irc_control_channel
     199{
     200        irc_control_channel_type_t type;
     201        struct bee_group *group;
     202        struct account *account;
     203        struct prpl *protocol;
     204        char modes[4];
     205};
     206
     207extern const struct bee_ui_funcs irc_ui_funcs;
     208
     209typedef enum
     210{
     211        IRC_CDU_SILENT,
     212        IRC_CDU_PART,
     213        IRC_CDU_KICK,
     214} irc_channel_del_user_type_t;
     215
     216/* irc.c */
    100217extern GSList *irc_connection_list;
    101218
     
    103220void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
    104221void irc_free( irc_t *irc );
    105 
    106 void irc_exec( irc_t *irc, char **cmd );
     222void irc_setpass (irc_t *irc, const char *pass);
     223
    107224void irc_process( irc_t *irc );
    108225char **irc_parse_line( char *line );
    109226char *irc_build_line( char **cmd );
    110227
    111 void irc_vawrite( irc_t *irc, char *format, va_list params );
    112228void irc_write( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
    113229void irc_write_all( int now, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
    114 void irc_reply( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
    115 G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
    116 char **irc_tokenize( char *buffer );
    117 
    118 void irc_login( irc_t *irc );
     230void irc_vawrite( irc_t *irc, char *format, va_list params );
     231
     232void irc_flush( irc_t *irc );
     233void irc_switch_fd( irc_t *irc, int fd );
     234void irc_sync( irc_t *irc );
     235void irc_desync( irc_t *irc );
     236
    119237int irc_check_login( irc_t *irc );
    120 void irc_motd( irc_t *irc );
    121 void irc_names( irc_t *irc, char *channel );
    122 void irc_topic( irc_t *irc, char *channel );
    123 void irc_umode_set( irc_t *irc, char *s, int allow_priv );
    124 void irc_who( irc_t *irc, char *channel );
    125 void irc_spawn( irc_t *irc, user_t *u );
    126 void irc_join( irc_t *irc, user_t *u, char *channel );
    127 void irc_part( irc_t *irc, user_t *u, char *channel );
    128 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker );
    129 void irc_kill( irc_t *irc, user_t *u );
    130 void irc_invite( irc_t *irc, char *nick, char *channel );
    131 void irc_whois( irc_t *irc, char *nick );
    132 void irc_setpass( irc_t *irc, const char *pass ); /* USE WITH CAUTION! */
    133 
    134 int irc_send( irc_t *irc, char *nick, char *s, int flags );
    135 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg );
    136 int irc_msgfrom( irc_t *irc, char *nick, char *msg );
    137 int irc_noticefrom( irc_t *irc, char *nick, char *msg );
    138 
    139 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags );
    140 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel );
     238
     239void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv );
     240
     241/* irc_channel.c */
     242irc_channel_t *irc_channel_new( irc_t *irc, const char *name );
     243irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name );
     244irc_channel_t *irc_channel_get( irc_t *irc, char *id );
     245int irc_channel_free( irc_channel_t *ic );
     246void irc_channel_free_soon( irc_channel_t *ic );
     247int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu );
     248int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg );
     249irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu );
     250int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who );
     251void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags );
     252void irc_channel_set_mode( irc_channel_t *ic, const char *s );
     253void irc_channel_auto_joins( irc_t *irc, struct account *acc );
     254void irc_channel_printf( irc_channel_t *ic, char *format, ... );
     255gboolean irc_channel_name_ok( const char *name );
     256void irc_channel_name_strip( char *name );
     257int irc_channel_name_cmp( const char *a_, const char *b_ );
     258void irc_channel_update_ops( irc_channel_t *ic, char *value );
     259char *set_eval_irc_channel_ops( struct set *set, char *value );
     260
     261/* irc_commands.c */
     262void irc_exec( irc_t *irc, char **cmd );
     263
     264/* irc_send.c */
     265void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
     266void irc_send_login( irc_t *irc );
     267void irc_send_motd( irc_t *irc );
     268void irc_usermsg( irc_t *irc, char *format, ... );
     269void irc_send_join( irc_channel_t *ic, irc_user_t *iu );
     270void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason );
     271void irc_send_quit( irc_user_t *iu, const char *reason );
     272void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason );
     273void irc_send_names( irc_channel_t *ic );
     274void irc_send_topic( irc_channel_t *ic, gboolean topic_change );
     275void irc_send_whois( irc_user_t *iu );
     276void irc_send_who( irc_t *irc, GSList *l, const char *channel );
     277void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix );
     278void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg );
     279void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... ) G_GNUC_PRINTF( 4, 5 );
     280void irc_send_nick( irc_user_t *iu, const char *new );
     281void irc_send_channel_user_mode_diff( irc_channel_t *ic, irc_user_t *iu,
     282                                      irc_channel_user_flags_t old, irc_channel_user_flags_t new );
     283
     284/* irc_user.c */
     285irc_user_t *irc_user_new( irc_t *irc, const char *nick );
     286int irc_user_free( irc_t *irc, irc_user_t *iu );
     287irc_user_t *irc_user_by_name( irc_t *irc, const char *nick );
     288int irc_user_set_nick( irc_user_t *iu, const char *new );
     289gint irc_user_cmp( gconstpointer a_, gconstpointer b_ );
     290const char *irc_user_get_away( irc_user_t *iu );
     291void irc_user_quit( irc_user_t *iu, const char *msg );
     292
     293/* irc_util.c */
     294char *set_eval_timezone( struct set *set, char *value );
     295char *irc_format_timestamp( irc_t *irc, time_t msg_ts );
     296
     297/* irc_im.c */
     298void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu );
    141299
    142300#endif
  • irc_commands.c

    ree6cc94 r2528cda  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2006 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    2727#include "bitlbee.h"
    2828#include "ipc.h"
    29 #include "chat.h"
    3029
    3130static void irc_cmd_pass( irc_t *irc, char **cmd )
     
    3938                   out we don't require it, which will break this feature.)
    4039                   Try to identify using the given password. */
    41                 return root_command( irc, send_cmd );
     40                root_command( irc, send_cmd );
     41                return;
    4242        }
    4343        /* Handling in pre-logged-in state, first see if this server is
     
    5353        else if( global.conf->auth_pass )
    5454        {
    55                 irc_reply( irc, 464, ":Incorrect password" );
     55                irc_send_num( irc, 464, ":Incorrect password" );
    5656        }
    5757        else
     
    6565static void irc_cmd_user( irc_t *irc, char **cmd )
    6666{
    67         irc->user = g_strdup( cmd[1] );
    68         irc->realname = g_strdup( cmd[4] );
     67        irc->user->user = g_strdup( cmd[1] );
     68        irc->user->fullname = g_strdup( cmd[4] );
    6969       
    7070        irc_check_login( irc );
     
    7373static void irc_cmd_nick( irc_t *irc, char **cmd )
    7474{
    75         if( irc->nick )
    76         {
    77                 irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" );
    78         }
    79         /* This is not clean, but for now it'll have to be like this... */
    80         else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) )
    81         {
    82                 irc_reply( irc, 433, ":This nick is already in use" );
     75        irc_user_t *iu;
     76       
     77        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user )
     78        {
     79                irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] );
    8380        }
    8481        else if( !nick_ok( cmd[1] ) )
    8582        {
    8683                /* [SH] Invalid characters. */
    87                 irc_reply( irc, 432, ":This nick contains invalid characters" );
    88         }
    89         else
    90         {
    91                 irc->nick = g_strdup( cmd[1] );
     84                irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] );
     85        }
     86        else if( irc->status & USTATUS_LOGGED_IN )
     87        {
     88                if( irc->status & USTATUS_IDENTIFIED )
     89                {
     90                        irc_setpass( irc, NULL );
     91                        irc->status &= ~USTATUS_IDENTIFIED;
     92                        irc_umode_set( irc, "-R", 1 );
     93                        irc_usermsg( irc, "Changing nicks resets your identify status. "
     94                                     "Re-identify or register a new account if you want "
     95                                     "your configuration to be saved. See \x02help "
     96                                     "nick_changes\x02." );
     97                }
     98               
     99                irc_user_set_nick( irc->user, cmd[1] );
     100        }
     101        else
     102        {
     103                g_free( irc->user->nick );
     104                irc->user->nick = g_strdup( cmd[1] );
    92105               
    93106                irc_check_login( irc );
     
    105118static void irc_cmd_ping( irc_t *irc, char **cmd )
    106119{
    107         irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost );
    108 }
     120        irc_write( irc, ":%s PONG %s :%s", irc->root->host,
     121                   irc->root->host, cmd[1]?cmd[1]:irc->root->host );
     122}
     123
     124static void irc_cmd_pong( irc_t *irc, char **cmd )
     125{
     126        /* We could check the value we get back from the user, but in
     127           fact we don't care, we're just happy s/he's still alive. */
     128        irc->last_pong = gettime();
     129        irc->pinging = 0;
     130}
     131
     132static void irc_cmd_join( irc_t *irc, char **cmd )
     133{
     134        char *comma, *s = cmd[1];
     135       
     136        while( s )
     137        {
     138                irc_channel_t *ic;
     139               
     140                if( ( comma = strchr( s, ',' ) ) )
     141                        *comma = '\0';
     142               
     143                if( ( ic = irc_channel_by_name( irc, s ) ) == NULL &&
     144                    ( ic = irc_channel_new( irc, s ) ) )
     145                {
     146                        if( strcmp( set_getstr( &ic->set, "type" ), "control" ) != 0 )
     147                        {
     148                                /* Autoconfiguration is for control channels only ATM. */
     149                        }
     150                        else if( bee_group_by_name( ic->irc->b, ic->name + 1, FALSE ) )
     151                        {
     152                                set_setstr( &ic->set, "group", ic->name + 1 );
     153                                set_setstr( &ic->set, "fill_by", "group" );
     154                        }
     155                        else if( set_setstr( &ic->set, "protocol", ic->name + 1 ) )
     156                        {
     157                                set_setstr( &ic->set, "fill_by", "protocol" );
     158                        }
     159                        else if( set_setstr( &ic->set, "account", ic->name + 1 ) )
     160                        {
     161                                set_setstr( &ic->set, "fill_by", "account" );
     162                        }
     163                        else
     164                        {
     165                                /* The set commands above will run this already,
     166                                   but if we didn't hit any, we have to fill the
     167                                   channel with the default population. */
     168                                bee_irc_channel_update( ic->irc, ic, NULL );
     169                        }
     170                }
     171                else if( ic == NULL )
     172                {
     173                        irc_send_num( irc, 479, "%s :Invalid channel name", s );
     174                        goto next;
     175                }
     176               
     177                if( ic->flags & IRC_CHANNEL_JOINED )
     178                        /* Dude, you're already there...
     179                           RFC doesn't have any reply for that though? */
     180                        goto next;
     181               
     182                if( ic->f->join && !ic->f->join( ic ) )
     183                        /* The story is: FALSE either means the handler
     184                           showed an error message, or is doing some work
     185                           before the join should be confirmed. (In the
     186                           latter case, the caller should take care of that
     187                           confirmation.) TRUE means all's good, let the
     188                           user join the channel right away. */
     189                        goto next;
     190               
     191                irc_channel_add_user( ic, irc->user );
     192               
     193next:
     194                if( comma )
     195                {
     196                        s = comma + 1;
     197                        *comma = ',';
     198                }
     199                else
     200                        break;
     201        }
     202}
     203
     204static void irc_cmd_names( irc_t *irc, char **cmd )
     205{
     206        irc_channel_t *ic;
     207       
     208        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
     209                irc_send_names( ic );
     210        /* With no args, we should show /names of all chans. Make the code
     211           below work well if necessary.
     212        else
     213        {
     214                GSList *l;
     215               
     216                for( l = irc->channels; l; l = l->next )
     217                        irc_send_names( l->data );
     218        }
     219        */
     220}
     221
     222static void irc_cmd_part( irc_t *irc, char **cmd )
     223{
     224        irc_channel_t *ic;
     225       
     226        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
     227        {
     228                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
     229        }
     230        else if( irc_channel_del_user( ic, irc->user, IRC_CDU_PART, cmd[2] ) )
     231        {
     232                if( ic->f->part )
     233                        ic->f->part( ic, NULL );
     234        }
     235        else
     236        {
     237                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
     238        }
     239}
     240
     241static void irc_cmd_whois( irc_t *irc, char **cmd )
     242{
     243        char *nick = cmd[1];
     244        irc_user_t *iu = irc_user_by_name( irc, nick );
     245       
     246        if( iu )
     247                irc_send_whois( iu );
     248        else
     249                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
     250}
     251
     252static void irc_cmd_whowas( irc_t *irc, char **cmd )
     253{
     254        /* For some reason irssi tries a whowas when whois fails. We can
     255           ignore this, but then the user never gets a "user not found"
     256           message from irssi which is a bit annoying. So just respond
     257           with not-found and irssi users will get better error messages */
     258       
     259        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
     260        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
     261}
     262
     263static void irc_cmd_motd( irc_t *irc, char **cmd )
     264{
     265        irc_send_motd( irc );
     266}
     267
     268static void irc_cmd_mode( irc_t *irc, char **cmd )
     269{
     270        if( irc_channel_name_ok( cmd[1] ) )
     271        {
     272                irc_channel_t *ic;
     273               
     274                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
     275                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
     276                else if( cmd[2] )
     277                {
     278                        if( *cmd[2] == '+' || *cmd[2] == '-' )
     279                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
     280                        else if( *cmd[2] == 'b' )
     281                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
     282                }
     283                else
     284                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
     285        }
     286        else
     287        {
     288                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
     289                {
     290                        if( cmd[2] )
     291                                irc_umode_set( irc, cmd[2], 0 );
     292                        else
     293                                irc_send_num( irc, 221, "+%s", irc->umode );
     294                }
     295                else
     296                        irc_send_num( irc, 502, ":Don't touch their modes" );
     297        }
     298}
     299
     300static void irc_cmd_who( irc_t *irc, char **cmd )
     301{
     302        char *channel = cmd[1];
     303        irc_channel_t *ic;
     304       
     305        if( !channel || *channel == '0' || *channel == '*' || !*channel )
     306                irc_send_who( irc, irc->users, "**" );
     307        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
     308                irc_send_who( irc, ic->users, channel );
     309        else
     310                irc_send_num( irc, 403, "%s :No such channel", channel );
     311}
     312
     313static void irc_cmd_privmsg( irc_t *irc, char **cmd )
     314{
     315        irc_channel_t *ic;
     316        irc_user_t *iu;
     317       
     318        if( !cmd[2] )
     319        {
     320                irc_send_num( irc, 412, ":No text to send" );
     321                return;
     322        }
     323       
     324        /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
     325        if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 )
     326        {
     327                cmd[2] += 4;
     328                memcpy( cmd[2], "/me", 3 );
     329                if( cmd[2][strlen(cmd[2])-1] == '\001' )
     330                        cmd[2][strlen(cmd[2])-1] = '\0';
     331        }
     332       
     333        if( irc_channel_name_ok( cmd[1] ) &&
     334            ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
     335        {
     336                if( cmd[2][0] == '\001' )
     337                {
     338                        /* CTCPs to channels? Nah. Maybe later. */
     339                }
     340                else if( ic->f->privmsg )
     341                        ic->f->privmsg( ic, cmd[2] );
     342        }
     343        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
     344        {
     345                if( cmd[2][0] == '\001' )
     346                {
     347                        char **ctcp;
     348                       
     349                        if( iu->f->ctcp == NULL )
     350                                return;
     351                        if( cmd[2][strlen(cmd[2])-1] == '\001' )
     352                                cmd[2][strlen(cmd[2])-1] = '\0';
     353                       
     354                        ctcp = split_command_parts( cmd[2] + 1 );
     355                        iu->f->ctcp( iu, ctcp );
     356                }
     357                else if( iu->f->privmsg )
     358                {
     359                        iu->last_channel = NULL;
     360                        iu->f->privmsg( iu, cmd[2] );
     361                }
     362        }
     363        else
     364        {
     365                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
     366        }
     367}
     368
     369static void irc_cmd_notice( irc_t *irc, char **cmd )
     370{
     371        if( !cmd[2] )
     372        {
     373                irc_send_num( irc, 412, ":No text to send" );
     374                return;
     375        }
     376       
     377        /* At least for now just echo. IIRC some IRC clients use self-notices
     378           for lag checks, so try to support that. */
     379        if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
     380                irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL );
     381}
     382
     383static void irc