Changeset 2945c6f


Ignore:
Timestamp:
2010-07-24T21:16:18Z (14 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
f1f7b5e
Parents:
ef14a83 (diff), 593971d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge ui-fix (which includes killerbee (i.e. file transfers and libpurple
support)). ui-fix rewrites the complete IRC core, fixing many things that
were broken/hacky/limited so far.

The list is too long to include here, but http://wiki.bitlbee.org/UiFix
has a summary, as does doc/CHANGES and of course the full revision history.

Files:
26 added
8 deleted
87 edited
7 moved

Legend:

Unmodified
Added
Removed
  • Makefile

    ref14a83 r2945c6f  
    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 = account.h 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 user.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/ft.h protocols/nogaim.h
    1414subdirs = lib protocols
    1515
     
    8282install-dev:
    8383        mkdir -p $(DESTDIR)$(INCLUDEDIR)
    84         install -m 0644 $(headers) $(DESTDIR)$(INCLUDEDIR)
     84        install -m 0644 config.h $(DESTDIR)$(INCLUDEDIR)
     85        for i in $(headers); do install -m 0644 $(SRCDIR)$$i $(DESTDIR)$(INCLUDEDIR); done
    8586        mkdir -p $(DESTDIR)$(PCDIR)
    8687        install -m 0644 bitlbee.pc $(DESTDIR)$(PCDIR)
     
    9394install-etc:
    9495        mkdir -p $(DESTDIR)$(ETCDIR)
    95         install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
    96         install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
     96        install -m 0644 $(SRCDIR)motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
     97        install -m 0644 $(SRCDIR)bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
    9798
    9899uninstall-etc:
     
    110111        @$(MAKE) -C $@ $(MAKECMDGOALS)
    111112
    112 $(objects): %.o: %.c
     113$(objects): %.o: $(SRCDIR)%.c
    113114        @echo '*' Compiling $<
    114115        @$(CC) -c $(CFLAGS) $< -o $@
  • bitlbee.c

    ref14a83 r2945c6f  
    121121        }
    122122       
    123         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 );
    124124       
    125125#ifndef _WIN32
     
    318318                        struct bitlbee_child *child;
    319319                       
     320                        /* TODO: Stuff like this belongs in ipc.c. */
    320321                        child = g_new0( struct bitlbee_child, 1 );
    321322                        child->pid = client_pid;
    322323                        child->ipc_fd = fds[0];
    323                         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;
    324326                        child_list = g_slist_append( child_list, child );
    325327                       
     
    349351                        /* We can store the IPC fd there now. */
    350352                        global.listen_socket = fds[1];
    351                         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 );
    352354                       
    353355                        close( fds[0] );
     
    370372        /* Try to save data for all active connections (if desired). */
    371373        while( irc_connection_list != NULL )
    372                 irc_free( irc_connection_list->data );
     374                irc_abort( irc_connection_list->data, FALSE,
     375                           "BitlBee server shutting down" );
    373376       
    374377        /* We'll only reach this point when not running in inetd mode: */
  • bitlbee.h

    ref14a83 r2945c6f  
    4343
    4444#if HAVE_CONFIG_H
    45 #include "config.h"
     45#include <config.h>
    4646#endif
    4747
     
    126126#define CONF_FILE_DEF ETCDIR "bitlbee.conf"
    127127
     128#include "bee.h"
    128129#include "irc.h"
    129130#include "storage.h"
     
    160161gboolean bitlbee_io_current_client_write( gpointer data, gint source, b_input_condition cond );
    161162
    162 void root_command_string( irc_t *irc, user_t *u, char *command, int flags );
     163void root_command_string( irc_t *irc, char *command );
    163164void root_command( irc_t *irc, char *command[] );
     165gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond );
    164166gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
    165167
  • conf.c

    ref14a83 r2945c6f  
    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;
     
    315318                                conf->user = g_strdup( ini->value );
    316319                        }
     320                        else if( g_strcasecmp( ini->key, "ft_max_size" ) == 0 )
     321                        {
     322                                size_t ft_max_size;
     323                                if( sscanf( ini->value, "%zu", &ft_max_size ) != 1 )
     324                                {
     325                                        fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
     326                                        return 0;
     327                                }
     328                                conf->ft_max_size = ft_max_size;
     329                        }
     330                        else if( g_strcasecmp( ini->key, "ft_max_kbps" ) == 0 )
     331                        {
     332                                if( sscanf( ini->value, "%d", &i ) != 1 )
     333                                {
     334                                        fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value );
     335                                        return 0;
     336                                }
     337                                conf->ft_max_kbps = i;
     338                        }
     339                        else if( g_strcasecmp( ini->key, "ft_listen" ) == 0 )
     340                        {
     341                                g_free( conf->ft_listen );
     342                                conf->ft_listen = g_strdup( ini->value );
     343                        }
    317344                        else if( g_strcasecmp( ini->key, "protocols" ) == 0 )
    318345                        {
     
    349376                if( g_strcasecmp( ini->section, "defaults" ) == 0 )
    350377                {
    351                         set_t *s = set_find( &irc->set, ini->key );
     378                        set_t *s = set_find( &irc->b->set, ini->key );
    352379                       
    353380                        if( s )
  • conf.h

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    2727yahoo=1
    2828twitter=1
     29twitter=1
     30purple=0
    2931
    3032debug=0
     
    6769--oscar=0/1     Disable/enable Oscar part (ICQ, AIM)    $oscar
    6870--yahoo=0/1     Disable/enable Yahoo part               $yahoo
    69 --twitter=0/1 Disable/enable Twitter part               $twitter
     71--twitter=0/1   Disable/enable Twitter part             $twitter
     72
     73--purple=0/1    Disable/enable libpurple support        $purple
    7074
    7175--debug=0/1     Disable/enable debugging                $debug
     
    121125EOF
    122126
     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
    123150cat<<EOF>config.h
    124151/* BitlBee settings, generated by configure
     
    158185
    159186echo CFLAGS=$CFLAGS >> Makefile.settings
    160 echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings
     187echo CFLAGS+=-I${srcdir} -I${srcdir}/lib -I${srcdir}/protocols -I. >> Makefile.settings
    161188
    162189echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings
     
    398425fi
    399426
    400 STORAGES="text xml"
     427STORAGES="xml"
    401428
    402429if [ "$ldap" = "auto" ]; then
     
    508535protoobjs=''
    509536
     537if [ "$purple" = 0 ]; then
     538        echo '#undef WITH_PURPLE' >> config.h
     539else
     540        if ! $PKG_CONFIG purple; then
     541                echo
     542                echo 'Cannot find libpurple development libraries, aborting. (Install libpurple-dev?)'
     543                exit 1
     544        fi
     545        echo '#define WITH_PURPLE' >> config.h
     546        cat<<EOF>>Makefile.settings
     547EFLAGS += $($PKG_CONFIG purple --libs)
     548PURPLE_CFLAGS += $($PKG_CONFIG purple --cflags)
     549EOF
     550        protocols=$protocols'purple '
     551        protoobjs=$protoobjs'purple_mod.o '
     552
     553        # Having both libpurple and native IM modules in one binary may
     554        # do strange things. Let's not do that.
     555        msn=0
     556        jabber=0
     557        oscar=0
     558        yahoo=0
     559        twitter=0
     560       
     561        if [ "$events" = "libevent" ]; then
     562                echo
     563                echo 'Warning: Some libpurple modules (including msn-pecan) do their event handling'
     564                echo 'outside libpurple, talking to GLib directly. At least for now the combination'
     565                echo 'libpurple + libevent is *not* recommended!'
     566        fi
     567fi
     568
    510569if [ "$msn" = 0 ]; then
    511570        echo '#undef WITH_MSN' >> config.h
  • debian/bitlbee-common.config

    • Property mode changed from 100755 to 100644
  • debian/bitlbee.init

    • Property mode changed from 100755 to 100644
  • debian/bitlbee.postinst

    • Property mode changed from 100755 to 100644
  • debian/bitlbee.postrm

    • Property mode changed from 100755 to 100644
  • debian/bitlbee.prerm

    • Property mode changed from 100755 to 100644
  • debian/changelog

    ref14a83 r2945c6f  
     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
    110bitlbee (1.2.8-1) unstable; urgency=low
    211
  • debian/control

    ref14a83 r2945c6f  
    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
    1719 say to people on other chat networks: Jabber, ICQ, AIM, MSN, Yahoo! and
    1820 Twitter.
    1921
     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, ICQ, AIM, MSN, Yahoo! and
     30 Twitter.
     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, ICQ, AIM, MSN, Yahoo! and
     47 Twitter.
     48 .
     49 This package contains common files (mostly documentation) for bitlbee and
     50 bitlbee-libpurple.
     51
    2052Package: bitlbee-dev
    2153Architecture: all
    22 Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~)
    23 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)
    2456 This program can be used as an IRC server which forwards everything you
    2557 say to people on other chat networks: Jabber, ICQ, AIM, MSN, Yahoo! and
  • debian/patches/bitlbee.conf.diff

    ref14a83 r2945c6f  
    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

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

    ref14a83 r2945c6f  
    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/+[^+]*$$//')
     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 ifdef BITLBEE_FORCE_VERSION
    102         dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0
    103 else
    104         dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev
    105 endif
    106 
    107         dpkg --build debian/bitlbee-dev ..
    108 
    109 binary: binary-arch binary-indep
    110 build: build-arch
    111 install: install-arch install-indep
    112 
    113 .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

    ref14a83 r2945c6f  
    33
    44http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on
     5
     6Version 1.3dev:
     7- Loads of new stuff, mostly ready for a new major release, but starting with
     8  a dev snapshot. A few changes that were planned for a long time already:
     9- Rewrote the IRC core, which brings:
     10  * Support for multiple (control) channels, so you can have one channel per
     11    buddy group, per IM account/protocol, or for example a &offline with all
     12    offline contacts. See "help channels" for information on how to use this.
     13  * You can also leave and rejoin all channels. Being in &bitlbee is no
     14    longer required.
     15  * Now you can start groupchats by just joining a new channel and inviting
     16    contacts. (The "chat with" command still works as well.)
     17  * You can change your nickname, whenever you want.
     18  * Root commands:
     19    + To hopefully resolve confusion about the "account set" syntax, the
     20      ordering was changed slightly: "account set acc/setting value"
     21      becomes "account acc set setting value". Obviously the same order
     22      should now be used for other account subcommands.
     23    + Shortcuts: Instead of "account list" you can say "acc li".
     24  * /whois shows idle/login times of your contacts when available.
     25  * paste_buffer (previously known as buddy_sendbuffer) now works for chats
     26    as well.
     27  * The nick_source setting was replaced with a nick_format setting, which
     28    looks more or less like a format string, and lets you tweak how nicknames
     29    for contacts are generated in more detail.
     30  * A per-channel show_users setting lets you configure exactly which kinds
     31    of contacts (online/away/offline) should show up in a channel and what
     32    modes (none/voice/etc) they should have.
     33  * If you connect (and identify) to a BitlBee server you're already
     34    connected to, you can take over the existing session instead of starting
     35    a new one.
     36  * More correct validation of channel names: They can contain pretty much
     37    any character, unlike nicknames.
     38- Support for using libpurple instead of BitlBee's built-in IM protocol
     39  modules. This can be enabled by passing --purple=1 to the configure script.
     40  * This adds support for many more IM protocols to BitlBee.
     41  * And new functionality to existing protocols.
     42  * This is and will always be optional and using it on public servers is
     43    *not* recommended. It should be pretty stable, but costs more RAM/etc.
     44  * When using this variant, type "help purple" to get a list of supported
     45    protocols.
     46- Support for file transfers, in and out. /DCC SEND a file to a contact and
     47  it becomes a file transfer, and incoming file transfers become /DCC SENDs
     48  to you. Note that this is mostly useful when combined with libpurple, as
     49  only the Jabber native protocol module currently supports file transfers.
     50
     51Finished ... 2010
    552
    653Version 1.2.8:
  • doc/Makefile

    ref14a83 r2945c6f  
    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/user-guide/Makefile

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    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>
     
    9999
    100100                <bitlbee-command name="del">
    101                         <syntax>account del &lt;account id&gt;</syntax>
     101                        <syntax>account &lt;account id&gt; del</syntax>
    102102
    103103                        <description>
     
    108108
    109109                                <para>
    110                                         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.
    111111                                </para>
    112112                        </description>
     
    114114
    115115                <bitlbee-command name="on">
    116                         <syntax>account on [&lt;account id&gt;]</syntax>
     116                        <syntax>account [&lt;account id&gt;] on</syntax>
    117117
    118118                        <description>
     
    122122
    123123                                <para>
    124                                         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.
    125125                                </para>
    126126                        </description>
     
    129129
    130130                <bitlbee-command name="off">
    131                         <syntax>account off [&lt;account id&gt;]</syntax>
     131                        <syntax>account [&lt;account id&gt;] off</syntax>
    132132
    133133                        <description>
     
    137137
    138138                                <para>
    139                                         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.
    140140                                </para>
    141141                        </description>
     
    153153
    154154                <bitlbee-command name="set">
    155                         <syntax>account set &lt;account id&gt;</syntax>
    156                         <syntax>account set &lt;account id&gt;/&lt;setting&gt;</syntax>
    157                         <syntax>account set &lt;account id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
    158                         <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>
    159159
    160160                        <description>
    161161                                <para>
    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 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>.
    163163                                </para>
    164164                               
     
    168168                               
    169169                                <para>
    170                                         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.
    171171                                </para>
    172172                        </description>
    173173                </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
    174233        </bitlbee-command>
    175234
     
    181240
    182241                        <para>
    183                                 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.
    184243                        </para>
    185244
     
    187246
    188247                <bitlbee-command name="add">
    189                         <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>
    190249
    191250                        <description>
     
    205264                </bitlbee-command>
    206265
    207                 <bitlbee-command name="del">
    208                         <syntax>chat del &lt;chat id&gt;</syntax>
     266                <bitlbee-command name="with">
     267                        <syntax>chat with &lt;nickname&gt;</syntax>
    209268
    210269                        <description>
    211270                                <para>
    212                                         This commands deletes an chatroom from your list.
    213                                 </para>
    214 
    215                                 <para>
    216                                         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.
    217272                                </para>
    218273                        </description>
    219274                </bitlbee-command>
    220 
    221                 <bitlbee-command name="list">
    222                         <syntax>chat list</syntax>
    223 
    224                         <description>
    225                                 <para>
    226                                         This command gives you a list of all the chatrooms known by BitlBee.
    227                                 </para>
    228                         </description>
    229                 </bitlbee-command>
    230 
    231                 <bitlbee-command name="with">
    232                         <syntax>chat with &lt;nickname&gt;</syntax>
    233 
    234                         <description>
    235                                 <para>
    236                                         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.
    237                                 </para>
    238                         </description>
    239                 </bitlbee-command>
    240 
    241                 <bitlbee-command name="set">
    242                         <syntax>chat set &lt;chat id&gt;</syntax>
    243                         <syntax>chat set &lt;chat id&gt;/&lt;setting&gt;</syntax>
    244                         <syntax>chat set &lt;chat id&gt;/&lt;setting&gt; &lt;value&gt;</syntax>
    245                         <syntax>chat set -del &lt;chat id&gt;/&lt;setting&gt;</syntax>
    246 
    247                         <description>
    248                                 <para>
    249                                         This command can be used to change various settings for chatrooms.
    250                                 </para>
    251                                
    252                                 <para>
    253                                         For more infomation about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
    254                                 </para>
    255                                
    256                                 <para>
    257                                         The room ID can be a number (see <emphasis>chat list</emphasis>), or (part of) the name of the room/channel.
    258                                 </para>
    259                         </description>
    260                 </bitlbee-command>
    261275        </bitlbee-command>
    262276
    263277        <bitlbee-command name="add">
    264278                <short-description>Add a buddy to your contact list</short-description>
    265                 <syntax>add &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
    266                 <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>
    267281
    268282                <description>
     
    273287                        <para>
    274288                                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.
    275293                        </para>
    276294                </description>
     
    398416        </bitlbee-command>
    399417
     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
    400437        <bitlbee-setting name="auto_connect" type="boolean" scope="both">
    401438                <default>true</default>
     
    412449        </bitlbee-setting>
    413450
    414         <bitlbee-setting name="auto_join" type="boolean" scope="chat">
     451        <bitlbee-setting name="auto_join" type="boolean" scope="channel">
    415452                <default>false</default>
    416453
    417454                <description>
    418455                        <para>
    419                                 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.
    420457                        </para>
    421458                </description>
     
    481518                                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.
    482519                        </para>
     520                       
     521                        <para>
     522                                Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
     523                        </para>
     524                </description>
     525        </bitlbee-setting>
     526
     527        <bitlbee-setting name="away_reply_timeout" type="integer" scope="global">
     528                <default>3600</default>
     529
     530                <description>
     531                        <para>
     532                                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.
     533                        </para>
     534
     535                        <para>
     536                                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.
     537                        </para>
    483538                </description>
    484539        </bitlbee-setting>
     
    498553                        <para>
    499554                                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.
    500                         </para>
    501                 </description>
    502         </bitlbee-setting>
    503 
    504         <bitlbee-setting name="buddy_sendbuffer" type="boolean" scope="global">
    505                 <default>false</default>
    506 
    507                 <description>
    508                         <para>
    509                                 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.
    510                         </para>
    511 
    512                         <para>
    513                                 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.
    514                         </para>
    515 
    516                         <para>
    517                                 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.
    518                         </para>
    519                 </description>
    520         </bitlbee-setting>
    521 
    522         <bitlbee-setting name="buddy_sendbuffer_delay" type="integer" scope="global">
    523                 <default>200</default>
    524 
    525                 <description>
    526 
    527                         <para>
    528                                 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.
    529                         </para>
    530 
    531                         <para>
    532                                 See also the <emphasis>buddy_sendbuffer</emphasis> setting.
    533555                        </para>
    534556                </description>
     
    561583        </bitlbee-setting>
    562584
     585        <bitlbee-setting name="chat_type" type="string" scope="channel">
     586                <default>groupchat</default>
     587                <possible-values>groupchat, room</possible-values>
     588
     589                <description>
     590                        <para>
     591                                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.
     592                        </para>
     593                       
     594                        <para>
     595                                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.
     596                        </para>
     597                       
     598                        <para>
     599                                For setting up named chatrooms, it's currently easier to just use the <emphasis>chat add</emphasis> command.
     600                        </para>
     601                </description>
     602        </bitlbee-setting>
     603
    563604        <bitlbee-setting name="debug" type="boolean" scope="global">
    564605                <default>false</default>
     
    610651        </bitlbee-setting>
    611652
     653        <bitlbee-setting name="fill_by" type="string" scope="channel">
     654                <default>all</default>
     655                <possible-values>all, group, account, protocol</possible-values>
     656
     657                <description>
     658                        <para>
     659                                For control channels only: This setting determines which contacts the channel gets populated with.
     660                        </para>
     661
     662                        <para>
     663                                By default, control channels will contain all your contacts. You instead select contacts by buddy group, IM account or IM protocol.
     664                        </para>
     665                       
     666                        <para>
     667                                Change this setting and the corresponding <emphasis>account</emphasis>/<emphasis>group</emphasis>/<emphasis>protocol</emphasis> setting to set up this selection.
     668                        </para>
     669                       
     670                        <para>
     671                                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>.
     672                        </para>
     673                </description>
     674        </bitlbee-setting>
     675
     676        <bitlbee-setting name="group" type="string" scope="channel">
     677
     678                <description>
     679                        <para>
     680                                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.
     681                        </para>
     682                </description>
     683        </bitlbee-setting>
     684
    612685        <bitlbee-setting name="handle_unknown" type="string" scope="global">
    613                 <default>root</default>
     686                <default>add_channel</default>
    614687                <possible-values>root, add, add_private, add_channel, ignore</possible-values>
    615688
     
    717790
    718791        <bitlbee-setting name="nick" type="string" scope="chat">
    719 
    720792                <description>
    721793                        <para>
    722794                                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.
     795                        </para>
     796                </description>
     797        </bitlbee-setting>
     798
     799        <bitlbee-setting name="nick_format" type="string" scope="both">
     800                <default>%-@nick</default>
     801
     802                <description>
     803                        <para>
     804                                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.
     805                        </para>
     806
     807                        <para>
     808                                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.
     809                        </para>
     810
     811                        <para>
     812                                It's easier to describe this setting using a few examples:
     813                        </para>
     814
     815                        <para>
     816                                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.
     817                        </para>
     818
     819                        <para>
     820                                [%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.
     821                        </para>
     822
     823                        <para>
     824                                See <emphasis>help nick_format</emphasis> for more information.
    723825                        </para>
    724826                </description>
     
    789891                </description>
    790892        </bitlbee-setting>
     893
     894        <bitlbee-setting name="paste_buffer" type="boolean" scope="global">
     895                <default>false</default>
     896
     897                <description>
     898                        <para>
     899                                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.
     900                        </para>
     901
     902                        <para>
     903                                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.
     904                        </para>
     905
     906                        <para>
     907                                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.
     908                        </para>
     909                </description>
     910        </bitlbee-setting>
     911
     912        <bitlbee-setting name="paste_buffer_delay" type="integer" scope="global">
     913                <default>200</default>
     914
     915                <description>
     916
     917                        <para>
     918                                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.
     919                        </para>
     920
     921                        <para>
     922                                See also the <emphasis>paste_buffer</emphasis> setting.
     923                        </para>
     924                </description>
     925        </bitlbee-setting>
    791926       
    792927        <bitlbee-setting name="port" type="integer" scope="account">
     
    822957                        <para>
    823958                                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.
     959                        </para>
     960                </description>
     961        </bitlbee-setting>
     962
     963        <bitlbee-setting name="protocol" type="string" scope="channel">
     964
     965                <description>
     966                        <para>
     967                                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.
    824968                        </para>
    825969                </description>
     
    9011045                                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.
    9021046                        </para>
     1047                       
     1048                        <para>
     1049                                Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
     1050                        </para>
     1051                </description>
     1052        </bitlbee-setting>
     1053
     1054        <bitlbee-setting name="show_users" type="string" scope="channel">
     1055                <default>online+,away</default>
     1056
     1057                <description>
     1058                        <para>
     1059                                Comma-separated list of statuses of users you want in the channel,
     1060                                and any modes they should have. The following statuses are currently
     1061                                recognised: <emphasis>online</emphasis> (i.e. available, not
     1062                                away), <emphasis>away</emphasis>, and <emphasis>offline</emphasis>.
     1063                        </para>
     1064                       
     1065                        <para>
     1066                                If a status is followed by a valid channel mode character
     1067                                (@, % or +), it will be given to users with that status.
     1068                                For example, <emphasis>online@,away+,offline</emphasis> will
     1069                                show all users in the channel. Online people will
     1070                                have +o, people who are online but away will have +v,
     1071                                and others will have no special modes.
     1072                        </para>
    9031073                </description>
    9041074        </bitlbee-setting>
     
    9671137                        <para>
    9681138                                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.
     1139                        </para>
     1140                </description>
     1141        </bitlbee-setting>
     1142
     1143        <bitlbee-setting name="tag" type="string" scope="account">
     1144                <description>
     1145                        <para>
     1146                                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.
     1147                        </para>
     1148
     1149                        <para>
     1150                                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.
    9691151                        </para>
    9701152                </description>
     
    10101192                        <para>
    10111193                                Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable.
     1194                        </para>
     1195                </description>
     1196        </bitlbee-setting>
     1197
     1198        <bitlbee-setting name="translate_to_nicks" type="boolean" scope="channel">
     1199                <default>true</default>
     1200
     1201                <description>
     1202                        <para>
     1203                                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.
     1204                        </para>
     1205
     1206                        <para>
     1207                                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.
     1208                        </para>
     1209                </description>
     1210        </bitlbee-setting>
     1211
     1212        <bitlbee-setting name="type" type="string" scope="channel">
     1213                <default>control</default>
     1214                <possible-values>control, chat</possible-values>
     1215
     1216                <description>
     1217                        <para>
     1218                                BitlBee supports two kinds of channels: control channels (usually with a name starting with a &amp;) and chatroom channels (name usually starts with a #).
     1219                        </para>
     1220
     1221                        <para>
     1222                                See <emphasis>help channels</emphasis> for a full description of channel types in BitlBee.
    10121223                        </para>
    10131224                </description>
     
    11461357
    11471358        <bitlbee-command name="identify">
    1148                 <syntax>identify &lt;password&gt;</syntax>
     1359                <syntax>identify [-noload|-force] &lt;password&gt;</syntax>
    11491360                <short-description>Identify yourself with your password</short-description>
    11501361
     
    11561367                        <para>
    11571368                                Once you're registered, you can change your password using <emphasis>set password &lt;password&gt;</emphasis>.
     1369                        </para>
     1370
     1371                        <para>
     1372                                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).
     1373                        </para>
     1374                       
     1375                        <para>
     1376                                <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).
    11581377                        </para>
    11591378                </description>
     
    11831402        </bitlbee-command>
    11841403
    1185         <bitlbee-command name="nick">
    1186                 <short-description>Change friendly name, nick</short-description>
    1187                 <syntax>nick &lt;connection&gt; [&lt;new nick&gt;]</syntax>
    1188                 <syntax>nick &lt;connection&gt;</syntax>
    1189 
    1190                 <description>
    1191                         <para>
    1192                                 Deprecated: Use the per-account <emphasis>display_name</emphasis> setting to read and change this information.
    1193                         </para>
    1194                 </description>
    1195 
    1196                 <ircexample>
    1197                         <ircline nick="wouter">account set 1/display_name "The majestik møøse"</ircline>
    1198                         <ircline nick="root">display_name = `The majestik møøse'</ircline>
    1199                 </ircexample>
    1200 
    1201         </bitlbee-command>
     1404        <bitlbee-command name="group">
     1405                <short-description>Contact group management</short-description>
     1406                <syntax>group list</syntax>
     1407
     1408                <description>
     1409                        <para>
     1410                                Only the <emphasis>group list</emphasis> command is supported at the moment, which shows a list of all groups defined so far.
     1411                        </para>
     1412                </description>
     1413        </bitlbee-command>
     1414       
     1415        <bitlbee-command name="transfers">
     1416                <short-description>Monitor, cancel, or reject file transfers</short-description>
     1417                <syntax>transfers [&lt;cancel&gt; id | &lt;reject&gt;]</syntax>
     1418               
     1419                <description>
     1420                        <para>
     1421                                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.
     1422                        </para>
     1423
     1424                        <ircexample>
     1425                                <ircline nick="ulim">transfers</ircline>
     1426                        </ircexample>
     1427                </description>
     1428               
     1429                <bitlbee-command name="cancel">
     1430                        <short-description>Cancels the file transfer with the given id</short-description>
     1431                        <syntax>transfers &lt;cancel&gt; id</syntax>
     1432
     1433                        <description>
     1434                                <para>Cancels the file transfer with the given id</para>
     1435                        </description>
     1436
     1437                        <ircexample>
     1438                                <ircline nick="ulim">transfers cancel 1</ircline>
     1439                                <ircline nick="root">Canceling file transfer for test</ircline>
     1440                        </ircexample>
     1441                </bitlbee-command>
     1442
     1443                <bitlbee-command name="reject">
     1444                        <short-description>Rejects all incoming transfers</short-description>
     1445                        <syntax>transfers &lt;reject&gt;</syntax>
     1446
     1447                        <description>
     1448                                <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>
     1449                        </description>
     1450
     1451                        <ircexample>
     1452                                <ircline nick="ulim">transfers reject</ircline>
     1453                        </ircexample>
     1454                </bitlbee-command>
     1455        </bitlbee-command>
     1456       
    12021457</chapter>
  • doc/user-guide/help.xml

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    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</variablelist>
     204
     205<para>
     206Invalid characters (like spaces) will always be stripped. Depending on your
     207locale settings, characters with accents will be converted to ASCII.
     208</para>
     209
     210<para>
     211See <emphasis>set nick_format2</emphasis> for some more information.
     212</para>
     213
     214</sect1>
     215
     216<sect1 id="nick_format2">
     217<title>Nickname formatting - modifiers</title>
     218
     219<para>
     220Two modifiers ares currently available: You can include only the first few
     221characters of a variable by putting a number right after the %. For
     222example, <emphasis>[%3group]%-@nick</emphasis> will include only the first
     223three characters of the group name in the nick.
     224</para>
     225
     226<para>
     227Also, you can truncate variables from a certain character using
     228the <emphasis>-</emphasis> modifier. For example, you may want to leave out
     229everything after the @. <emphasis>%-@handle</emphasis> will expand to
     230everything in the handle up to the first @.
     231</para>
     232
     233</sect1>
     234
    119235</chapter>
  • help.c

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    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        {
     424                irc->status |= USTATUS_SHUTDOWN;
     425                irc_abort( irc, FALSE, NULL );
     426        }
     427}
     428
     429static void ipc_child_cmd_takeover_no( void *data )
     430{
     431        ipc_to_master_str( "TAKEOVER NO\r\n" );
     432        cmd_identify_finish( data, 0, 0 );
    191433}
    192434
     
    199441        { "kill",       2, ipc_child_cmd_kill,        0 },
    200442        { "hello",      0, ipc_child_cmd_hello,       0 },
     443        { "takeover",   1, ipc_child_cmd_takeover,    0 },
    201444        { NULL }
    202445};
    203446
     447gboolean ipc_child_identify( irc_t *irc )
     448{
     449        if( global.conf->runmode == RUNMODE_FORKDAEMON )
     450        {
     451                if( !ipc_send_fd( global.listen_socket, irc->fd ) )
     452                        ipc_child_disable();
     453       
     454                ipc_to_master_str( "IDENTIFY %s :%s\r\n", irc->user->nick, irc->password );
     455               
     456                return TRUE;
     457        }
     458        else if( global.conf->runmode == RUNMODE_DAEMON )
     459        {
     460                GSList *l;
     461                irc_t *old;
     462                char *to_init[] = { "TAKEOVER", "INIT", NULL };
     463               
     464                for( l = irc_connection_list; l; l = l->next )
     465                {
     466                        old = l->data;
     467                       
     468                        if( irc != old &&
     469                            irc->user->nick && old->user->nick &&
     470                            irc->password && old->password &&
     471                            strcmp( irc->user->nick, old->user->nick ) == 0 &&
     472                            strcmp( irc->password, old->password ) == 0 )
     473                                break;
     474                }
     475                if( l == NULL ||
     476                    !set_getbool( &irc->b->set, "allow_takeover" ) ||
     477                    !set_getbool( &old->b->set, "allow_takeover" ) )
     478                        return FALSE;
     479               
     480                ipc_child_cmd_takeover( irc, to_init );
     481               
     482                return TRUE;
     483        }
     484        else
     485                return FALSE;
     486}
     487
     488static void ipc_master_takeover_fail( struct bitlbee_child *child, gboolean both )
     489{
     490        if( child == NULL || g_slist_find( child_list, child ) == NULL )
     491                return;
     492       
     493        if( both && child->to_child != NULL )
     494                ipc_master_takeover_fail( child->to_child, FALSE );
     495       
     496        if( child->to_fd > -1 )
     497        {
     498                /* Send this error only to the new connection, which can be
     499                   recognised by to_fd being set. */
     500                if( write( child->ipc_fd, "TAKEOVER FAIL\r\n", 15 ) != 15 )
     501                {
     502                        ipc_master_free_one( child );
     503                        return;
     504                }
     505                close( child->to_fd );
     506                child->to_fd = -1;
     507        }
     508        child->to_child = NULL;
     509}
    204510
    205511static void ipc_command_exec( void *data, char **cmd, const command_t *commands )
     
    230536/* Return just one line. Returns NULL if something broke, an empty string
    231537   on temporary "errors" (EAGAIN and friends). */
    232 static char *ipc_readline( int fd )
    233 {
     538static char *ipc_readline( int fd, int *recv_fd )
     539{
     540        struct msghdr msg;
     541        struct iovec iov;
     542        char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
     543        struct cmsghdr *cmsg;
    234544        char buf[513], *eol;
    235545        int size;
     
    253563                size = eol - buf + 2;
    254564       
    255         if( recv( fd, buf, size, 0 ) != size )
     565        iov.iov_base = buf;
     566        iov.iov_len = size;
     567       
     568        memset( &msg, 0, sizeof( msg ) );
     569        msg.msg_iov = &iov;
     570        msg.msg_iovlen = 1;
     571        msg.msg_control = ccmsg;
     572        msg.msg_controllen = sizeof( ccmsg );
     573       
     574        if( recvmsg( fd, &msg, 0 ) != size )
    256575                return NULL;
    257         else
    258                 return g_strndup( buf, size - 2 );
     576       
     577        if( recv_fd )
     578                for( cmsg = CMSG_FIRSTHDR( &msg ); cmsg; cmsg = CMSG_NXTHDR( &msg, cmsg ) )
     579                        if( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS )
     580                        {
     581                                /* Getting more than one shouldn't happen but if it does,
     582                                   make sure we don't leave them around. */
     583                                if( *recv_fd != -1 )
     584                                        close( *recv_fd );
     585                               
     586                                *recv_fd = *(int*) CMSG_DATA( cmsg );
     587                                /*
     588                                fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
     589                                */
     590                        }
     591       
     592        /*
     593        fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
     594        */
     595        return g_strndup( buf, size - 2 );
    259596}
    260597
    261598gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
    262599{
     600        struct bitlbee_child *child = data;
    263601        char *buf, **cmd;
    264602       
    265         if( ( buf = ipc_readline( source ) ) )
     603        if( ( buf = ipc_readline( source, &child->to_fd ) ) )
    266604        {
    267605                cmd = irc_parse_line( buf );
    268606                if( cmd )
    269607                {
    270                         ipc_command_exec( data, cmd, ipc_master_commands );
     608                        ipc_command_exec( child, cmd, ipc_master_commands );
    271609                        g_free( cmd );
    272610                }
     
    285623        char *buf, **cmd;
    286624       
    287         if( ( buf = ipc_readline( source ) ) )
     625        if( ( buf = ipc_readline( source, &ipc_child_recv_fd ) ) )
    288626        {
    289627                cmd = irc_parse_line( buf );
     
    392730                        next = l->next;
    393731                        if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
    394                         {
    395732                                ipc_master_free_one( c );
    396                                 child_list = g_slist_remove( child_list, c );
    397                         }
    398733                }
    399734        }
     
    413748}
    414749
     750static gboolean ipc_send_fd( int fd, int send_fd )
     751{
     752        struct msghdr msg;
     753        struct iovec iov;
     754        char ccmsg[CMSG_SPACE(sizeof(fd))];
     755        struct cmsghdr *cmsg;
     756       
     757        memset( &msg, 0, sizeof( msg ) );
     758        iov.iov_base = "0x90\r\n";         /* Ja, noppes */
     759        iov.iov_len = 6;
     760        msg.msg_iov = &iov;
     761        msg.msg_iovlen = 1;
     762       
     763        msg.msg_control = ccmsg;
     764        msg.msg_controllen = sizeof( ccmsg );
     765        cmsg = CMSG_FIRSTHDR( &msg );
     766        cmsg->cmsg_level = SOL_SOCKET;
     767        cmsg->cmsg_type = SCM_RIGHTS;
     768        cmsg->cmsg_len = CMSG_LEN( sizeof( send_fd ) );
     769        *(int*)CMSG_DATA( cmsg ) = send_fd;
     770        msg.msg_controllen = cmsg->cmsg_len;
     771       
     772        return sendmsg( fd, &msg, 0 ) == 6;
     773}
     774
    415775void ipc_master_free_one( struct bitlbee_child *c )
    416776{
     777        GSList *l;
     778       
    417779        b_event_remove( c->ipc_inpa );
    418780        closesocket( c->ipc_fd );
     781       
     782        if( c->to_fd != -1 )
     783                close( c->to_fd );
    419784       
    420785        g_free( c->host );
    421786        g_free( c->nick );
    422787        g_free( c->realname );
     788        g_free( c->password );
    423789        g_free( c );
     790       
     791        child_list = g_slist_remove( child_list, c );
     792       
     793        /* Also, if any child has a reference to this one, remove it. */
     794        for( l = child_list; l; l = l->next )
     795        {
     796                struct bitlbee_child *oc = l->data;
     797               
     798                if( oc->to_child == c )
     799                        ipc_master_takeover_fail( oc, FALSE );
     800        }
    424801}
    425802
     
    435812                {
    436813                        ipc_master_free_one( c );
    437                         child_list = g_slist_remove( child_list, c );
    438814                        break;
    439815                }
     
    443819void ipc_master_free_all()
    444820{
    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;
     821        while( child_list )
     822                ipc_master_free_one( child_list->data );
    452823}
    453824
     
    506877        struct bitlbee_child *child = g_new0( struct bitlbee_child, 1 );
    507878       
     879        child->to_fd = -1;
    508880        child->ipc_fd = accept( serversock, NULL, 0 );
    509        
    510881        if( child->ipc_fd == -1 )
    511882        {
     
    514885        }
    515886               
    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 );
     887        child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     888       
     889        child_list = g_slist_prepend( child_list, child );
    519890       
    520891        return TRUE;
     
    552923        }
    553924       
    554         b_input_add( serversock, GAIM_INPUT_READ, new_ipc_client, NULL );
     925        b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL );
    555926       
    556927        return 1;
     
    597968                        return 0;
    598969                }
    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 );
     970                child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child );
     971                child->to_fd = -1;
     972               
     973                child_list = g_slist_prepend( child_list, child );
    602974        }
    603975       
  • ipc.h

    ref14a83 r2945c6f  
    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

    ref14a83 r2945c6f  
    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         char *test;
    55         gsize test_bytes = 0;
    56         GIConv ic, oc;
    57 
    58         if( g_strcasecmp( value, "none" ) == 0 )
    59                 value = g_strdup( "utf-8" );
    60 
    61         if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
    62         {
    63                 return NULL;
    64         }
    65        
    66         /* Do a test iconv to see if the user picked an IRC-compatible
    67            charset (for example utf-16 goes *horribly* wrong). */
    68         if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
    69             test_bytes > 1 )
    70         {
    71                 g_free( test );
    72                 g_iconv_close( oc );
    73                 irc_usermsg( irc, "Unsupported character set: The IRC protocol "
    74                                   "only supports 8-bit character sets." );
    75                 return NULL;
    76         }
    77         g_free( test );
    78        
    79         if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
    80         {
    81                 g_iconv_close( oc );
    82                 return NULL;
    83         }
    84        
    85         if( irc->iconv != (GIConv) -1 )
    86                 g_iconv_close( irc->iconv );
    87         if( irc->oconv != (GIConv) -1 )
    88                 g_iconv_close( irc->oconv );
    89        
    90         irc->iconv = ic;
    91         irc->oconv = oc;
    92 
    93         return value;
    94 }
    95 
    96 static char *set_eval_away_status( set_t *set, char *value )
    97 {
    98         irc_t *irc = set->data;
    99         account_t *a;
    100        
    101         g_free( set->value );
    102         set->value = g_strdup( value );
    103        
    104         for( a = irc->accounts; a; a = a->next )
    105         {
    106                 struct im_connection *ic = a->ic;
    107                
    108                 if( ic && ic->flags & OPT_LOGGED_IN )
    109                         imc_away_send_update( ic );
    110         }
    111        
    112         return value;
    113 }
     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 );
    11436
    11537irc_t *irc_new( int fd )
     
    11840        struct sockaddr_storage sock;
    11941        socklen_t socklen = sizeof( sock );
     42        char *host = NULL, *myhost = NULL;
     43        irc_user_t *iu;
    12044        set_t *s;
     45        bee_t *b;
    12146       
    12247        irc = g_new0( irc_t, 1 );
     
    12550        sock_make_nonblocking( irc->fd );
    12651       
    127         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 );
    12853       
    12954        irc->status = USTATUS_OFFLINE;
    13055        irc->last_pong = gettime();
    13156       
    132         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 );
    13358        irc->watches = g_hash_table_new( g_str_hash, g_str_equal );
    134        
    135         strcpy( irc->umode, UMODE );
    136         irc->mynick = g_strdup( ROOT_NICK );
    137         irc->channel = g_strdup( ROOT_CHAN );
    13859       
    13960        irc->iconv = (GIConv) -1;
     
    14263        if( global.conf->hostname )
    14364        {
    144                 irc->myhost = g_strdup( global.conf->hostname );
     65                myhost = g_strdup( global.conf->hostname );
    14566        }
    14667        else if( getsockname( irc->fd, (struct sockaddr*) &sock, &socklen ) == 0 )
     
    15172                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    15273                {
    153                         irc->myhost = g_strdup( ipv6_unwrap( buf ) );
     74                        myhost = g_strdup( ipv6_unwrap( buf ) );
    15475                }
    15576        }
     
    16283                                 NI_MAXHOST, NULL, 0, 0 ) == 0 )
    16384                {
    164                         irc->host = g_strdup( ipv6_unwrap( buf ) );
    165                 }
    166         }
    167        
    168         if( irc->host == NULL )
    169                 irc->host = g_strdup( "localhost.localdomain" );
    170         if( irc->myhost == NULL )
    171                 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" );
    17293       
    17394        if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 )
    17495                irc->ping_source_id = b_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc );
    175        
    176         irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" );
    17796
    17897        irc_connection_list = g_slist_append( irc_connection_list, irc );
    17998       
    180         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 );
    181120        s->flags |= SET_NULL_OK;
    182         s = set_add( &irc->set, "away_devoice", "true",  set_eval_away_devoice, irc );
    183         s = set_add( &irc->set, "auto_connect", "true", set_eval_bool, irc );
    184         s = set_add( &irc->set, "auto_reconnect", "true", set_eval_bool, irc );
    185         s = set_add( &irc->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, irc );
    186         s = set_add( &irc->set, "buddy_sendbuffer", "false", set_eval_bool, irc );
    187         s = set_add( &irc->set, "buddy_sendbuffer_delay", "200", set_eval_int, irc );
    188         s = set_add( &irc->set, "charset", "utf-8", set_eval_charset, irc );
    189         s = set_add( &irc->set, "control_channel", irc->channel, set_eval_control_channel, irc );
    190         s = set_add( &irc->set, "debug", "false", set_eval_bool, irc );
    191         s = set_add( &irc->set, "default_target", "root", NULL, irc );
    192         s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc );
    193         s = set_add( &irc->set, "display_timestamps", "true", set_eval_bool, irc );
    194         s = set_add( &irc->set, "handle_unknown", "root", NULL, irc );
    195         s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc );
    196         s = set_add( &irc->set, "ops", "both", set_eval_ops, irc );
    197         s = set_add( &irc->set, "password", NULL, set_eval_password, irc );
    198         s->flags |= SET_NULL_OK;
    199         s = set_add( &irc->set, "private", "true", set_eval_bool, irc );
    200         s = set_add( &irc->set, "query_order", "lifo", NULL, irc );
    201         s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
    202         s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
    203         s = set_add( &irc->set, "show_offline", "false", set_eval_bool, irc );
    204         s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
    205         s = set_add( &irc->set, "status", NULL,  set_eval_away_status, irc );
    206         s->flags |= SET_NULL_OK;
    207         s = set_add( &irc->set, "strip_html", "true", NULL, irc );
    208         s = set_add( &irc->set, "timezone", "local", set_eval_timezone, irc );
    209         s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc );
    210         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 );
    211142       
    212143        conf_loaddefaults( irc );
    213144       
    214145        /* Evaluator sets the iconv/oconv structures. */
    215         set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
    216        
    217         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       
     150        g_free( myhost );
     151        g_free( host );
     152       
     153        nogaim_init();
     154       
     155        return irc;
    218156}
    219157
     
    236174               
    237175                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    238                                    irc->nick ? irc->nick : "(NONE)", irc->host, reason );
     176                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, reason );
    239177               
    240178                g_free( reason );
     
    246184               
    247185                ipc_to_master_str( "OPERMSG :Client exiting: %s@%s [%s]\r\n",
    248                                    irc->nick ? irc->nick : "(NONE)", irc->host, "No reason given" );
     186                                   irc->user->nick ? irc->user->nick : "(NONE)", irc->user->host, "No reason given" );
    249187        }
    250188       
     
    267205}
    268206
    269 static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
    270 {
    271         g_free( key );
    272        
    273         return( TRUE );
    274 }
    275 
    276 /* Because we have no garbage collection, this is quite annoying */
     207static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data );
     208
    277209void irc_free( irc_t * irc )
    278210{
    279         user_t *user, *usertmp;
    280        
    281211        log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
    282212       
    283         if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->set, "save_on_quit" ) )
     213        if( irc->status & USTATUS_IDENTIFIED && set_getbool( &irc->b->set, "save_on_quit" ) )
    284214                if( storage_save( irc, NULL, TRUE ) != STORAGE_OK )
    285                         irc_usermsg( irc, "Error while saving settings!" );
     215                        log_message( LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick );
    286216       
    287217        irc_connection_list = g_slist_remove( irc_connection_list, irc );
    288        
    289         while( irc->accounts )
    290         {
    291                 if( irc->accounts->ic )
    292                         imc_logout( irc->accounts->ic, FALSE );
    293                 else if( irc->accounts->reconnect )
    294                         cancel_auto_reconnect( irc->accounts );
    295                
    296                 if( irc->accounts->ic == NULL )
    297                         account_del( irc, irc->accounts );
    298                 else
    299                         /* Nasty hack, but account_del() doesn't work in this
    300                            case and we don't want infinite loops, do we? ;-) */
    301                         irc->accounts = irc->accounts->next;
    302         }
    303218       
    304219        while( irc->queries != NULL )
    305220                query_del( irc, irc->queries );
    306221       
    307         while( irc->set )
    308                 set_del( &irc->set, irc->set->key );
    309        
    310         if (irc->users != NULL)
    311         {
    312                 user = irc->users;
    313                 while( user != NULL )
    314                 {
    315                         g_free( user->nick );
    316                         g_free( user->away );
    317                         g_free( user->handle );
    318                         if( user->user != user->nick ) g_free( user->user );
    319                         if( user->host != user->nick ) g_free( user->host );
    320                         if( user->realname != user->nick ) g_free( user->realname );
    321                         b_event_remove( user->sendbuf_timer );
    322                                        
    323                         usertmp = user;
    324                         user = user->next;
    325                         g_free( usertmp );
    326                 }
    327         }
     222        /* This is a little bit messy: bee_free() frees all b->users which
     223           calls us back to free the corresponding irc->users. So do this
     224           before we clear the remaining ones ourselves. */
     225        bee_free( irc->b );
     226       
     227        while( irc->users )
     228                irc_user_free( irc, (irc_user_t *) irc->users->data );
     229       
     230        while( irc->channels )
     231                irc_channel_free( irc->channels->data );
    328232       
    329233        if( irc->ping_source_id > 0 )
     
    337241        irc->fd = -1;
    338242       
    339         g_hash_table_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
    340         g_hash_table_destroy( irc->userhash );
     243        g_hash_table_foreach_remove( irc->nick_user_hash, irc_free_hashkey, NULL );
     244        g_hash_table_destroy( irc->nick_user_hash );
    341245       
    342246        g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, NULL );
     
    350254        g_free( irc->sendbuffer );
    351255        g_free( irc->readbuffer );
    352        
    353         g_free( irc->nick );
    354         g_free( irc->user );
    355         g_free( irc->host );
    356         g_free( irc->realname );
    357256        g_free( irc->password );
    358        
    359         g_free( irc->myhost );
    360         g_free( irc->mynick );
    361        
    362         g_free( irc->channel );
    363        
    364         g_free( irc->last_target );
     257        g_free( irc->last_root_cmd );
    365258       
    366259        g_free( irc );
     
    374267}
    375268
     269static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
     270{
     271        g_free( key );
     272       
     273        return( TRUE );
     274}
     275
    376276/* USE WITH CAUTION!
    377277   Sets pass without checking */
    378 void irc_setpass (irc_t *irc, const char *pass) 
     278void irc_setpass (irc_t *irc, const char *pass)
    379279{
    380280        g_free (irc->password);
     
    387287}
    388288
     289static char *set_eval_password( set_t *set, char *value )
     290{
     291        irc_t *irc = set->data;
     292       
     293        if( irc->status & USTATUS_IDENTIFIED && value )
     294        {
     295                irc_setpass( irc, value );
     296                return NULL;
     297        }
     298        else
     299        {
     300                return SET_INVALID;
     301        }
     302}
     303
     304static char **irc_splitlines( char *buffer );
     305
    389306void irc_process( irc_t *irc )
    390307{
     
    394311        if( irc->readbuffer != NULL )
    395312        {
    396                 lines = irc_tokenize( irc->readbuffer );
     313                lines = irc_splitlines( irc->readbuffer );
    397314               
    398315                for( i = 0; *lines[i] != '\0'; i ++ )
     
    431348                                                                  "`help set charset' for more information. Your "
    432349                                                                  "message was ignored.",
    433                                                                   set_getstr( &irc->set, "charset" ) );
     350                                                                  set_getstr( &irc->b->set, "charset" ) );
    434351                                               
    435352                                                g_free( conv );
     
    438355                                        else
    439356                                        {
    440                                                 irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
     357                                                irc_write( irc, ":%s NOTICE AUTH :%s", irc->root->host,
    441358                                                           "Warning: invalid characters received at login time." );
    442359                                               
     
    476393}
    477394
    478 /* Splits a long string into separate lines. The array is NULL-terminated and, unless the string
    479    contains an incomplete line at the end, ends with an empty string. */
    480 char **irc_tokenize( char *buffer )
     395/* Splits a long string into separate lines. The array is NULL-terminated
     396   and, unless the string contains an incomplete line at the end, ends with
     397   an empty string. Could use g_strsplit() but this one does it in-place.
     398   (So yes, it's destructive.) */
     399static char **irc_splitlines( char *buffer )
    481400{
    482401        int i, j, n = 3;
     
    609528}
    610529
    611 void irc_reply( irc_t *irc, int code, char *format, ... )
    612 {
    613         char text[IRC_MAX_LINE];
    614         va_list params;
    615        
    616         va_start( params, format );
    617         g_vsnprintf( text, IRC_MAX_LINE, format, params );
    618         va_end( params );
    619         irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text );
    620        
    621         return;
    622 }
    623 
    624 int irc_usermsg( irc_t *irc, char *format, ... )
    625 {
    626         char text[1024];
    627         va_list params;
    628         char is_private = 0;
    629         user_t *u;
    630        
    631         u = user_find( irc, irc->mynick );
    632         is_private = u->is_private;
    633        
    634         va_start( params, format );
    635         g_vsnprintf( text, sizeof( text ), format, params );
    636         va_end( params );
    637        
    638         return( irc_msgfrom( irc, u->nick, text ) );
    639 }
    640 
    641530void irc_write( irc_t *irc, char *format, ... )
    642531{
     
    649538        return;
    650539}
     540
     541void irc_write_all( int now, char *format, ... )
     542{
     543        va_list params;
     544        GSList *temp;   
     545       
     546        va_start( params, format );
     547       
     548        temp = irc_connection_list;
     549        while( temp != NULL )
     550        {
     551                irc_t *irc = temp->data;
     552               
     553                if( now )
     554                {
     555                        g_free( irc->sendbuffer );
     556                        irc->sendbuffer = g_strdup( "\r\n" );
     557                }
     558                irc_vawrite( temp->data, format, params );
     559                if( now )
     560                {
     561                        bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE );
     562                }
     563                temp = temp->next;
     564        }
     565       
     566        va_end( params );
     567        return;
     568}
    651569
    652570void irc_vawrite( irc_t *irc, char *format, va_list params )
     
    696614                   in the event queue. */
    697615                /* Really can't be done as long as the code doesn't do error checking very well:
    698                 if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */
     616                if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
    699617               
    700618                /* So just always do it via the event handler. */
    701                 irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc );
     619                irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc );
    702620        }
    703621       
     
    705623}
    706624
    707 void irc_write_all( int now, char *format, ... )
    708 {
    709         va_list params;
    710         GSList *temp;   
    711        
    712         va_start( params, format );
    713        
    714         temp = irc_connection_list;
    715         while( temp != NULL )
    716         {
    717                 irc_t *irc = temp->data;
    718                
    719                 if( now )
    720                 {
    721                         g_free( irc->sendbuffer );
    722                         irc->sendbuffer = g_strdup( "\r\n" );
    723                 }
    724                 irc_vawrite( temp->data, format, params );
    725                 if( now )
    726                 {
    727                         bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE );
    728                 }
    729                 temp = temp->next;
    730         }
    731        
    732         va_end( params );
    733         return;
    734 }
    735 
    736 void irc_names( irc_t *irc, char *channel )
    737 {
    738         user_t *u;
    739         char namelist[385] = "";
    740         struct groupchat *c = NULL;
    741         char *ops = set_getstr( &irc->set, "ops" );
    742        
    743         /* RFCs say there is no error reply allowed on NAMES, so when the
    744            channel is invalid, just give an empty reply. */
    745        
    746         if( g_strcasecmp( channel, irc->channel ) == 0 )
    747         {
    748                 for( u = irc->users; u; u = u->next ) if( u->online )
    749                 {
    750                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
     625/* Flush sendbuffer if you can. If it fails, fail silently and let some
     626   I/O event handler clean up. */
     627void irc_flush( irc_t *irc )
     628{
     629        ssize_t n;
     630        size_t len;
     631       
     632        if( irc->sendbuffer == NULL )
     633                return;
     634       
     635        len = strlen( irc->sendbuffer );
     636        if( ( n = send( irc->fd, irc->sendbuffer, len, 0 ) ) == len )
     637        {
     638                g_free( irc->sendbuffer );
     639                irc->sendbuffer = NULL;
     640               
     641                b_event_remove( irc->w_watch_source_id );
     642                irc->w_watch_source_id = 0;
     643        }
     644        else if( n > 0 )
     645        {
     646                char *s = g_strdup( irc->sendbuffer + n );
     647                g_free( irc->sendbuffer );
     648                irc->sendbuffer = s;
     649        }
     650        /* Otherwise something went wrong and we don't currently care
     651           what the error was. We may or may not succeed later, we
     652           were just trying to flush the buffer immediately. */
     653}
     654
     655/* Meant for takeover functionality. Transfer an IRC connection to a different
     656   socket. */
     657void irc_switch_fd( irc_t *irc, int fd )
     658{
     659        irc_write( irc, "ERROR :Transferring session to a new connection" );
     660        irc_flush( irc ); /* Write it now or forget about it forever. */
     661       
     662        if( irc->sendbuffer )
     663        {
     664                b_event_remove( irc->w_watch_source_id );
     665                irc->w_watch_source_id = 0;
     666                g_free( irc->sendbuffer );
     667                irc->sendbuffer = NULL;
     668        }
     669       
     670        b_event_remove( irc->r_watch_source_id );
     671        closesocket( irc->fd );
     672        irc->fd = fd;
     673        irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc );
     674}
     675
     676void irc_sync( irc_t *irc )
     677{
     678        GSList *l;
     679       
     680        irc_write( irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
     681                   irc->user->user, irc->user->host, irc->user->nick,
     682                   irc->umode );
     683       
     684        for( l = irc->channels; l; l = l->next )
     685        {
     686                irc_channel_t *ic = l->data;
     687                if( ic->flags & IRC_CHANNEL_JOINED )
     688                        irc_send_join( ic, irc->user );
     689        }
     690}
     691
     692void irc_desync( irc_t *irc )
     693{
     694        GSList *l;
     695       
     696        for( l = irc->channels; l; l = l->next )
     697                irc_channel_del_user( l->data, irc->user, IRC_CDU_KICK,
     698                                      "Switching to old session" );
     699       
     700        irc_write( irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
     701                   irc->user->user, irc->user->host, irc->user->nick,
     702                   irc->umode );
     703}
     704
     705int irc_check_login( irc_t *irc )
     706{
     707        if( irc->user->user && irc->user->nick )
     708        {
     709                if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
     710                {
     711                        irc_send_num( irc, 464, ":This server is password-protected." );
     712                        return 0;
     713                }
     714                else
     715                {
     716                        irc_channel_t *ic;
     717                        irc_user_t *iu = irc->user;
     718                       
     719                        irc->user = irc_user_new( irc, iu->nick );
     720                        irc->user->user = iu->user;
     721                        irc->user->host = iu->host;
     722                        irc->user->fullname = iu->fullname;
     723                        irc->user->f = &irc_user_self_funcs;
     724                        g_free( iu->nick );
     725                        g_free( iu );
     726                       
     727                        if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
     728                                ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname );
     729                       
     730                        irc->status |= USTATUS_LOGGED_IN;
     731                       
     732                        irc_send_login( irc );
     733                       
     734                        irc->umode[0] = '\0';
     735                        irc_umode_set( irc, "+" UMODE, TRUE );
     736                       
     737                        ic = irc->default_channel = irc_channel_new( irc, ROOT_CHAN );
     738                        irc_channel_set_topic( ic, CONTROL_TOPIC, irc->root );
     739                        set_setstr( &ic->set, "auto_join", "true" );
     740                        irc_channel_auto_joins( irc, NULL );
     741                       
     742                        irc->last_root_cmd = g_strdup( ROOT_CHAN );
     743                       
     744                        irc_send_msg( irc->root, "PRIVMSG", ROOT_CHAN,
     745                                      "Welcome to the BitlBee gateway!\n\n"
     746                                      "If you've never used BitlBee before, please do read the help "
     747                                      "information using the \x02help\x02 command. Lots of FAQs are "
     748                                      "answered there.\n"
     749                                      "If you already have an account on this server, just use the "
     750                                      "\x02identify\x02 command to identify yourself.", NULL );
     751                       
     752                        /* This is for bug #209 (use PASS to identify to NickServ). */
     753                        if( irc->password != NULL )
    751754                        {
    752                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    753                                 *namelist = 0;
     755                                char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
     756                               
     757                                irc_setpass( irc, NULL );
     758                                root_command( irc, send_cmd );
     759                                g_free( send_cmd[1] );
    754760                        }
    755761                       
    756                         if( u->ic && !u->away && set_getbool( &irc->set, "away_devoice" ) )
    757                                 strcat( namelist, "+" );
    758                         else if( ( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) == 0 ) ) ||
    759                                  ( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) == 0 ) ) )
    760                                 strcat( namelist, "@" );
    761                        
    762                         strcat( namelist, u->nick );
    763                         strcat( namelist, " " );
    764                 }
    765         }
    766         else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    767         {
    768                 GList *l;
    769                
    770                 /* root and the user aren't in the channel userlist but should
    771                    show up in /NAMES, so list them first: */
    772                 sprintf( namelist, "%s%s %s%s ", strcmp( ops, "root" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->mynick,
    773                                                  strcmp( ops, "user" ) == 0 || strcmp( ops, "both" ) ? "@" : "", irc->nick );
    774                
    775                 for( l = c->in_room; l; l = l->next ) if( ( u = user_findhandle( c->ic, l->data ) ) )
    776                 {
    777                         if( strlen( namelist ) + strlen( u->nick ) > sizeof( namelist ) - 4 )
    778                         {
    779                                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    780                                 *namelist = 0;
    781                         }
    782                        
    783                         strcat( namelist, u->nick );
    784                         strcat( namelist, " " );
    785                 }
    786         }
    787        
    788         if( *namelist )
    789                 irc_reply( irc, 353, "= %s :%s", channel, namelist );
    790        
    791         irc_reply( irc, 366, "%s :End of /NAMES list", channel );
    792 }
    793 
    794 int irc_check_login( irc_t *irc )
    795 {
    796         if( irc->user && irc->nick )
    797         {
    798                 if( global.conf->authmode == AUTHMODE_CLOSED && !( irc->status & USTATUS_AUTHORIZED ) )
    799                 {
    800                         irc_reply( irc, 464, ":This server is password-protected." );
    801                         return 0;
    802                 }
    803                 else
    804                 {
    805                         irc_login( irc );
    806762                        return 1;
    807763                }
     
    814770}
    815771
    816 void irc_login( irc_t *irc )
    817 {
    818         user_t *u;
    819        
    820         irc_reply( irc,   1, ":Welcome to the BitlBee gateway, %s", irc->nick );
    821         irc_reply( irc,   2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost );
    822         irc_reply( irc,   3, ":%s", IRCD_INFO );
    823         irc_reply( irc,   4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES );
    824         irc_reply( irc,   5, "PREFIX=(ov)@+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d NETWORK=BitlBee "
    825                              "CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 :are supported by this server",
    826                              CTYPES, CMODES, MAX_NICK_LENGTH - 1 );
    827         irc_motd( irc );
    828         irc->umode[0] = '\0';
    829         irc_umode_set( irc, "+" UMODE, 1 );
    830 
    831         u = user_add( irc, irc->mynick );
    832         u->host = g_strdup( irc->myhost );
    833         u->realname = g_strdup( ROOT_FN );
    834         u->online = 1;
    835         u->send_handler = root_command_string;
    836         u->is_private = 0; /* [SH] The channel is root's personal playground. */
    837         irc_spawn( irc, u );
    838        
    839         u = user_add( irc, NS_NICK );
    840         u->host = g_strdup( irc->myhost );
    841         u->realname = g_strdup( ROOT_FN );
    842         u->online = 0;
    843         u->send_handler = root_command_string;
    844         u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */
    845        
    846         u = user_add( irc, irc->nick );
    847         u->user = g_strdup( irc->user );
    848         u->host = g_strdup( irc->host );
    849         u->realname = g_strdup( irc->realname );
    850         u->online = 1;
    851         irc_spawn( irc, u );
    852        
    853         irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\n"
    854                           "If you've never used BitlBee before, please do read the help "
    855                           "information using the \x02help\x02 command. Lots of FAQs are "
    856                           "answered there.\n"
    857                           "If you already have an account on this server, just use the "
    858                           "\x02identify\x02 command to identify yourself." );
    859        
    860         if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
    861                 ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
    862        
    863         irc->status |= USTATUS_LOGGED_IN;
    864        
    865         /* This is for bug #209 (use PASS to identify to NickServ). */
    866         if( irc->password != NULL )
    867         {
    868                 char *send_cmd[] = { "identify", g_strdup( irc->password ), NULL };
    869                
    870                 irc_setpass( irc, NULL );
    871                 root_command( irc, send_cmd );
    872                 g_free( send_cmd[1] );
    873         }
    874 }
    875 
    876 void irc_motd( irc_t *irc )
    877 {
    878         int fd;
    879        
    880         fd = open( global.conf->motdfile, O_RDONLY );
    881         if( fd == -1 )
    882         {
    883                 irc_reply( irc, 422, ":We don't need MOTDs." );
    884         }
    885         else
    886         {
    887                 char linebuf[80];       /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */
    888                 char *add, max;
    889                 int len;
    890                
    891                 linebuf[79] = len = 0;
    892                 max = sizeof( linebuf ) - 1;
    893                
    894                 irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost );
    895                 while( read( fd, linebuf + len, 1 ) == 1 )
    896                 {
    897                         if( linebuf[len] == '\n' || len == max )
    898                         {
    899                                 linebuf[len] = 0;
    900                                 irc_reply( irc, 372, ":- %s", linebuf );
    901                                 len = 0;
    902                         }
    903                         else if( linebuf[len] == '%' )
    904                         {
    905                                 read( fd, linebuf + len, 1 );
    906                                 if( linebuf[len] == 'h' )
    907                                         add = irc->myhost;
    908                                 else if( linebuf[len] == 'v' )
    909                                         add = BITLBEE_VERSION;
    910                                 else if( linebuf[len] == 'n' )
    911                                         add = irc->nick;
    912                                 else
    913                                         add = "%";
    914                                
    915                                 strncpy( linebuf + len, add, max - len );
    916                                 while( linebuf[++len] );
    917                         }
    918                         else if( len < max )
    919                         {
    920                                 len ++;
    921                         }
    922                 }
    923                 irc_reply( irc, 376, ":End of MOTD" );
    924                 close( fd );
    925         }
    926 }
    927 
    928 void irc_topic( irc_t *irc, char *channel )
    929 {
    930         struct groupchat *c = irc_chat_by_channel( irc, channel );
    931        
    932         if( c && c->topic )
    933                 irc_reply( irc, 332, "%s :%s", channel, c->topic );
    934         else if( g_strcasecmp( channel, irc->channel ) == 0 )
    935                 irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC );
    936         else
    937                 irc_reply( irc, 331, "%s :No topic for this channel", channel );
    938 }
    939 
    940 void irc_umode_set( irc_t *irc, char *s, int allow_priv )
     772void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv )
    941773{
    942774        /* allow_priv: Set to 0 if s contains user input, 1 if you want
    943775           to set a "privileged" mode (+o, +R, etc). */
    944         char m[256], st = 1, *t;
     776        char m[128], st = 1;
     777        const char *t;
    945778        int i;
    946779        char changes[512], *p, st2 = 2;
     
    950783       
    951784        for( t = irc->umode; *t; t ++ )
    952                 m[(int)*t] = 1;
    953 
     785                if( *t < sizeof( m ) )
     786                        m[(int)*t] = 1;
     787       
    954788        p = changes;
    955789        for( t = s; *t; t ++ )
     
    957791                if( *t == '+' || *t == '-' )
    958792                        st = *t == '+';
    959                 else if( st == 0 || ( strchr( UMODES, *t ) || ( allow_priv && strchr( UMODES_PRIV, *t ) ) ) )
     793                else if( ( st == 0 && ( !strchr( UMODES_KEEP, *t ) || allow_priv ) ) ||
     794                         ( st == 1 && strchr( UMODES, *t ) ) ||
     795                         ( st == 1 && allow_priv && strchr( UMODES_PRIV, *t ) ) )
    960796                {
    961797                        if( m[(int)*t] != st)
     
    974810        memset( irc->umode, 0, sizeof( irc->umode ) );
    975811       
    976         for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
     812        for( i = 'A'; i <= 'z' && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ )
    977813                if( m[i] )
    978814                        irc->umode[strlen(irc->umode)] = i;
    979815       
    980816        if( badflag )
    981                 irc_reply( irc, 501, ":Unknown MODE flag" );
    982         /* Deliberately no !user@host on the prefix here */
     817                irc_send_num( irc, 501, ":Unknown MODE flag" );
    983818        if( *changes )
    984                 irc_write( irc, ":%s MODE %s %s", irc->nick, irc->nick, changes );
    985 }
    986 
    987 void irc_spawn( irc_t *irc, user_t *u )
    988 {
    989         irc_join( irc, u, irc->channel );
    990 }
    991 
    992 void irc_join( irc_t *irc, user_t *u, char *channel )
    993 {
    994         char *nick;
    995        
    996         if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) )
    997                 irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel );
    998        
    999         if( nick_cmp( u->nick, irc->nick ) == 0 )
    1000         {
    1001                 irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE );
    1002                 irc_names( irc, channel );
    1003                 irc_topic( irc, channel );
    1004         }
    1005        
    1006         nick = g_strdup( u->nick );
    1007         nick_lc( nick );
    1008         if( g_hash_table_lookup( irc->watches, nick ) )
    1009         {
    1010                 irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged online" );
    1011         }
    1012         g_free( nick );
    1013 }
    1014 
    1015 void irc_part( irc_t *irc, user_t *u, char *channel )
    1016 {
    1017         irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" );
    1018 }
    1019 
    1020 void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker )
    1021 {
    1022         irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" );
    1023 }
    1024 
    1025 void irc_kill( irc_t *irc, user_t *u )
    1026 {
    1027         char *nick, *s;
    1028         char reason[128];
    1029        
    1030         if( u->ic && u->ic->flags & OPT_LOGGING_OUT && set_getbool( &irc->set, "simulate_netsplit" ) )
    1031         {
    1032                 if( u->ic->acc->server )
    1033                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1034                                     u->ic->acc->server );
    1035                 else if( ( s = strchr( u->ic->acc->user, '@' ) ) )
    1036                         g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
    1037                                     s + 1 );
    1038                 else
    1039                         g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
    1040                                     u->ic->acc->prpl->name, irc->myhost );
    1041                
    1042                 /* proto_opt might contain garbage after the : */
    1043                 if( ( s = strchr( reason, ':' ) ) )
    1044                         *s = 0;
    1045         }
    1046         else
    1047         {
    1048                 strcpy( reason, "Leaving..." );
    1049         }
    1050        
    1051         irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
    1052        
    1053         nick = g_strdup( u->nick );
    1054         nick_lc( nick );
    1055         if( g_hash_table_lookup( irc->watches, nick ) )
    1056         {
    1057                 irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "logged offline" );
    1058         }
    1059         g_free( nick );
    1060 }
    1061 
    1062 int irc_send( irc_t *irc, char *nick, char *s, int flags )
    1063 {
    1064         struct groupchat *c = NULL;
    1065         user_t *u = NULL;
    1066        
    1067         if( strchr( CTYPES, *nick ) )
    1068         {
    1069                 if( !( c = irc_chat_by_channel( irc, nick ) ) )
    1070                 {
    1071                         irc_reply( irc, 403, "%s :Channel does not exist", nick );
    1072                         return( 0 );
    1073                 }
    1074         }
    1075         else
    1076         {
    1077                 u = user_find( irc, nick );
    1078                
    1079                 if( !u )
    1080                 {
    1081                         if( irc->is_private )
    1082                                 irc_reply( irc, 401, "%s :Nick does not exist", nick );
    1083                         else
    1084                                 irc_usermsg( irc, "Nick `%s' does not exist!", nick );
    1085                         return( 0 );
    1086                 }
    1087         }
    1088        
    1089         if( *s == 1 && s[strlen(s)-1] == 1 )
    1090         {
    1091                 if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 )
    1092                 {
    1093                         if( s[7] == ' ' ) s ++;
    1094                         s += 3;
    1095                         *(s++) = '/';
    1096                         *(s++) = 'm';
    1097                         *(s++) = 'e';
    1098                         *(s++) = ' ';
    1099                         s -= 4;
    1100                         s[strlen(s)-1] = 0;
    1101                 }
    1102                 else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 )
    1103                 {
    1104                         u = user_find( irc, irc->mynick );
    1105                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" );
    1106                         return( 1 );
    1107                 }
    1108                 else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 )
    1109                 {
    1110                         u = user_find( irc, irc->mynick );
    1111                         irc_privmsg( irc, u, "NOTICE", irc->nick, "", s );
    1112                         return( 1 );
    1113                 }
    1114                 else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 )
    1115                 {
    1116                         if( u && u->ic && u->ic->acc->prpl->send_typing && strlen( s ) >= 10 )
    1117                         {
    1118                                 time_t current_typing_notice = time( NULL );
    1119                                
    1120                                 if( current_typing_notice - u->last_typing_notice >= 5 )
    1121                                 {
    1122                                         u->ic->acc->prpl->send_typing( u->ic, u->handle, ( s[8] - '0' ) << 8 );
    1123                                         u->last_typing_notice = current_typing_notice;
    1124                                 }
    1125                         }
    1126                         return( 1 );
    1127                 }
    1128                 else
    1129                 {
    1130                         irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" );
    1131                         return( 0 );
    1132                 }
    1133         }
    1134        
    1135         if( u )
    1136         {
    1137                 /* For the next message, we probably do have to send new notices... */
    1138                 u->last_typing_notice = 0;
    1139                 u->is_private = irc->is_private;
    1140                
    1141                 if( u->is_private )
    1142                 {
    1143                         if( !u->online )
    1144                                 irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
    1145                         else if( u->away )
    1146                                 irc_reply( irc, 301, "%s :%s", u->nick, u->away );
    1147                 }
    1148                
    1149                 if( u->send_handler )
    1150                 {
    1151                         u->send_handler( irc, u, s, flags );
    1152                         return 1;
    1153                 }
    1154         }
    1155         else if( c && c->ic && c->ic->acc && c->ic->acc->prpl )
    1156         {
    1157                 return( imc_chat_msg( c, s, 0 ) );
    1158         }
    1159        
    1160         return( 0 );
    1161 }
    1162 
    1163 static gboolean buddy_send_handler_delayed( gpointer data, gint fd, b_input_condition cond )
    1164 {
    1165         user_t *u = data;
    1166        
    1167         /* Shouldn't happen, but just to be sure. */
    1168         if( u->sendbuf_len < 2 )
    1169                 return FALSE;
    1170        
    1171         u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */
    1172         imc_buddy_msg( u->ic, u->handle, u->sendbuf, u->sendbuf_flags );
    1173        
    1174         g_free( u->sendbuf );
    1175         u->sendbuf = NULL;
    1176         u->sendbuf_len = 0;
    1177         u->sendbuf_timer = 0;
    1178         u->sendbuf_flags = 0;
    1179        
    1180         return FALSE;
    1181 }
    1182 
    1183 void buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags )
    1184 {
    1185         if( !u || !u->ic ) return;
    1186        
    1187         if( set_getbool( &irc->set, "buddy_sendbuffer" ) && set_getint( &irc->set, "buddy_sendbuffer_delay" ) > 0 )
    1188         {
    1189                 int delay;
    1190                
    1191                 if( u->sendbuf_len > 0 && u->sendbuf_flags != flags)
    1192                 {
    1193                         /* Flush the buffer */
    1194                         b_event_remove( u->sendbuf_timer );
    1195                         buddy_send_handler_delayed( u, -1, 0 );
    1196                 }
    1197 
    1198                 if( u->sendbuf_len == 0 )
    1199                 {
    1200                         u->sendbuf_len = strlen( msg ) + 2;
    1201                         u->sendbuf = g_new( char, u->sendbuf_len );
    1202                         u->sendbuf[0] = 0;
    1203                         u->sendbuf_flags = flags;
    1204                 }
    1205                 else
    1206                 {
    1207                         u->sendbuf_len += strlen( msg ) + 1;
    1208                         u->sendbuf = g_renew( char, u->sendbuf, u->sendbuf_len );
    1209                 }
    1210                
    1211                 strcat( u->sendbuf, msg );
    1212                 strcat( u->sendbuf, "\n" );
    1213                
    1214                 delay = set_getint( &irc->set, "buddy_sendbuffer_delay" );
    1215                 if( delay <= 5 )
    1216                         delay *= 1000;
    1217                
    1218                 if( u->sendbuf_timer > 0 )
    1219                         b_event_remove( u->sendbuf_timer );
    1220                 u->sendbuf_timer = b_timeout_add( delay, buddy_send_handler_delayed, u );
    1221         }
    1222         else
    1223         {
    1224                 imc_buddy_msg( u->ic, u->handle, msg, flags );
    1225         }
    1226 }
    1227 
    1228 int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg )
    1229 {
    1230         char last = 0;
    1231         char *s = msg, *line = msg;
    1232        
    1233         /* The almighty linesplitter .. woohoo!! */
    1234         while( !last )
    1235         {
    1236                 if( *s == '\r' && *(s+1) == '\n' )
    1237                         *(s++) = 0;
    1238                 if( *s == '\n' )
    1239                 {
    1240                         last = s[1] == 0;
    1241                         *s = 0;
    1242                 }
    1243                 else
    1244                 {
    1245                         last = s[0] == 0;
    1246                 }
    1247                 if( *s == 0 )
    1248                 {
    1249                         if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 )
    1250                         {
    1251                                 irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host,
    1252                                            type, to, line + 4 );
    1253                         }
    1254                         else
    1255                         {
    1256                                 irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host,
    1257                                            type, to, prefix ? prefix : "", line );
    1258                         }
    1259                         line = s + 1;
    1260                 }
    1261                 s ++;
    1262         }
    1263        
    1264         return( 1 );
    1265 }
    1266 
    1267 int irc_msgfrom( irc_t *irc, char *nick, char *msg )
    1268 {
    1269         user_t *u = user_find( irc, nick );
    1270         static char *prefix = NULL;
    1271        
    1272         if( !u ) return( 0 );
    1273         if( prefix && *prefix ) g_free( prefix );
    1274        
    1275         if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 )
    1276         {
    1277                 int len = strlen( irc->nick) + 3;
    1278                 prefix = g_new (char, len );
    1279                 g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( &irc->set, "to_char" ) );
    1280                 prefix[len-1] = 0;
    1281         }
    1282         else
    1283         {
    1284                 prefix = "";
    1285         }
    1286        
    1287         return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) );
    1288 }
    1289 
    1290 int irc_noticefrom( irc_t *irc, char *nick, char *msg )
    1291 {
    1292         user_t *u = user_find( irc, nick );
    1293        
    1294         if( u )
    1295                 return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) );
    1296         else
    1297                 return( 0 );
     819                irc_write( irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
     820                           irc->user->user, irc->user->host, irc->user->nick,
     821                           changes );
    1298822}
    1299823
     
    1334858}
    1335859
    1336 struct groupchat *irc_chat_by_channel( irc_t *irc, char *channel )
    1337 {
    1338         struct groupchat *c;
    1339         account_t *a;
    1340        
    1341         /* This finds the connection which has a conversation which belongs to this channel */
    1342         for( a = irc->accounts; a; a = a->next )
    1343         {
    1344                 if( a->ic == NULL )
    1345                         continue;
    1346                
    1347                 c = a->ic->groupchats;
    1348                 while( c )
    1349                 {
    1350                         if( c->channel && g_strcasecmp( c->channel, channel ) == 0 )
    1351                                 return c;
    1352                        
    1353                         c = c->next;
    1354                 }
    1355         }
    1356        
    1357         return NULL;
    1358 }
     860static char *set_eval_charset( set_t *set, char *value )
     861{
     862        irc_t *irc = (irc_t*) set->data;
     863        char *test;
     864        gsize test_bytes = 0;
     865        GIConv ic, oc;
     866
     867        if( g_strcasecmp( value, "none" ) == 0 )
     868                value = g_strdup( "utf-8" );
     869
     870        if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
     871        {
     872                return NULL;
     873        }
     874       
     875        /* Do a test iconv to see if the user picked an IRC-compatible
     876           charset (for example utf-16 goes *horribly* wrong). */
     877        if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL ||
     878            test_bytes > 1 )
     879        {
     880                g_free( test );
     881                g_iconv_close( oc );
     882                irc_usermsg( irc, "Unsupported character set: The IRC protocol "
     883                                  "only supports 8-bit character sets." );
     884                return NULL;
     885        }
     886        g_free( test );
     887       
     888        if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
     889        {
     890                g_iconv_close( oc );
     891                return NULL;
     892        }
     893       
     894        if( irc->iconv != (GIConv) -1 )
     895                g_iconv_close( irc->iconv );
     896        if( irc->oconv != (GIConv) -1 )
     897                g_iconv_close( irc->oconv );
     898       
     899        irc->iconv = ic;
     900        irc->oconv = oc;
     901
     902        return value;
     903}
     904
     905/* Mostly meant for upgrades. If one of these is set to the non-default,
     906   set show_users of all channels to something with the same effect. */
     907static char *set_eval_bw_compat( set_t *set, char *value )
     908{
     909        irc_t *irc = set->data;
     910        char *val;
     911        GSList *l;
     912       
     913        irc_usermsg( irc, "Setting `%s' is obsolete, use the `show_users' "
     914                     "channel setting instead.", set->key );
     915       
     916        if( strcmp( set->key, "away_devoice" ) == 0 && !bool2int( value ) )
     917                val = "online,away";
     918        else if( strcmp( set->key, "show_offline" ) == 0 && bool2int( value ) )
     919                val = "online@,away+,offline";
     920        else
     921                return SET_INVALID;
     922       
     923        for( l = irc->channels; l; l = l->next )
     924        {
     925                irc_channel_t *ic = l->data;
     926                /* No need to check channel type, if the setting doesn't exist it
     927                   will just be ignored. */
     928                set_setstr( &ic->set, "show_users", val );
     929        }
     930       
     931        return SET_INVALID;
     932}
  • irc.h

    ref14a83 r2945c6f  
    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 "nt"        /* 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
     
    4850        USTATUS_SHUTDOWN = 8
    4951} irc_status_t;
     52
     53struct irc_user;
    5054
    5155typedef struct irc
     
    5963        GIConv iconv, oconv;
    6064
    61         int sentbytes;
    62         time_t oldtime;
    63 
     65        struct irc_user *root;
     66        struct irc_user *user;
     67       
     68        char *last_root_cmd; /* Either the nickname from which the last root
     69                                msg came, or the last channel root was talked
     70                                to. */
     71
     72        char *password; /* HACK: Used to save the user's password, but before
     73                           logging in, this may contain a password we should
     74                           send to identify after USER/NICK are received. */
     75
     76        char umode[8];
     77       
     78        struct query *queries;
     79        GSList *file_transfers;
     80       
     81        GSList *users, *channels;
     82        struct irc_channel *default_channel;
     83        GHashTable *nick_user_hash;
     84        GHashTable *watches; /* See irc_cmd_watch() */
     85
     86        gint r_watch_source_id;
     87        gint w_watch_source_id;
     88        gint ping_source_id;
     89        gint login_source_id; /* To slightly delay some events at login time. */
     90       
     91        struct bee *b;
     92} irc_t;
     93
     94typedef enum
     95{
     96        /* Replaced with iu->last_channel IRC_USER_PRIVATE = 1, */
     97        IRC_USER_AWAY = 2,
     98} irc_user_flags_t;
     99
     100typedef struct irc_user
     101{
     102        irc_t *irc;
     103       
    64104        char *nick;
    65105        char *user;
    66106        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;
     107        char *fullname;
     108       
     109        /* Nickname in lowercase for case sensitive searches */
     110        char *key;
     111       
     112        irc_user_flags_t flags;
     113        struct irc_channel *last_channel;
     114       
     115        GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
     116        guint pastebuf_timer;
     117        time_t away_reply_timeout; /* Only send a 301 if this time passed. */
     118       
     119        struct bee_user *bu;
     120       
     121        const struct irc_user_funcs *f;
     122} irc_user_t;
     123
     124struct irc_user_funcs
     125{
     126        gboolean (*privmsg)( irc_user_t *iu, const char *msg );
     127        gboolean (*ctcp)( irc_user_t *iu, char * const* ctcp );
     128};
     129
     130extern const struct irc_user_funcs irc_user_root_funcs;
     131extern const struct irc_user_funcs irc_user_self_funcs;
     132
     133typedef enum
     134{
     135        IRC_CHANNEL_JOINED = 1, /* The user is currently in the channel. */
     136        IRC_CHANNEL_TEMP = 2,   /* Erase the channel when the user leaves,
     137                                   and don't save it. */
     138       
     139        /* Hack: Set this flag right before jumping into IM when we expect
     140           a call to imcb_chat_new(). */
     141        IRC_CHANNEL_CHAT_PICKME = 0x10000,
     142} irc_channel_flags_t;
     143
     144typedef struct irc_channel
     145{
     146        irc_t *irc;
     147        char *name;
     148        char mode[8];
     149        int flags;
     150       
     151        char *topic;
     152        char *topic_who;
     153        time_t topic_time;
     154       
     155        GSList *users; /* struct irc_channel_user */
    91156        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 
     157       
     158        GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
     159        guint pastebuf_timer;
     160       
     161        const struct irc_channel_funcs *f;
     162        void *data;
     163} irc_channel_t;
     164
     165struct irc_channel_funcs
     166{
     167        gboolean (*privmsg)( irc_channel_t *ic, const char *msg );
     168        gboolean (*join)( irc_channel_t *ic );
     169        gboolean (*part)( irc_channel_t *ic, const char *msg );
     170        gboolean (*topic)( irc_channel_t *ic, const char *new );
     171        gboolean (*invite)( irc_channel_t *ic, irc_user_t *iu );
     172       
     173        gboolean (*_init)( irc_channel_t *ic );
     174        gboolean (*_free)( irc_channel_t *ic );
     175};
     176
     177typedef enum
     178{
     179        IRC_CHANNEL_USER_OP = 1,
     180        IRC_CHANNEL_USER_HALFOP = 2,
     181        IRC_CHANNEL_USER_VOICE = 4,
     182        IRC_CHANNEL_USER_NONE = 8,
     183} irc_channel_user_flags_t;
     184
     185typedef struct irc_channel_user
     186{
     187        irc_user_t *iu;
     188        int flags;
     189} irc_channel_user_t;
     190
     191typedef enum
     192{
     193        IRC_CC_TYPE_DEFAULT,
     194        IRC_CC_TYPE_REST,
     195        IRC_CC_TYPE_GROUP,
     196        IRC_CC_TYPE_ACCOUNT,
     197        IRC_CC_TYPE_PROTOCOL,
     198} irc_control_channel_type_t;
     199
     200struct irc_control_channel
     201{
     202        irc_control_channel_type_t type;
     203        struct bee_group *group;
     204        struct account *account;
     205        struct prpl *protocol;
     206        char modes[4];
     207};
     208
     209extern const struct bee_ui_funcs irc_ui_funcs;
     210
     211typedef enum
     212{
     213        IRC_CDU_SILENT,
     214        IRC_CDU_PART,
     215        IRC_CDU_KICK,
     216} irc_channel_del_user_type_t;
     217
     218/* irc.c */
    100219extern GSList *irc_connection_list;
    101220
     
    103222void irc_abort( irc_t *irc, int immed, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
    104223void irc_free( irc_t *irc );
    105 
    106 void irc_exec( irc_t *irc, char **cmd );
     224void irc_setpass (irc_t *irc, const char *pass);
     225
    107226void irc_process( irc_t *irc );
    108227char **irc_parse_line( char *line );
    109228char *irc_build_line( char **cmd );
    110229
    111 void irc_vawrite( irc_t *irc, char *format, va_list params );
    112230void irc_write( irc_t *irc, char *format, ... ) G_GNUC_PRINTF( 2, 3 );
    113231void 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 );
     232void irc_vawrite( irc_t *irc, char *format, va_list params );
     233
     234void irc_flush( irc_t *irc );
     235void irc_switch_fd( irc_t *irc, int fd );
     236void irc_sync( irc_t *irc );
     237void irc_desync( irc_t *irc );
     238
    119239int 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 );
     240
     241void irc_umode_set( irc_t *irc, const char *s, gboolean allow_priv );
     242
     243/* irc_channel.c */
     244irc_channel_t *irc_channel_new( irc_t *irc, const char *name );
     245irc_channel_t *irc_channel_by_name( irc_t *irc, const char *name );
     246irc_channel_t *irc_channel_get( irc_t *irc, char *id );
     247int irc_channel_free( irc_channel_t *ic );
     248void irc_channel_free_soon( irc_channel_t *ic );
     249int irc_channel_add_user( irc_channel_t *ic, irc_user_t *iu );
     250int irc_channel_del_user( irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg );
     251irc_channel_user_t *irc_channel_has_user( irc_channel_t *ic, irc_user_t *iu );
     252int irc_channel_set_topic( irc_channel_t *ic, const char *topic, const irc_user_t *who );
     253void irc_channel_user_set_mode( irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags );
     254void irc_channel_auto_joins( irc_t *irc, struct account *acc );
     255void irc_channel_printf( irc_channel_t *ic, char *format, ... );
     256gboolean irc_channel_name_ok( const char *name );
     257void irc_channel_name_strip( char *name );
     258int irc_channel_name_cmp( const char *a_, const char *b_ );
     259void irc_channel_update_ops( irc_channel_t *ic, char *value );
     260char *set_eval_irc_channel_ops( struct set *set, char *value );
     261
     262/* irc_commands.c */
     263void irc_exec( irc_t *irc, char **cmd );
     264
     265/* irc_send.c */
     266void irc_send_num( irc_t *irc, int code, char *format, ... ) G_GNUC_PRINTF( 3, 4 );
     267void irc_send_login( irc_t *irc );
     268void irc_send_motd( irc_t *irc );
     269void irc_usermsg( irc_t *irc, char *format, ... );
     270void irc_send_join( irc_channel_t *ic, irc_user_t *iu );
     271void irc_send_part( irc_channel_t *ic, irc_user_t *iu, const char *reason );
     272void irc_send_quit( irc_user_t *iu, const char *reason );
     273void irc_send_kick( irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason );
     274void irc_send_names( irc_channel_t *ic );
     275void irc_send_topic( irc_channel_t *ic, gboolean topic_change );
     276void irc_send_whois( irc_user_t *iu );
     277void irc_send_who( irc_t *irc, GSList *l, const char *channel );
     278void irc_send_msg( irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix );
     279void irc_send_msg_raw( irc_user_t *iu, const char *type, const char *dst, const char *msg );
     280void irc_send_msg_f( irc_user_t *iu, const char *type, const char *dst, const char *format, ... ) G_GNUC_PRINTF( 4, 5 );
     281void irc_send_nick( irc_user_t *iu, const char *new );
     282void irc_send_channel_user_mode_diff( irc_channel_t *ic, irc_user_t *iu,
     283                                      irc_channel_user_flags_t old, irc_channel_user_flags_t new );
     284
     285/* irc_user.c */
     286irc_user_t *irc_user_new( irc_t *irc, const char *nick );
     287int irc_user_free( irc_t *irc, irc_user_t *iu );
     288irc_user_t *irc_user_by_name( irc_t *irc, const char *nick );
     289int irc_user_set_nick( irc_user_t *iu, const char *new );
     290gint irc_user_cmp( gconstpointer a_, gconstpointer b_ );
     291const char *irc_user_get_away( irc_user_t *iu );
     292void irc_user_quit( irc_user_t *iu, const char *msg );
     293
     294/* irc_util.c */
     295char *set_eval_timezone( struct set *set, char *value );
     296char *irc_format_timestamp( irc_t *irc, time_t msg_ts );
     297
     298/* irc_im.c */
     299void bee_irc_channel_update( irc_t *irc, irc_channel_t *ic, irc_user_t *iu );
    141300
    142301#endif
  • irc_commands.c

    ref14a83 r2945c6f  
    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 )
     
    5352        else if( global.conf->auth_pass )
    5453        {
    55                 irc_reply( irc, 464, ":Incorrect password" );
     54                irc_send_num( irc, 464, ":Incorrect password" );
    5655        }
    5756        else
     
    6564static void irc_cmd_user( irc_t *irc, char **cmd )
    6665{
    67         irc->user = g_strdup( cmd[1] );
    68         irc->realname = g_strdup( cmd[4] );
     66        irc->user->user = g_strdup( cmd[1] );
     67        irc->user->fullname = g_strdup( cmd[4] );
    6968       
    7069        irc_check_login( irc );
     
    7372static void irc_cmd_nick( irc_t *irc, char **cmd )
    7473{
    75         if( irc->status & USTATUS_IDENTIFIED && irc->nick )
    76         {
    77                 irc_reply( irc, 438, "%s %s :You can only change your nick if you're not "
    78                            "logged in (i.e. pre-identify)", irc->nick, cmd[1] );
    79         }
    80         /* This is not clean, but for now it'll have to be like this... */
    81         else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) || ( user_find( irc, cmd[1] ) != NULL ) )
    82         {
    83                 irc_reply( irc, 433, "%s :This nick is already in use", cmd[1] );
     74        irc_user_t *iu;
     75       
     76        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) && iu != irc->user )
     77        {
     78                irc_send_num( irc, 433, "%s :This nick is already in use", cmd[1] );
    8479        }
    8580        else if( !nick_ok( cmd[1] ) )
    8681        {
    8782                /* [SH] Invalid characters. */
    88                 irc_reply( irc, 432, "%s :This nick contains invalid characters", cmd[1] );
    89         }
    90         else if(irc->nick)
    91         {
    92                 if( user_find( irc, irc->nick ) )
    93                         user_rename(irc, irc->nick, cmd[1]);
    94 
    95                 irc_write( irc, ":%s!%s@%s NICK %s", irc->nick, irc->user, irc->host, cmd[1] );
    96                 g_free(irc->nick);
    97                 irc->nick = g_strdup( cmd[1] );
    98         }
    99         else
    100         {
    101                 irc->nick = g_strdup( cmd[1] );
     83                irc_send_num( irc, 432, "%s :This nick contains invalid characters", cmd[1] );
     84        }
     85        else if( irc->status & USTATUS_LOGGED_IN )
     86        {
     87                if( irc->status & USTATUS_IDENTIFIED )
     88                {
     89                        irc_setpass( irc, NULL );
     90                        irc->status &= ~USTATUS_IDENTIFIED;
     91                        irc_umode_set( irc, "-R", 1 );
     92                        irc_usermsg( irc, "Changing nicks resets your identify status. "
     93                                     "Re-identify or register a new account if you want "
     94                                     "your configuration to be saved. See \x02help "
     95                                     "nick_changes\x02." );
     96                }
     97               
     98                irc_user_set_nick( irc->user, cmd[1] );
     99        }
     100        else
     101        {
     102                g_free( irc->user->nick );
     103                irc->user->nick = g_strdup( cmd[1] );
    102104               
    103105                irc_check_login( irc );
     
    115117static void irc_cmd_ping( irc_t *irc, char **cmd )
    116118{
    117         irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost );
    118 }
     119        irc_write( irc, ":%s PONG %s :%s", irc->root->host,
     120                   irc->root->host, cmd[1]?cmd[1]:irc->root->host );
     121}
     122
     123static void irc_cmd_pong( irc_t *irc, char **cmd )
     124{
     125        /* We could check the value we get back from the user, but in
     126           fact we don't care, we're just happy s/he's still alive. */
     127        irc->last_pong = gettime();
     128        irc->pinging = 0;
     129}
     130
     131static void irc_cmd_join( irc_t *irc, char **cmd )
     132{
     133        char *comma, *s = cmd[1];
     134       
     135        while( s )
     136        {
     137                irc_channel_t *ic;
     138               
     139                if( ( comma = strchr( s, ',' ) ) )
     140                        *comma = '\0';
     141               
     142                if( ( ic = irc_channel_by_name( irc, s ) ) == NULL )
     143                {
     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                                bee_irc_channel_update( ic->irc, ic, NULL );
     166                        }
     167                }
     168               
     169                if( ic == NULL )
     170                {
     171                        irc_send_num( irc, 479, "%s :Invalid channel name", s );
     172                        goto next;
     173                }
     174               
     175                if( ic->flags & IRC_CHANNEL_JOINED )
     176                        /* Dude, you're already there...
     177                           RFC doesn't have any reply for that though? */
     178                        goto next;
     179               
     180                if( ic->f->join && !ic->f->join( ic ) )
     181                        /* The story is: FALSE either means the handler
     182                           showed an error message, or is doing some work
     183                           before the join should be confirmed. (In the
     184                           latter case, the caller should take care of that
     185                           confirmation.) TRUE means all's good, let the
     186                           user join the channel right away. */
     187                        goto next;
     188               
     189                irc_channel_add_user( ic, irc->user );
     190               
     191next:
     192                if( comma )
     193                {
     194                        s = comma + 1;
     195                        *comma = ',';
     196                }
     197                else
     198                        break;
     199        }
     200}
     201
     202static void irc_cmd_names( irc_t *irc, char **cmd )
     203{
     204        irc_channel_t *ic;
     205       
     206        if( cmd[1] && ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
     207                irc_send_names( ic );
     208        /* With no args, we should show /names of all chans. Make the code
     209           below work well if necessary.
     210        else
     211        {
     212                GSList *l;
     213               
     214                for( l = irc->channels; l; l = l->next )
     215                        irc_send_names( l->data );
     216        }
     217        */
     218}
     219
     220static void irc_cmd_part( irc_t *irc, char **cmd )
     221{
     222        irc_channel_t *ic;
     223       
     224        if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
     225        {
     226                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
     227        }
     228        else if( irc_channel_del_user( ic, irc->user, IRC_CDU_PART, cmd[2] ) )
     229        {
     230                if( ic->f->part )
     231                        ic->f->part( ic, NULL );
     232        }
     233        else
     234        {
     235                irc_send_num( irc, 442, "%s :You're not on that channel", cmd[1] );
     236        }
     237}
     238
     239static void irc_cmd_whois( irc_t *irc, char **cmd )
     240{
     241        char *nick = cmd[1];
     242        irc_user_t *iu = irc_user_by_name( irc, nick );
     243       
     244        if( iu )
     245                irc_send_whois( iu );
     246        else
     247                irc_send_num( irc, 401, "%s :Nick does not exist", nick );
     248}
     249
     250static void irc_cmd_whowas( irc_t *irc, char **cmd )
     251{
     252        /* For some reason irssi tries a whowas when whois fails. We can
     253           ignore this, but then the user never gets a "user not found"
     254           message from irssi which is a bit annoying. So just respond
     255           with not-found and irssi users will get better error messages */
     256       
     257        irc_send_num( irc, 406, "%s :Nick does not exist", cmd[1] );
     258        irc_send_num( irc, 369, "%s :End of WHOWAS", cmd[1] );
     259}
     260
     261static void irc_cmd_motd( irc_t *irc, char **cmd )
     262{
     263        irc_send_motd( irc );
     264}
     265
     266static void irc_cmd_mode( irc_t *irc, char **cmd )
     267{
     268        if( irc_channel_name_ok( cmd[1] ) )
     269        {
     270                irc_channel_t *ic;
     271               
     272                if( ( ic = irc_channel_by_name( irc, cmd[1] ) ) == NULL )
     273                        irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
     274                else if( cmd[2] )
     275                {
     276                        if( *cmd[2] == '+' || *cmd[2] == '-' )
     277                                irc_send_num( irc, 477, "%s :Can't change channel modes", cmd[1] );
     278                        else if( *cmd[2] == 'b' )
     279                                irc_send_num( irc, 368, "%s :No bans possible", cmd[1] );
     280                }
     281                else
     282                        irc_send_num( irc, 324, "%s +%s", cmd[1], ic->mode );
     283        }
     284        else
     285        {
     286                if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
     287                {
     288                        if( cmd[2] )
     289                                irc_umode_set( irc, cmd[2], 0 );
     290                        else
     291                                irc_send_num( irc, 221, "+%s", irc->umode );
     292                }
     293                else
     294                        irc_send_num( irc, 502, ":Don't touch their modes" );
     295        }
     296}
     297
     298static void irc_cmd_who( irc_t *irc, char **cmd )
     299{
     300        char *channel = cmd[1];
     301        irc_channel_t *ic;
     302       
     303        if( !channel || *channel == '0' || *channel == '*' || !*channel )
     304                irc_send_who( irc, irc->users, "**" );
     305        else if( ( ic = irc_channel_by_name( irc, channel ) ) )
     306                irc_send_who( irc, ic->users, channel );
     307        else
     308                irc_send_num( irc, 403, "%s :No such channel", channel );
     309}
     310
     311static void irc_cmd_privmsg( irc_t *irc, char **cmd )
     312{
     313        irc_channel_t *ic;
     314        irc_user_t *iu;
     315       
     316        if( !cmd[2] )
     317        {
     318                irc_send_num( irc, 412, ":No text to send" );
     319                return;
     320        }
     321       
     322        /* Don't treat CTCP actions as real CTCPs, just convert them right now. */
     323        if( g_strncasecmp( cmd[2], "\001ACTION", 7 ) == 0 )
     324        {
     325                cmd[2] += 4;
     326                memcpy( cmd[2], "/me", 3 );
     327                if( cmd[2][strlen(cmd[2])-1] == '\001' )
     328                        cmd[2][strlen(cmd[2])-1] = '\0';
     329        }
     330       
     331        if( irc_channel_name_ok( cmd[1] ) &&
     332            ( ic = irc_channel_by_name( irc, cmd[1] ) ) )
     333        {
     334                if( ic->f->privmsg )
     335                        ic->f->privmsg( ic, cmd[2] );
     336        }
     337        else if( ( iu = irc_user_by_name( irc, cmd[1] ) ) )
     338        {
     339                if( cmd[2][0] == '\001' )
     340                {
     341                        char **ctcp;
     342                       
     343                        if( iu->f->ctcp == NULL )
     344                                return;
     345                        if( cmd[2][strlen(cmd[2])-1] == '\001' )
     346                                cmd[2][strlen(cmd[2])-1] = '\0';
     347                       
     348                        ctcp = split_command_parts( cmd[2] + 1 );
     349                        iu->f->ctcp( iu, ctcp );
     350                }
     351                else if( iu->f->privmsg )
     352                {
     353                        iu->last_channel = NULL;
     354                        iu->f->privmsg( iu, cmd[2] );
     355                }
     356        }
     357        else
     358        {
     359                irc_send_num( irc, 401, "%s :No such nick/channel", cmd[1] );
     360        }
     361}
     362
     363static void irc_cmd_notice( irc_t *irc, char **cmd )
     364{
     365        if( !cmd[2] )
     366        {
     367                irc_send_num( irc, 412, ":No text to send" );
     368                return;
     369        }
     370       
     371        /* At least for now just echo. IIRC some IRC clients use self-notices
     372           for lag checks, so try to support that. */
     373        if( nick_cmp( cmd[1], irc->user->nick ) == 0 )
     374                irc_send_msg( irc->user, "NOTICE", irc->user->nick, cmd[2], NULL );
     375}
     376
     377static void irc_cmd_nickserv( irc_t *irc, char **cmd )
     378{
     379        /* [SH] This aliases the NickServ command to PRIVMSG root */
     380        /* [TV] This aliases the NS command to PRIVMSG root as well */
     381        root_command( irc, cmd + 1 );
     382}
     383
     384
    119385
    120386static void irc_cmd_oper( irc_t *irc, char **cmd )
     
    126392        {
    127393                irc_umode_set( irc, "+o", 1 );
    128                 irc_reply( irc, 381, ":Password accepted" );
    129         }
    130         else
    131         {
    132                 irc_reply( irc, 432, ":Incorrect password" );
    133         }
    134 }
    135 
    136 static void irc_cmd_mode( irc_t *irc, char **cmd )
    137 {
    138         if( strchr( CTYPES, *cmd[1] ) )
    139         {
    140                 if( cmd[2] )
    141                 {
    142                         if( *cmd[2] == '+' || *cmd[2] == '-' )
    143                                 irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] );
    144                         else if( *cmd[2] == 'b' )
    145                                 irc_reply( irc, 368, "%s :No bans possible", cmd[1] );
    146                 }
    147                 else
    148                         irc_reply( irc, 324, "%s +%s", cmd[1], CMODE );
    149         }
    150         else
    151         {
    152                 if( nick_cmp( cmd[1], irc->nick ) == 0 )
    153                 {
    154                         if( cmd[2] )
    155                                 irc_umode_set( irc, cmd[2], 0 );
    156                         else
    157                                 irc_reply( irc, 221, "+%s", irc->umode );
    158                 }
    159                 else
    160                         irc_reply( irc, 502, ":Don't touch their modes" );
    161         }
    162 }
    163 
    164 static void irc_cmd_names( irc_t *irc, char **cmd )
    165 {
    166         irc_names( irc, cmd[1]?cmd[1]:irc->channel );
    167 }
    168 
    169 static void irc_cmd_part( irc_t *irc, char **cmd )
    170 {
    171         struct groupchat *c;
    172        
    173         if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
    174         {
    175                 user_t *u = user_find( irc, irc->nick );
    176                
    177                 /* Not allowed to leave control channel */
    178                 irc_part( irc, u, irc->channel );
    179                 irc_join( irc, u, irc->channel );
    180         }
    181         else if( ( c = irc_chat_by_channel( irc, cmd[1] ) ) )
    182         {
    183                 user_t *u = user_find( irc, irc->nick );
    184                
    185                 irc_part( irc, u, c->channel );
    186                
    187                 if( c->ic )
    188                 {
    189                         c->joined = 0;
    190                         c->ic->acc->prpl->chat_leave( c );
    191                 }
    192         }
    193         else
    194         {
    195                 irc_reply( irc, 403, "%s :No such channel", cmd[1] );
    196         }
    197 }
    198 
    199 static void irc_cmd_join( irc_t *irc, char **cmd )
    200 {
    201         if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
    202                 ; /* Dude, you're already there...
    203                      RFC doesn't have any reply for that though? */
    204         else if( cmd[1] )
    205         {
    206                 struct chat *c;
    207                
    208                 if( strchr( CTYPES, cmd[1][0] ) == NULL || cmd[1][1] == 0 )
    209                         irc_reply( irc, 479, "%s :Invalid channel name", cmd[1] );
    210                 else if( ( c = chat_bychannel( irc, cmd[1] ) ) && c->acc && c->acc->ic )
    211                         chat_join( irc, c, cmd[2] );
    212                 else
    213                         irc_reply( irc, 403, "%s :No such channel", cmd[1] );
     394                irc_send_num( irc, 381, ":Password accepted" );
     395        }
     396        else
     397        {
     398                irc_send_num( irc, 432, ":Incorrect password" );
    214399        }
    215400}
     
    217402static void irc_cmd_invite( irc_t *irc, char **cmd )
    218403{
    219         char *nick = cmd[1], *channel = cmd[2];
    220         struct groupchat *c = irc_chat_by_channel( irc, channel );
    221         user_t *u = user_find( irc, nick );
    222        
    223         if( u && c && ( u->ic == c->ic ) )
    224                 if( c->ic && c->ic->acc->prpl->chat_invite )
    225                 {
    226                         c->ic->acc->prpl->chat_invite( c, u->handle, NULL );
    227                         irc_reply( irc, 341, "%s %s", nick, channel );
    228                         return;
    229                 }
    230        
    231         irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel );
    232 }
    233 
    234 static void irc_cmd_privmsg( irc_t *irc, char **cmd )
    235 {
    236         if ( !cmd[2] )
    237         {
    238                 irc_reply( irc, 412, ":No text to send" );
    239         }
    240         else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 )
    241         {
    242                 irc_write( irc, ":%s!%s@%s %s %s :%s", irc->nick, irc->user, irc->host, cmd[0], cmd[1], cmd[2] );
    243         }
    244         else
    245         {
    246                 if( g_strcasecmp( cmd[1], irc->channel ) == 0 )
    247                 {
    248                         unsigned int i;
    249                         char *t = set_getstr( &irc->set, "default_target" );
    250                        
    251                         if( g_strcasecmp( t, "last" ) == 0 && irc->last_target )
    252                                 cmd[1] = irc->last_target;
    253                         else if( g_strcasecmp( t, "root" ) == 0 )
    254                                 cmd[1] = irc->mynick;
    255                        
    256                         for( i = 0; i < strlen( cmd[2] ); i ++ )
    257                         {
    258                                 if( cmd[2][i] == ' ' ) break;
    259                                 if( cmd[2][i] == ':' || cmd[2][i] == ',' )
    260                                 {
    261                                         cmd[1] = cmd[2];
    262                                         cmd[2] += i;
    263                                         *cmd[2] = 0;
    264                                         while( *(++cmd[2]) == ' ' );
    265                                         break;
    266                                 }
    267                         }
    268                        
    269                         irc->is_private = 0;
    270                        
    271                         if( cmd[1] != irc->last_target )
    272                         {
    273                                 g_free( irc->last_target );
    274                                 irc->last_target = g_strdup( cmd[1] );
    275                         }
    276                 }
    277                 else
    278                 {
    279                         irc->is_private = 1;
    280                 }
    281                 irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? OPT_AWAY : 0 );
    282         }
    283 }
    284 
    285 static void irc_cmd_who( irc_t *irc, char **cmd )
    286 {
    287         char *channel = cmd[1];
    288         user_t *u = irc->users;
    289         struct groupchat *c;
    290         GList *l;
    291        
    292         if( !channel || *channel == '0' || *channel == '*' || !*channel )
    293                 while( u )
    294                 {
    295                         irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
    296                         u = u->next;
    297                 }
    298         else if( g_strcasecmp( channel, irc->channel ) == 0 )
    299                 while( u )
    300                 {
    301                         if( u->online )
    302                                 irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
    303                         u = u->next;
    304                 }
    305         else if( ( c = irc_chat_by_channel( irc, channel ) ) )
    306                 for( l = c->in_room; l; l = l->next )
    307                 {
    308                         if( ( u = user_findhandle( c->ic, l->data ) ) )
    309                                 irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname );
    310                 }
    311         else if( ( u = user_find( irc, channel ) ) )
    312                 irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname );
    313        
    314         irc_reply( irc, 315, "%s :End of /WHO list", channel?channel:"**" );
     404        irc_channel_t *ic;
     405        irc_user_t *iu;
     406       
     407        if( ( iu = irc_user_by_name( irc, cmd[1] ) ) == NULL )
     408        {
     409                irc_send_num( irc, 401, "%s :No such nick", cmd[1] );
     410                return;
     411        }
     412        else if( ( ic = irc_channel_by_name( irc, cmd[2] ) ) == NULL )
     413        {
     414                irc_send_num( irc, 403, "%s :No such channel", cmd[2] );
     415                return;
     416        }
     417       
     418        if( !ic->f->invite )
     419                irc_send_num( irc, 482, "%s :Can't invite people here", cmd[2] );
     420        else if( ic->f->invite( ic, iu ) )
     421                irc_send_num( irc, 341, "%s %s", iu->nick, ic->name );
    315422}
    316423
    317424static void irc_cmd_userhost( irc_t *irc, char **cmd )
    318425{
    319         user_t *u;
    320426        int i;
    321427       
     
    327433       
    328434        for( i = 1; cmd[i]; i ++ )
    329                 if( ( u = user_find( irc, cmd[i] ) ) )
    330                 {
    331                         if( u->online && u->away )
    332                                 irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host );
    333                         else
    334                                 irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host );
    335                 }
     435        {
     436                irc_user_t *iu = irc_user_by_name( irc, cmd[i] );
     437               
     438                if( iu )
     439                        irc_send_num( irc, 302, ":%s=%c%s@%s", iu->nick,
     440                                      irc_user_get_away( iu ) ? '-' : '+',
     441                                      iu->user, iu->host );
     442        }
    336443}
    337444
    338445static void irc_cmd_ison( irc_t *irc, char **cmd )
    339446{
    340         user_t *u;
    341447        char buff[IRC_MAX_LINE];
    342448        int lenleft, i;
     
    354460                while( *this )
    355461                {
     462                        irc_user_t *iu;
     463                       
    356464                        if( ( next = strchr( this, ' ' ) ) )
    357465                                *next = 0;
    358466                       
    359                         if( ( u = user_find( irc, this ) ) && u->online )
    360                         {
    361                                 lenleft -= strlen( u->nick ) + 1;
     467                        if( ( iu = irc_user_by_name( irc, this ) ) &&
     468                            iu->bu && iu->bu->flags & BEE_USER_ONLINE )
     469                        {
     470                                lenleft -= strlen( iu->nick ) + 1;
    362471                               
    363472                                if( lenleft < 0 )
    364473                                        break;
    365474                               
    366                                 strcat( buff, u->nick );
     475                                strcat( buff, iu->nick );
    367476                                strcat( buff, " " );
    368477                        }
     
    387496                buff[strlen(buff)-1] = '\0';
    388497       
    389         irc_reply( irc, 303, ":%s", buff );
     498        irc_send_num( irc, 303, ":%s", buff );
    390499}
    391500
     
    400509        {
    401510                char *nick;
    402                 user_t *u;
     511                irc_user_t *iu;
    403512               
    404513                if( !cmd[i][0] || !cmd[i][1] )
     
    408517                nick_lc( nick );
    409518               
    410                 u = user_find( irc, nick );
     519                iu = irc_user_by_name( irc, nick );
    411520               
    412521                if( cmd[i][0] == '+' )
     
    415524                                g_hash_table_insert( irc->watches, nick, nick );
    416525                       
    417                         if( u && u->online )
    418                                 irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, (int) time( NULL ), "is online" );
     526                        if( iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE )
     527                                irc_send_num( irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
     528                                              iu->host, (int) time( NULL ), "is online" );
    419529                        else
    420                                 irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", (int) time( NULL ), "is offline" );
     530                                irc_send_num( irc, 605, "%s %s %s %d :%s", nick, "*", "*",
     531                                              (int) time( NULL ), "is offline" );
    421532                }
    422533                else if( cmd[i][0] == '-' )
     
    429540                                g_free( okey );
    430541                               
    431                                 irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
     542                                irc_send_num( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" );
    432543                        }
    433544                }
     
    437548static void irc_cmd_topic( irc_t *irc, char **cmd )
    438549{
    439         char *channel = cmd[1];
    440         char *topic = cmd[2];
    441        
    442         if( topic )
    443         {
    444                 /* Send the topic */
    445                 struct groupchat *c = irc_chat_by_channel( irc, channel );
    446                 if( c && c->ic && c->ic->acc->prpl->chat_topic )
    447                         c->ic->acc->prpl->chat_topic( c, topic );
    448         }
    449         else
    450         {
    451                 /* Get the topic */
    452                 irc_topic( irc, channel );
     550        irc_channel_t *ic = irc_channel_by_name( irc, cmd[1] );
     551        const char *new = cmd[2];
     552       
     553        if( ic == NULL )
     554        {
     555                irc_send_num( irc, 403, "%s :No such channel", cmd[1] );
     556        }
     557        else if( new )
     558        {
     559                if( ic->f->topic == NULL )
     560                        irc_send_num( irc, 482, "%s :Can't change this channel's topic", ic->name );
     561                else if( ic->f->topic( ic, new ) )
     562                        irc_send_topic( ic, TRUE );
     563        }
     564        else
     565        {
     566                irc_send_topic( ic, FALSE );
    453567        }
    454568}
     
    456570static void irc_cmd_away( irc_t *irc, char **cmd )
    457571{
    458         user_t *u = user_find( irc, irc->nick );
    459         char *away = cmd[1];
    460        
    461         if( !u ) return;
    462        
    463         if( away && *away )
    464         {
     572        if( cmd[1] && *cmd[1] )
     573        {
     574                char away[strlen(cmd[1])+1];
    465575                int i, j;
    466576               
    467577                /* Copy away string, but skip control chars. Mainly because
    468578                   Jabber really doesn't like them. */
    469                 u->away = g_malloc( strlen( away ) + 1 );
    470                 for( i = j = 0; away[i]; i ++ )
    471                         if( ( u->away[j] = away[i] ) >= ' ' )
     579                for( i = j = 0; cmd[1][i]; i ++ )
     580                        if( ( away[j] = cmd[1][i] ) >= ' ' )
    472581                                j ++;
    473                 u->away[j] = 0;
    474                
    475                 irc_reply( irc, 306, ":You're now away: %s", u->away );
    476                 /* irc_umode_set( irc, irc->myhost, "+a" ); */
    477         }
    478         else
    479         {
    480                 if( u->away ) g_free( u->away );
    481                 u->away = NULL;
    482                 /* irc_umode_set( irc, irc->myhost, "-a" ); */
    483                 irc_reply( irc, 305, ":Welcome back" );
    484         }
    485        
    486         set_setstr( &irc->set, "away", u->away );
    487 }
    488 
    489 static void irc_cmd_whois( irc_t *irc, char **cmd )
    490 {
    491         char *nick = cmd[1];
    492         user_t *u = user_find( irc, nick );
    493        
    494         if( u )
    495         {
    496                 irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname );
    497                
    498                 if( u->ic )
    499                         irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->ic->acc->user,
    500                                    u->ic->acc->server && *u->ic->acc->server ? u->ic->acc->server : "",
    501                                    u->ic->acc->prpl->name );
    502                 else
    503                         irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO );
    504                
    505                 if( !u->online )
    506                         irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" );
    507                 else if( u->away )
    508                         irc_reply( irc, 301, "%s :%s", u->nick, u->away );
    509                 if( u->status_msg )
    510                         irc_reply( irc, 320, "%s :%s", u->nick, u->status_msg );
    511                
    512                 irc_reply( irc, 318, "%s :End of /WHOIS list", nick );
    513         }
    514         else
    515         {
    516                 irc_reply( irc, 401, "%s :Nick does not exist", nick );
    517         }
    518 }
    519 
    520 static void irc_cmd_whowas( irc_t *irc, char **cmd )
    521 {
    522         /* For some reason irssi tries a whowas when whois fails. We can
    523            ignore this, but then the user never gets a "user not found"
    524            message from irssi which is a bit annoying. So just respond
    525            with not-found and irssi users will get better error messages */
    526        
    527         irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] );
    528         irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] );
    529 }
    530 
    531 static void irc_cmd_nickserv( irc_t *irc, char **cmd )
    532 {
    533         /* [SH] This aliases the NickServ command to PRIVMSG root */
    534         /* [TV] This aliases the NS command to PRIVMSG root as well */
    535         root_command( irc, cmd + 1 );
    536 }
    537 
    538 static void irc_cmd_motd( irc_t *irc, char **cmd )
    539 {
    540         irc_motd( irc );
    541 }
    542 
    543 static void irc_cmd_pong( irc_t *irc, char **cmd )
    544 {
    545         /* We could check the value we get back from the user, but in
    546            fact we don't care, we're just happy he's still alive. */
    547         irc->last_pong = gettime();
    548         irc->pinging = 0;
     582                away[j] = '\0';
     583               
     584                irc_send_num( irc, 306, ":You're now away: %s", away );
     585                set_setstr( &irc->b->set, "away", away );
     586        }
     587        else
     588        {
     589                irc_send_num( irc, 305, ":Welcome back" );
     590                set_setstr( &irc->b->set, "away", NULL );
     591        }
    549592}
    550593
    551594static void irc_cmd_version( irc_t *irc, char **cmd )
    552595{
    553         irc_reply( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
     596        irc_send_num( irc, 351, "bitlbee-%s. %s :%s/%s ",
     597                      BITLBEE_VERSION, irc->root->host, ARCH, CPU );
    554598}
    555599
    556600static void irc_cmd_completions( irc_t *irc, char **cmd )
    557601{
    558         user_t *u = user_find( irc, irc->mynick );
    559602        help_t *h;
    560603        set_t *s;
    561604        int i;
    562605       
    563         irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" );
     606        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK" );
    564607       
    565608        for( i = 0; commands[i].command; i ++ )
    566                 irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command );
     609                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", commands[i].command );
    567610       
    568611        for( h = global.help; h; h = h->next )
    569                 irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->title );
    570        
    571         for( s = irc->set; s; s = s->next )
    572                 irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key );
    573        
    574         irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" );
     612                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title );
     613       
     614        for( s = irc->b->set; s; s = s->next )
     615                irc_send_msg_f( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key );
     616       
     617        irc_send_msg_raw( irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END" );
    575618}
    576619
     
    582625                ipc_to_master( cmd );
    583626       
    584         irc_reply( irc, 382, "%s :Rehashing", global.conf_file );
     627        irc_send_num( irc, 382, "%s :Rehashing", global.conf_file );
    585628}
    586629
     
    591634        { "quit",        0, irc_cmd_quit,        0 },
    592635        { "ping",        0, irc_cmd_ping,        0 },
    593         { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
     636        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
     637        { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
     638        { "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
     639        { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
     640        { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
     641        { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
     642        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
    594643        { "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
    595         { "names",       0, irc_cmd_names,       IRC_CMD_LOGGED_IN },
    596         { "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
    597         { "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
    598         { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
     644        { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
    599645        { "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
    600         { "notice",      1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
    601         { "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
     646        { "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
     647        { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
     648        { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
     649        { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
     650        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
     651        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
    602652        { "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
    603653        { "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
    604654        { "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
     655        { "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
    605656        { "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
    606         { "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
    607         { "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
    608         { "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
    609         { "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
    610         { "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
    611         { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
    612         { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
    613         { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
    614         { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
     657        { "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
    615658        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
    616659        { "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
     
    638681                        if( irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN )
    639682                        {
    640                                 irc_reply( irc, 462, ":Only allowed before logging in" );
     683                                irc_send_num( irc, 462, ":Only allowed before logging in" );
    641684                        }
    642685                        else if( irc_commands[i].flags & IRC_CMD_LOGGED_IN && !( irc->status & USTATUS_LOGGED_IN ) )
    643686                        {
    644                                 irc_reply( irc, 451, ":Register first" );
     687                                irc_send_num( irc, 451, ":Register first" );
    645688                        }
    646689                        else if( irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr( irc->umode, 'o' ) )
    647690                        {
    648                                 irc_reply( irc, 481, ":Permission denied - You're not an IRC operator" );
     691                                irc_send_num( irc, 481, ":Permission denied - You're not an IRC operator" );
    649692                        }
    650693                        else if( n_arg < irc_commands[i].required_parameters )
    651694                        {
    652                                 irc_reply( irc, 461, "%s :Need more parameters", cmd[0] );
     695                                irc_send_num( irc, 461, "%s :Need more parameters", cmd[0] );
    653696                        }
    654697                        else if( irc_commands[i].flags & IRC_CMD_TO_MASTER )
     
    667710       
    668711        if( irc->status >= USTATUS_LOGGED_IN )
    669                 irc_reply( irc, 421, "%s :Unknown command", cmd[0] );
    670 }
     712                irc_send_num( irc, 421, "%s :Unknown command", cmd[0] );
     713}
  • lib/Makefile

    ref14a83 r2945c6f  
    88
    99-include ../Makefile.settings
     10ifdef SRCDIR
     11SRCDIR := $(SRCDIR)lib/
     12endif
    1013
    1114# [SH] Program variables
    12 objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
     15objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
    1316
    1417CFLAGS += -Wall
     
    3740$(objects): ../Makefile.settings Makefile
    3841
    39 $(objects): %.o: %.c
     42$(objects): %.o: $(SRCDIR)%.c
    4043        @echo '*' Compiling $<
    4144        @$(CC) -c $(CFLAGS) $< -o $@
  • lib/events.h

    ref14a83 r2945c6f  
    4848   the given callback function. */
    4949typedef enum {
    50         GAIM_INPUT_READ = 1 << 1,
    51         GAIM_INPUT_WRITE = 1 << 2
     50        B_EV_IO_READ = 1 << 0,
     51        B_EV_IO_WRITE = 1 << 1,
     52        B_EV_FLAG_FORCE_ONCE = 1 << 16,
     53        B_EV_FLAG_FORCE_REPEAT = 1 << 17,
    5254} b_input_condition;
    5355typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond);
  • lib/events_glib.c

    ref14a83 r2945c6f  
    4949        b_event_handler function;
    5050        gpointer data;
     51        guint flags;
    5152} GaimIOClosure;
    5253
     
    7677
    7778        if (condition & GAIM_READ_COND)
    78                 gaim_cond |= GAIM_INPUT_READ;
     79                gaim_cond |= B_EV_IO_READ;
    7980        if (condition & GAIM_WRITE_COND)
    80                 gaim_cond |= GAIM_INPUT_WRITE;
     81                gaim_cond |= B_EV_IO_WRITE;
    8182       
    8283        event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data );
     
    8788                event_debug( "Returned FALSE, cancelling.\n" );
    8889       
    89         return st;
     90        if (closure->flags & B_EV_FLAG_FORCE_ONCE)
     91                return FALSE;
     92        else if (closure->flags & B_EV_FLAG_FORCE_REPEAT)
     93                return TRUE;
     94        else
     95                return st;
    9096}
    9197
     
    105111        closure->function = function;
    106112        closure->data = data;
     113        closure->flags = condition;
    107114       
    108         if (condition & GAIM_INPUT_READ)
     115        if (condition & B_EV_IO_READ)
    109116                cond |= GAIM_READ_COND;
    110         if (condition & GAIM_INPUT_WRITE)
     117        if (condition & B_EV_IO_WRITE)
    111118                cond |= GAIM_WRITE_COND;
    112119       
  • lib/events_libevent.c

    ref14a83 r2945c6f  
    6060        b_event_handler function;
    6161        void *data;
     62        guint flags;
    6263};
    6364
     
    126127        {
    127128                if( event & EV_READ )
    128                         cond |= GAIM_INPUT_READ;
     129                        cond |= B_EV_IO_READ;
    129130                if( event & EV_WRITE )
    130                         cond |= GAIM_INPUT_WRITE;
     131                        cond |= B_EV_IO_WRITE;
    131132        }
    132133       
     
    150151                return;
    151152        }
    152         else if( !st )
     153        else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) )
    153154        {
    154155                event_debug( "Handler returned FALSE: " );
     
    174175        event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
    175176       
    176         if( ( condition & GAIM_INPUT_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
    177             ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
     177        if( ( condition & B_EV_IO_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) ||
     178            ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
    178179        {
    179180                /* We'll stick with this libevent entry, but give it a new BitlBee id. */
     
    198199               
    199200                out_cond = EV_PERSIST;
    200                 if( condition & GAIM_INPUT_READ )
     201                if( condition & B_EV_IO_READ )
    201202                        out_cond |= EV_READ;
    202                 if( condition & GAIM_INPUT_WRITE )
     203                if( condition & B_EV_IO_WRITE )
    203204                        out_cond |= EV_WRITE;
    204205               
     
    212213        }
    213214       
     215        b_ev->flags = condition;
    214216        g_hash_table_insert( id_hash, &b_ev->id, b_ev );
    215217        return b_ev->id;
  • lib/http_client.c

    ref14a83 r2945c6f  
    151151        if( req->bytes_written < req->request_length )
    152152                req->inpa = b_input_add( source,
    153                                          req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE,
     153                                         req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE,
    154154                                         http_connected, req );
    155155        else
    156                 req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
     156                req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req );
    157157       
    158158        return FALSE;
     
    236236        /* There will be more! */
    237237        req->inpa = b_input_add( req->fd,
    238                                  req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ,
     238                                 req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ,
    239239